Inkbird IBS-TH1 からBluetooth LEで温度・湿度情報を取得して可視化する

新型コロナウイルスの影響により、自宅で過ごす時間が長くなりました。これを機に生活環境の情報を取得・可視化しようと思い、Bluetooth対応の温湿度センサーを導入し、Linux機からPythonで収集したデータを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を指定したところ、応答のバイト数が異なり温湿度のデータが含まれていないようでした。

qiita.com

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を利用している方もいて、今回はこちらでうまくいきました。

GitHub - viyh/inkbird: Script for interacting over bluetooth with an Inkbird IBS-TH1 temperature probe.

$ 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でグラフ化します。以上で温湿度情報を可視化することができました。

f:id:HichTakk:20200429181509p:plain