🔆

SwitchBot Hub 2 のステータス取得

2024/03/04に公開

SwitchBot ハブ2を買ったので、温度や湿度を取得してzabbixに送ってみました。

参考にしたのは、以下とか。

いやもう参考サイトでやりたいことはできてしまっているのですが、うちの計測系は独自の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クラス(とそのサブクラス)は、メンバclientSwitchBotClientを持っているので、リクエストを出すたびに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