展示会で困る事
それは展示会場で担当者が不在だった時ですよね。呼び出したいけどどうすれば?という問題を「あのボタン」で解決しようとした試みです。
こんな感じ
JAWS FESTA 2018 OSAKA でお披露目しました。
こんな感じのSPA (Single Page Application) です。
あのボタン「SORACOM LTE-M Button」を押すと、担当者に Slack のメンションが飛びます。
また、担当者が「OK ...」と Slack 上でメッセージ送信すると「現場急行中!」となる仕組みです。
名前のとなりに状況が表示されてますね。
実装
考え方ですが、担当者を "モノ" として扱い、そのステータス (= Shadow) を MQTT over Websocket でブラウザに表示するという仕組みです。
各担当者は、このように「モノ」扱いです。もちろんソラコムはそういう会社じゃないですよ!便宜上、やむを得ず、しかたなく、断腸の思いでこのような実装になってるだけです!
Shadow はこの通りです。
アーキテクチャ
大きく2系統あります。
1系統目: SORACOM LTE-M Button から Slack へ POST
SORACOM LTE-M Button (= AWS IoT 1-Click) から起動される関数(python3.6)
Slack の Incoming Webhook を利用しています。
メンションを飛ばす方法は SlackのIncoming Webhooksでメンションを飛ばす方法 に助けていただきました。
同時に Shadow を「呼び出し中... (state=calling)
」に update します。これは Websocket を通じてブラウザにリアルタイムでpushされます。
2系統目: Slack から AWS IoT Core の Shadow を更新
Slack から API Gateway を経由して AWS IoT Core の ThingShadow を更新する関数
Slack の Outgoing Webhook を利用しています。
トリガーキーワードに "ok" を指定し、担当者は ok ...
と入れてもらうようにしました。
受けは API Gateway です。Slack からは Content-Type:application/x-www-form-urlencoded
で送信されるので、本来は API Gateway 上で JSON 化したかったのですが、私には難しかったので Lambda proxy としてそのまま Lambda 関数に引き渡し、その関数内(Python3.6)上で urllib.parse.parse_qs(event['body'])
としてパースする軟弱実装です。
その後 Shadow を「現場急行中! (state=running)
」に update します。先ほど同様、ブラウザにリアルタイム push されます。
共通してやってること
一定時間経過したら state
を idle
に更新する関数(python3.6)
Shadow が「現場急行中!」や「呼び出し中...」のままだと格好悪いので、一定期間で「呼び出しOK! (state=idle)
」に戻してあげたかったので、Lambda 関数を作っています。
各 Lambda 関数から呼び出される形のコードです。呼び出し方は後述の TIPS で紹介します。
実装は time.sleep(sec)
を使いました。
→ [11/7] Amazon SQS を使ったプロセス間通信という方法を教えていただきました。今度はそれでやってみよう。
Lambda 関数から Lambda 関数を呼ぶ
Amazon SQS を使った Lambda 関数間の通信の方が良さそうですが、こういう方法もあるよという話です。
import boto3
f = boto3.client("lambda")
f.invoke(FunctionName=...,
InvocationType='Event',
Payload=json.dumps({'key1': 'value1'})
)
InvocationType については AWS LambdaからLambda呼んでハマった話。 / Qiita が参考になりました。
結論 'Event'
が非同期呼び出し 'RequestResponse'
が同期呼び出しです。
MQTT over Websocket による ThingShadow のリアルタイム表示
AWS IoT Core の ThingShadow を MQTT Subscribe して Push ベースで状態を表示する HTML
AWS IoT Core の ThingShadow へのアクセスは AWS Cognito の "認証されていない ID に対してアクセスを有効にする" (Unauth) を利用しています。
- AWS Cognito のリージョンは AWS IoT Core のリージョンと合わせておきましょう。
- Unauth のロールへ "AWSIoTDataAccess" ポリシーを割り当てましょう。
AWS IoT Device SDK から Websocket で接続する方法がわからなかったので、とりあえず Paho の MQTT over Websocket で $aws/things/...
を Subscribe するようにしています。
AWS IoT Core に対する ping
Websocket を使った SPA アプリケーションの起動時に AWS IoT Core のトピックに対して ping を送るようにしました。
これは Shadow である {"state":{"reported":{...}}}
を読み出すのに、ともかく更新が発生する ping を publish をすることで、state
全体が subscribe に振ってくるようにしたものです。
これをやらないと、SPA アプリ起動時に各モノの状態がわからないという状態になります。
が、今から思えば AWS IoT Device SDK の awsIot.thingShadow#get()
を使えばよかったんですよね。
コードを公開します。
- Lambda 関数
- Web ページ (SPA; Single Page Application)
改善ポイント
- 呼び出し不可 (do not disturb) モードを作っても良かったかな
あとがき
さっきまで作ってた。
そのうちコードは公開します。
やっと全部書いたよ。
EoT