Alexa対応のセンサーを自作する
Alexa対応センサーは種類が少なすぎる
Alexaに対応したセンサーはいくつかの種類が販売されており、センサーの値の変化を実行条件にした定形アクションを組むことができます。
センサーの種類として、例えば、Echo Flexにそのまま接続できるような人感センサー。
他には、電池で駆動するドアセンサー。
似たような物として、スマホアプリと連動した漏水センサーや温度計などもありますが、現時点ではAlexaの定形アクション実行条件に対応していることが確認できなかったため、自分としては実質「人感センサーとドアセンサーしかAlexaの定形アクション実行条件に対応していない」という認識です。
もっと豊富な種類のセンサーを扱いたい
Alexa対応のセンサーが今後増えていく可能性はありますが、いつ出るともわかりませんし、自分の検知したい機能を有しているかもわかりません。
無いなら作りましょう。
たとえば自分は以前、測距センサーを使って猫がいるかどうかを判定し、IFTTTに対してイベントをトリガーする電子工作を行いました。
moyashipan.hatenablog.com
これをIFTTTを経由せず、Alexaにデバイス登録できるセンサーとして作ってみました。
完成した物
測距センサーで一定の距離内に遮蔽物がある/ないことを検知して、その結果をドアセンサーの閉/開としてAlexaに伝える仕組みを作りました。
M5ATOM Matrixと測距センサで、猫がいる時だけホットカーペットONにするやつができた。
— もやしパン(猫がいます) (@Moyashipan) 2020年10月22日
・起動時にNTPサーバで時刻合わせ
・直接AlexaへChangeReportイベントを投げる
・ボタンを押すとLEDインジケータが非表示になる pic.twitter.com/pKvmi7wM5q
3Dプリンターでケースも作ってみました。
距離センサーとM5ATOM Matrix用ケースができた #m5stack pic.twitter.com/w63ZimuWjx
— もやしパン(猫がいます) (@Moyashipan) 2020年10月27日
作り方
こちらを参考にしました。 qiita.com
必要なことを大きくまとめると以下です。
- Alexa Smart Homeのカスタムスキルを登録する
- Lambdaの関数コードを登録する
- デバイス側からChangeReportを送る
ただし、Lambdaのコードは以下のように変更してあります。
- 今回はContactSensorとして登録する(いつ動きがあったか、ではなく今いるのかどうかで管理したい)
Discover
とAcceptGrant
にだけ反応して、それ以外はChangeReport
イベントをデバイスから送ることにする
コードは記事の最後にまとめて記載しています。
上手くいけば、Alexa上に自分が設定したデバイスが追加されているはずです。
client_id
, client_secret
, refresh_token
が取得できたら、デバイスの準備に移ります。
デバイスの準備
今回は以下を使いました。
- M5ATOM Matrix
- 測距センサー
M5ATOM Matrixの背面にある「33ピン」「5V」「GND」に測距センサーを接続し、後述のコードの所定の箇所を以下の情報で上書きし、書き込みます。
上手くいけば、Alexa上の当デバイスの開閉の値が更新されるのが見れるはずです。
Alexaの画面上だけでなく、距離の度合いがLEDアレイに大きく表示されるので動作確認がしやすくて良いですね。
上手くいかない場合にはPostmanなどを利用して、どの通信が上手くいっていないかを確かめると良いでしょう。
ホットカーペットの準備
今回はセンサーの値に応じてホットカーペットをON/OFFするというねらいなので、スマートプラグでON/OFFされるホットカーペットを準備します。
例えば以下のような製品をつないで、スマートプラグにホットカーペットという名前を付けて、Alexaにデバイス登録するだけです。
これだけでも(=センサーが無くても)声や時刻でON/OFFできるので便利ですね。
定形アクションの設定
センサーとホットカーペットの準備ができたので、最後に定形アクションの設定を行います。
以下のように2つの定形アクションを設定します。
これにより、猫がいる間だけホットカーペットが自動でONになる仕組みが実現できました。
さいごに
スマートホームに対するこだわりが増してくると、既存のセンサーだけでは満足できなくなってきます。
そんな時、自分でセンサーを繋いだデバイスを作れると便利です。
今回は測距センサーを用いましたが、それ以外のセンサーを繋げば、例えば
- 明るくなったら/暗くなったら
- 重くなったら/軽くなったら
- うるさくなったら/静かになったら
といった場合にAlexaに通知・トリガーイベントを送信するデバイスも作れるでしょう。
慣れないうちはAWSの操作でまごつくこともありますが、スマートホームの自由度が増すのでぜひ試してみてください。
Lambda関数コード
exports.handler = function (request, context) { if (request.directive.header.namespace === 'Alexa.Discovery' && request.directive.header.name === 'Discover') { log("DEBUG:", "Alexa.Discovery:", JSON.stringify(request)); handleDiscovery(request, context, ""); } else if (request.directive.header.name === 'AcceptGrant') { log("DEBUG:", "AcceptGrant:", JSON.stringify(request)); // AcceptGrantの内容をlogに出力 var response = { "event": { "header": { "namespace": "Alexa.Authorization", "name": "AcceptGrant.Response", "messageId": request.directive.header.messageId, "payloadVersion": "3" }, "payload": {} } }; context.succeed(response); } else { log("DEBUG:", "Other request:", JSON.stringify(request)); log("DEBUG:", "Other context:", JSON.stringify(context)); } function handleDiscovery(request, context) { var payload = { "endpoints": [ { "endpointId": "sensor-001", "manufacturerName": "MOYASHIPAN Cat Carpet Sensor", "friendlyName": "ホットカーペット猫センサー", "description": "ホットカーペットに猫が乗っているかどうかを検出するセンサーです", "displayCategories": ["CONTACT_SENSOR"], "cookie": { "key1": "このエンドポイントのキーと値のペアです", "key2": "複数のエントリーがある場合があります", "key3": "参照目的で使用します", "key4": "現在のエンドポイントの状態を維持するためには使わないでください" }, "capabilities": [ { "type": "AlexaInterface", "interface": "Alexa", "version": "3" }, { "type": "AlexaInterface", "interface": "Alexa.ContactSensor", "version": "3", "properties": { "supported": [ { "name": "detectionState" } ], "proactivelyReported": true, "retrievable": true } }, { "type": "AlexaInterface", "interface": "Alexa.EndpointHealth", "version": "3", "properties": { "supported": [ { "name": "connectivity" } ], "proactivelyReported": true, "retrievable": true } } ] } ] }; var header = request.directive.header; header.name = "Discover.Response"; log("DEBUG", "Discovery Response:", JSON.stringify({ header: header, payload: payload })); context.succeed({ event: { header: header, payload: payload } }); } function log(message, message1, message2) { console.log(message + message1 + message2); } };