今回はWEB APIを使ってSesameを解錠させます。
全体の概要はこちら
iotblog5296.hatenablog.com
- UUIDの確認
- APIキーの取得
- Sesameの鍵の状態(RESTer)
- secret_keyの取得
- pythonでSesameの状態取得
- pythonで解錠処理
- 特定のカードをタッチしたときのみアクションを起こす
APIキーの取得
公式サイトからこちらからAPIキーを取得します
https://partners.candyhouse.co/login
リンクに飛んでアドレスを入力し、指定したアドレスに記載してある数字を入力すると
黒塗りしている部分に個人のAPIキーが表示されるのでそれをメモします。
Sesameの鍵の状態(RESTer)
chromeの拡張機能を使って動作確認してきます。
Method: GET
URL: https://app.candyhouse.co/api/sesame2/[操作するSESAMEのUUID]
Header:
Name:x-api-key
Value: [API_KEY]
上記の[ ]部分は先程メモした
UUIDとAPIキーを入力します(入力する際は[ ]は不要です)
正常に動作したので200が返ってきています。
取得した状態の内容は
batteryPercentage: 電池残量
batteryVoltage: 電池電圧
position: サムターン角度
CHSesame2Status: 状態
timestamp: 更新時刻
statusの箇所で"locked"と表示されており、実際に鍵も施錠しているので一致していました。
正常に動作しない場合、どこかしらのネットが切れている可能性が高いのでWifiモジュールがエラーを起こしていないか、そもそもネット自体切断されていないかなどの確認をしてみてください。
secret_keyの取得
WEB APIを取得したページを開き赤丸部分を押して保存しておいたQRコードの写真を選択します。
すると赤枠部分にsecret_keyが表示されるのでメモします。
sesameのweb apiを取得するページの表示方法が気が付いたら変わっていて、以前は自分でシークレットキーをhexに変換していましたがこちらのページ上で表示されるようになったため不要になりましたがこういう方法もあります
↓
pythonでSesameの状態取得
web apiでいけることがわかったので今度はpythonでコードを書いて試していきましょう
コードを書くにあたりライブラリをインストールします。
ターミナルを起動して下のコードを入力します。
$ pip install pycryptodome, requests
インストールが終わったら動かしてみましょう。Thonny python IDEを起動して
下のコードをコピペして各々のUUID、API、シークレットキーを入力してRunします。
import datetime, base64, requests, json from threading import Thread, Timer from Crypto.Hash import CMAC from Crypto.Cipher import AES uuid = '個人のUUID' secret_key = '個人のシークレットキー' api_key = '個人のAPIキー' # HTTP header headers = { 'x-api-key': api_key } # signの生成 cmac = CMAC.new(bytes.fromhex(secret_key), ciphermod=AES) message = int(datetime.datetime.now().timestamp()).to_bytes(4, 'little', signed=False)[1:4] cmac.update(message) sign = cmac.hexdigest() #鍵の状態取得 surl = f'https://app.candyhouse.co/api/sesame2/{uuid}' sres = requests.get(surl, headers=headers) #リクエスト print(sres.text)
するとShell部分に先程chromeの拡張機能で行った状態と同じものが表示されます。
pythonで解錠処理
状態取得は確認できたので解錠処理も確認していきます。先程と同様に下のコードをコピペしてRunします
import datetime, base64, requests, json from Crypto.Hash import CMAC from Crypto.Cipher import AES uuid = '個人のUUID' secret_key = '個人のシークレットキー' api_key = '個人のAPIキー' # 鍵の操作(toggle/lock/unlock) cmd = 83 # 88/82/83 = toggle/lock/unlock # HTTP header headers = { 'x-api-key': api_key } # 履歴に残す内容 history = 'open (Web API)' # 半角21文字/全角不明 history = base64.b64encode(history.encode()).decode() # signの生成 cmac = CMAC.new(bytes.fromhex(secret_key), ciphermod=AES) message = int(datetime.datetime.now().timestamp()).to_bytes(4, 'little', signed=False)[1:4] cmac.update(message) sign = cmac.hexdigest() # 鍵の操作 url = f'https://app.candyhouse.co/api/sesame2/{uuid}/cmd' body = { 'cmd': cmd, 'history': history, 'sign': sign } res = requests.post(url, json.dumps(body), headers=headers) print(res.status_code, res.text)
正常に動作するとshellに200が表示されます。
特定のカードをタッチしたときのみアクションを起こす
状態取得するコードと解錠するコード、以前書いたpasoriでidmを抜き出すコードを組み込んでいきます。
import datetime, base64, requests, json, binascii, nfc, os, time from threading import Thread, Timer from Crypto.Hash import CMAC from Crypto.Cipher import AES idm1 = b"お手持ちのICカード1" idm2 = b"お手持ちのICカード2" #ICカード待受の1サイクル秒 TIME_cycle = 1.0 #ICカード待受の反応インターバル秒 TIME_interval = 0.2 #タッチされてから次を開始するまでの無効化する秒 TIME_wait = 3 #NFC接続リクエストのための準備 #212F(Felica)で設定 target_req_ic = nfc.clf.RemoteTarget("212F") #0003(IC cart) target_req_ic.sensf_req = bytearray.fromhex("0000030000") print('ICカードをタッチしてください...') while True: #USB接続されたカードリーダーをインスタンス化 clf = nfc.ContactlessFrontend('usb') #ICカード待受開始 target_res = clf.sense(target_req_ic, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval) if not target_res is None: tag = nfc.tag.activate_tt3(clf, target_res) tag.sys = 3 #IDmを取り出す idm = binascii.hexlify(tag.idm) print(idm) #特定のIDmだった場合のアクション if idm == idm1 or idm == idm2: uuid = '個人のUUID' secret_key = '個人のシークレットキー' api_key = '個人のAPIキー' # HTTP header headers = { 'x-api-key': api_key } # signの生成 cmac = CMAC.new(bytes.fromhex(secret_key), ciphermod=AES) message = int(datetime.datetime.now().timestamp()).to_bytes(4, 'little', signed=False)[1:4] cmac.update(message) sign = cmac.hexdigest() print("【 特定のIDmにより施解錠 】") #鍵の状態取得 surl = f'https://app.candyhouse.co/api/sesame2/{uuid}' sres = requests.get(surl, headers=headers) #リクエスト print(sres.text) if "unlocked" in sres.text: print("close") cmd = 82 history = 'close (Web API)' else: print("open") cmd = 83 history = 'open (Web API)' history = base64.b64encode(history.encode()).decode() #履歴に「open」or「close」を記載 url = f'https://app.candyhouse.co/api/sesame2/{uuid}/cmd' body = { 'cmd': cmd, 'history': history, 'sign': sign } res = requests.post(url, json.dumps(body), headers=headers) print(res.status_code, res.text) print('sleep'+ str(TIME_wait)+ ' second') time.sleep(TIME_wait) clf.close() #end while
正常だとshellにこんな感じで表示されます。
土台となるギミックはこれで完成です。
あとは必要に応じてGmailで通知を送ったりブザーを鳴らしたり機能を追加していきます。
次回:エラー時にブザーを鳴らす
iotblog5296.hatenablog.com