クリスマスの定番（？） Raspberry Pi と Philips Hue でクリスマスツリーをライティング

> この記事は、 [North Detail Advent Calendar 2019](https://qiita.com/advent-calendar/2019/northdetail) の25日目の記事です。

> 2020/04/02 追記
> 本記事は、NorthDetail ブログにも投稿しています。
> https://www.northdetail.co.jp/blog/455/

メリークリスマス!
アドベントカレンダーも最終日のクリスマス、そしてクリスマスと言えばラズパイですよね!

ということで、 North Detail Advent Calendar 2019 の最後はクリスマスツリーを照らすライトを制御する仕組みをRaspberry Piで作ってみたいと思います。

* 2018年: https://www.indetail.co.jp/blog/181225/
* 2017年: https://www.indetail.co.jp/blog/171225/

## 動画

[![Controlled Philips Hue by Raspberry Pi (with Gyroscope)](http://img.youtube.com/vi/3I9leFDmfEQ/0.jpg)](https://youtu.be/3I9leFDmfEQ)
クリックすると、 YouTube で動画を観ることができます。

Raspberry Piに取り付けたセンサーを振ることでライトのON/OFFを、傾きで明るさや色合いの調整を行なえるようにしています。

## 揃えるもの

### Raspberry Pi

* [Raspberry Pi Zero WH](https://www.switch-science.com/catalog/3646/) (リンクは スイッチサイエンスさん のページ)

### センサー

傾きなどを検出できるものが用意できればOK。今回はこちらを使用。

* [ADRSZGR ゼロワン 9軸センサ拡張基板](https://bit-trade-one.co.jp/adrszgr/)

### Hue

#### ライト

今回は、以前に "Echo Show" 購入時にセットで買ったものを(ようやく)活用。

* [Philips Hue ホワイトグラデーション シングルランプ(電球色~昼光色)](https://www.amazon.co.jp/dp/B01MSITPT1) (リンクは Amazon.co.jpさん のページ)

#### ブリッジ

ブリッジを使って、 Raspberry Pi から Hue を繋ぎます。

より正確に言えばブリッジがWebサーバになっているので、 Raspberry Pi からブリッジに HTTP のリクエストを出すことで、 Hue の制御ができるようになります。

* [Philips Hueブリッジ](https://www.amazon.co.jp/dp/B01C6S7DDA) (リンクは Amazon.co.jpさん のページ)

### その他

* ライトスタンド(LEDライトつけて大丈夫なもの)
* ツリーなどの飾り

LEDライト共通の注意事項ですが、白熱電球とは **主に熱を持つ部分が異なります** 。

白熱電球は発光部分のフィラメントを中心に熱を持ちますが、LEDライトはソケット部分を中心に熱を持ちます。
そのため、ソケット周辺がきちんと放熱される構造のスタンドをご使用ください。

## Hue の設定

最初はスマホアプリを使って、ブリッジにライトを登録しておきます。
この辺りは、アプリの使用方法に沿って作業してください。

以降は、ブリッジと Raspberry Pi 、作業用のPCが全て同一のネットワークに存在する前提で進みます。

### ブリッジのIPアドレス確認

ブリッジへHTTPリクエストを出すことになるので、IPアドレスを知っておく必要があります。
これは、ブリッジが正しく設定できていれば下記アドレスへアクセスすることで取得できます。

https://discovery.meethue.com/

``` JSON
[
  {
    id: "XXXXXXXXXXXXXXXX",
    internalipaddress: "10.xxx.xxx.xxx"
  }
]
```

この `internalipaddress` が、現在接続されているネットワークでのIPアドレスになります。
メモっておきましょう。

### ブリッジへユーザー登録

Raspberry Pi から操作する場合、ブリッジ上に一意のユーザーIDが必要になります。
ブリッジ本体にあるボタン(物理)を押した後に、下記のような形でHTTPリクエストを投げると、レスポンスの中に `username` が入っています。
こちらを、今後のHTTPリクエストで使うのでこちらもメモしておいてください。

ちなみに、 `devicetype` は各自で自由に設定可能です。

```
POST　http://`internalipaddress`/api

body: {"devicetype":"tacck_hue_app#raspi"}
```

``` response.json
[
  {
    "success": {
      "username": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    }
  }
]
```

### ライトのID取得

続いて、ライトの操作をするためにブリッジに登録されているライトのIDを取得します。

```
GET http://`internalipaddress`/api/`username`/lights
```

``` response.json
{
  "1":{"state":{"on":false,"bri":254,...}
}
```

たくさん情報が返ってきますが、最初の `"1"` がIDになります。

下記のHTTPリクエストを投げると、ライトが点灯するはずです。
(`true`のところを`false`にすれば、消灯します。)

`internalipaddress`、`username`、`light_id`は、各自のものに置き換えてください。

```
PUT　http://`internalipaddress`/api/`username`/lights/`light_id`/state

body: {"on":true}
```


## Raspberry Pi

続いて、Raspberry Pi側のセッティングです。
Raspberry Piについては、すでにRaspbianが起動できている状態で、WiFiが利用可能な前提とします。
また、センサー(ADRSZGR)もRaspberry Pi本体と接続済みの想定です。

### センサー

まずは、センサーの値を取得できるか確認です。
こちらは、ベースとなるソースコードが提供されているので、それを使ってみます。

ベースのコード
https://github.com/bit-trade-one/RasPi-Zero-One-Series/tree/master/3rd/ADRSZGR_9-Axis_Gyro

Raspberry Pi上で実行してみましょう。

```
$ git clone https://github.com/bit-trade-one/RasPi-Zero-One-Series.git
$ cd RasPi-Zero-One-Series/3rd/ADRSZGR_9-Axis_Gyro
$ python3 ADRSZGR_Sample.py
10秒間、センサを動かさないでください
あと1秒 
キャリブレーション終了
X:-0.045608688186813184	Y:0.7310804429945055	Z:-4.547119140625

2.7179718017578125e-05,0.01416015625,0.0185546875,1.0087890625,-0.045608688186813184,-0.12341174450549453,-0.152587890625,-14.843956043956045,6.447374847374848,-1.6493284493284492,
0.05398225784301758,0.0126953125,0.01513671875,1.00927734375,0.13749678056318682,0.059693724244505475,-0.213623046875,-16.193406593406593,6.297435897435897,-0.8996336996336997,
(以下続くので、適当なところで Ctrl+C で中断。)
```

このように値が出てくれば、センサーが動いているのでOKです。

### 実装

では、センサーの値を使って Hue のライトを操作するように実装してみましょう。
センサーの制御はベースにある `GYRO.py` が全てやってくれるので、値をどう使うか、だけを考えれば大丈夫です。

今回は、センサーを振ったらON/OFF、傾けたら明るさと色調の変化、という設計で実装していきます。

先ほど確認で使った `ADRSZGR_Sample.py` を `index.py` としてコピーし、こちらを使っていきます。
また、私は普段Pythonを書かないので、インデントの谷[^1]に注意しながら実装していきます。

[^1]: 私の造語です。 https://speakerdeck.com/tacck/phpergawen-kitaipythonfalseikutukafalsekoto-pycon-mini-sapporo-2019-number-pyconsap?slide=18

色々と調整して、下記のようなコードで落ち着きました。

```
.
├── GYRO.py
└── index.py
```

``` index.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import signal
import requests
import time
import GYRO

GYR_THRESHOLD = 200.0

HUE_BRI_MIN = 0
HUE_BRI_MAX = 254
HUE_BRI_HALF = round((HUE_BRI_MAX - HUE_BRI_MIN + 1) / 2)
HUE_CT_MIN = 153
HUE_CT_MAX = 454
HUE_CT_HALF = round((HUE_CT_MAX - HUE_CT_MIN + 1) / 2)

HUE_API = 'http://`internalipaddress`/api/`username`/lights'

if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    gyro = GYRO.GYRO()
    time.sleep(0.1)
    gyro.sensor_calib()

    # 照明の点灯状態
    light_on = False
    prev_light_on = light_on

    light_bri = HUE_BRI_MIN + HUE_BRI_HALF
    light_ct = HUE_CT_MIN + HUE_CT_HALF


    while True:
        # センサー値の読み取りと必要な変数へ格納
        values = gyro.get_sense_value()
        axs_x = values[0]
        axs_y = values[1]
        # axs_z = values[2]
        # print('{0:4.2f}'.format(axs_x), end=",")
        # print('{0:4.2f}'.format(axs_y), end=",")
        # print('{0:4.2f}'.format(axs_z), end=",")

        # gyr_x = values[3]
        # gyr_y = values[4]
        gyr_z = values[5]
        # print('{0:4.2f}'.format(gyr_x), end=",")
        # print('{0:4.2f}'.format(gyr_y), end=",")
        # print('{0:4.2f}'.format(gyr_z), end=",")

        # X軸の傾きから色調を変更
        light_ct += axs_x * HUE_CT_HALF
        if light_ct < HUE_CT_MIN:
            light_ct = HUE_CT_MIN
        if light_ct > HUE_CT_MAX:
            light_ct = HUE_CT_MAX

        # Y軸の傾きから輝度を変更
        light_bri += axs_y * HUE_CT_HALF
        if light_bri < HUE_BRI_MIN:
            light_bri = HUE_BRI_MIN
        if light_bri > HUE_BRI_MAX:
            light_bri = HUE_BRI_MAX

        # Z方向の角速度が閾値を超えたら照明のON/OFFを制御
        if gyr_z < -GYR_THRESHOLD:
            light_on = not light_on

        # Hue へ投げる
        if prev_light_on != light_on or light_on:
            requests.put(HUE_API + '/`light_id`/state', json = {"on": light_on, "bri": round(light_bri), "ct": round(light_ct)})
        
        # ON/OFF 確認用ログ
        if prev_light_on != light_on:
            print ('lignht_on:', light_on)

        prev_light_on = light_on

        time.sleep(0.10)
```

実行した結果が、冒頭の動画となります。
これを踏まえて、ぜひもう一度ご覧ください。

[![Controlled Philips Hue by Raspberry Pi (with Gyroscope)](http://img.youtube.com/vi/3I9leFDmfEQ/0.jpg)](https://youtu.be/3I9leFDmfEQ)

### 注意

今回は0.1秒ごとに投げていますが、ネットワークやブリッジの負荷は自己責任でお願いします。

## まとめ

色々と組み合わせれば、部屋の照明も自在に操れるようになりますね。
家のライトを Hue のシリーズで揃えると、かなり楽しいことができそうです。