身の回りの困りごと
我が家のスマートロックがちょっとスマートじゃないこと
我が家のスマートロック事情
- Echo Dot 第三世代
- SESAME3 * 2 (ドアの鍵がダブルロックなので2つ必要)
- SESAME bot * 2 (マンションの入り口のオートロック解除のために)
- SESAME WiFiモジュール
なぜSESAMEを使うのか
- 製品が全体的に安い 圧倒的に安い
- ダブルロックでSESAMEも2つ必要だしマンションの入り口のオートロック解除用にbotも2つ必要なので値段は大事
- APIが公開されている
しかし。。。
SESAMEはまだ完璧ではない
ギリギリ族の私にとって鍵を開ける時間も閉める時間も惜しいのです
早起きは無理なのです
具体的に何に困っているのかというと
1. Alexa経由でのSESAMEの解錠がスマートじゃない
公式のAlexaスキルでSESAMEを解錠しようとするとパスコードを要求されるため鍵を開けるのに2回指示しないといけない
しかも、我が家はダブルロックでSESAMEが2つあるのでAlexaに4回指示する必要がある
私「アレクサ、『上の鍵』を開けて」
アレクサ「『上の鍵』の確認コードはなんですか?」
私「xxxx」
アレクサ「ロック解除しています。お待ちください」
鍵「ウィーンガチャン」
私「アレクサ、『下の鍵』を開けて」
アレクサ「『下の鍵』の確認コードはなんですか?」
私「yyyy」
アレクサ「ロック解除しています。お待ちください」
鍵「ウィーンガチャン」
なんてめんどくさいんだ!!!
2. SESAMEのオートロックは秒数設定しかできないのでスマートじゃない
設定秒数が短すぎると荷物を受け取ってる間に施錠されてしまったり
設定秒数が長すぎるとドアが閉まってから施錠されるまで待たされたり
かといって本当の意味でのオートロック機能がついてるスマートロックは高い。。。
理想を追い求めてみる
ゴール1. Alexaへ1回の指示だけで2つの鍵を解錠できること
ゴール2. ドアが閉まったら鍵が閉まること
Alexaへ1回の指示だけで2つの鍵を解錠できるようにする
具体的にどうするかと言うと
Alexa → IFTTT → GAS → SESAME API
という流れで解錠できるようにする
1. GAS → SESAME API
の連携
1-1. SESAMEのAPI利用に必要なパラメータとGASのエンドポイント認証用のハッシュ値を用意する
- SESAMEの
uuid
とsecretKey
とapiKey
を取得する(参考)- 今だとドキュメントに記載されているAPIKEYを取得できる管理画面から
uuid
やsecretKey
も結構簡単に取得できるみたい
- 今だとドキュメントに記載されているAPIKEYを取得できる管理画面から
- 任意の文字列をsha512でハッシュ化し、GASのエンドポイントで認証するためのハッシュ値を作る
- Macならターミナルから
shasum
で簡単に生成できます(サンプルなのでくれぐれもこのハッシュ値は使わないように。。。。) - ここでは行いませんがストレッチングなどもお好みで
$ echo -n 'oreno_password_daze' | shasum -a 512 40fc44138fa114b4c7a604052e8e0f7f105c1123ab7b8410870ade930b87857f0c339e52f24f8d76265cacfaddbdce7cd8dd2cbfce5633c742f5e1aa7d4bcb79 -
- Macならターミナルから
1-2. 秘匿情報をGASのスクリプトプロパティに設定する
先ほど用意したapiKeyなどは秘匿情報なのでソースに書かず以下のような形でスクリプトプロパティに設定する(プロジェクトの設定の中)
API_KEY: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
DEVICES: [{ "uuid": "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA", "secretKey": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}, { "uuid": "BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB", "secretKey": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" }]
PASSWORD_DIGEST: 40fc44138fa114b4c7a604052e8e0f7f105c1123ab7b8410870ade930b87857f0c339e52f24f8d76265cacfaddbdce7cd8dd2cbfce5633c742f5e1aa7d4bcb79
1-3. スクリプトプロパティから秘匿情報を取得する処理を書く
DEVICES
はオブジェクトの配列で扱うためにJSONで保存しているのでパースします
const passwordDigest = PropertiesService.getScriptProperties().getProperty('PASSWORD_DIGEST');
const apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');
const devices = JSON.parse(PropertiesService.getScriptProperties().getProperty('DEVICES'));
1-4. GASでAES-CMAC署名
できるようにする
-
SESAMEのAPIを叩くためには
AES-CMAC署名
が必要だがGASそのままではできないのでこちらを参考にしてcryptojs-extention
からコードを頂く
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(u,p)....割愛
/*
* The MIT License
*
* (MIT)Copyright (c) 2015 artjomb
*/
!function(t){var r;r=t.hasOwnProperty(....割愛
1-5. 認証とSESAMEのAPIを叩く処理を書く
- GASでのハッシュ化の参考リンク
- 署名処理は引き続きこちらを参考にさせて頂きました
function doPost(e) {
const params = JSON.parse(e.postData.getDataAsString());
const encodedUserName = Utilities.base64Encode(params.userName, Utilities.Charset.UTF_8);
const action = { toggle: 88, lock: 82, unlock: 83 }[params.action];
if (isAuthed(params.password)) {
UrlFetchApp.fetchAll(
devices.map(device => ({
url: `https://app.candyhouse.co/api/sesame2/${device.uuid}/cmd`,
method : "post",
payload: JSON.stringify({ cmd: action, history: encodedUserName, sign: generateCmacSign(device.secretKey) }),
headers: { 'x-api-key': apiKey },
}))
);
}
return ContentService.createTextOutput(JSON.stringify({ result: 'ok' }));
}
function isAuthed(inputPassword) {
raw = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_512, inputPassword, Utilities.Charset.UTF_8);
// https://qiita.com/tatataichi/items/7e3a27e1b69200d3fd86#%E3%82%B3%E3%83%BC%E3%83%89%E5%86%85%E5%AE%B9
let inputPasswordDigest = ''; //ハッシュ値の文字列
let numHash = 0; //ハッシュ値の数値
for (i = 0; i < raw.length; i++) {
numHash = raw[i];
if (numHash < 0) { //ゼロ未満だったら
numHash += 256; //マイナスの除去
}
if (numHash.toString(16).length == 1) {
inputPasswordDigest += '0'; //1桁なら0をくっつける
}
inputPasswordDigest += numHash.toString(16); //16進数表記の文字列に変換
}
return passwordDigest === inputPasswordDigest;
}
// https://zmzlz.blogspot.com/2021/06/sesame3-sesame-os2-gas-web-api.html
function generateCmacSign(secKey) {
const date = Math.floor(Date.now() / 1000);
const dateDate = new DataView(new ArrayBuffer(4));
dateDate.setUint32(0, date, true);
const msg = dateDate.getUint32(0).toString(16).slice(2, 8);
const hex = CryptoJS.enc.Hex.parse;
return CryptoJS.CMAC(hex(secKey), hex(msg)).toString();
}
1-6. ウェブアプリとしてデプロイする
あとは右上の「デプロイ」から「ウェブアプリ」としてデプロイする
デプロイ後に発行されるこんな感じのURLをメモっておく
https://script.google.com/macros/s/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/exec
2. IFTTT → GAS
の設定
2-1. 右上の Create
からAppletの作成
2-2. If This
を設定
2-3. Amazon Alexa
を選択
2-4. Say a specific phrase
を選択
2-5. Alexaアカウント連携して鍵を開ける時のフレーズを設定
2-6. Then That
を設定
2-7. Webhooks
を選択
2-8. Make a web request
を選択
2-9. web requestの設定
-
URL
はGASをデプロイした時に発行されたURL (https://script.google.com/macros/s/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/exec
) -
Method
はPOST
-
Content Type
はapplication/json
-
Body
は{ "password": "oreno_password_daze", "userName": "alexa_ifttt", "action": "unlock" }
-
password
の値にはcredentials.gs
に定義したpasswordDigest
の作成時に使った(ハッシュ化前の)文字列 -
userName
はSESAMEのアプリの操作履歴に表示されるので自身がわかりやすい文字列を -
action
は今回は解錠が目的なのでunlock
で
-
こんな感じで Create action
2-10. あとはContinue
, Finish
で設定完了
Applet Title
は適当に任意の名前で
3. Alexa → IFTTT
の設定
3-1. アプリからAlexaの提携アクションを新規作成する
3-2. 定型アクション名を決める
ここの名前もお好みで適当に
3-3. 実行条件を設定する
音声
を選択してフレーズを決める
3-4. アクションを設定する
IFTTT
を選択してさきほど作成したアプレットを選択する
これでAlexa経由での解錠の問題は解決!
「アレクサ、解放してくれ!」の1回で鍵が2つとも開く!
ドアが閉まったら鍵が閉まるようにする
1. SwitchBot開閉センサとSwithBot Hub Miniを購入・設置しAlexaと連携させる
Alexaとの連携はこちらの公式の記事を参考に
2. SESAMEもAlexaと連携させる
こちらも公式の記事を参考に
3. Alexaアプリで定型アクションを設定する
3-1. 定型アクション名を設定
3-2. 実行条件を設定
開閉センサの「閉」をトリガーに設定する
3-3. アクションを設定
SESAMEの施錠をアクションに設定する
我が家は鍵が2つあるので施錠のアクションを2つ設定する
これで扉が閉まるとSESAMEも施錠されるようになりました!
便利便利
これで完璧我が家のスマートロック
靴を履きながらAlexaに解錠を指示したり
家を出たら鍵が閉まるのを横目に確認しながらエレベータを呼んだり
SwitchBot開閉センサは開けっぱなしの時に通知したり、動体検知したら通知したりもできるので便利
SESAMEに限らず
APIが解放されてたりAlexaに対応してたりIFTTT対応しているスマートロックなら同じことできそう
IFTTT、GAS経由で解錠できるようすることのリスクについて
- GASのソースには秘匿情報置いていないので流出しても問題がない
- Alexa経由での解錠時にパスコードが不要になったがフレーズをユニークにすることでリスクを抑えることできる
- また、もともとパスコードを声で言う必要があるのでどっちにしても盗聴に対するリスクは変わらない
- Alexaをドアや窓の近くに置かないという定番の対策もしておくに越したことはない
万が一IFTTTにあるパスワードやスクリプトプロパティにあるAPI_KEY
などが流出した場合
- スクリプトプロパティにある
PASSWORD_DIGEST
を変更する -
SESAMEの管理画面 から
API KEY
のリフレッシュする - SESAME自体をリセットして
Secret Key
をリフレッシュする
※ この仕組みで何かあっても責任は取れないので導入する際には自己責任でお願いします