RaspberryPiでスマートホーム 〜JavascriptでMQTTを使って指令を送る〜
2019/09/12 |
|
2017/12/18 |
|
前回AWS IoTを使いMQTTでJSONデータをラズパイ側で受信、USB赤外線リモートアドバンスを使って照明の操作ができるようになりました。今度はJSONデータを組み立てて送信する側を作成したいと思います。
いろんなデバイスから送信したいので、HTMLとJavascriptを使って送信することにしました。これならWebSocketに対応したブラウザがあればどの端末からでも操作できるようになります。
準備
IAMユーザーの作成
まずAWS IoTにMQTTでデータを送信するために、今回のアプリ(送信側)専用のIAMユーザーをAWSで作成しておきます。AWSにログインし、IAMの画面に行きます。
画面左のメニューで”ユーザー”を選択し、”ユーザーを追加”ボタンを押します。
”ユーザー名”はここでは”homeapp”としています。今回はJavascriptからの送信に使うので”プログラムによるアクセス”にチェックを入れます。”次のステップ:アクセス権限”ボタンを押して、次へ進みます。
”既存のポリシーを直接アタッチ”を選択し、検索窓で”iot”を検索して出てきた”AWSIoTDataAccess”を選択します。”次のステップ:タグ”を押して次へ。
今回はタグは必要ないので、”次のステップ:確認”を押して進みます。
確認画面です。問題なければ”ユーザーの作成”を押します。
これで、JavaScriptからAWSIoTへアクセスする用のユーザーができました。
”アクセスキーID”と”表示”を押すと表示される”シークレットアクセスキー”は、Javascriptからの呼び出し時に使用するのでコピーしておきます。
作ってみる
JavascriptでAWS IoTに接続
AWSを呼び出す場合はURLを署名する必要があり、以下を参考にします。
”ウェブアプリケーションでの WebSocket プロトコルの使用”に書かれているコードを”SigV4Utils.js”という名前で保存して、呼び出して使えるようにしておきます。以下のコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
function SigV4Utils() {} SigV4Utils.getSignatureKey = function (key, date, region, service) { var kDate = AWS.util.crypto.hmac('AWS4' + key, date, 'buffer'); var kRegion = AWS.util.crypto.hmac(kDate, region, 'buffer'); var kService = AWS.util.crypto.hmac(kRegion, service, 'buffer'); var kCredentials = AWS.util.crypto.hmac(kService, 'aws4_request', 'buffer'); return kCredentials; }; SigV4Utils.getSignedUrl = function(host, region, credentials) { var datetime = AWS.util.date.iso8601(new Date()).replace(/[:\-]|\.\d{3}/g, ''); var date = datetime.substr(0, 8); var method = 'GET'; var protocol = 'wss'; var uri = '/mqtt'; var service = 'iotdevicegateway'; var algorithm = 'AWS4-HMAC-SHA256'; var credentialScope = date + '/' + region + '/' + service + '/' + 'aws4_request'; var canonicalQuerystring = 'X-Amz-Algorithm=' + algorithm; canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(credentials.accessKeyId + '/' + credentialScope); canonicalQuerystring += '&X-Amz-Date=' + datetime; canonicalQuerystring += '&X-Amz-SignedHeaders=host'; var canonicalHeaders = 'host:' + host + '\n'; var payloadHash = AWS.util.crypto.sha256('', 'hex') var canonicalRequest = method + '\n' + uri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash; var stringToSign = algorithm + '\n' + datetime + '\n' + credentialScope + '\n' + AWS.util.crypto.sha256(canonicalRequest, 'hex'); var signingKey = SigV4Utils.getSignatureKey(credentials.secretAccessKey, date, region, service); var signature = AWS.util.crypto.hmac(signingKey, stringToSign, 'hex'); canonicalQuerystring += '&X-Amz-Signature=' + signature; if (credentials.sessionToken) { canonicalQuerystring += '&X-Amz-Security-Token=' + encodeURIComponent(credentials.sessionToken); } var requestUrl = protocol + '://' + host + uri + '?' + canonicalQuerystring; return requestUrl; }; |
これを使ってアクセス先のURLを組み立て署名、そこへWebSocketで接続、JSONを送信という流れになります。
以下のスクリプトを”homeapp.js”という名前で保存します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
var credentials = {}; credentials.accessKeyId = "(アクセスキー)"; credentials.secretAccessKey = "(シークレットキー)"; var requestUrl = SigV4Utils.getSignedUrl('(エンドポイント)', '(リージョン)', credentials); var clientId = 'homeapp'; var topic = 'homeapp/to'; var client = new Paho.Client(requestUrl, clientId); var connectOptions = { useSSL: true, timeout: 3, mqttVersion: 4, onSuccess: onConnect }; client.connect(connectOptions); function buildPayload(resource, command, parameters) { payload = { "resource" : resource, "command" : command, "parameters" : parameters }; return JSON.stringify(payload); } function send(ele) { var resource = ele.getAttribute("data-resource"); var command = ele.getAttribute("data-command"); var parameters = ele.value; var payload = buildPayload(resource, command, parameters); console.log('topic:"' + topic + '" payload:' + payload); var msg = new Paho.Message(payload); msg.destinationName = topic; client.send(msg); } function onConnect() { console.log("connected"); } |
(アクセスキー)、(シークレットキー)、(エンドポイント)、(リージョン)は自分のものに置き換えておきます。
(アクセスキー)、(シークレットキー)は先ほどIAMでユーザーを作成したときに最後に表示された”アクセスキーID”と”シークレットアクセスキー”です。
(エンドポイント)は前回ラズパイを登録したときに指定されたものです。
AWSのサービス一覧で”iot”で検索、”IoT Core”選択
IoT Coreの画面で”管理”→”モノ”をクリック、登録したラズパイを選択。
”操作”をクリック、表示されたHTTPSがエンドポイントになります。
(リージョン)は東京なら”ap-northeast-1”になります。
次に画面を作ります。
AWS IoTのJavascript用SDKと、前回も使用したPaho MQTTのJavascript版を使用します。いずれもCDN経由で使ってます。また先ほど保存した2つのスクリプトも読み込ませます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.1.0/paho-mqtt.js" type="text/javascript"></script> <script src="https://sdk.amazonaws.com/js/aws-sdk-2.527.0.min.js" type="text/javascript"></script> <script src="SigV4Utils.js" type="text/javascript"></script> <script src="homeapp.js" type="text/javascript"></script> </head> <body> 【照明】<br> 電源: <input type="radio" name="light_power" value="on" data-resource="light" data-command="power" onclick="javascript:send(this)">オン <input type="radio" name="light_power" value="off" data-resource="light" data-command="power" onclick="javascript:send(this)">オフ </body> </head> |
とりあえずこんな感じです。何も飾ってないのでデザインはちょっとださいですが…
各コンポーネントがクリックされた時に送信するようにしています。
テスト
上記3つのファイル
- SigV4Utils.js
- homeapp.js
- homeapp.html
を同じフォルダに入れ、homeapp.htmlを読み込ませてテストします。ローカルでJavascriptを動かせない場合は、apacheでも使ってローカルにWebサーバーを立てましょう。
照明をつけて見ます。つきました!
前回使ったAWSのテスト用ページも使うと便利です。トピックに接続して監視しておくと、メッセージが来ると表示されるはずです。
もしメッセージが来なければ送信側の問題、メッセージが来てるのに照明がつかなければメッセージが間違っている、もしくはラズパイ側の問題、と原因の切り分けが簡単になります。
運用する
これでブラウザから家の照明を操作できるようになりました。
レンタルサーバーに置けば外からでも…
このHTMLとスクリプトをレンタルサーバーにでも置いてアクセスできるようにしておけばどこからでも操作できるようになります。もちろんBasic認証などパスワードかけとかないとやばいですけど…
ただ、外からだと本当に照明がついたかどうかはわからないんですよね…赤外線リモコンだと命令送るだけでステータスが取れません。WEBカメラで見れるようにするとかしないといけないですね。
あとページを表示したままの場合、スリープするとWebSocketの接続が切れるので、ページをリロードしてやる必要があります。
感想
接続先はAWSIoTになるのですが、接続時にURLを署名しないといけないのが面倒ですね。と、いってもライブラリ呼び出すだけではあるんですが…
前回のコマンドラインで照明がついたり消えたりも感動しましたが、今回のWEBページをクリックしたら照明がつく、というのもなかなかのものです。他にも赤外線リモコンの家電があるので随時追加していこうと思います。
追記
PCの電源のオン・オフができるようになりました。
- アイリスオーヤマ(IRIS OHYAMA)