Switchbotの温湿度計のデータをLinuxマシンで読み取る方法について紹介してきました。
ただ、現状最新環境のDebian12では上記で紹介した方法は使えなくなっています。そこで、今回は大幅に内容を書き直して、Debian12で使う方法について紹介します。
Debian12(Bookworm)で何が変わったか?
Debian12、RaspberryPiOSでいうとBookworm世代からpythonの実行には仮想環境を用いることが推奨(強制)されるようになりました。端的に言えば
$sudo pip install hogehoge
が使えなくなってしまいました。これまでの記事で紹介してきた方法ではBLEのスキャン、データの読み取りの部分ではbluepyというライブラリを使用しており、パーミッションの関係でsuにbluepyをインストールしないとBluetoothのスキャンなどで問題が起こってしまいます。
また、bluepy自体も4年ほどすでに更新がありません。そこで今回はbleakというライブラリを使用して全体を書き直しました。
準備
検証に使用した環境は、RaspberryPi5(RaspberryPiOS Bookworm64bit lite)です。
ライブラリのインストール
まずは必要なライブラリなどのインストールを行います。
$sudo apt update $sudo apt upgrade $sudo apt -y install libglib2.0-dev libbluetooth-dev $sudo apt -y install python3-dev $sudo apt -y install python3-pip
仮想化環境の準備
仮想化環境を準備します。ここではホームディレクトリ以下に作成していますが、各自お好きな場所に。
$python3 -m venv ~/bleak-env $source ~/bleak-env/bin/activate $pip install bleak
pythonプログラム
Switchbotの製品によってデータ構造が微妙に違いますので、それぞれに合わせた以下3つのプログラム例を載せます。引数に指定したMACアドレスの温湿度計のデータ(バッテリー、温度、湿度、CO2濃度)を返します。
Swichbot温湿度計
【Works with Alexa認定】SwitchBot 温湿度計 デジタル スマート家電 高精度 スイス製センサー スマホで温...
import asyncio import sys from bleak import BleakScanner import binascii # 引数からMACアドレス取得(小文字に統一) macaddr = sys.argv[1].lower() # グローバル変数(データ保持用) battery = None humid = None temp = None async def run(): # スキャン終了を通知するFuture(イベントループに紐づけ) scan_complete = asyncio.get_event_loop().create_future() def parse_advertisement_data(advertisement_data): global battery, humid, temp # Service Data (AD Type 0x16) service_data = advertisement_data.service_data if '0000fd3d-0000-1000-8000-00805f9b34fb' in service_data: servicedata = service_data['0000fd3d-0000-1000-8000-00805f9b34fb'] battery = servicedata[2] & 0b01111111 temp=(servicedata[3] & 0b00001111) / 10 + (servicedata[4] & 0b01111111) isOverZero=servicedata[4] & 0b10000000 if not isOverZero: temp=-temp humid=servicedata[5] & 0b01111111 # バッテリーが取得できたら出力して終了 if battery is not None and not scan_complete.done(): print(f"{battery},{temp},{humid}") scan_complete.set_result(True) def detection_callback(device, advertisement_data): if device.address.lower() == macaddr: manufacturer_data = advertisement_data.manufacturer_data parse_advertisement_data(advertisement_data) # スキャナ開始 scanner = BleakScanner(detection_callback) await scanner.start() try: # 最大20秒間スキャン(途中で完了すれば即終了) await asyncio.wait_for(scan_complete, timeout=20.0) except asyncio.TimeoutError: pass await scanner.stop() # 実行 asyncio.run(run())
Swichbot防水型温湿度計
【熱中症対策】SwitchBot 防水温湿度計 Alexa 温度計 湿度計 - スイッチボット ワイヤレス 室内 屋外用 温...
import asyncio import sys from bleak import BleakScanner import binascii # 引数からMACアドレス取得(小文字に統一) macaddr = sys.argv[1].lower() # グローバル変数(データ保持用) battery = None humid = None temp = None async def run(): # スキャン終了を通知するFuture(イベントループに紐づけ) scan_complete = asyncio.get_event_loop().create_future() def parse_advertisement_data(advertisement_data): global battery, humid, temp # Service Data (AD Type 0x16) service_data = advertisement_data.service_data if '0000fd3d-0000-1000-8000-00805f9b34fb' in service_data: servicedata = service_data['0000fd3d-0000-1000-8000-00805f9b34fb'] battery = servicedata[2] & 0b01111111 # Manufacturer Data (AD Type 0xFF) manufacturer_data = advertisement_data.manufacturer_data raw_data = next(iter(manufacturer_data.values())) s_payload = raw_data[6:] humid = s_payload[4] & 0b01111111 temp = (s_payload[2] & 0b00001111) / 10 + (s_payload[3] & 0b01111111) isOverZero=(s_payload[3]&0b10000000) if not isOverZero: temp=-temp # バッテリーが取得できたら出力して終了 if battery is not None and not scan_complete.done(): print(f"{battery},{temp},{humid}") scan_complete.set_result(True) def detection_callback(device, advertisement_data): if device.address.lower() == macaddr: manufacturer_data = advertisement_data.manufacturer_data parse_advertisement_data(advertisement_data) # スキャナ開始 scanner = BleakScanner(detection_callback) await scanner.start() try: # 最大20秒間スキャン(途中で完了すれば即終了) await asyncio.wait_for(scan_complete, timeout=20.0) except asyncio.TimeoutError: pass await scanner.stop() # 実行 asyncio.run(run())
Switchbot CO2センサー
SwitchBot CO2センサー 二酸化炭素濃度計 温湿度計 アラーム - スイッチボット デジタル 大画面 高精度 CO2...
import asyncio import sys from bleak import BleakScanner import binascii # 引数からMACアドレス取得(小文字に統一) macaddr = sys.argv[1].lower() # グローバル変数(データ保持用) battery = None humid = None temp = None co2=None async def run(): # スキャン終了を通知するFuture(イベントループに紐づけ) scan_complete = asyncio.get_event_loop().create_future() def parse_advertisement_data(advertisement_data): global battery, humid, temp # Service Data (AD Type 0x16) service_data = advertisement_data.service_data if '0000fd3d-0000-1000-8000-00805f9b34fb' in service_data: servicedata = service_data['0000fd3d-0000-1000-8000-00805f9b34fb'] battery = servicedata[2] & 0b01111111 # Manufacturer Data (AD Type 0xFF) manufacturer_data = advertisement_data.manufacturer_data raw_data = next(iter(manufacturer_data.values())) s_payload = raw_data[6:] co2=s_payload[7]*256 + s_payload[8] humid = s_payload[4] & 0b01111111 temp = (s_payload[2] & 0b00001111) / 10 + (s_payload[3] & 0b01111111) isOverZero=(s_payload[3]&0b10000000) if not isOverZero: temp=-temp # バッテリーが取得できたら出力して終了 if battery is not None and not scan_complete.done(): print(f"{battery},{temp},{humid},{co2}") scan_complete.set_result(True) def detection_callback(device, advertisement_data): if device.address.lower() == macaddr: manufacturer_data = advertisement_data.manufacturer_data parse_advertisement_data(advertisement_data) # スキャナ開始 scanner = BleakScanner(detection_callback) await scanner.start() try: # 最大20秒間スキャン(途中で完了すれば即終了) await asyncio.wait_for(scan_complete, timeout=20.0) except asyncio.TimeoutError: pass await scanner.stop() # 実行 asyncio.run(run())
Bashスクリプト例
仮想化環境で動かしますので、そのままでは動きません。どの仮想化環境を使うかを明示して実行するbashスクリプトの例を以下に載せます。
#!/bin/bash SCRIPT_DIR=`dirname $0` cd $SCRIPT_DIR #引数(MACアドレス) macaddr=$1 # 仮想環境のパス VENV_PATH="./bleak-env" # 実行したい Python スクリプト SCRIPT_PATH="./sbm_e.py" # 仮想環境の Python を使って実行 "$VENV_PATH/bin/python" "$SCRIPT_PATH" $macaddr
まとめ
今回紹介した方法で、最新のOS(Debian12系)でもswitchbotの温湿度計のデータを読めるようになります。
ご参考に。
コメント