Switchbot APIのv1.1が登場
この記事を書いているのが2022/09/08ですが2日前くらいにSwitchbotのAPIがv1.0 -> v1.1に移行した模様です。
僕的に一番大きな変更点としてSmart Lockを制御できるようになったことです。
今までセキュリティ上の観点から公開されているAPIでは、SwitchBotロックを制御することはできませんでしたが(なぜか一時期使用できていましたが)、v1.1の登場により制御ができるようになりました。
他にも変更点があると思いますが、本記事ではSwitchBotロックに着目し、Pythonを使って制御してみようかと思います。なおSwitchbotのSwitchBotロックを制御したりするためにはSwitchbotのHubが必要になるので注意してください。
Switchbotに関するコードをGitHubに公開しています。本記事に関するコードはsrc/utils.py
,src/lock.py
になるので良ければ参考にしてください。
署名の追加
v1.0
からv1.1
への一番大きな変更は署名が必要になったことです。
この署名の追加でセキュリティ面でのリスクが減ったことにより、SwitchBotロックを制御できるようになったと考えられます。
今までSwitchbotのAPIを使用するためには、専用のAPI用のtoken
が必要でした。
このtoken
を使うことにより、自分が持っているデバイスの見れたり制御できたりしました。
しかしv1.1のAPIではtoken
だけでなく、secret
キーが必要になります。
そしてこの2つを使用して作成した署名を使って制御することになります。
署名の作成は公式ドキュメントに載っていますがPython3用の署名作成コードを以下にも記述しておきます。
import time
import hashlib
import hmac
import base64
# open token
token = '' # copy and paste from the SwitchBot app V6.14 or later
# secret key
secret = '' # copy and paste from the SwitchBot app V6.14 or later
nonce = ''
t = int(round(time.time() * 1000))
string_to_sign = '{}{}{}'.format(token, t, nonce)
string_to_sign = bytes(string_to_sign, 'utf-8')
secret = bytes(secret, 'utf-8')
sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest())
print ('Authorization: {}'.format(token))
print ('t: {}'.format(t))
print ('sign: {}'.format(str(sign, 'utf-8')))
print ('nonce: {}'.format(nonce))
ここで作成したt
,sign
,nonce
をAPIのheaderとして新たに追加します。
次からはこのコードを絡めたデバイス一覧の取得からSmartLockの制御まで説明していきます。
手順
アプリをアップデートする
Switchbot API v1.1を使用するためには、secret
キーが必要になります。
そのためにアプリのバージョンをv6.14
以降にする必要があります。
まだ行っていない方はアップデートをしましょう。
tokenとsecretを取得する
次にSwitchbotアプリ内からtoken
とsecret
を取得します。
以下の手順に沿って取得しましょう。
1. SwitchBotのアカウント登録し、ログインする
2. アプリ内の下にあるタブからプロフィール > 設定 の順にタップ
3. アプリバージョンを10回タップ > 開発者向けオプションが表示される
4. 開発者向けオプションを選択
5. 上のtoken
、下のsecret
が表示されるのでコピーする
ここで表示されるtoken
とsecret
は絶対に公開しないでください。
他人が制御できるようになってしまいます。もし公開してしまった場合はアプリ内のボタンからキーをリセットしましょう。
デバイス一覧を取得する
v1.0でもデバイス一覧を取得することができていましたが、今回v1.1になったということで取得の仕方が変更されています。上記にもありますが、署名が必要になります。
以下にtoken
,secret
を使用して署名作成、HTTPリクエストヘッダーの作成までのコードを書いていきましょう。
import os
import time
import hashlib
import hmac
import base64
def make_sign(token: str,secret: str):
nonce = ''
t = int(round(time.time() * 1000))
string_to_sign = bytes(f'{token}{t}{nonce}', 'utf-8')
secret = bytes(secret, 'utf-8')
sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest())
return sign, str(t), nonce
def make_request_header(token: str,secret: str) -> dict:
sign,t,nonce = make_sign(token, secret)
headers={
"Authorization": token,
"sign": sign,
"t": str(t),
"nonce": nonce
}
return headers
署名作成はmake_sign
関数、HTTPリクエストヘッダー作成はmake_request_header
関数になります。
おそらくリクエストの度に署名を生成する必要があるため、関数化しておいた方がいいと思います。
make_request_header
関数に引数としてtoken
とsecret
を入れてあげると返り値としてheaders
が返ってきます。リクエストを送る際にはこのheaders
を使用するだけです。そのまま流用しても問題ないと思います。
次にこのheaders
を利用して自分のSwitchbotデバイスの一覧を取得してみましょう。
import requests
import json
base_url = 'https://api.switch-bot.com'
def get_device_list(deviceListJson='deviceList.json'):
# tokenとsecretを貼り付ける
token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
devices_url = base_url + "/v1.1/devices"
headers = make_request_header(token, secret)
try:
# APIでデバイスの取得を試みる
res = requests.get(devices_url, headers=headers)
res.raise_for_status()
print(res.text)
deviceList = json.loads(res.text)
# 取得データをjsonファイルに書き込み
with open(deviceListJson, mode='wt', encoding='utf-8') as f:
json.dump(deviceList, f, ensure_ascii=False, indent=2)
except requests.exceptions.RequestException as e:
print('response error:',e)
if __name__ == "__main__":
get_device_list()
GET
リクエストからデバイス一覧を取得する関数になります。
token
とsecret
にはアプリから取得した値をそのまま貼り付けましょう。キーをどう管理するかは、各々に任せます。
リクエストが成功していればデバイス一覧が出力されます。失敗している場合、エラーメッセージが表示されると思います。
またjsonファイルにdumpされます。このjsonファイルは関数の引数で指定してあげてください。今回のコードではdeviceList.json
がデフォルトになっています。
v1.1のAPIを試したところで次にSwitchBotロックを制御してみます。
SwitchBotロックを制御する
それでは本題となるSwitchBotロックの制御になります。ここまで来れたらここからは難しくないと思います。
APIを使用するときは、公式のAPIドキュメントを見ましょう。APIに対してどのようなリクエストが必要か、どのようなレスポンスが返ってくるかがぎっしり書かれています。
公式ドキュメントは英語で書かれていますが、海外産のAPIを使用する機会は多いと思うので、頑張って読み解きましょう。Smart Lockを制御するときはcommand
としてlock
,unlock
を使用するそうです。
ロックの状態を取得する
def get_lock_status(deviceId: str):
devices_url = base_url + "/v1.1/devices/" + deviceId + "/status"
try:
# ロックの状態を取得
res = requests.get(devices_url, headers=headers)
res.raise_for_status()
return res.json()
except requests.exceptions.RequestException as e:
print('response error:',e)
get_lock_status
関数の引数にはSmart LockのdeviceId
を指定してあげてください。成功した場合、以下のレスポンスが表示されると思います。
{'statusCode': 100, 'body': {'deviceId': 'xxxxxxxxxx', 'deviceType': 'Smart Lock', 'hubDeviceId': 'xxxxxxxxxx', 'lockState': 'unlocked', 'doorState': 'opened', 'calibrate': True}, 'message': 'success'}
このlockState
がロック/アンロックの状態になります。
ロック/アンロックする
ロック/アンロック用のメソッドを用意しました。
引数には先ほどと同様、deviceId
を指定してあげてください。
post
リクエストをする場合はrequests
のpost
メソッドを使用します。
ロック/アンロックする場合は、パラメータとして以下の用に指定してあげる必要があります。
ロックする場合はcommand
をlock
に、アンロックする場合はcommand
をunlock
にしてあげましょう。
data={
"commandType": "command",
"command": "lock",
"parameter": "default",
}
def lock(deviceId: str):
devices_url = base_url + "/v1.1/devices/" + deviceId + "/commands"
data={
"commandType": "command",
"command": "lock",
"parameter": "default",
}
try:
# ロック
res = requests.post(devices_url, headers=headers, json=data)
res.raise_for_status()
print(res.text)
except requests.exceptions.RequestException as e:
print('response error:',e)
def unlock(deviceId: str):
devices_url = base_url + "/v1.1/devices/" + deviceId + "/commands"
data={
"commandType": "command",
"command": "unlock",
"parameter": "default",
}
try:
# アンロック
res = requests.post(devices_url, headers=headers, json=data)
res.raise_for_status()
print(res.text)
except requests.exceptions.RequestException as e:
print('response error:',e)
成功すれば以下のようなレスポンスが返ってくると思います。
{"statusCode":100,"body":{},"message":"success"}
最後に
今回APIがアップデートされたということで早速今までAPIで制御できなかったSwitchBotロックの制御を行ってみました。セキュリティが向上したということで今後も色々と追加されていきそうですね。今後もSwitchbotに関するAPIで面白いことができそうだったら、記事をあげていこうと思います。
Switchbotに関するコードをGitHubに公開しています。今回紹介した部分はsrc/utils.py
とsrc/lock.py
になりますので、よければ参考にしてください。