SwitchBot温湿度計 から Bluetooth 通信で現在値を取得するメモ。
動作確認環境
- macOS 12.1
- node v17.3.0
- npm v8.3.0
- SwitchBot温湿度計
node-switchbot で値を取る
OpenWonderLabsが公開しているnode-switchbotを使うと、非常に楽。
必要なモジュールをインストールして、
npm install @abandonware/noble
npm install node-switchbot
Quick Start のコードをapp.js等に保存して実行するだけで、値が取得できる。
// Load the node-switchbot and get a `Switchbot` constructor object
const Switchbot = require('node-switchbot');
// Create an `Switchbot` object
const switchbot = new Switchbot();
(async () => {
// Start to monitor advertisement packets
await switchbot.startScan();
// Set an event hander
switchbot.onadvertisement = (ad) => {
console.log(JSON.stringify(ad, null, ' '));
};
// Wait 10 seconds
await switchbot.wait(10000); // 10秒だと受信しないことがあるので、受信確認用に60秒くらいにしておくと良い
// Stop to monitor
switchbot.stopScan();
process.exit();
})();
こんな感じのデータが受信できる。
{
"id": "11d6e84dd3e18b4e3aab47da4aecb60a",
"address": "XX:XX:XX:XX:XX:XX",
"rssi": -54,
"serviceData": {
"model": "T",
"modelName": "WoSensorTH",
"temperature": {
"c": 21.7,
"f": 71.1
},
"fahrenheit": false,
"humidity": 53,
"battery": 100
}
}
serviceData.temperature.c が気温(℃)
serviceData.humidity が湿度(%)
serviceData.address が対象端末のMACアドレスなので、これで個体を識別する
・データタイプ(modelName)には、
Bot, Meter, Curtain があるようで、
温湿度計はMeterに属する。
https://github.com/OpenWonderLabs/node-switchbot#advertisement-data
・データの受信はプログラム実行直後にくるとは限らず、
実行から30秒くらいかかることもある。
・アパートの一部屋で実行すると、ご近所さんのSwitchBotからも ガンガン データを受信する
Bluetooth API で値を取る
SwitchBot温湿度計は、Bluetooth通信規格に BLE(Bluetooth Low Energy)を採用しているので、とってもエコ。
通信データの内容が公開されているので、任意のBLE通信APIからアクセスしてデータを活用できる。
手元では、abandonware/nobleを使ってデータを取得することもできた。
abandonware/noble
nodeのBLE通信モジュールとしてnobleが有名みたいだが、本家は更新が止まっているので、
abandonwareさん?がそういった有益なOSSを引き継いで開発保守をしてくれているらしい。
BLEでは、[親機(central module)] 1 対 [子機(peripheral)] 多 で通信が行われ、
nobleは親機の実装用モジュールとなる。
雑にデータを取りに行くだけならこんな感じ。
const UUIDs = ['cba20d00224d11e69fb80002a5d5c51b'];
const noble = require('@abandonware/noble');
noble.on('stateChange', state => {
// nobleの初期stateはUnknown. poweredOn になってからスキャンを開始しなければいけない
if (state == 'poweredOn'){
// UUIDsで指定された対象だけが、スキャンの対象となる。
// ここで設定している値は、SwitchBot機器用に公開されているUUID
// 第2引数は duplicate. 同端末からは1回のみデータを受け取るならば false にする
noble.startScanning(UUIDs, true);
}
});
noble.on('discover', peripheral => {
console.log('' + peripheral);
});
こんな感じにデータが取れる
{
"id": "11d6e84dd3e18b4e3aab47da4aecb60a",
"address": "",
"addressType": "unknown",
"connectable": true,
"advertisement": {
"manufacturerData": {
"type": "Buffer",
"data": [ 89, 0, 220, 48, 188, 73, 157, 176 ]
},
"serviceData": [
{
"uuid": "0d00",
"data": {
"type": "Buffer",
"data": [ 84, 16, 100, 8, 148, 54 ]
}
}
],
"serviceUuids": [
"cba20d00224d11e69fb80002a5d5c51b"
]
},
"rssi": -55,
"mtu": null,
"state": "disconnected"
}
Meter BLE open API
abandonware/nobleでデータ取得ができるようになったら、
Meter BLE open APIで公開されているデータ仕様を参考にして必要な値を取得する。
※ここにUUIDについての記載もある
UUID: cba20d00-224d-11e6-9fb8-0002a5d5c51b and fee7
温湿度計用には(New) Broadcast Messageのあたりが直接参考になるので、コードにおこすとこんな感じになる。
noble.on('discover', peripheral => {
const sd = peripheral.advertisement.serviceData[0].data;
const md = peripheral.advertisement.manufacturerData;
const temperatureAboveZero = (sd[4] & 0b10000000) != 0;
const data = {
'type': sd[0] & 0b01111111,
'battery': sd[2] & 0b01111111,
'temperature': ((sd[3] & 0b00001111) / 10 + (sd[4] & 0b01111111)) * (temperatureAboveZero?1:-1),
'humidity': sd[5] & 0b01111111,
'rssi': peripheral.rssi,
'address': `${md[2].toString(16)}:${md[3].toString(16)}:${md[4].toString(16)}:${md[5].toString(16)}:${md[6].toString(16)}:${md[7].toString(16)}`
};
console.log(JSON.stringify(data));
});