MQTTでIRKitやPhilips Hueの状況確認・操作を集約

IRKitやPhilips Hueのようなスマートな感じのデバイスを日常的に使っているのですが、とりあえずで標準アプリを使ってこれらを使っています。こうしていると、やはりデバイスごとに複数のアプリを使い分けるのが面倒くさくなり、「なんか自宅用のアプリを作るかなあ」という気持ちになってきます。

これらのデバイスはAPIも非常にわかりやすく、ライブラリもすぐに見つかり非常によい感じです。ただ、デバイスの発見、現在の状態をポーリングしてチェックなど、状態管理的なコードを書かなければならない部分がどうしても出てきて(これはやむを得ない部分ではありますが)、ちょっと面倒だなという気持ちになります。

そんな中ここ最近よく聞くようになったMQTTを思い出しました。
Pepperのお仕事なども通じて、MQTTのようなPublish/Subscribeモデルは、こういったデバイスが散在している状況を束ねていくのに非常によい感じがするんですよね。

そんな感じで、ちょっとネタとしてMQTTとこれらのデバイスをからめてみるようなコードを書いてみたのでそのメモ。

# MQTTでまとめようと思った経緯

正直、これらIRKitなどのデバイスはAPI, ライブラリが非常に充実しているため、いちいち余計なことをせずそのまま使うのがいいのかなとも思ったわけですが、今回MQTTでまとめようと思った経緯を少し書き留めておきます。

## 1.デバイスの発見

デバイスの発見はネットワークにつながるデバイスには必ずつきまとう問題です。例えば今回ネタにするIRKitとHueの場合でも、

- IRKit ... Bonjour
- Hue ... SSDP (UPnP)

みたいな感じで、ライブラリなど世の中にあるものの(特に[IRKit](http://getirkit.com/)は公式サイトで非常に丁寧にまとめられている)、リクエスト投げつけたらレスポンス返ってくるぞ、とまでシンプルにはいきません。

また、この手のライブラリを利用するにはある程度の状態管理の処理(見つかっているデバイスにはどのようなものがあるか、など)が必要になります。「認識されているデバイスすべてに一斉送信！」とか、「認識しているデバイスすべてを対象に何がおきたか監視」みたいなラフな感じで処理を記述したくなるのが人情というものです。

## 2.デバイスの監視

お仕事でシステムの運用監視みたいなことをやっている関係上、こちらから一方的に操作だけでなく、変化を記録することで何かやりたくなってしまいます。たとえば、ある赤外線信号が得られたら照度センサーから得られる値が変わるとか何かに学習させたくなります。

当然、これらのデバイスも情報取得の方法を用意してくれていますが、少なくともこれらのデバイスに関しては、こちらから「情報をくれ」とリクエストすると「ほらよ」と教えてくれる形式なので、常時監視しようとすると何らかの方法で一定間隔でポーリングする必要があります。

また、これらの組み込みデバイスのプロセッサはPCのようなリッチなものではありませんので、できるだけ同時にリクエストしないとか、負荷のことを気遣ってあげたりしたいところです。こういったことを配慮することを、アプリ開発ではできるだけ減らしておきたいところです。たとえば、特定のトピックにハンドラ登録しておけば呼び出してくれるようなお手軽さが欲しくなってしまいます。

## そんなわけで

というようなことをつらつら考え、

- デバイスの追加、削除に関する情報の取得
- デバイスの状態変化の取得
- デバイスへの操作指示

みたいなことを以下のような枠組みで考えてみようかなという気持ちになりました。

![image](https://qiita-image-store.s3.amazonaws.com/0/28770/53358966-be0a-1bd2-9b68-6c88053c5efe.png)

MQTT Adapterはデバイスが見つかったりデバイスの状態に変化があるとMQTT BrokerにメッセージをPublishします。アプリケーションは必要に応じてこれらの情報をSubscribeして利用することができます。

一方、アプリケーションがデバイスの状態を変化させたいときはMQTT Brokerに指示メッセージをPublishします。MQTT AdapterはこれらのメッセージをSubscribeしていて、メッセージに応じてデバイスに指示を送ります。

このように、MQTTでメッセージのPublish/Subscribeの形で記述できると幸せになれるかもなあー、ということで。

# 作ったもの

https://github.com/yacchin1205/mqtt-adapters に置いてあります。

インストールはpipで、

```
$ pip install git+https://github.com/yacchin1205/mqtt-adapters.git
```

みたいにしてもらう想定で。適宜sudoなりvirtualenvなり準備してください。

以下のコマンドがインストールされます。ちなみにQoSとかはあんまり考えていません。また、メッセージ数などももっと節約するようにせねば・・・という感じがあります。今後使いながら改善していく予定。

## mqtt-irkit ... MQTT Adapter for IRKit

IRKitの探索と、IRKitに入力された赤外線信号を監視、操作する機能を提供するコマンドです。

mqtt-irkitを実行すると、実行オプションで指定されたMQTT Brokerに接続したのち、以下のような動作をします。

1. ネットワークに接続されているIRKitを探索し、発見すると、 `irkit/デバイス名` というトピックにデバイスが追加されたことや削除されたことをPublishする
2. 発見したIRKitに対しては5秒おきに赤外線信号の受信の有無を調べ、赤外線信号を受信した場合は `irkit/デバイス名/messages` というトピックに赤外線信号の情報(JSON)をPublishする
3. `irkit/デバイス名/messages` というトピックをSubscribeしており、ここにメッセージがきたらpayloadを赤外線信号の情報(JSON)と解釈しIRKitに送信を指示する

トピック名の先頭 `irkit/` は実行オプションで変更できるようになっています。

## mqtt-hue ... MQTT Adapter for Hue

Hueブリッジの探索と、Hueブリッジに接続されているライトの状態を監視、操作する機能を提供するコマンドです。

mqtt-hueを実行すると、実行オプションで指定されたMQTT Brokerに接続したのち、以下のような動作をします。

1. ネットワークに接続されているHueブリッジを探索し、発見すると、 `hue/デバイスのUDN` というトピックにデバイスが追加されたことや削除されたことをPublishする(※)
2. 発見したHueブリッジに対しては1秒おきにライトの情報を調べ、ライトの状態が変化した場合は `hue/デバイスのUDN/light/ライトID/status` というトピックにライトの情報(on, saturation, hue, brightnessの情報をJSONで格納したもの)をPublishする
3. `hue/デバイスのUDN/light/ライトID/status` というトピックをSubscribeしており、ここにメッセージがきたらpayloadをJSONと解釈し、on, staturation, hue, brightnessの値をライトに反映する

※初回実行時は「30秒以内にリンクボタンが押されていないぞ」と怒られるので、Hueブリッジのリンクボタンを押してから再実行する。

トピック名の先頭 `hue/` は実行オプションで変更できるようになっています。

# 使い方の例

うちでは以下のような構成で使ってみています。

- MQTT Broker: sango (https://sango.shiguredo.jp/)
- mqtt-* を動作させるマシン: Raspberry Pi2 Model B(Raspbian Wheezy)

![image](https://qiita-image-store.s3.amazonaws.com/0/28770/f3c57763-0772-dcd0-5d99-c8130f083a2f.png)

Raspberry Piにpipで `git+https://github.com/yacchin1205/mqtt-adapters.git` をインストールしています。Raspberry Piにpipなどのツールをどう準備するかなどはたくさん記事があるので、ここでは触れません。

## コマンドの実行例: サービスとして起動する設定の例

Raspberry Piに電源が入ったら自動でmqtt-irkitとmqtt-hueが起動するように設定したくなります。
今回はsupervisorを使ってmqtt-irkitとmqtt-hueを実行しています。

手順は以下のような感じ。supervisorに関する説明はいろいろあるので、詳しくは他を参照してください。

1. supervisorをインストール

    ```
    $ sudo apt-get install supervisor
    ```

2. supervisorの設定ファイルを記述

    ```sh:/etc/supervisor/conf.d/hue.conf
    [program:hue]
    command=mqtt-hue -H lite.mqtt.shiguredo.jp -p 1883 -u sangoのユーザ名 -P sangoのパスワード -t sangoのユーザ名/hue/
    numprocs=1
    redirect_stderr=true
    stdout_logfile=/var/log/hue.log
    user=root
    ```

    ```sh:/etc/supervisor/conf.d/irkit.conf
    [program:irkit]
    command=mqtt-irkit -H lite.mqtt.shiguredo.jp -p 1883 -u sangoのユーザ名 -P sangoのパスワード -t sangoのユーザ名/irkit/
    numprocs=1
    redirect_stderr=true
    stdout_logfile=/var/log/irkit.log
    user=pi
    ```

3. supervisorにサービスを追加

    ```
    $ sudo supervisorctl reread
    $ sudo supervisorctl add irkit
    $ sudo supervisorctl add hue
    ```

4. サービスが起動したか確認

    ```
    $ sudo supervisorctl status
    hue                              RUNNING    pid 2353, uptime 0:12:36
    irkit                            RUNNING    pid 2430, uptime 0:12:35
    ```

みたいな感じで。

こういうデバイスはとにかく使ってみないといけない感じがするので、しばらく使ってみながら改善していこうと考え中。