Inkbird IBS-TH1 からBluetooth LEで温度・湿度情報を取得して可視化する
新型コロナウイルスの影響により、自宅で過ごす時間が長くなりました。これを機に生活環境の情報を取得・可視化しようと思い、Bluetooth対応の温湿度センサーを導入し、Linux機からPythonで収集したデータをPrometheusとGrafanaで可視化を行うことにしました。
- 環境
- 温湿度センサー / Inkbird IBS-TH1
- Bluetoothでのデータ取得
- Pythonからbluepyで温湿度を取得する
- Prometheusへデータをエクスポート
- Grafanaで時系列データの可視化
環境
温湿度センサー / Inkbird IBS-TH1
今回、InkbirdのIBS-TH1を導入しました。Inkbird IBS-TH1シリーズはこれ以外にも液晶画面付きで単体でも利用可能なIBS-TH1 PlusやIBS-TH1 Miniがありますが、今回購入したのは標準版です。
https://www.amazon.co.jp/dp/B0774BGBHS
Bluetoothでのデータ取得
既に同様のことをされている方がいらっしゃるので、下記を参考にIBS-TH1のMACアドレス確認とgattool
でのデータ取得確認を行いました。しかし、私の環境ではhandleに0x002d
を指定したところ、応答のバイト数が異なり温湿度のデータが含まれていないようでした。
handleに0x002d
を指定した場合。7バイトの応答を期待しているところ、5バイトの応答が返ってきていました。
$ gatttool -b XX:XX:XX:XX:XX:XX --char-read --handle=0x002d Characteristic value/descriptor: 02 2e 00 f4 ff
他のケースを探してみると、handleに0x0028
を利用している方もいて、今回はこちらでうまくいきました。
$ gatttool -b XX:XX:XX:XX:XX:XX --char-read --handle=0x0028 Characteristic value/descriptor: 0f 09 8e 19 00 18 26
1,2バイト目に温度、3,4バイト目に湿度がリトルエンディアンで格納されています。
Pythonからbluepyで温湿度を取得する
pythonからbluetoothデバイスへの通信はbluepyを利用しました。取得したデータのバイト列から整数値への変換は、Python 3.2以降で利用可能な、整数型のfrom_bytesメソッドが簡単でした。
python3ならintとbytesの変換が楽勝になる - BlankTar
from bluepy.btle import Peripheral, ADDR_TYPE_PUBLIC mac = "XX:XX:XX:XX:XX:XX" # IBS-TH1のMACアドレス peripheral = Peripheral(mac, addrType=ADDR_TYPE_PUBLIC) characteristic = peripheral.readCharacteristic(0x0028) temperature = int.from_bytes(characteristic[0:2], "little") / 100 humidity = int.from_bytes(characteristic[2:4], "little") / 100 print("Temperature: {} C".format(temperature)) print("Humidity: {} %".format(humidity))
Prometheusへデータをエクスポート
取得できるようになったデータをPrometheusで時系列に保存し、可視化できるようにします。 prometheusの公式のクライアントライブラリであるprometheus_clientを利用し、簡易なエクスポーターを作成しています。
import time from bluepy.btle import Peripheral, ADDR_TYPE_PUBLIC from prometheus_client import start_http_server, Gauge from prometheus_client import REGISTRY, PROCESS_COLLECTOR, PLATFORM_COLLECTOR, GC_COLLECTOR # 温湿度以外のメトリクスを非表示にする REGISTRY.unregister(PROCESS_COLLECTOR) REGISTRY.unregister(PLATFORM_COLLECTOR) REGISTRY.unregister(GC_COLLECTOR) # 温湿度のメトリクスを作成 TEMPERATURE = Gauge("inkbird_temperature", "current temperature", ["device"]) HUMIDITY = Gauge("inkbird_humidity", "current humidity", ["device"]) MAC = "XX:XX:XX:XX:XX:XX" # IBS-TH1のMACアドレス def run(): peripheral = Peripheral(MAC, addrType=ADDR_TYPE_PUBLIC) characteristic = peripheral.readCharacteristic(0x0028) temperature = int.from_bytes(characteristic[0:2], "little") / 100 humidity = int.from_bytes(characteristic[2:4], "little") / 100 TEMPERATURE.labels(MAC).set(temperature) HUMIDITY.labels(MAC).set(humidity) if __name__ == '__main__': start_http_server(8080) # サーバポートを指定してHTTPサーバを開始 while True: run() time.sleep(60) # 60秒毎にデータ更新
実行し、ホストの8080ポートへブラウザでアクセスすると、下記のような温度と湿度の情報が表示されると思います。
# HELP inkbird_temperature current temperature # TYPE inkbird_temperature gauge inkbird_temperature{device="XX:XX:XX:XX:XX:XX"} 23.06 # HELP inkbird_humidity current humidity # TYPE inkbird_humidity gauge inkbird_humidity{device="F8:30:02:FF:45:3F"} 47.58
これをPrometheusのターゲットとして登録し、定期的なデータ取得と保存が可能となりました。
Grafanaで時系列データの可視化
あとはPrometheusのデータをGrafanaでグラフ化します。以上で温湿度情報を可視化することができました。