AWS
button
SORACOM
awsIoT
あのボタン

SORACOM LTE-M Button と AWS IoT Core の ThingShadow で「呼び出しシステム」

展示会で困る事

それは展示会場で担当者が不在だった時ですよね。呼び出したいけどどうすれば?という問題を「あのボタン」で解決しようとした試みです。

こんな感じ

JAWS FESTA 2018 OSAKA でお披露目しました。
こんな感じのSPA (Single Page Application) です。

あのボタン「SORACOM LTE-M Button」を押すと、担当者に Slack のメンションが飛びます。
また、担当者が「OK ...」と Slack 上でメッセージ送信すると「現場急行中!」となる仕組みです。

web.png

名前のとなりに状況が表示されてますね。

実装

考え方ですが、担当者を "モノ" として扱い、そのステータス (= Shadow) を MQTT over Websocket でブラウザに表示するという仕組みです。

jaws-festa-2018 / architecture

各担当者は、このように「モノ」扱いです。もちろんソラコムはそういう会社じゃないですよ!便宜上、やむを得ず、しかたなく、断腸の思いでこのような実装になってるだけです!

aws-iot-core-things.png

Shadow はこの通りです。

shadow.png

アーキテクチャ

大きく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']) としてパースする軟弱実装です。

image.png

その後 Shadow を「現場急行中! (state=running)」に update します。先ほど同様、ブラウザにリアルタイム push されます。

共通してやってること

一定時間経過したら stateidle に更新する関数(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

web.png

この画面は Vue.jsUIKit です。

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() を使えばよかったんですよね。

コードを公開します。

改善ポイント

  • 呼び出し不可 (do not disturb) モードを作っても良かったかな

あとがき

さっきまで作ってた。
そのうちコードは公開します。

やっと全部書いたよ。

EoT