この記事は以下の 2つの記事の続きで、完結編となる内容です。
- SwitchBot温湿度計を Web Bluetooth API でスキャンする【試行錯誤中】 - Qiita
- SwitchBot温湿度計のデータを Web Bluetooth API で取得する:【未完】準備や試行錯誤のメモ - Qiita
これまでの流れ
やろうとしていること
これまでの記事・この記事でやろうとしていることは、以下の「SwitchBot温湿度計」の温湿度のデータを Web Bluetooth API を使って取得する、というものです。
BLE による温湿度の取得自体は、検索をすると Python・Node.js・Go などで実現した事例が出てきます。しかし Web Bluetooth API を使ったものはなさそうだったので軽い気持ちで試してみようと思ったのがきっかけでした。
そして、思ったより試行錯誤が必要な内容が出てきて、その過程をメモにして残していたら記事が 3つ目となりました。
これまでに得られた情報など
上記の 2つの記事を書いた中で、調べたり試したりして得られた情報は以下でした。
- SwitchBot温湿度計 の BLE まわりの情報
- サービスの UUID
cba20d00-224d-11e6-9fb8-0002a5d5c51b
- デバイス名
WoSensorTH
- サービスの UUID
- Web Bluetooth API関連
- スキャンには
navigator.bluetooth.requestLEScan
を用いる- 現状 Chrome で利用する場合
chrome://flags/#enable-experimental-web-platform-features
を Enable にする必要あり - 筆者の環境では Mac・Windows では意図した通りに動かないことがあり Androidスマホが安定していそうだった(Androidスマホは Pixie 3)
- PC の Chrome と Androidスマホの Chrome を連携させる仕組み(
chrome://inspect/#devices
を開いて使うもの)を活用すると開発が効率的にできそう
- 現状 Chrome で利用する場合
- スキャンには
上記の「Web Bluetooth API関連」という項目の下に書いた話は、過去の記事の 2つ目に関連する内容を書いていますので、よろしければご覧ください。
この記事の内容を試すために必要なもの・設定等
デバイス関連
この後に書く内容を試す場合、以下が必要になります。
- SwitchBot温湿度計
- PC
- Androidスマホ
- PC と Androidスマホをつなぐ USBケーブル
そして、PC と Androidスマホに関して、以下のアプリ・仕組みを使います。
- PC
- Chrome
- ローカルでサーバを立てられる何か
- Androidスマホ
- Chrome
上記の PC の部分の「ローカルでサーバを立てられる何か」という部分は、過去の他の方の記事でも書かれているワンライナーを使うのがオススメです。
●ワンライナーWebサーバを集めてみた - Qiita
https://qiita.com/sudahiroshi/items/e74d61d939f18779970d
また Androidスマホ関連の補足ですが、この後の手順を同じように進めるためには PC で Androidスマホを USBデバッグできる状態にしておく必要もあります。
なお Androidスマホの要件について、最低限、以下で Web Bluetooth に対応していると出ている Chrome が入っている必要がありそうです。
●Can I use... Support tables for HTML5, CSS3, etc
https://caniuse.com/?search=webbluetooth
Androidスマホの Chrome について、「 chrome://flags/#enable-experimental-web-platform-features
を Enable にして navigator.bluetooth.requestLEScan
を動作させられるという条件」が、上記と完全に一致するかどうかは不明瞭な部分があります(※ 自分は手元にあった Pixel 3 で試して、動作させられるのを確認できているだけの状況)。
PC・Androidスマホの Chrome関連の設定まわり
以下の内容を進める前準備として、前回の記事の「PC の Chrome と Androidスマホの Chrome を連携させる」で書いていた内容ができている前提となっています。
なお全く同じ方法でなくても、とりあえず Androidスマホの Chrome のコンソール出力が確認できれば似たような流れで進めていけると思います。
Androidスマホの Chrome で BLE のスキャンを行う
Androidスマホの Chrome の設定を確認する
これまでの内容・前準備の確認を兼ねて、Androidスマホの Chrome のコンソール上で navigator.bluetooth.requestLEScan
を用いたスキャンを試してみます。
再掲になりますが、その処理を実行するためには現状 chrome://flags/#enable-experimental-web-platform-features
を Enable にする必要ありますので注意してください。
上記の準備がすんでいる PC と Androidスマホ を USB でつないだ状態にして、PC
上で Chrome を開き chrome://inspect/#devices
を開きます。
筆者の環境では上の画像のようなページが開きました。
この中の自分の Androidスマホに該当する部分で、上の赤枠で囲んだ部分があると思います。ここに chrome://flags/#enable-experimental-web-platform-features
を入力して「Openボタン」を押しましょう。
スマホで以下のページが開くので、Experimental Web Platform features が以下のように Enable になった状態にしてください(切り替えをした際にブラウザの再起動を求められるので、それに従って再起動してください)。
なお上記の操作は、もちろん Androidスマホ側の Chrome で URL入力を行うなどして進めても問題ありません。
ちなみに、上記の Androidスマホの画面をキャプチャする際には「scrcpy」を利用しているのですが、この scrcpy も画面のミラーリングだけでなく PC からの Androidスマホの操作を行う仕組みとして利用可能です。
Androidスマホの Chrome で簡単なスキャンを試す
ここで前回の記事で使ったソースコードに少し変更を加えた( navigator.bluetooth.addEventListener
の部分を追加した)以下の内容で、簡単なスキャンを試します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Web Bluetooth API によるスキャン</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.css"
/>
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">操作用ボタン</h1>
<div class="buttons" style="margin-top: 1.5rem">
<button
class="button is-success is-light"
type="button"
onclick="onStartButtonClick()"
>
スキャン
</button>
</div>
</div>
</section>
<script>
async function onStartButtonClick() {
await navigator.bluetooth
.requestLEScan({
filters: [{ services: ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] }],
keepRepeatedDevices: true,
})
.then(() => {
navigator.bluetooth.addEventListener(
"advertisementreceived",
(event) => {
console.log(event);
}
);
});
}
</script>
</body>
</html>
PC上にローカルサーバーをたてる
前回の記事では Web上に上記の内容を書いた HTMLファイルををアップして、Androidスマホの Chrome で開きました。今回はそれと異なり、PC上でにファイルを置いた状態で Androidスマホの Chrome からアクセスできるようにします。
その準備として、PC でローカルのサーバーをたててから、上記の内容を書いた HTMLファイルをローカルのサーバ経由でアクセスできるようにしましょう。
自分の場合は、以下の Python 3.x用のワンライナーでサーバーをたてました。
python -m http.server 8000
PC上の Chrome で http://localhost:8000/
のアドレスで、上記の HTMLファイルにアクセスできていれば OK です。以下のようなテキストとボタンが表示されると思います。
Androidスマホの Chrome で PC の localhost にアクセスする
それでは PC の Chrome でアクセスを試した HTMLファイルを、今度は Androidスマホの Chrome で開けるようにしていきます。
PC の Chrome で chrome://inspect/#devices
にアクセスして開いたページで、以下の赤矢印で示したボタンを押します。
「Port Forwarding Setting」の画面が開くので、以下の赤線で示したような「Port」と「IP address and port」の設定を行ってください。「IP address and port」のほうは上記の PC でたてたローカルサーバーの設定に合わせたものを入れてください(※ 自分の場合は localhost:8000)。そして Port はその設定のポート番号と合わせておくのが分かりやすいと思います。
また、画面左下の「Enable port forwarding」にチェックを入れてから、最後に右下にある「Doneボタン」を押してください。
あとは、PC側の操作でも Androidスマホ側の操作でも良いので、Androidスマホの Chrome で localhost:8000
を開いて、PC で表示したのと同じページにアクセスできるのを確認してください。
そして PC側の Chrome で、以下にある Inspect を押してウィンドウを開いておいてください。
自分の場合は、以下のウィンドウが表示されました。
そして、Androidスマホの Chrome上で表示されたページのスキャンボタンを押しましょう。ちなみに、PC側に表示されているボタンを押すことも可能です。
そうすると Androidスマホの Chrome側にのみ、以下の表示が出ていると思います。
ここで少し待ってみて「不明またはサポートされていないデバイス」と表示されるのを確認しましょう。そして「許可ボタン」を押してください。
その後に、PC側のコンソール(Androidスマホの Chrome を表示しているウィンドウのコンソール)を見てみてください。
以下のように、スキャン結果が表示されていれば、ここまでの手順は OK です。
なお、上記で折りたたまれて出力されている内容を展開すると、以下のような情報が得られていました。
Androidスマホの Chrome で温湿度情報を取得する
温湿度が含まれる場所を確認(仕様の確認)
それでは最後の仕上げを進めるため、仕様の確認を行って取り出すべき情報を特定します。
公式の仕様や他の方が書いた記事を参照してみます。
●Meter BLE open API · OpenWonderLabs/python-host Wiki
https://github.com/OpenWonderLabs/python-host/wiki/Meter-BLE-open-API
先ほど取得できていたデータの中の「serviceData」に関わる部分からバイナリデータを取り出し、その中の特定のバイト列を処理してやれば温湿度の値を得られそうです。
温湿度を取り出してみる
それでは、上で書いていたソースコードに手を加えて、温湿度の出力も行ってみます。
「serviceData」に含まれる温湿度の内容を含んだバイナリデータを取得する方法をあれこれ調査していたところ、以下の別製品の事例で参考になりそうなものがありました。
●Reading Xiaomi Mi Scale data with Web Bluetooth Scanning API - DEV Community
https://dev.to/henrylim96/reading-xiaomi-mi-scale-data-with-web-bluetooth-scanning-api-1mb9
具体的には以下の部分です。
event.serviceData.forEach((valueDataView) => {
console.log(valueDataView.buffer);
});
上で利用したソースコードに上記の内容を加えたところ、コンソールで 6バイト分のデータが出力されるのが確認できました。仕様通りのデータが得られていそうなので、あとはここに含まれるバイト列の処理を行うだけです。
公式が提供している Node.js用のライブラリのソースコードの一部(switchbot-advertising.js の 149行目からの内容)を参考にして実装するのが良さそうでした。
上記を参考に作った最終版のソースコードは以下のとおりです。
バイナリデータを扱う部分は Node.js のサンプル通りに使えない処理が含まれているため、その部分の書きかえを行っています。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Web Bluetooth API によるスキャン</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.css"
/>
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">操作用ボタン</h1>
<div class="buttons" style="margin-top: 1.5rem">
<button
class="button is-success is-light"
type="button"
onclick="onStartButtonClick()"
>
スキャン
</button>
</div>
</div>
</section>
<script>
async function onStartButtonClick() {
await navigator.bluetooth
.requestLEScan({
filters: [{ services: ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] }],
keepRepeatedDevices: true,
// active: true,
})
.then(() => {
navigator.bluetooth.addEventListener(
"advertisementreceived",
(event) => {
event.serviceData.forEach((valueDataView) => {
if (valueDataView.buffer.byteLength === 6) {
const buf = new Uint8Array(valueDataView.buffer);
// console.log(buf);
let byte2 = buf[2];
let byte3 = buf[3];
let byte4 = buf[4];
let byte5 = buf[5];
let temp_sign = byte4 & 0b10000000 ? 1 : -1;
let temp_c =
temp_sign * ((byte4 & 0b01111111) + byte3 / 10);
let humidity = byte5 & 0b01111111;
console.log(`温度 ${temp_c}℃、 湿度 ${humidity}%`);
}
});
}
);
});
}
</script>
</body>
</html>
そして、実行結果は以下のとおりです。
SwitchBot温湿度計に表示された値と同じものをコンソールに出力できました。
まとめ
試行錯誤を続けた結果、無事に 3つ目の記事で完結させられました!
参考情報のメモ
本文中には含めていないものの、調査の過程で参照したページです。
- Web Bluetooth Scanning
- SwitchBot 温湿度計の測定値を BLE Advertisement パケットから直接読み取る - Qiita
- SwitchBot温湿度計をGrafanaで可視化 - kamijin-fanta
- MacでSwitchBot Meter(温度計)の値を読む | たくのこ Web
- node-switchbot/switchbot.js
- node-switchbot/switchbot-advertising.js
- Web Bluetooth Samples
- Web Bluetooth / Scanning Sample
余談
なお、記事の中で少し登場した公式の Node.js 用のライブラリを利用すれば、簡単に温湿度の値を取得できそうでした。
●OpenWonderLabs/node-switchbot
https://github.com/OpenWonderLabs/node-switchbot
準備も簡単で、npm を使ったセットアップは以下だけで完了です。
$ npm install @abandonware/noble
$ npm install node-switchbot
そして、公式ページに書かれたサンプルコードを実行するだけで、以下のように簡単にデータを取得することができました。
なお、実行した公式のサンプルコードは以下でした(※ 表示を折りたたんでいます)。
// 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);
// Stop to monitor
switchbot.stopScan();
process.exit();
})();