良きプロダクトをつくるために、「プロダクトマネジメントツール」を選んでみた

この記事は Akerun Advent Calendar 2019 - Qiita の19日目の記事です。

こんにちは、PhotosynthのPM(プロダクトマネージャー/PdM)のaraayaです。

夏にPM1人目として入社しました。元々私はビジネスやデザインの志向性が強いのですが、最近はエンジニア/QAと話す機会が多く、視野の広がりを感じる今日この頃です。

さて最近、PM界隈で「”プロダクトマネジメントツール”を使ってプロダクト企画・管理をしている」という話をちらほら耳にするようになりました。 CRM/SFA/MAの領域でSalesforceが、Tech領域でGithubが普及したように、 PMの業務や役割にあわせた専用ツールが導入されはじめたようです。

プロダクトマネジメントツール(以下PMツール)ってどれがいいのかなー、とネットをざっと探したのですが、日本語の記事があまり多くなかったので、この機会に実際に触ってみました。

*ツールの使い方をまとめた記事ではありません。

ツールの検討を通してPMの業務の話をまとめているので、プロダクトづくりに興味がある方に読んでいただけたら嬉しいです。

目次

PMの業務で解決したいこと

PMツールを検討するにあたり、はじめにPMの業務で解決したいことを整理します。

1:顧客要望の集約

多くの新機能やプロダクトのタネは、顧客のアイデアやフィードバックから生まれます。 ただ、目先の顧客の声に捕われ、真意を汲み取れないと、結局目的と異なる・使われない機能を作ってしまうそして炎上することも・・・。

顧客の要望を収集する作業はとても大事です。

* 「顧客の規模・業界・環境・用途・担当者は?」「どんな課題を真に解決したいか?」

* 要望度の優先度づけ

といった情報を上手に管理したいなーと思っています。

また、社内のSlackや顧客管理ツール、SNSに挙がった情報がフローで流れてしまうので 自動的に一元集約する場所があると助かります。

顧客要望の集約をPMツールが助けてくれるなら、ツール要件は下記が理想です。

  •  要望顧客・背景の詳細管理
  •  優先度管理
  •  外部チャット/顧客管理ツール連携

2:ロードマップの管理

1の顧客要望や優先度付けに基づいて、将来的なプロダクト/機能リリース計画を「ロードマップ」にまとめるのも、PMの重要な業務のひとつです。

現状spreadsheetを使ってロードマップを作成していますが、表形式では情報量が少なく、表現の幅が狭いことが課題です。

例えばエンジニアは「機能スコープと優先度」を、ビジネスサイドは「事業的なKPIとプロダクトの関係性」を知りたいと考えています。関係者によって必要な情報が異なるので、情報をそれぞれにわかりやすく整理できると嬉しいなーと思います。

ロードマップの管理をPMツールが助けてくれるなら、ツール要件は下記が理想です。

  •  プロダクトと機能スコープの紐付け
  •  プロダクトとKPIの紐付け
  •  目的ごとのビューの切替え

3:タスク管理

2のロードマップから、プロダクトや機能のスコープを定義したら、次に開発単位ごとにタスク(チケット)を切る作業が発生します。

(CS)→PM→デザイナー→エンジニア→QAとチケットが渡りますが、このタスク進捗を別のツールやMTGで追いかけるのは、多大な工数が発生します。

(エンジニア氏:せっかくチケットを更新したのに、MTG出るの・・・!)

タスク管理の自動化をPMツールが助けてくれるなら、ツール要件は下記が理想です。

  • PMツール上でのスケジュール/カンバン管理
  • 外部タスク管理ツール連携

プロダクトマネジメントツールを選んでみた

さて、本題に移ります。 顧客要望の集約・ロードマップ管理・タスク管理、3つが揃っているツールを探すと 海外の「Productboard」「Craft」が良さそうなことがわかりました。

その他、ProdPadやAha!なども機能が豊富なようですが、UIの観点で今回は対象外としました。 国内でもギルトワークスが、何をつくるべきかを構想するサービス「GuildHub」を19年7月にβ版立ち上げたようで、今後の進化に注目です。

Productboard

狭義のプロダクトマネジメントツールのシェア、おそらくNo1のサービスです。 Microsoft, Zendeskなどで採用されているようです。

www.productboard.com

大きく4つの機能を持っています。

① Insight(顧客インサイト

顧客要望やインサイトを管理するページです。 「どの顧客からの要望か」「顧客ニーズの強さ」を細かく管理できます。

WEBのアンケートフォームを共有することもできますし、 Slackと連携すれば、Slackの特定チャンネルから直接要望を拾ってくることもできます。

f:id:photosynth-inc:20191220034015p:plain
Productboard - Insight
f:id:photosynth-inc:20191220001259p:plain
Productboard - Insight

② Features(機能要件/タスク管理)

プロダクトの機能を管理するためのページです。 機能に対して、「定量的な目標」と「定性的な目標」を設定することができます。 また、「セグメント」(例 SMB/Enterprise)や「優先度」を設定することも可能です。

f:id:photosynth-inc:20191220001757p:plain
Productboard - Features

③ Roadmap(ロードマップ)

ロードマップを可視化・管理する機能です。 左のタブでロードマップの表示方法を切り替えることができ、リスト・カンバン・スケジュール表示など目的に応じて見せ方を変えることができます。

f:id:photosynth-inc:20191219234531p:plain
Productboard - Roadmap

Portal(プロダクトの要件管理)

最後は、プロダクトづくりに欠かせない、簡易的なドキュメントツールの機能です。 仕様を書いたり、他の誰かがコメントを記載したり、画像を貼ってUI画面ごとの要件を記載するなどが可能です。

Craft

イスラエルのテルアビブ発のスタートアップで、ShopifyやUberもこのサービスを利用しています。 スケジュール管理からドキュメント管理まで、自由度が高いことが特徴です。

f:id:photosynth-inc:20191220034531p:plain
Craft
craft.io

① Strategy(プロダクト戦略)

アプリやサービスのコアとなる戦略や背景を管理するページです。 自由なドキュメントを作成することができ、「ペルソナの設定」「ゴール目標の設定」「競合との差別化や優位性」「ターゲットユーザー」などを定義することができます。

f:id:photosynth-inc:20191219235124p:plain
Craft - Strategy

② Planning(タスク / ロードマップ管理)

プロダクトの機能を管理するためのページです。タスクをスケジュール/カンバン/リストから、好みのビューで表示できます。 また、開発・QA・ビジネス・PM・UI/UXなどそれぞれのグループ単位で人をアサインできます。

③ Discovery(顧客要望)

顧客の要望を管理するページです。 要望ごとに「ポジティブ」「ネガティブ」「UI改善」「新機能」「不具合」などでタグ付けができます。 こちらもSlackや顧客管理サービスと連携が可能です。

f:id:photosynth-inc:20191220002857p:plain
Craft - Discovery

Productboard vs Craft の比較

PMの業務で解決したい要件に対して、星取表を作成してみました。

Productboardは使い勝手が良い反面、価格が高いのは難点です。

f:id:photosynth-inc:20191220041637p:plain
Productboard vs craft 比較

いずれも、サービス連携ツールZapier経由で、例えばHubspotに繋ぐことも可能です。

JIRAやTrelloなどタスク管理ツールの更新と完全同期できるのも素晴らしいですね!

*今回紹介できなかった観点でも、まだまだ多くの機能があります。

所感

ProductboardとCraftともに、機能的には、既存のツールを駆使し代替できる機能も多くあります。

一方、「プロダクトマネジメント」に特化したテンプレートが豊富なので、 つい企画フェーズで落としがちな観点を漏れなく整理し、関係者に共有するには便利です。

また、これ1つでプロダクトづくりに必要なことはすべて揃うので、 スモールチームで0→1のサービスをつくるには、使い勝手が良さそうです。

今後もツールの検討を通して、理想的な業務の話をする機会につながると嬉しいです。 (ツールはあくまで手段なので、結果的に導入しないとしても。)

さいごに

ストレスフリーなお仕事には、イケてるツールが欠かせないよね!

というわけで、Photosynthではエンジニア/QAだけではなく 一緒に仕事をしてくれるPM/UIUXデザイナーを探しています。


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proの購入はこちらから akerun.com

【保存版】ハードもソフトもできるフルスタックエンジニアになりたい(2019年入門編)

こちらは Akerun Advent Calendar 2019 - Qiita の記事です。 今日はyuyakmが担当します。

みんなでフルスタックエンジニアを目指そうぜ

突然ですが、フルスタックエンジニアって憧れますよね。 フルスタックとまではいかなくても、別の領域の開発に挑戦してみたい!他のレイヤーのエンジニアとより深い議論をしたい!ひとりでいろいろと触って試作してみたい! という方も多いのではないでしょうか。弊社でも他の技術に挑戦したいという声が増えてきました。

フルスタックの定義はプロジェクトによって違うと思いますが、IoTではハードもソフトもやる必要があるので大変です。

フォトシンスではIoT製品を開発しており、ハードウェアからソフトウェア、インフラまで幅広い専門性を持つエンジニアが在籍しています。 そこで、それぞれのエンジニアに"とりあえずやってみる"には何から手をつければ良いか聞いてみました。

※これからドヤ顔で紹介していきますが、以下は他のメンバーのコメントを多く含みます

ハードウェア系

メカ(機構)

Fusion360

www.autodesk.co.jp 無料で始められる3D CADです。応力解析やCGのレンダリング、工作機械を制御するための機能もあります。 これでAkerunも設計しています。

Fusion 360 マスターズガイド ベーシック編 改訂第2版

著者の一人の藤村祐爾さんはAutodeskの中の人でユーザーとしてもFusion360マスターです。

プロトラブズ

www.protolabs.co.jp 切削加工で試作部品をつくることができます。WEBですぐに見積りが出て便利。射出成形(金型)の見積りも自動で出ます。 見積り結果は金額だけでなく、抜き勾配や肉厚等で成形に問題が出そうなところを教えてくれるので勉強にもなります。 金型の保存期間が無期限ってすごい。

MONOist「甚さん」シリーズ

monoist.atmarkit.co.jp メカ設計は何をするのかわからない業務の筆頭ですが、OJT方式で考えるべきことを教えてもらえます。

基礎から学ぶ 射出成形の不良対策

基礎から学ぶ 射出成形の不良対策

基礎から学ぶ 射出成形の不良対策

  • 作者:本間 精一
  • 発売日: 2011/04/26
  • メディア: 単行本(ソフトカバー)
樹脂で量産部品をつくるなら射出成形のことも学ぶ必要があります。 成形不良にはどういったものがあるのか、それをどうのような設計で対策することができるのかを学びましょう。 知ってしまうと世の中の製品でけっこう気になるところが出てきます。

エレキ(回路)

KiCAD

www.kicad-pcb.org フリーで使えて回路設計からアートワーク設計までできます。 EagleやQuadceptもおすすめです。

トランジスタ技術SPECIAL

www.cqpub.co.jp KiCADを使った回路設計やパターン設計のやり方、基板の発注方法を解説しています。 トラ技は他にも沢山あります。

TINA-TI

www.tij.co.jp TINAは回路シミュレーションで有名ですが回路設計ソフトとしても使えます。 丸文株式会社の紹介ページ

始める電子回路

startelc.com 初心者向けに回路図の読み方から解説しています。

ソフトウェア系

共通

サルでもわかるGit入門

backlog.com Gitがわからないと開発に参加できません!共同作業するなら覚えましょう。 未経験の年長者におすすめするとタイトルが邪魔をして読んでくれないことがあるらしい。

obniz

obniz.io ブラウザでプログラミングすればWi-Fi経由ですぐに動く!回路の知識がなくてもセンサーで値を取ってモーターを動かすことができます。 スマートロックの試作はこれがあればすぐにできちゃいますね。

ドットインストール

dotinstall.com

沢山のテーマを動画で解説しているので初心者にもわかりやすいです。例えば、iOS, Androidアプリを初めて作る人は公式のデベロッパーサイトだけを見るよりも挫折せずに進められるのでは。

IT専科 UML入門

www.itsenka.com 設計書を読む・書くためにUML(Unified Modeling Language)の知識は必須です。

完全マスターしたい人のためのイーサネットTCP/IP入門

OSI参照モデル(特にTCP/IPプロトコル)の理解が下地にあると理解が深まります。

DevDocs

devdocs.io 様々なプログラミング言語やライブラリ、ツールのドキュメントを検索して確認することができます。 カバーしている範囲が広く、オフライン状態で読める機能も便利です。

FW(組み込み)

組込み現場の「C」プログラミング基礎からわかる徹底入門

組み込みCの入門におすすめです。書き方やモジュール設計の話がわりとしっかり書いています。

qiita.com ステートマシンについても理解しておきましょう。フォトシンスのエンジニアブログも参考になりますね!

WEB

WEB DEVELOPER ROADMAP - 2019

github.com Webエンジニアは分野がだだっ広いので、こういうのを見てやりたいことや補完したいことを見つけましょう。

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう

railstutorial.jp

統計WEB

bellcurve.jp データ分析するためには統計を学びましょう。

インフラエンジニアの教科書

インフラエンジニアの教科書

インフラエンジニアの教科書

インフラ初学向けにおすすめです。インフラ系を目指す方は、Webアプリケーション一般を起点にして学んでいくのが良いと思います。技術分野としてはネットワーク、サーバ管理、運用監視 など低いレイヤから比較的高いところまでを網羅できると良いです。

AWS認定資格試験テキスト

クラウドサービスの理解も必須です。AWSを学ぶ場合、資格向けテキストは網羅性が高いです。

スマートフォンアプリ

Swift UI

developer.apple.com 宣言的UIフレームワークを使うパラダイムシフトが起きているので、これからiOS開発を覚える方はSwift UIをおすすめします。Xcodeのストーリーボードを使わずにSwiftで直接UIをつくることができます。

Flutter

flutter.dev Googleが開発している宣言的UIフレームワークで、iOSAndroidどちらにも対応しています。

Android Developers

developer.android.com 日本語が増えてきましたが、英語の情報も多いです。今後のためにも英語を勉強しながら頑張りましょう。

Google Play | APP DOJO

events.withgoogle.com Google Play チームが定期的にセミナーやワークショップを行っているので参加してみましょう。

さいごに

つまみ食いするだけでも楽しい年末年始が過ごせそうですね!一つだけでも世界が広がると思います。 親切に教えてくれるメンバーでとても助かりました。 それにしても、これ全部できる人は存在するのでしょうか。

なんでこれがないんだよ!初心者には足りないよ!というツッコミは歓迎します。よろしければコメントをください。

株式会社フォトシンスでは様々なレイヤーに挑戦したいエンジニアを募集しています! 興味を持っていただける方はぜひご連絡ください。 hrmos.co

Akerun入退室管理システムもぜひよろしくお願いします。 akerun.com

専用アプリはもう要らない?LINE BotからAkerunを操作する(その1)

この記事は Akerun Advent Calendar 2019 - Qiita の17日目の記事です。

はじめに

はじめまして、先月Photosynthに入社したばかりのWebエンジニアのBunと申します。主にApp(iOS)開発を担当しています。 最近、Akerun Developersのメンテも行なっていますので、 Akerun API + いろんなサービスと連携して行こうと思います。

まず第一弾として、LINE Botと連携してみました。 LINEを使っている人は多いので、普段使っているLINEだけでAkerunの施錠解錠ができれば、めっちゃ楽になりますね。 以前LINE Botのグループ機能を使ったBot(LINE BOT AWARDS グループトーク部門賞)を作成した経験もあって、グループで使えるAkerun Botにする予定でしたが、内容が長くなってしまうので、2回に分けました。

  • LINE Bot単体からAkerunを操作する。(今回の記事)
  • LINE Botグループで機能を使って、グループ内のユーザーなら誰でもAkerunを操作できる。(後日公開予定)
    ※こちらの方が、ユーザーをグループに追加するだけで、簡単にAkerunを施錠解錠できるので、専用アプリが要らなくなるかも?!

その2はこちらです。

akerun.hateblo.jp

ちなみに14日目の記事ではAlexaと連携した内容が書かれていますので、興味がある方はぜひご参照ください。

akerun.hateblo.jp

Akerun APIを活用できれば、いろいろ面白いサービスを提供できそうですね。

akerun.hateblo.jp

事前準備

まず、AkerunProの接続、必要なAPIの利用申請、アカウント作成、サーバーサイトの準備を行います。

Akerun Proを接続

Akerun Remote経由でAkerun Proを操作できるように設定します。詳細についてAkerunサポートページをご参照ください。

Akerun APIを申請

Akerun Developersのお申し込みページからAkerun API利用申請を行います。
※Akerun ProとAkerun 入退室管理システムご利用いただいている場合のみお申込み可能です。

developers.akerun.com

Akerun APIのアクセストークンを作成

Akerun Developersの認証ページの手順に従って、アクセストークンを作成します。

アクセストークンのスコープを設定

Akerun Managerの「連携アプリ」ページでスコープを設定する必要があります。 詳細についてAkerun Developersのスコープ設定ページをご参照ください。

LINE Providerを作成

LINEアカウントがあれば、LINE Developersにログイン(あるいはアカウント作成)可能です。

developers.line.biz

  • Provider作成

LINE DevelopersのコンソールからProviderを作成します。

f:id:photosynth-inc:20191215221648p:plain
LINE Provider 作成

サーバーサイト(heroku)を準備

LINE Bot用のサーバーが必要ですので、手軽に使えるPaaSのherokuで実装したいと思います。
※無料範囲内の場合、一定時間アクセスが無いとスリープモードに入り、次回アクセス時少し時間が掛かります。

jp.heroku.com

アカウントがない場合、上記herokuからアカウントを作成します。

  • Messaging API channel作成

3つのチャンネルの中からBot用のMessaging API channelを作成します。

f:id:photosynth-inc:20191215221740p:plain
Messaging API channel 作成

botアプリを作成

heroku-cliをインストール&ログイン

heroku-cliからダウンロード/インストールするか、以下のコマンドでダウンロード/インストールします。 loginすると、herokuのログインページが表示されるので、ログインします。
※必要な環境変数は後ほど設定します。

brew tap heroku/brew && brew install heroku
heroku login

f:id:photosynth-inc:20191216145452p:plain
heroku login

heroku appを作成

mkdir akerun-bot-demo
cd akerun-bot-demo

git init

heroku create akerun-bot-demo
Creating ⬢ akerun-bot-demo... done
https://akerun-bot-demo.herokuapp.com/ | https://git.heroku.com/akerun-bot-demo.git

git remote -v
heroku  https://git.heroku.com/akerun-bot-demo.git (fetch)
heroku  https://git.heroku.com/akerun-bot-demo.git (push)

※git initとheroku createの順番が逆になると、remoteにリポジトリが設定されないので、手動で設定します。(git remote add heroku {app url})

SDK/Libsイントール

pythonで実装するので、以下のLINE SDKと必要なlibsをインストールします。
pythonは事前にインストールします。pyenvなどを使うと楽になります。

pip install line-bot-sdk
pip install flask
pip install requests
pip install gunicorn

botを実装

  • ベース部分実装

オウム返しのメインファイルbot.pyを作成します。

from flask import Flask, request, abort

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import *

app = Flask(__name__)

line_channel_id = os.getenv('LINE_CHANNEL_ID')
line_channel_secret = os.getenv('LINE_CHANNEL_SECRET')
line_channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN')
akerun_api_access_token = os.getenv('AKERUN_API_ACCESS_TOKEN')

line_bot_api = LineBotApi(line_channel_access_token)
handler = WebhookHandler(line_channel_secret)

# test
@app.route("/")
def hello_bot():
    return "akerun bot!"

@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)

    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))


if __name__ == "__main__":
    port = int(os.getenv("LINE_BOT_PORT", '5000'))
    app.run(host="0.0.0.0", port=port)
  • Procfile作成(プログラムの実行方法を定義)
vi Procfile
web: gunicorn bot:app --log-file=-
  • Requirements.txt作成(インストールするライブラリの記載)
pip freeze > requirements.txt
  • runtime.txt作成(Pythonのバージョンを記載

pythonバージョンは自分の環境に合わせてください。

vi runtime.txt
python-3.8.0
  • 環境設定

heroku上のbotアプリのSettingsページで環境変数を設定します。
※一旦任意の文字列を設定し、下記手順でAkerun API、LINE関連のトークンが取得できたら、更新します。

f:id:photosynth-inc:20191216141000p:plain
環境変数設定

以下のherokuコマンドからも設定可能です。

heroku config:set LINE_CHANNEL_SECRET="" --app {app name}
heroku config:set LINE_CHANNEL_ACCESS_TOKEN="" --app {app name}
heroku config:set LIN_BOT_PORT=5000 --app {app name}

ローカル環境で確認する場合、以下のコマンドで環境変数を登録しておきます。

export LINE_CHANNEL_SECRET=xxx
export LINE_CHANNEL_ACCESS_TOKEN=xxx
export LIN_BOT_PORT=5000

ターミナルを再度開く時、上記の設定が消えてしまうので、必要な場合、~/.bash_profile(Mac)などに設定します。

vi ~/.bash_profile
export LINE_CHANNEL_SECRET=xxx
export LINE_CHANNEL_ACCESS_TOKEN=xxx
export LIN_BOT_PORT=5000
source ~/.bash_profile

デプロイ

変更内容をコミットし、herokuへpushすると、自動的にherokuへデプロイされます。 https://akerun-bot-demo.herokuapp.com/にアクセスし、テストメッセージ「akerun bot!」が表示されればOKです。

git add .
git commit -m "first commit"
git push heroku master

ローカル動作確認環境作成

コードを変更する度にherokuへデプロイするのは面倒なので、ngrokを使用して、localhostを外部に公開できるようにします。

  • ngrokにログイン(或いはサインイン)し、ngrokをダウンロードする。
  • ダウンロードしたngrok(zip)を任意のディレクトリに解答する。
  • ngrokにログイン後、トークンをコピーし、以下の実行する
cd ngrok解凍ディレクトリ
./ngrok authtoken xxx
  • ローカルでbot.pyを実行し、外部に公開する。
    ※LINE Botのwebhookに設定するので、外部公開URL(https://xxxx.ngrok.io)をメモしておく。
python bot.py
ngrok http 5000

ngrok by @inconshreveable                                       (Ctrl+C to quit)
                                                                                
Session Status                online                                            
Account                       xxx (Plan: Free)                         
Version                       2.3.35                                            
Region                        United States (us)                                
Web Interface                 http://127.0.0.1:4040                             
Forwarding                    http://xxx.ngrok.io -> http://localhost:5000 
Forwarding                    https://xxxx.ngrok.io -> http://localhost:5000

Akerun施錠解錠処理を実装

まず、施錠解錠するための最小限の実装を行います。Akerun APIの詳細についてAkerun Developersをご参照ください。

# 組織一覧を取得
def akerun_bot_get_organizations(akerun_token):
    ak_url = 'https://api.akerun.com/v3/organizations'
    ak_headers = {'Authorization': 'Bearer ' + akerun_token}
    return akerun_bot_api(ak_url, ak_headers)

# 組織詳細情報を取得
def akerun_bot_get_organization_info(akerun_token, org_id):
    ak_url = 'https://api.akerun.com/v3/organizations/{}'.format(org_id)
    ak_headers = {'Authorization': 'Bearer ' + akerun_token}
    return akerun_bot_api(ak_url, ak_headers)

# 組織に属するAkerunの一覧を取得
def akerun_bot_get_akeruns(akerun_token, org_id):
    ak_url = 'https://api.akerun.com/v3/organizations/{}/akeruns'.format(org_id)
    ak_headers = {'Authorization': 'Bearer ' + akerun_token}
    return akerun_bot_api(ak_url, ak_headers)

# 施錠リクエストを送信
def akerun_bot_lock(akerun_token, org_id, akerun_id):
    ak_url = 'https://api.akerun.com/v3/organizations/{}/akeruns/{}/jobs/lock'.format(org_id, akerun_id)
    ak_headers = {'Authorization': 'Bearer ' + akerun_token}
    return akerun_bot_api(ak_url, ak_headers)

# 施錠結果を取得
def akerun_bot_lock_state(akerun_token, org_id, job_id):
    ak_url = 'https://api.akerun.com/v3/organizations/{}/jobs/lock/{}'.format(org_id, job_id)
    ak_headers = {'Authorization': 'Bearer ' + akerun_token}
    return akerun_bot_api(ak_url, ak_headers)

# 解錠リクエストを送信
def akerun_bot_unlock(akerun_token, org_id, akerun_id):
    ak_url = 'https://api.akerun.com/v3/organizations/{}/akeruns/{}/jobs/unlock'.format(org_id, akerun_id)
    ak_headers = {'Authorization': 'Bearer ' + akerun_token}
    return akerun_bot_api(ak_url, ak_headers)

# 解錠結果を取得
def akerun_bot_unlock_state(akerun_token, org_id, job_id):
    ak_url = 'https://api.akerun.com/v3/organizations/{}/jobs/unlock/{}'.format(org_id, job_id)
    ak_headers = {'Authorization': 'Bearer ' + akerun_token}
    return akerun_bot_api(ak_url, ak_headers)

# Akerun APIをCall
def akerun_bot_api(url, headers):
    obj_session = Session()
    obj_request = Request("POST",
                          url,
                          headers=headers
                          )
    obj_prepped = obj_session.prepare_request(obj_request)
    obj_response = obj_session.send(obj_prepped,
                                    verify=True,
                                    timeout=60
                                    )
    print('status_code:' + str(obj_response.status_code))
    print('obj_response.text:' + obj_response.text)
    response_dict = json.loads(obj_response.text)
    print(response_dict)
    #return response_dict.job.id
    return response_dict['job']['id']

LINE Botを設定

Messaging API channelを作成

LINE DevelopersのProviderにアクセスし、channelを作成します。

f:id:photosynth-inc:20191216145002p:plain
Messaging API channel作成

f:id:photosynth-inc:20191216011412p:plain
Messaging API channel選択

必須項目を入力し、channelを作成します。

f:id:photosynth-inc:20191216011233p:plain
Messaging API channel作成完了

secretとtokenを取得

作成したchannelの設定ページからChannel secretとChannel access tokenを取得します。

f:id:photosynth-inc:20191216012406p:plain
Basic setting ページ

f:id:photosynth-inc:20191216012318p:plain
Channel secret

f:id:photosynth-inc:20191216012454p:plain
Messaging API ページ

f:id:photosynth-inc:20191216012342p:plain
Channel access token

webhookを設定

LINEユーザーのメッセージ(Akerun操作コマンド)をbot(herokuアプリ)に送信するために、Messaging API ページでwebhookを設定します。

  • herokuアプリへのwebhook

    f:id:photosynth-inc:20191216013603p:plain
    webhook heroku app

  • localhostを環境を使った場合、ngrokのURLを設定

    f:id:photosynth-inc:20191216013744p:plain
    webhook localhost

botの応答設定

ユーザーがメッセージを送信する度に自動的に応答メッセージが返ってくるので、一旦無効にします。

  • 応答メッセージ編集画面に遷移

    f:id:photosynth-inc:20191216102531p:plain
    応答メッセージ編集画面に遷移

  • 応答メッセージをOFF

その他設定はデフォルトのまま(ON)でOKです。

応答メッセージをOFFにする
応答メッセージをOFFにする

リッチメニュー作成

ユーザーが施錠解錠ボタン(画像)を押して、Akerunを施錠解錠できるようにリッチメニューを作成します。
※詳細についてリッチメニューを新規作成するをご参照ください。

  • LINE Official Account Managerにログインし、アカウントリストからアカウント(bot)を選択する

    f:id:photosynth-inc:20191216145244p:plain
    LINE Official Account Managerに遷移し、アカウントを選択

  • リッチメニューを新規作成

リッチメニューを新規作成

  • 基本設定を行う

タイトルと表示期間を設定し、テンプレートを選択します。その他設定はデフォルト設定でOKです。

f:id:photosynth-inc:20191216150818p:plain
基本設定を行う

  • テンプレートを選択

f:id:photosynth-inc:20191216164126p:plain
テンプレートを選択

  • 施錠解錠コマンドを設定

アクションBとDのタイプをテキストに設定し、それぞれ「>lock」、「>unlock」コマンドのテキストを入力します。
※Prefix「>」はその他コマンドと区別する為に設定していますが、無くても問題ありません。

f:id:photosynth-inc:20191216164142p:plain
施錠解錠コマンドを設定

  • 施錠解錠の画像を作成する

「画像作成」画面で施錠解錠の画像をアップロードし、画像の下にテキストを追加します。位置は設定メニューで簡単に調整可能です。

f:id:photosynth-inc:20191216164200p:plain
施錠解錠画像設定

  • 設定を保存し、LINE上で確認する

上記の設定を保存します。LINE Developersに戻って、「Messaging API設定」ページのQRコードからbotを追加します。

f:id:photosynth-inc:20191216144541p:plain
QRコードからbotを追加

以下のメニューが表示されます。

f:id:photosynth-inc:20191217080350p:plain
メニュー

連携する

Akerun APIから施錠解錠の実装とLINE Botの施錠解錠(リッチメニュー)を作成しましたので、LINEから施錠解錠メニュー(画像)を押した時の動作を実装します。 handle_messageに以下の処理を追加します。施錠(>lock)/解錠(>unlock)コマンドを受信したら、それぞれ施錠解錠リクエストを送信します。
※全体コードは別途公開する予定ですが、Akerunの組織ID、AkerunIDはAkerun Developersを参考に取得してください。
※また、リクエストを送信してから施錠解錠結果をポーリングする必要がありますが、簡単にする為、リクエスト直後そのまま結果をユーザーに返しています。

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    reply_msgs = []
    akerun_token = akerun_api_access_token
    org_id = 'xxxx'
    akerun_id = 'xxxx'

    if event.message.type == 'text':
        if event.message.text[0] == cmd_prefix:
            cmd = event.message.text
            if cmd == cmd_prefix + 'lock':
                # 施錠リクエストを送信
                akerun_bot_lock(akerun_token, org_id, job_id)
                reply_msgs.append(TextSendMessage(text=u'施錠'))
            elif cmd == cmd_prefix + 'unlock':
                # 解錠リクエストを送信
                akerun_bot_unlock(akerun_token, org_id, job_id)
                reply_msgs.append(TextSendMessage(text=u'解錠'))
    if len(reply_msgs):
        line_bot_api.reply_message(
            event.reply_token,
            reply_msgs)

施錠

botのメニューからLOCKを押します。少しタイムラグはありますが、施錠できました。

f:id:photosynth-inc:20191216152717p:plain
LOCKコマンド送信

f:id:photosynth-inc:20191216152800j:plain
AkerunPro施錠

解錠

botのメニューからUNLOCKを押します。こちらも少しタイムラグはありますが、解錠できました。

f:id:photosynth-inc:20191216152915p:plain
UNLOCKコマンドを送信

f:id:photosynth-inc:20191216152939j:plain
AkerunPro解錠

まとめ

長文になってしまいましたが、無事LINE BotからAkerunを施錠解錠ができました!

  • Akerun APIを利用するには、申し込み申請が必要で、アクセストークンを発行するまで結構手間がかかります。これについては改善していきたいと思います。アクセストークンさえ発行できれば、API自体はシンプルで使いやすいのではないかと思います。
  • 内容からも分かると思いますが、LINEの設定項目がたくさんあります。ただ、操作しやすいUIになっているので、それ程難しくはないですね。
  • その2ではLINE Botグループ機能を入れて、Botアプリを完成させたいと思います。Akerun APIのアクセストークン、組織ID、AkerunIDについて、ハードコーディングではなく、Bot上で選択出来るようにします。
  • その2が完了後、ソースコードgithubに上げる予定です。実際のBotも期間限定で公開してみようかと思います。

株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proの購入はこちらから akerun.com

組み込みでも使える?シリアライザ FlatBuffers を使ってみた

この記事はAkerun Advent Calendar 16日目の記事です。

今回はファームウェアエンジニア いとう が担当です。今回は FlatBuffers と呼ばれるGoogleが開発したシリアライザを使ってみたので書いていこうと思います。以下「組み込み」のワードが出てきますが想定するのはプア目なマイコン(Coretex-M系とか)で組み込みLinuxとかの富豪環境ではないので悪しからず。

リアライゼーションについて

組み込みエンジニア(リソースプア勢)のみなさん、何らかのシリアライザって使ったことありますか?ここでいうシリアライザはオブジェクトデータを通信や永続化に使えるようにするやつです。Webの世界でシリアライズフォーマットとして一番よく使われるのはJSONでしょうか?最近はProtocol Buffersもよく聞きます。

Akerun Proの組み込みファームウェアの中でも色々な場面でデータのシリアライズを行なっています。

  • FlashROMに格納する不揮発データの永続化
  • BLE通信データのペイロード
  • ・・・(そんなになかった)

ですが、Akerun Pro本体のファームウェアでは今現在、特定のシリアライズのライブラリを使用してはいません。組み込みの世界で、シリアライズのライブラリがあまり使われない理由としては色々あると思います。

  • データ内容によってサイズが異なると困る
    • JSONは数値データでも文字列で表現されているため桁が増えるとデータサイズも増えます
    • 「FlashROMに履歴情報は**件保持すること」を保証するためにはデータサイズは固定出ないといけません
  • リソースがシビアなのでリッチなシリアライズフォーマットが取り扱えない
    • 非力なCPUでは処理速度も重要
    • メモリを動的確保されると辛い
  • あんまりC言語用のライブラリがない
  • #pragam pack(1)の構造体でよくね?

個人的に組み込みでも使えるシリアライズフォーマットを探していたのですが、いまいちしっくり来るものがありませんでした。そんな中2014年にGoogleからリリースされたFlatBuffersはその謳い文句から組み込みで使えるんじゃないかと注目しており、今回機会があったので使ってみました。FlatBuffersの謳い文句次の通りです。

  • Access to serialized data without parsing/unpacking(シリアライズされたデータへのパースなしでのアクセス)
  • Memory efficiency and speed(高メモリ効率&速度)
  • Flexible(柔軟)
  • Tiny code footprint (小さいコードフットプリント)
  • Strongly typed(厳密な型)
  • Convenient to use (使いやすい)
  • Cross platform code with no dependencies(クロスプラットフォーム

FlatBuffersのメリット

そもそも、なぜわざわざ特定のシリアライズフォーマットを採用するのでしょうか?私の認識では次のようなメリットがあります。

  1. スキーマを介して複数デバイス間でデータ構造を共有できる
  2. データの前方・後方互換性を(頑張れば)保てる

利点の1番目は明らかですね。Akerun Proは様々なデバイスと直接通信しています。また間接的ですがゲートウェイバイスを通じてクラウド上のサーバーとも通信しています。

FlatBuffersスキーマを作成し、一元的にこれらの多数のデバイス間通信データのデータ構造を自動生成できるとすれば、開発側に大きなメリットです。また、FlatBuffersは複数のプログラミング言語に対応しており、特定の言語に縛られることがないためDX的にプラスです。公式サイトによると FlatBuffersC++, C#, C, Go, Java, JavaScript, Lobster, Lua, TypeScript, PHP, Python, Rustに対応しているそうです。

2番目の利点についてはうまく説明できないのですが・・・

例えば設定情報をFlashROMに保存しているデバイスで、ファームウェアバージョンアップに伴い保存するデータフィールドを増やしたとしましょう。その状態で下記のようなケースに対応できるコードを問題なく書けるでしょうか?

FlatBuffersではProtocol Buffersよりは柔軟性にかけますが少し頑張ればデータ構造の前方&後方互換性を保ったままデータフォーマットを拡張できます。

FlatBuffers を Zephyr RTOS で使ってみた

早速、FlatBuffersを使ってみましょう。今回は Zephyr RTOSのプロジェクトに組み込む方法を説明します。 FlatBuffersは通常はflatcというスキーマコンパイラを使いますが、PureなC言語だけはFlatCCを使います。仲間外れです。

f:id:photosynth-inc:20191213194908p:plain

必要なものは次の通り

1. FlatCCのビルド

スキーマコンパイラ自体をビルドします。これはビルドするホスト環境でのビルドです。必要なツール類のインストールが終わっていれば公式Readmeの通りにすれば問題なくビルドできると思います。こちらではZephyr RTOSのビルド環境コンテナに組み込ん使用してみました。

ENV FLATCC_ARCHIVE_BASE_URL "https://github.com/dvidelabs/flatcc/archive/"
ENV FLATCC_ARCHIVE_TAG "master"

RUN cd /tmp && wget -O flatcc.tar.gz "${FLATCC_ARCHIVE_BASE_URL}/${FLATCC_ARCHIVE_TAG}.tar.gz" \
    && tar xzf flatcc.tar.gz \
    && mv flatcc-* flatcc \
    && cd flatcc \
    && ./scripts/initbuild.sh make \
    && ./scripts/build.sh \
    && cp ./bin/flatcc /usr/local/bin/ \
    && rm -rf /tmp/* /var/tmp/*

2. ランタイムライブラリのビルド

Zephyrの外部ライブラリとしてビルドさせるため、flatccのディレクトリをZEPHYR_MODULESで指定されている場所に展開し、次のようなファイルを作成しましょう。flatcもCMakeを使っているので相性はバッチリです。

  • flatcc/zephyr/CMakeLists.txt
  • flatcc/zephyr/Kconfig
  • flatcc/zephyr/module.yml

CMakeLists.txt

option(FLATCC_TEST "enable tests" OFF)
option(FLATCC_PORTABLE "include extra headers for compilers that do not support certain C11 features" OFF)
option(FLATCC_GNU_POSIX_MEMALIGN "use posix_memalign on gnu systems also when C11 is configured" ON)
option(FLATCC_RTONLY "enable build of runtime library only" ON)
option(FLATCC_INSTALL "enable build of runtime library only" OFF)
option(FLATCC_COVERAGE "enable coverage" OFF)
option(FLATCC_DEBUG_VERIFY "assert on verify failure in runtime lib" OFF)
option(FLATCC_TRACE_VERIFY "assert on verify failure in runtime lib" OFF)
option(FLATCC_REFLECTION "generation of binary flatbuffer schema files" OFF)
option(FLATCC_NATIVE_OPTIM "use machine native optimizations like SSE 4.2" OFF)
option(FLATCC_FAST_DOUBLE "faster but slightly incorrect floating point parser (json)" OFF)
option(FLATCC_ALLOW_WERROR "allow -Werror to be configured" ON)
option(FLATCC_IGNORE_CONST_COND "silence const condition warnings" OFF)

if(CONFIG_ENABLE_FLATCC)

zephyr_include_directories(../include)

zephyr_sources(
    ../src/runtime/builder.c
    ../src/runtime/emitter.c
    ../src/runtime/refmap.c
    ../src/runtime/verifier.c
    ../src/runtime/json_parser.c
    ../src/runtime/json_printer.c
)

endif()

Kconfig

config ENABLE_FLATCC
    bool "Enable the flatbuffer runtime lib"
    select REQUIRES_FULL_LIBC

LIBCは確かmath.hのマクロ定義か何かのためだけに必要だったのでどうにかしたら外せるかも・・・

module.yml

build:
  cmake: zephyr/

3. スキーマコンパイル

次にスキーマコンパイルです。Zephyr RTOSプロジェクトの中のCMakeLists.txtからinclude()する形で作成しています。

set(file_gen "_builder.h" "_reader.h" )
list(TRANSFORM file_gen PREPEND flatbuffers_common)
list(TRANSFORM file_gen PREPEND ${PROJECT_BINARY_DIR}/include/generated/)
list(APPEND output_file_list ${file_gen})

add_custom_command(
  OUTPUT ${file_gen}
  COMMAND flatcc -a -o ${PROJECT_BINARY_DIR}/include/generated/
  COMMENT "Generate ${flatbuffers_common}"
)

list(TRANSFORM flatbuffer_include_dirs PREPEND "-I")

foreach(fbs_name IN LISTS flatbuffer_schema_list)

  get_filename_component(output_file_name ${fbs_name} NAME_WE) 
  set(file_gen "_builder.h" "_json_parser.h" "_json_printer.h" "_reader.h" "_verifier.h")
  list(TRANSFORM file_gen PREPEND ${output_file_name})
  list(TRANSFORM file_gen PREPEND ${PROJECT_BINARY_DIR}/include/generated/)

  set(dep_file_name "${fbs_name}.d" )
  set(dep_file ${PROJECT_BINARY_DIR}/include/generated/${output_file_name})

  add_custom_command(
      OUTPUT  ${file_gen}
      COMMAND flatcc -wvr --json -o ${PROJECT_BINARY_DIR}/include/generated/  ${flatbuffer_include_dirs} ${fbs_name}
      DEPENDS ${flatbuffer_schema_list}
      COMMENT "Generate from ${fbs_name}"
  )
  list(APPEND output_file_list ${file_gen})
endforeach(fbs_name)


add_custom_target(
    flatcc_target
    DEPENDS ${output_file_list}
)

add_dependencies(app flatcc_target)
list(APPEND app_module_include_dirs ${PROJECT_BINARY_DIR}/include/)

これで下のように flatbuffer_schema_list という名前のリストにスキーマを追加していけばビルド時に./zephyr/include/generated/の下にヘッダーファイルが生成されます。

list(APPEND flatbuffer_schema_list 
    ${CMAKE_CURRENT_SOURCE_DIR}/monster.fbs
)

あとは公式サイトのチュートリアルのC言語バージョンをみて勉強してみましょう〜

パフォーマンス測定

やりたかったのですが厳密な測定は時間が足りなかったので今回はパス。時間ができればやります。公式でのベンチマークはこちら

ざっくり感想↓

  • データサイズ
    • 当たり前ですがC言語の構造体のサイズと比べて+αされる
    • けどJSONとかと比べると十分小さい
    • 小さいデータ構造であれば(+前方互換性無視すれば)BLEの20バイトパケットでも意外と実用的
  • シリアライズ速度
    • デコードは速いのでコマンドパースとかには向いている
    • エンコードも不揮発データ保存用とかには十分速い
    • 内部のステートマシンになげるイベント生成にも使ってみたら流石に遅かった

所感

正直なところ、もう少し使い込まないと結論出せませんが十分使える感触を得ています。スキーマに日本語コメントでコンパイルNGなるのでそこはどうにかならないかと検討してます。

意外といいなと思ったのはスキーマで定義したenumとかは自動的に次のようなメンバーの名前を返す関数が定義されルためprintfデバッグが捗りました。スキーマ定義しておけばCの構造体→JSON化の機能もあるので文字列化のためだけでも使ってもいいかな〜

namespace Thumbturn;
enum Direction : ubyte {
cw = 0,
ccw = 1,
}
  • 自動生成されたコード
static inline const char *Thumbturn_Direction_name(Thumbturn_Direction_enum_t value)
{
    switch (value) {
    case Thumbturn_Direction_cw: return "cw";
    case Thumbturn_Direction_ccw: return "ccw";
    default: return "";
    }
}

static inline int Thumbturn_Direction_is_known_value(Thumbturn_Direction_enum_t value)
{
    switch (value) {
    case Thumbturn_Direction_cw: return 1;
    case Thumbturn_Direction_ccw: return 1;
    default: return 0;
    }
}

おわり

FlatBuffers の記事は色々ありますがPureなC言語から使うのはあまり見かけないので参考になったら幸いです!

・・・

株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proの購入はこちらから akerun.com

UNIXとAkerunの浅くて深い関係

この記事は Akerun Advent Calendar 2019 - Qiita の15日目の記事です。

どうも、RoRエンジニアのdaikw - Qiita です。

今日は、考え方次第でなんにでも通じるものってあるよね、という話をします。ポエムです。

UNIX哲学

エンジニアリングに関する思想の一つに、UNIX哲学というのがあります。

UNIX哲学とは、ソフトウェア開発に関する文化的な規範と哲学的アプローチのまとまりであり、UNIX OSの開発者たちの経験に基づくものとされている。

UNIX哲学は、それを策定する協会がある訳でもなく、UNIXを生み出した人(ケン・トンプソンら)が言い出したものでもありません。 色々な人が適当なことを語っている感じがあります。 それでも今だに引用されたりするのは、Mike Gancarzというエンジニアが書いたこちらの書籍のおかげかもしれません。

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

  • 作者:Mike Gancarz
  • 発売日: 2001/02/01
  • メディア: 単行本

GancarzによるUNIX哲学は、特に重要な9のhard tenetと、比較的重要でない10のsoft tenetからなります。

抜粋して紹介すると、

hard tenets:

  • 小さいものは美しい 小さいものは、大きい物にない利点がいくつもある。小さいもの同士なら、簡単に独特の便利な方法で組み合わせることができる
  • 一つのプログラムにはーつのことをうまくやらせる 一つのことに集中することで、プログラムに不要な部分をなくせる。不要な部分があると、実行速度が遅くなり、不必要に複雑になり、融通が効かなくなる。

soft tenets:

  • 同時に考える たいていの仕事は、いくつかの小さい部分に分けられる。これらの小さい部分を同時に実行すると、大きな一つの仕事にかかるのと同じ時間でより多くの仕事ができる。
  • 部分の総和は全体よりも大きい 小さい部品を集めて大きなApplicationを作る方が、大規模プログラムを単体で構成するよりも柔軟性に富む。

僕が特に重要だなと感じるのは、二つめの「一つのプログラムには一つのことをうまくやらせる」というtenetです。

一つのことをうまくやる

一つのことをうまくやる、というのは本当に重要です。

UNIXコマンドはどれも小さく、それ自体の機能はシンプルに作られています。

ソフトウェアの文脈では、「高凝集疎結合」と呼ばれたり、スケールが大きくなると「マイクロサービス」と呼ばれたりもします。要は取り回しやすく、影響範囲が限定されわかりやすい、ということです。

Google Searchは良い例で、「検索」という機能に特化することで他の追随を許さぬ結果を産みました。

これは実はソフトウェアに限ったことではありません。機械設計の分野には「一部品一機能」という言葉があります。

3-3 工数に関する設変事例:一部品一機能と一部品多機能
 3-3-1 事例:一部品多機能でダブルトラブル発生
 3-3-2 事例:従来の失敗事例集で技術力は向上しない
 3-3-3 事例:一部品二機能で低コスト化設計
 3-3-4 事例:共締めに一部品二機能を駆使する
 3-3-5 一部品一機能、二機能、多機能と共締めのまとめ

解決したい問題に対して、過度な複雑さを受け入れると、そこには地獄が広がっています。機械設計でも同じなんですね。

AkerunとUNIX哲学

わざわざUNIX哲学の話をしたのは、Akerun(に限らずIoT全般)のアーキテクチャも、そういう側面があるなと感じたからです。

一つの商品は、ある一つの問題解決に特化して作られています。

それらが組み合わさって、より高次の問題解決(入退室管理・勤怠連携など)が可能になります。部分の総和は全体よりも大きいわけですね。

Akerun本体

とにかくサムターンを回すことに特化しています。一つのことをうまくやっています。

硬いサムターンでもしっかり回すことができるように、たくさんの電池とトルクの強いモータを積んでいますし、 ある程度の設置ずれなども吸収できるように機構が工夫されています。

一方で外部とのやりとりはBLE通信しかサポートしておらず、外部ネットワークに繋がる能力は持ちません。 本体からするとドアの開閉も知ったことではないし、カードをかざされても「…?」という感じです。

Remote (ゲートウェイ)

Akerunをインターネット経由で操作するには、このRemoteが必要になります。

ゲートウェイとしての機能に特化しており、サーバと本体の間をつなぐためのあらゆる機能をサポートしています。

Akerun本体の設定変更や開閉リクエストは、サーバで一旦受け取ってjobqueとして処理しますが、このjobを取りに行くのもRemoteの仕事の一つです。

ドアセンサ・カードリーダ

ドアセンサはドアの開閉を検知する機能に特化していて、それ以上でもそれ以下でもありません。 それゆえに、開閉検知を確実に行えるようになっています。一つのことをうまくやっています。

本体内蔵だと、キャリブレーションやら何やらで大変みたいですが、、()

カードリーダーも同じですね。一つのことをうまくやっているわけです。

最後に

ポエミーな内容になったので、少しwebっぽいコンテンツを、と思い、UNIX的かどうかのチェッカーをサクッと(個人的に)作ってみました。 github.com

create-react-appでTypescript版をscaffoldして、ant-dをUIフレームワークに使用し、firebaseでホスティングしてあります。 unix-phl-checker.web.app

あなたの考え方はUNIX的ですか?(スマホだと表示が崩れるので注意


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proの購入はこちらから akerun.com

Alexaスキル開発 〜男一人暮らしがただいまを言わなきゃいけないこんな世の中じゃ、Akerun〜

この記事は Akerun Advent Calendar 2019 - Qiita の14日目の記事です。

はじめに

はじめまして、今年10月にPhotosynthにWebエンジニアとして入社したt-ikeと申します。 IoTサービス想定してた以上に面白くて毎日楽しみながら仕事してます。ハードが絡むといいですね。 大変な部分もありますがw

本題

みなさんスマートホーム化してますか?

自分も半年前ぐらいから急に普段の生活の無駄を感じるようになり、alexaを使って自宅の家電を徐々にスマートホーム化してます。 照明、エアコン、テレビあたりは声やアプリで操作できるようになりました。

そして、入社を機に、ドッグフーディングとしてAkerunを自宅にとりつけてスマートロックも実現できるようになりました。 携帯(NFCカード)で鍵を開けたり、オートロックにしたり、本当に便利です。

が、人間というのは貪欲な生き物で、慣れるとまた新たに不便だなと感じるポイントがでてきます。

「家帰ってきて、鍵開けてから、”アレクサ、ただいま”っていうのだるいな・・・」

そうです、男の1人暮らしで、家の中で発話をするという行為は基本不要なもので、 何よりも恥ずかしく、エネルギーのかかることなのです・・・!

Akerunを使ったスマートホーム

やりたかったこと

  • 家の鍵が空いた時に、照明やエアコンなどの電源も一緒につける

が、ダメっ・・・・!

大変残念なことに、Akerunでは鍵の開閉を検知してAlexaなどのサービスにプロアクティブに通知する機能は現状提供していないのです。。

なので鍵の開閉をトリガーにはできませんでした。

やったこと

  • alexaアプリの定型アクションで「鍵の解錠・家電スイッチON」をできるようにする

Akerun契機で動かせないなら、Alexa契機で動かそうということで、alexaからakerunを操作できるようにして、定型アクションに組み込んでしまおうというわけです。

Alexa SKill開発

Alexa Skillとは

Amazon Echoなどで受けた発話の内容を解析し、内容に応じた処理を可能にするもの。 天気やニュースを通知する機能もAlexaSkillの一つです。

スマートホームスキル

Alexa Skillにはいくつかの種類がありますが、デバイスを操作するスキルはスマートホームスキルに該当します。

スマートホームスキルと通常のカスタムスキルとの違いは以下になります。

  • ビルトインの発話解析が利用できる(「アレクサ、〇〇つけて」のような発話に対する処理の紐付け(インテント)が不要)
  • 発話だけでなく、アプリでデバイス操作ができる

今回はスマートホームスキルの開発を行いました。

使ってみよう

とりあえず難しい話はおいといて実際に作ったものを使ってみましょう

alexaアプリ

アカウントリンク

最初にAlexaとAkerunのアカウントリンクを行います。

開発中のスキルはアプリ上で開発者のみに表示されます。 公開すると全Alexaユーザが利用できますが、現状はまだその予定はありません。 βテストとして指定ユーザのみに公開することができるので、今はその運用をするつもりです。

f:id:photosynth-inc:20191208125554j:plain

スキルを選択すると有効にするか選べます。

f:id:photosynth-inc:20191208125828j:plain

有効にすると、akerunのログイン画面に遷移します。

f:id:photosynth-inc:20191208125953j:plain

認証が完了すると、AlexaからAkerunの操作を許可するか選択します。

f:id:photosynth-inc:20191208130104j:plain

許可を押すと、アカウントリンク完了の画面が表示されます。

f:id:photosynth-inc:20191208130220j:plain

バイスの検出

アカウントリンク後、デバイス検出画面が表示されます。

f:id:photosynth-inc:20191208130251j:plain

ただ、ここでデバイスを検出しても、見つかりません。。

f:id:photosynth-inc:20191208130439j:plain

f:id:photosynth-inc:20191208130452j:plain

Akerun Managerで発行された認証トークンのスコープ設定を行う必要があります (少し面倒で申し訳ない。。)

アカウント画面の連携アプリタブで発行されたトークンが確認できます。 f:id:photosynth-inc:20191208130603j:plain

そこから以下のスコープを有効にして下さい。

  • アカウント
    • account:organization:read
  • 組織
    • organization:akerun:read
    • organization:akerun:lock
    • organization:akerun:state

組織に複数所属している場合は、検出したいデバイスのある組織のスコープだけ有効にしてください

f:id:photosynth-inc:20191208130629j:plain

f:id:photosynth-inc:20191208130652j:plain

その後、もう一度検出するのですが、その前にAkerun名を「玄関」のような名前に変更しておくことをお勧めします。 f:id:photosynth-inc:20191208131336j:plain

もう一度検出をすすめると、デバイスが検出されるはずです。

もし、画面を閉じてしまった場合は、Alexaに「アレクサ、デバイスを検出して」と話しかけてください。 f:id:photosynth-inc:20191208131313j:plain

Akerun操作

見つかったスイッチデバイスを選ぶと以下のような画面が表示されます。

f:id:photosynth-inc:20191208132007j:plain

各ボタンは以下のような意味を表しています。

  • オフ: 施錠
  • オン: 解錠

ここで疑問に思う人もいると思うのですが、他のスマートロック系のスキルではロックデバイスが検出され、ロック・アンロックと選べます。

なぜ、スイッチデバイスとして検出しているかというと、ロックデバイス定型アクションでアンロックできないからなんです。

(※公式確認済み)

自分の要望はあくまで定型アクションでアンロックすることだったので仕方なくSwitchデバイスで検出をしました。

もし公開するとなった場合にはこの仕様は変わるかもしれません。

そして、あとは定型アクションをつくります。

f:id:photosynth-inc:20191208132618j:plain

こんな感じでアクションを設定すれば、アプリから1ボタンで全アクションが実行できます。

やったぜ。

このスキルでできること

自分はあくまで定型アクションのために実装しましたが、他にもこのスキルによって以下のようなことができます。

  • インターホンで呼び出しがあった時に、「アレクサ、玄関をオンにして」で鍵を開けられるので玄関まで行かなくて良い
  • (オートロック機能を使ってない人は)寝る前に定型アクションに施錠の操作を組み込めば、鍵の閉め忘れ対策ができる

他にも使い道はあるかもしれません。

学び

  • AlexaのアーキテクチャとAlexa SKillの開発方法
  • Lambdaの開発環境構築、Serverless Frameworkを用いたデプロイ方法
  • github actionsを用いた自動デプロイ
  • IoTサービスのアーキテクチャはどうあるべきか
  • VUIのあるべき姿、ユーザにどういうものが受けるか

実益メインで、業務に直接関係はない作業のつもりでしたが、 手を動かしてみると色々課題が出てくるし、最先端の企業が提供するサービスに触れることでベストプラクティス的な部分も学べたのはよかったです。

いつか、ここらへんも余力があればまとめてみたいですね

余談

作ってる途中でAmazon Echo Flexなるものがでていたことを知り、 モーションセンサを定型アクションに組み込めるようになり、 一体自分は何をやっていたのかと感じながら、 心を殺してポチりましたとさ...

今回はこの辺で

最後に、エンジニア募集してます!イケてるIoTサービス一緒に作り上げていきましょう!

hrmos.co

Zephyr RTOS と自動テストで捗るファームウェア開発の話

この記事はAkerun Advent Calendar 13日目の記事です。

今回はファームウェアエンジニア いとう が担当です。

前回からの流れで

前回 の記事でZephyr RTOSのビルドに使える開発環境を作成しました。これをもう少し活用できないでしょうか?ってことで この環境をそのまま CI/CDの実行環境として使ってみます。

TL;DR

できたこと

  • ✅ GitLabと連動した自動ビルド&自動テスト
  • クラウド上で高速かつ単体でも結合でもできるテスト環境

f:id:photosynth-inc:20191213194908p:plain

GitLab CIで自動ビルド

まずは、GitLab CIで自動ビルドをやってみます。GitLab CIには任意のDockerイメージをCIの実行環境として指定できます。ここに前回作成したイメージを指定すると、GitLab runner 側(CI実行サーバー)に各種ツールをインストールしておかなくてもDockerだけでビルドができてしまいます。

Zephyrならではの悩みどころとしては west を使って複数のGitリポジトリでプロジェクトを構成している場合、せっかくGitLab runnerがクローンしてくれた環境を無視して改めてwest init + west updateが必要となってしまいます。これについては今の所解決策は見当たらず、愚直に新しいディレクトリを作成してその中でwest init + west updateをやってます。そのためにssh系のクレデンシャルも必要となりました。ちょうどGitLabにはCIで実行する環境変数を権限のないメンバーから見えなくする機能があるのでこれを使っています。

.gitlab-ci.yml抜粋

image:
  name: odorenotokorono.gitlabnourl.com:5555/hogehoge/dev-env-docker:0.1.1
  entrypoint: [""]

stages:
  - lint
  - build
  - test
  - deploy

before_script:
  - eval $(ssh-agent -s)
  - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
  - mkdir -p ~/.ssh
  - chmod 700 ~/.ssh
  - ssh-keyscan odorenotokorono.gitlabnourl.com >> ~/.ssh/known_hosts
  - chmod 644 ~/.ssh/known_hosts
  - mkdir -p .ccache
  - export CCACHE_BASEDIR=${PWD}
  - export CCACHE_DIR=${PWD}/.ccache
  - mkdir -p .pip
  - export PIPCACHE_DIR=${PWD}/.pip

cache:
  paths:
    - .pip
    - .ccache

variables:
  WEST_DIR: __west__
  BUILD_TYPE: Debug

.extract_west: &extract_west |-
  west init -m ${CI_REPOSITORY_URL} --mr ${CI_COMMIT_SHA} ${WEST_DIR}
  cd ${WEST_DIR}
  west update
  pip3 --cache-dir=${PIPCACHE_DIR} install -r ./zephyr/scripts/requirements.txt
  source ./zephyr/zephyr-env.sh

.branch_param: &branch_param
  BUILD_ARTIFACT_NAME: "${CI_PROJECT_NAME}-${BUILD_BOARD}-${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}"

.tag_param: &tag_param
  BUILD_ARTIFACT_NAME: "${CI_PROJECT_NAME}-${BUILD_BOARD}-${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}"

.generic_artifact: &generic_artifact
  name: "${BUILD_ARTIFACT_NAME}"
  paths:
    - ./*.hex
    - ./*.exe
    - ./debug_files

.generic_build: &generic_build
  stage: build
  script:
    - *extract_west
    - source ./app/VERSION
    - >-
      if [ -z "${EXTRAVERSION}" ]; then
        export VERSION_STRING=${VERSION_MAJOR}.${VERSION_MINOR}.${PATCHLEVEL};
      else
        export VERSION_STRING=${VERSION_MAJOR}.${VERSION_MINOR}.${PATCHLEVEL}-${EXTRAVERSION};
      fi
    - >-
      if [ -z "$CI_COMMIT_TAG" ]; then
        export BUILD_APP_NAME=${CI_PROJECT_NAME}-${BUILD_BOARD}-${CI_COMMIT_REF_SLUG}-${BUILD_TYPE}-${CI_COMMIT_SHORT_SHA};
      else
        export BUILD_APP_NAME=${CI_PROJECT_NAME}-${BUILD_BOARD}-v${VERSION_STRING}-${BUILD_TYPE};
      fi
    - echo ${VERSION_STRING}
    - west build -b ${BUILD_BOARD} -d build_${BUILD_BOARD} ./app -- -D CMAKE_BUILD_TYPE=${BUILD_TYPE}
    - cd ${CI_PROJECT_DIR}
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/${BUILD_KERNEL_NAME}.hex ./${BUILD_APP_NAME}.hex || true
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/${BUILD_KERNEL_NAME}.exe ./${BUILD_APP_NAME}.exe || true
    - mkdir debug_files
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/${BUILD_KERNEL_NAME}* ./debug_files/
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/.config ./debug_files/
  artifacts:
    <<: *generic_artifact
  tags:
    - docker

.foo_model: &foo_model
  BUILD_BOARD: foo_model
  BUILD_KERNEL_NAME: foo_model

build-firmware: 
  <<: *generic_build
  artifacts:
    <<: *generic_artifact
    expire_in: 1 week
  variables:
    <<: *foo_model
    <<: *branch_param
  only:
    - branches

build-firmware-release: 
  <<: *generic_build
  artifacts:
    <<: *generic_artifact
    expire_in: 1 week
  variables:
    <<: *foo_model
    <<: *branch_param
    BUILD_TYPE: Release
  only:
    - branches

少し長めになってますがポイントは3つ

  • キャッシュ

Zephyrのビルドは標準でcchaceによる、コンパイラキャッシュが有効となっています。これにより同じコンパイラオプションであれば再ビルドしても爆速でコンパイルが完了します(クリーンビルドでも)。ですがGitLab-CIのDocker環境は毎回フレッシュな環境になってしまうのでこのcchaceが効きません。このようなキャッシュデータを引き継ぐ機能をGitlab-CIは提供しています。下記抜粋中のcache:\n paths:のところで指定しているディレクトリは別のビルドジョブの時も引き継がれます。設定さえしておけばこのキャッシュはCI実行マシンをまたいで共有できます。

before_script:
  - export CCACHE_BASEDIR=${PWD}
  - export CCACHE_DIR=${PWD}/.ccache
  - export PIPCACHE_DIR=${PWD}/.pip

cache:
  paths:
    - .pip
    - .ccache

同様にpipのダウンロードデータもキャッシュ化してビルドジョブごとに使い回しています。

west使っている部分を抜き出すと次の通り、通常のターミナルで行う処理と変わりません。Release/Debugビルド両方でビルドできるように変数で切り替えを行なっています。ここでビルドに必要なpipパッケージのインストールも行なっています。

##中略
.extract_west: &extract_west |-
  west init -m ${CI_REPOSITORY_URL} --mr ${CI_COMMIT_SHA} ${WEST_DIR}
  cd ${WEST_DIR}
  west update
  pip3 --cache-dir=${PIPCACHE_DIR} install -r ./zephyr/scripts/requirements.txt
  source ./zephyr/zephyr-env.sh
##中略
.generic_build: &generic_build
  stage: build
  script:
##中略
    west build -b ${BUILD_BOARD} -d build_${BUILD_BOARD} ./app -- -D CMAKE_BUILD_TYPE=${BUILD_TYPE}
##中略
  • ビルド結果の保存

さて、ビルドもできたのでビルドでできたファイルをGitLabサーバーに保存しておきましょう。*.elfはもちろん、デバッグ時に必要となりそうなファイルをごそっと取っておくとあとできっと役に立ちます。GitLab-CIではこのようなファイルはアーティファクトと呼ばれています。

##中略

.branch_param: &branch_param
  BUILD_ARTIFACT_NAME: "${CI_PROJECT_NAME}-${BUILD_BOARD}-${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}"

##中略
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/${BUILD_KERNEL_NAME}.hex ./${BUILD_APP_NAME}.hex || true
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/${BUILD_KERNEL_NAME}.exe ./${BUILD_APP_NAME}.exe || true
    - mkdir debug_files
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/${BUILD_KERNEL_NAME}* ./debug_files/
    - mv ${WEST_DIR}/build_${BUILD_BOARD}/zephyr/.config ./debug_files/

##中略
.generic_artifact: &generic_artifact
  name: "${BUILD_ARTIFACT_NAME}"
  paths:
    - ./*.hex
    - ./*.exe
    - ./debug_files

小さなtipsですが生成されるファイルが毎回同じだと作業中に取りちがえる可能性があるため、ブランチの名前とコミットのハッシュの一部をファイル名にしてアーティファクト指定しています。アーティファクトは生存期間も指定できるの少し長めに設定しておけば、いつの間にかバグが入り込んだ時の「どこからバグが入り込んだかチェック」が少しは捗ります。

GitLab CIで自動テスト

自動でビルドができれば次は自動テストをやってみましょう。変更をプッシュするたびにテストが行われてどこも壊れていないことが保証されれば怖いもの無しです。Zephyr にはテストフレームワークが同梱されています。これを使うしかないでしょう。内容の紹介は省きますが普通に使えるやつです。

unit-test:
  stage: test
  script:
    - *extract_west
    - ./zephyr/scripts/sanitycheck -v -i --ninja -T ./app/test
  tags:
    - docker

さて、組み込みで自動テストはどのようにすればいいのでしょうか?私の知っている範囲で組み込みの自動テストは次の3種類にざっくり分類されると思います。

それぞれメリット・デメリットがあると思います。しっかりしたプロジェクトでは複数のテスト種類を実施しているのではないでしょうか?(しっかりしたプロジェクトを寡聞にして知りませんが・・・)Zephyrのテストフレームワークはいずれにも対応しているようです。今回はクラウド上で動作しているGitLab-CIで実施する自動テストなので実機でのテストはなしにするとして、実行スピード重視でホスト環境での単体テストにすべきか?組み合わせテストができるけど遅いエミュレーターを使うのが正解でしょうか・・・?

救世主 native_posix !

先ほどの悩みは native_posix で一気に解決できました。

docs.zephyrproject.org

これはZephyrをホスト側OS(今回はLinux)のアプリケーションとしてビルドしてしまい実行できるようにするBOARDです。ビルドする際にボードの指定に-b native_posixとしてやれば zephyr.elfの代わりに zephyr.exe が生成されます。

  • 実行はネイティブ!
    • ./zephyr.exe で実行できるし速い
  • いろんなカーネルコールも呼べる!
    • マルチスレッドもOK
  • 各種ペリフェラルでテスト可能!
    • 割り込み
    • ハードウェアタイマー
    • Bluetooth!
    • FlashROM
    • その他諸々

これは素晴らしいですね。FlashROMの動作を確かめたところ同一階層にあるファイルに読み書きでき、実機で使うのと同じAPI(NVS)で読み書きができました。経験上ファームウェアのバージョンアップ時に不揮発データのデータ追加などを行うとマイグレーション時に不具合でがちなのでこの辺を重点的にテストできれば安心感が増します。

これをZephyrのテスト環境として使うのは ***/hogehoge/testcase.yamlの対応基板のホワイトリストnative_posixを指定するだけでOKです。unit_testと違いprintk()k_sleep(ms)呼び放題でテストを組めますし実行は速いです。

tests:
  hogehoge.genral:
    platform_whitelist: native_posix
    tags: test_framework

あとはガシガシテストを書いていくだけですね💪!捗る〜

できたこと&やりたいこと

できたこと

  • ✅ GitLabと連動した自動ビルド&自動テスト
  • クラウド上で高速かつ単体でも結合でもできるテスト環境

今後やりたいこと

  • [ ] Raspberry piでGitLab runnerを動かして J-Link経由で実機自動テスト
    • これは時間がなくてやっていないだけでやればできそう
    • プッシュするたびAkerunがガシャガシャなったら楽しそう
  • [ ] Raspberry pi (ARM64) で native_posix を動かしてなおかつBluetoothも動かせればAkerunのテストしたい範囲がほぼほぼカバーできそう
    • GitHubのissueに上がっていたので現状は無理らしい
    • Raspberry piだったら安いしオフィスに転がっているので 10台ぐらい並べて並列自動テストしてみたい
    • Raspberry piだったらI2CやSPIも出ているので、HAL書いてやれば最高の環境じゃないですか?SoC選定前にソフト作れちゃうしデバイス評価もできちゃう

以上です!最後まで読んでいただきありがとうございました〜 👋

・・・

株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proの購入はこちらから akerun.com