SwitchBot Hub 2 のステータス取得
SwitchBot ハブ2を買ったので、温度や湿度を取得してzabbixに送ってみました。
参考にしたのは、以下とか。
- SwitchBot API v1.0を使用して温湿度計とハブ2のステータスを取得する方法 | まっさの電子ぶろっぐ
- 【SwitchBot】温湿度計のデータをPython × InfluxDB × Grafanaでダッシュボード化する
いやもう参考サイトでやりたいことはできてしまっているのですが、うちの計測系は独自のPostgreSQLデータベースかzabbixを使っているので、InfluxDBじゃなくて自分でやっておこうと思ったわけです。
参考サイトのうち前者はcurlでv1.0のAPIを、後者はpythonの実装でv1.1のAPIを叩いています。
v1.0はそのうち使えなくなるかもしれないので、v1.1を叩きたいところです。
python switchbot API
とかでぐぐると、以下がヒットします。
変更履歴を見ると、API v1.1に対応しているようなので、これを使うことにします。
起動時に device = switchbot.device(ID)
でターゲットのハブ2のIDを指定して Device を取得し、1分毎に device.status()
を呼び出して情報を取得します。
最初はうまく動いているように見えましたが、5分ほど経つとサーバーからの応答が 401 になります。(SwitchBot API server returns status 401
と言う RunetimeErrorが出ます)
HTTP 401 は Unauthorized ですから、認証が通っていないと言うエラーです。
ソースを読むと、SwitchBotClient と言うクラスのコンストラクタでリクエストヘッダを作っているのですが、この中に timestamp があります。
APIリファレンスからはtimestampの有効期限は読み取れませんでしたが、結果から見ると5分程度過ぎると無効判断をされるようです。
python-switchbot に issue を立てて、PRを送ることも考えたのですが、面倒なのでad hocな対応をすることにしました。
Device
クラス(とそのサブクラス)は、メンバclient
にSwitchBotClient
を持っているので、リクエストを出すたびにclient
を新しいもので上書きしてあげます。(自分でSwitchBotClient
のインスタンスを作っても良いのですが、SwitchBot
を作ってclient
メンバを取り出すことにします)
# 初期化
switchbot = SwitchBot(token, secret)
device = switchbot.device(device_id)
(・・・)
# 使うときに SwitchBotClient を上書き
switchbot = SwitchBot(token, secret)
device.client = switchbot.client
※ switchbot.device(device_id)
は、中で毎回 switchbot.devices()
を呼んでいます。複数のidがあってAPIの呼び出し回数を気にする場合は、自分で switchbot.devices()
を呼び出してidを探した方が良さそうです。
できあがったものがこれ
しばらく動かしてみましたが、以下の現象が起きます。
- サーバーの応答が HTTP 500 Internal Server Error になる
- だいたい5分〜30分に一度くらい起きる
-
api.switch-bot.com
の DNS が引けなくなる- NameResolutionError(Temporary failure in name resolution) を起因とする requests.exceptions.ConnectionError が発生する
- この環境は tailscale を使っているので、/etc/resolv.conf を見ると
nameserver 100.100.100.100
となっているので、それ関連の気がしなくもない・・・ - 頻度はそんなに高くない。1日1回くらい?
- python の組み込み例外 ConnectionError と同じ名前なので、try で捕捉するには requests.exceptions.ConnectionError と書く必要がある
Discussion