はじめに
実際に自分で集めたデータを用いて、機械学習の予測モデル構築をしたい。
そのための、第一歩としてswitchBot APIを用いて自室に設置された温湿度計のデータを取得することにしました。
switchBot 室温度計
また、機械学習をpythonで行うことを考慮し、pythonを使用しswitchBot APIを叩きました。
動作環境
- Dockerfile
FROM python:3.11 COPY requirements.txt . RUN pip install -r requirements.txt WORKDIR /app COPY ./app .
- requirements.txt
certifi==2023.11.17 charset-normalizer==3.3.2 idna==3.4 requests==2.31.0 urllib3==2.1.0
- docker-compose.yml
version: '3' services: app: container_name: switchbot_api build: context: . dockerfile: Dockerfile volumes: - ./app:/app - ./requirements.txt:/requirements.txt env_file: - .env tty: true
SwitchBot API v1.1 について
- 条件
- SwitchBotアプリでアカウント登録した後、tokenとsecretの発行が必要
- 1日当たりの呼び出し回数は、1000回まで
- Http ヘッダーに認証情報の付与が必要
- Http ヘッダー
- 必要な情報
key 説明 Authorization SwitchBotアプリにて取得したtoken t UNIXエポック時刻(1970年からのミリ秒) nonce 署名する文字列にブレンドするために開発者自身によって生成されたランダムな UUID。 sign 特定のアルゴリズムを使用してトークンと秘密鍵から生成された署名。 - t
- UNIXエポック時刻(1970年からのミリ秒)
-
SwitchBot API v1.1のpythonサンプルプログラム
t = int(round(time.time() * 1000))
- nonce
- 署名する文字列にブレンドするために開発者自身によって生成されたランダムな UUID。
-
SwitchBot API v1.1のpythonサンプルプログラム
nonce = str(uuid.uuid4())
- sign
- 特定のアルゴリズムを使用してトークンと秘密鍵から生成された署名。
- 特定のアルゴリズム
- token,t,nonceを結合し、一つの文字列を作成
- その文字列をバイト列に変換
- SwitchBotアプリにて取得したsecretを秘密鍵としてバイト列にHMAC-SHA256署名を行う
- 署名をBase64形式の文字列にエンコード
- pythonプログラム
string_to_sign = "{}{}{}".format(token, t, nonce) string_to_sign = bytes(string_to_sign, "utf-8") sign = base64.b64encode( hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest() )
- 必要な情報
デバイスの一覧を取得する
-
url
GET https://api.switch-bot.com/v1.1/devices
-
プログラム
import os import time import json import hashlib import hmac import base64 import uuid import requests import json import datetime from os.path import exists dir_name = "../responses" if not exists(dir_name): os.makedirs(dir_name) token = os.environ["SWITCHBOT_TOKEN"] secret = os.environ["SWITCHBOT_SECRET"] nonce = str(uuid.uuid4()) 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() ) apiHeader = {} apiHeader["Authorization"] = token apiHeader["Content-Type"] = "application/json" apiHeader["charset"] = "utf8" apiHeader["t"] = str(t) apiHeader["sign"] = str(sign, "utf-8") apiHeader["nonce"] = nonce response = requests.get("https://api.switch-bot.com/v1.1/devices", headers=apiHeader) devices = response.json() timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") response_file = f"{dir_name}/devices_{timestamp}.json" with open(response_file, "w") as f: json.dump(devices, f) print("Success get devices list.")
(https://github.com/taiki-kuraishi/switchBotAPI/blob/main/app/src/getDeviceStatus.py)
-
Response
- deviceIdは、XXXXXに変更してあります。
{ "statusCode": 100, "body": { "deviceList": [ { "deviceId": "XXXXXXXXXXXX", "deviceName": "ハブミニ", "deviceType": "Hub Mini", "hubDeviceId": "XXXXXXXXXXXX" }, { "deviceId": "XXXXXXXXXXXX", "deviceName": "温湿度計", "deviceType": "Meter", "enableCloudService": true, "hubDeviceId": "XXXXXXXXXXXX" }, { "deviceId": "XXXXXXXXXXXX", "deviceName": "パソコン", "deviceType": "Bot", "enableCloudService": true, "hubDeviceId": "XXXXXXXXXXXX" } ], "infraredRemoteList": [ { "deviceId": "XX-XXXXXXXXXXXX-XXXXXXXXXXXX", "deviceName": "エアコン", "remoteType": "Air Conditioner", "hubDeviceId": "XXXXXXXXXXXX" }, { "deviceId": "XX-XXXXXXXXXXXX-XXXXXXXXXXXX", "deviceName": "扇風機", "remoteType": "DIY Fan", "hubDeviceId": "XXXXXXXXXXXX" }, { "deviceId": "XX-XXXXXXXXXXXX-XXXXXXXXXXXX", "deviceName": "ライト", "remoteType": "DIY Light", "hubDeviceId": "XXXXXXXXXXXX" } ] }, "message": "success" }
温湿度計のデータを取得する
-
GET https://api.switch-bot.com/v1.1/devices
で取得したdeviceIdをもとに、温湿度計のデータを取得する -
url
GET https://api.switch-bot.com/v1.1/devices/ "deviceId" /status
-
プログラム
import os import time import json import hashlib import hmac import base64 import uuid import requests import json import datetime from os.path import exists dir_name = "../responses" device_id = os.environ["SWITCHBOT_METER_ID"] if not exists(dir_name): os.makedirs(dir_name) token = os.environ["SWITCHBOT_TOKEN"] secret = os.environ["SWITCHBOT_SECRET"] nonce = str(uuid.uuid4()) 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() ) apiHeader = {} apiHeader["Authorization"] = token apiHeader["Content-Type"] = "application/json" apiHeader["charset"] = "utf8" apiHeader["t"] = str(t) apiHeader["sign"] = str(sign, "utf-8") apiHeader["nonce"] = nonce response = requests.get( f"https://api.switch-bot.com/v1.1/devices/{device_id}/status", headers=apiHeader, ) devices = response.json() timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") response_file = f"{dir_name}/status_{device_id}_{timestamp}.json" with open(response_file, "w") as f: json.dump(devices, f) print("Success get device status.")
(https://github.com/taiki-kuraishi/switchBotAPI/blob/main/app/src/getDevicesList.py)
-
Response
- deviceIdは、XXXXXXXXXXXXに変更してあります。
{ "statusCode": 100, "body": { "deviceId": "XXXXXXXXXXXX", "deviceType": "Meter", "hubDeviceId": "XXXXXXXXXXXX", "humidity": 52, "temperature": 22.1, "version": "V2.6", "battery": 64 }, "message": "success" }
- 湿度 : 52%
- 気温 : 22.1度
まとめ
switchBot APIを通してcsrf_tokenやJWTとは異なるHTTPヘッダーに認証情報や署名を含めるセキュアな方法を学ぶことができました。
プログラムを書く前に、postmanを使用してswitchBot APIの動作を確認しようと考えていましたが、httpヘッダーに必要な認証情報の付与にはプログラムが必要なため、postmanの使用は断念しました。