Bluetooth対応のHue(スマートライト)をRubyとBLEで制御してみた

Bluetooth対応のHue(スマートライト)をRubyとBLEで制御してみた

去年くらいに、PhilipsからBluetooth LEに対応したHue (スマート電球) が発売されました。
HueはもともとZigbeeを使用しており、色や明るさの制御にはHueブリッジという装置をLANに接続する必要があったのですが、Bluetooth LEに対応したことでブリッジがなくともスマートフォンの専用アプリから操作することができるようになり、導入がしやすくなったということです。


Philips Hue フルカラー シングルランプ Bluetooth + Zigbee対応|E26 LED電球 スマートライト


これを、スマートフォンアプリを介さずにRaspberry Pi等から制御できないか試してみました。

結論から言えば操作は可能でした。

ただし、古いバージョンのbluezだとペアリングがうまくいかなかったり、スマートフォンアプリからファクトリーリセットを行わないとペアリングができなかったりするなど、ハマりどころが結構多く、やや面倒だなという感じです。
また、PhilipsはBLEでの制御インターフェースは正式サポートしているわけではないので今後アップデートで変わる可能性もあるかも、としているようで、素直にブリッジを導入してそのHue APIを使うのが無難ではあります。

やり方

先に、制御する側のPCやRaspberry Pi等に最新版のbluezを導入し、Bluetooth LEが使えるようにしておいてください。
5.50-1.2~deb10u1 というバージョンでは動作を確認できましたが、これより古い物では動作しませんでした。

sudo apt upgrade bluez
sudo systemctl start bluetooth
sudo hcitool lescan 

を実行して、ずらずらとBLEデバイスが検出されればOKです。うまくいかない場合、

sudo hciconfig hci0 down
sudo hciconfig hci0 up

BluetoothアダプタをOFF/ONしてみるとうまく動作することがあります。


制御用のスクリプトRuby gemにしてあるので、まずはgemをinstallします。(gemのコードはこちらです → https://github.com/NeoCat/ruby_hue_ble )
まず適当なディレクトリに Gemfile を作って以下のように記述します。
ble gemに依存していますが、これが割と古くて現在のbluezのインターフェースと変更があるので、パッチを当てたバージョンを指定する必要があります。(Merge Requet取り込んでくれないかなあ)

source 'https://rubygems.org'

gem 'hue_ble'

# ruby_ble-1.0.0 is not compatible with recent bluez DBus interface, so use patched version...
gem 'ble', git: 'https://gitlab.com/NeoCat/ruby-ble.git'

そしてgemをインストールします。

gem install bundler # bundler が入っていない場合
bundle install --path vendor/bundle

bundle exec irb を起動して試すのが簡単です。
まずライブラリを require し、Hueデバイスをスキャンします。
スキャンの前に、以下のペアリングが可能な条件を満たすようにする必要があります。

  • スマートフォンアプリ "Phillips Hue Bluetooth" を使って電球を登録したあと、「リセット」を実行する
  • リセットから時間が経っている場合、電球の電源をOFF/ONし直す
  • RasPi等のBLEアダプターと電球を十分に(<90cm)接近させる

ペアリングは一台のホストとしかできませんので、 "Hue Bluetooth"アプリとRasPi両方から制御するといったことはできません。

> require 'hue_ble'
=> true
> HueBLE::scan_cli
Scanning devices ...
Found 1 Hue devices:
  C1:23:45:67:89:AB  Hue Lamp
Found a new Hue Lamp  E2:34:56:78:9A:BC : Do you want to pair? (Y/n)> y

ペアリング済みのHue電球の一覧が(あれば)表示されます。
新規に見つかったHue電球がある場合、ペアリングするか聞いてきます。
なお、このアドレスはファクトリーリセットするとランダムに変わります。

ここで y を選択するとペアリングを試み、上記の条件が満たされていれば成功するはずです。

うまくいかない場合、

sudo hciconfig hci0 down
sudo hciconfig hci0 up

で改善されることもあります。
(しかし systemctl restart bluetooth するとすでにペアリングしていたデバイスが消えてしまいます…。こうなるとまた電球をスマートフォンアプリからリセットしてやり直すしかありません。
リセット時は HueBLE::scan_cli(true) とすると、全デバイスがbluezから一旦削除されるので、これでやり直しができます。)


スキャンが成功すると、 HueBLE クラス内にHue電球の一覧が登録され、 HueBLE.hues でハッシュとして取得できるようになります。(keys = BLEアドレス、values = デバイス制御用のオブジェクト)

登録されたら、

HueBLE.hues.each_value { |hue| hue.on }
HueBLE.hues.each_value { |hue| hue.off }

で全部まとめてON/OFFしたり、色(色温度またはフルカラー電球の場合は色)や明るさを取得したり制御したりできます。

> hue = HueBLE.hues['C1:23:45:67:89:AB']
> hue.brightness  # 明るさ: 1 - 254
254
> hue.brightness = 200
> hue.color_temperature = 100  # 色温度: 1 (白) - 511 (黄)
> hue.color = [16000, 1]  # 色: x, y. それぞれ 1 - 65534 , 範囲外だとエラーとなる。この例は紫色

色は、CIE XYZのx, yを、1〜65534までのいずれかの値に変換した数値で指定するようです。色空間の範囲外だとエラーが返ってきます。
CIE 1931 色空間 - Wikipedia

party!

# 全電球をカラフルにランダム変化させる
def party!
  30.times do
    HueBLE.hues.each_value do |hue|
      hue.brightness = rand(253) + 1
      hue.color_temperature = rand(510) + 1
      10.times do
        hue.color = [rand(65533) + 1, rand(65533) + 1]
        break
      rescue BLE::Characteristic::NotFound
        break
      rescue
        next
      end
    end
    sleep 1
  end
end

party!


とかやって楽しみましょう。