最近のNode.jsでBLEのアドバタイジングパケットを補足してみる

# はじめに

[noble](https://github.com/noble/noble)が虫の息というか、もう息の根を止めてあげていい状態なので他に移行しなければならない。さて次は何がいいだろうかと調べてみてラズパイ用途で行き当たるのは以下の2つくらいじゃないだろうか。

[node-ble](https://github.com/chrvadala/node-ble)
[node-bluez](https://github.com/WaeCo/node-bluez)

node-bleが開発が活発に見えるが、node-bluezもシンプルでよさげ。両方やってみることにする。試した環境はこちら

```shell
uname -a
Linux raspberrypi.local 5.8.0-1016-raspi #19-Ubuntu SMP PREEMPT Tue Feb 9 20:12:43 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux
```

# 前準備

node-bluzが依存するライブラリとnode.jsをインストールする

```shell
sudo apt install -y libglib2.0-dev libdbus-1-dev
sudo apt install -y nodejs npm
```

ここで入るnodeのバージョンは12.xで古い。nで最新を入れる

```shell
sudo npm i -g n
sudo n latest
sudo apt remove --purge nodejs
sudo apt autoremove
```

`/usr/local/bin` にlatest versionが入るのでパスを通しておく

```~/.profile
export "$HOME/usr/local/bin:$PATH"
```
```shell
source ~/.profile
node -v
v15.13.0
```

# TypeScriptの準備

```shell
mkdir -p work && cd $_
npm init -f
npm i -S typescript ts-node bluez node-ble sleep-promise
npx tsc --init
```

Promiseまわりのエラー回避で tsconfig.jsonのlibを以下にする

```tsconfig.json
lib: ["ES2015", "dom"],
```

# node-bleでやってみた

```node-ble.ts
import { createBluetooth } from 'node-ble';
import sleep from 'sleep-promise';

const main = async () => {
  const { bluetooth, destroy }: any = createBluetooth();
  const adapter: any = await bluetooth.defaultAdapter();
  if (! await adapter.isDiscovering()) {
    console.log('Discovering:');
    await adapter.startDiscovery();
    while (true) {
      const devices: any = await adapter.devices();
      console.log(devices);
      await sleep(3000);
    }
  }
};

main();
```

実行する

```
npx ts-node node-ble.ts 
Discovering:
[ '4E:2E:8E:F1:63:91', 'E1:A6:93:4A:ED:C3' ]
[ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D', 'E1:A6:93:4A:ED:C3' ]
[ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D' ]
[ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D' ]
[ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D', 'E1:A6:93:4A:ED:C3' ]
[ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D', 'E1:A6:93:4A:ED:C3' ]
```

とれた。Adapterにイベントハンドラがないので `Adapter.device()`を一定時間ループで回してとる手法。nobleでイベントドリブンな取得に慣れていたのでこれ思いつかなくてしばらくハマった。一回device()しただけじゃ絶対とれないし。これはこれでコールバックじゃない分制御の汎用性は高いと思うけど。好みの問題だろうか。

# node-bluezでやってみた

```node-bluez.ts
import Bluez from 'bluez';

const bluetooth = new Bluez();

const handleOn = async (address: string, props: any) => {
  console.log(`[NEW] Device: ${address}, ${JSON.stringify(props.Name)}`);
  const dev = await bluetooth.getDevice(address);
  dev.on("PropertiesChanged", (props) => {
    console.log(`[CHG] Device: ${address}, ${props}`);
  });
};

const main = async () => {
  try {
    bluetooth.on('device', handleOn);
    await bluetooth.init();
    const adapter = await bluetooth.getAdapter();
    await adapter.StartDiscovery();
    console.log("Discovering");
  } catch (e) {
   throw new Error(e);
  }
}

main();
```

実行する

```shell
npx ts-node node-bluez.ts 
Discovering
[NEW] Device: 54:AB:0C:6F:15:E6, undefined
[NEW] Device: E1:A6:93:4A:ED:C3, "tkr"
[NEW] Device: D1:1C:63:61:E0:01, undefined
[NEW] Device: E1:A6:93:4A:ED:C3, "tkr"
[NEW] Device: FB:33:A1:32:7F:75, "tkr"
[NEW] Device: E1:A6:93:4A:ED:C3, "tkr"
```

とれた。こちらはAdaptorにイベントハンドラがあって受信したタイミングでパケットが降ってくる。`tkr`は財布とかに入れてある忘れ物防止タグのTrackRだ。

# おわりに

どちらもざっくりコードをみるかぎりDbus APIのラッパーな感じ。アドバタイジングとるだけならイベントドリブンなnode-bluezのほうが好み。ケースバイケースで使っていこうと思う。やっとnobleの葬式ができそう。