HueをブリッジなしでラズパイからBLE(Bluetooth)で操作する

# 概要

スマートホームに組み込むにはHueブリッジもしくはzigbeeドングルがいることを知らずに[Philips Hue](https://www.philips-hue.com/ja-jp/product-overview)のBLE(Bluetooth)対応版(色つけられないやつ、2300円くらい)を買ってしまったので、ラズバイからBluez経由でどうにかHueをBLE操作するメモ。

これさえできればブリッジや追加機材の投資なしで[Home Assistant](https://www.home-assistant.io/)なんかのハブ経由で他のスマートホーム機器と同様に操作できるようになるのでがんばって調べてみた。


# 諸注意

* スマホの[Philips Hue Bluetooth](https://play.google.com/store/apps/details?id=com.signify.hue.blue)アプリで登録していたり、すでに別の機器で一度ペアリングしている場合は、以下の操作をしてもうまくいかないようです。(おそらく非公開の特別な操作をしないと最初に接続操作した機器以外からの操作は受け付けない仕様だと思われる)
* Philips Hue Bluetoothに登録されている場合は、「**リセット**」(「削除」ではないので注意)をして工場出荷状態に初期化したうえで登録解除します。
* すでに一度別の機器にペアリングしている場合は、**Philips Hue Bluetoothアプリで再度登録**した上で「リセット」します。
* Hueは「リセット」するとHue本体のMACアドレスが全く別の値に変わってしまう仕様になっているようです。うっかりリセット前のMACアドレスと間違えないように気をつけましょう。逆に言えばMACアドレスが変わっていない場合は間違えて削除したなどでリセットに失敗しています。
* いくらなんでも古いBluetooth対応前のHueでは無理なんじゃないかと思います。

# Bluezを最新版に

Raspberry Pi4に入っているBluezが古いとHueに接続しようとしてもConnection Refused(111)で問答無用でハネられるようなので、最新版にアップデートする。

```
$ sudo apt-get install bluetooth bluez blueman bluez-hcidump
```

ちなみに動いたバージョンは5.50でした

```
$ bluetoothd -v
5.50
```

# HueのMACアドレスを探す

hcltoolで探します。

```
$ sudo hcitool lescan
```

Hue lampとかHue white lampとか出ているものがそれです。

# gatttoolで接続してみる

bluezをインストールしていればgatttool(t3つだゾ)コマンドが使えるはずなので一度接続してみる。

```
$ gatttool -l low -t random -b [さっき調べたMACアドレス] -I
[MACアドレス][LE]> 
```

-l lowはなくてもいいかもしれないけど一応、-t randomはつけないと接続できない模様。なんでなのかはよくわからない・・・
-Iはインタラクティブモードで実行するということです。

問題がなければ[MACアドレス][LE]>といったプロンプトが出るので、connectコマンドで接続する。

```
[MACアドレス][LE]> connect
Attempting to connect to MACアドレス
Connection successful
[MACアドレス][LE]> ←色が変わっている 
```
MACアドレス部分の色が変わっていると接続中ということになります。以下の操作はこの状態で行います。

# UUIDと対応するハンドルを調べる

詳しくはわからないが、BLEは特定の64bitか128bitのUUIDを指定してそれを読み書きすることで操作したり状態を取得したりする仕組みになっている模様。

ググってみた感じシグニファイはBLE経由のHue操作はオープンにしない方針のようですが、[調べて表にしてくれている方がいるので](https://gist.github.com/shinyquagsire23/f7907fdf6b470200702e75a30135caf3)ありがたく参照すると、UUID[932c32bd-0002-47a2-835a-a8d455b859dd]に00か01を書き込むことでBluetooth経由でon/offできるらしい。

ただ、gatttoolの書き込みはなぜかUUIDではなく対応するハンドル番号で指定する必要があるようなので、UUIDに対応するハンドル番号を調べるためchar-descコマンドを実行する。

```
[MACアドレス][LE]> char-desc
　　　　　　　　　　　　　〜　中略　〜
handle: 0x0023, uuid: 97fe6561-2001-4f62-86e9-b71ee2da3d22
handle: 0x0024, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0025, uuid: 97fe6561-2002-4f62-86e9-b71ee2da3d22
handle: 0x0026, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0027, uuid: 97fe6561-2004-4f62-86e9-b71ee2da3d22
handle: 0x0028, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0029, uuid: 97fe6561-a001-4f62-86e9-b71ee2da3d22
handle: 0x002a, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002b, uuid: 97fe6561-a002-4f62-86e9-b71ee2da3d22
handle: 0x002c, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x002d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002e, uuid: 932c32bd-0001-47a2-835a-a8d455b859dd
handle: 0x002f, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0030, uuid: 932c32bd-0002-47a2-835a-a8d455b859dd
handle: 0x0031, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0032, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0033, uuid: 932c32bd-0003-47a2-835a-a8d455b859dd
handle: 0x0034, uuid: 00002902-0000-1000-8000-00805f9b34fb
```

実行するとこんな感じでハンドル番号とそれに対応するUUIDの一覧が表示される。

ここではhandle 0x0030が先程調べたon/offのUUIDに対応していることがわかる。0x0033が調光かな？

個体やバージョンアップ等でハンドルが異なるかもしれないので、都度調べるのが無難。実際一度Hueのファームウェアを最新バージョンにバージョンアップしたら対応するハンドル番号が変わりました。

表が公開されていない別のBLE製品を使う場合はスマホの[nRF Connect for Mobile](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=ja)を使うとUUIDの一覧が表示され、そこから読み込み書き込み操作ができるので、対応するUUIDを調査しやすいのではないでしょうか？

# 一度読み書きしてみる

読み書きするコマンドはいろいろあるようですが、Hueでon/offする場合はchar-read-hnd、char-write-reqを使うようです。

ここではon/offに対応するハンドル番号が0x0030とします、違う場合は適宜読み替えてください。

```
[MACアドレス][LE]> char-read-hnd 0x0030
Characteristic value/descriptor: 01 
```
これでランプの状態が取得できました。今は01でonになっているということです。

今度はこれをoffにします

```
[MACアドレス][LE]> char-write-req 0x0030 00
Characteristic value was written successfully
```

ハンドル番号は0xをつけないとうまく行きませんが、書き込む値の指定に16進数を使う場合は逆に0xをつけるとうまく行かないようです。なんでこんなことになってるのかよくわかりません・・・。
うまく行けば「Characteristic value was written successfully」と出てきます。

# コマンド1行で実行する

今まではgatttoolをインタラクティブモードで実行して接続・操作をしましたが、gatttoolはこの操作をコマンド1行で実行することもできるようです。

Hueライトのonをgatttool1行で実行する場合は

```
$ gatttool -l low -t random -b [HueのMACアドレス] --char-write-req -a 0x0030 -n 01
```
-a のあとにハンドル番号、-nのあとに値を指定する(この場合01がonで00がoff)と上記でインタラクティブモードでやったことと同じことがコマンド1行でできるようになります。

上記のコマンドを指示に応じてスマートホームハブから実行するように設定すればスマートホーム経由で操作できるようになります。