MQTTでiTunesとコレクションケースのHueを連携してみる

この記事は [おうちハック Advent Calendar 2015](http://qiita.com/advent-calendar/2015/ouch-hack) 16日目の記事です。

いろいろたまっておりまして、Advent Calendarに少々遅れました・・・ということで16日目です。もう今年も何日もないですなあ・・・
思い返すと、今年はいろいろ変化があって、その中で広めの部屋に引っ越したりもしまして、「おうちハックの環境が整ったぞ！」といろいろ遊んでいた一年でした。

これまで[MQTTでIRKitやPhilips Hueの状況確認・操作を集約](http://qiita.com/yacchin1205/items/703677e2fc6015535a54)みたいな感じで、IRKitやHueのようなデバイスを手動で制御して遊んでいたわけですが、手動ができるようになると次は自動で何かしたくなってしまうのが人間というもの。

そんなわけで、デバイスをアプリケーション、特にiTunesの動作と同期させる、みたいなことを試して遊んでいるので書き留めておきます。

# Hueをコレクションケースに・・・でもどう制御？

引っ越しに乗じてコレクションケースを買って、[超合金魂 勇者王ガオガイガー](http://tamashii.jp/item/10814/)を買って飾ったりしています。非常にデカくて出来の良いおもちゃだけに、ただ飾っているだけでなく何か演出を加えたいと思っていました。

というわけで、安直ではありますが、余っていたHueを適当に付けてみました。

![image](https://qiita-image-store.s3.amazonaws.com/0/28770/9d3920f1-de06-d958-7a70-b683e7c98076.png)

Hueのアプリから色や明るさをいじって遊ぶだけでもだいぶ雰囲気が変わってテンションが上がるわけですが・・・おうちの何らかの変化をトリガーにして自動的にこのライトが動作するともっとテンションが上がりますよね。

# iTunesとHueを連携？

一方で、おうちはホームオーディオはiTunesに統一していて、MacからAirPlayでApple TVにつながったスピーカーで楽しむみたいな構成になっています。

当然この環境で「勇者王誕生！」とかアツい曲も楽しんでいるわけですが、**iTunesと同期する形でコレクションケースの照明が変わるとテンションが上がるんでは？**となりました。


## iTunesとMQTTを連携？

すでに先の記事でMQTT経由でHueを監視・制御できるようにしていたのもあり、**iTunesで再生中の曲情報がMQTTに伝わったり、MQTTに投げたメッセージによってiTunesで曲が再生されたりする**という形がいいんではと思って検討を始めました。

iTunesとその他のアプリを連携ということですぐ思いついたのが、DAAPを使ってiTunesの再生状況をリモートで監視・制御できんもんか。以前は野良DAAPライブラリでもiTunesとつなげたような気がしましたが、ちょっと試してみたところ、ここ最近のバージョンではうまくいかないケースに遭遇し断念。

そんな感じで探し求めてたどり着いたのがAppleScriptです。灯台下暗しというか。
AppleScriptを使うことで、他のMacのアプリケーションと連携するようなスクリプトを簡単に記述することができるわけですね・・・

MQTTとのやりとりとかはPythonで作りたかったわけですが、Pythonとの連携は[py-applescript](https://pypi.python.org/pypi/py-applescript/)があるので、こいつをインストールしておけばPythonからもAppleScriptを実行できます。以下のような感じ。

```py
import applescript

...

script = applescript.AppleScript('''
on current_state()
    tell application "iTunes"
        try
            set c to container of current track
        on error
            return {}
        end try
        set cinfo to {playlist_name:name of c}
        set tinfo to {track_name:name of current track, track_artist:artist of current track, track_album:album of current track}
        return cinfo & tinfo
    end tell
end current_state
''')

...

state = script.call('current_state')
```

これでiTunesのcurrent trackの情報がPythonから取得できます。素敵。

## iTunesとMQTTをつなぐやつを書く

ということで書きました。

- https://github.com/yacchin1205/mqtt-adapters

こいつをインストールすると、 **mqtt-itunes** というコマンドがインストールされます。
このコマンドを実行した状態で、MQTTサーバの `オプションで指定したトピック名/オプションで指定したiTunesの識別名/current` というトピックを購読することで、iTunesで再生中の曲の情報を得ることができます。

このトピックのデータはこんな感じ。

```json
{"state": "playing", "track_name": "勇者王誕生!", "playlist_name": "ミュージック",
 "track_album": "勇者王ガオガイガー～オリジナル・サウンドトラック 1",
 "track_artist": "遠藤正明"}
```

逆に、同じ形式のJSONをこのトピックに対して発行することで、曲を再生・一時停止・停止することもできるようにしてあります。

# MQTT経由でのiTunesとHueの連携

これで、すでにあったHue-MQTTの連携に加えてiTunes-MQTTの連携も可能になりました。あとは、以下のような機能を持つコードを書いて動かすだけです。

- iTunesのトピックを購読し、現在再生中の曲の情報を得る
- 再生中の曲の情報に合わせて、Hueのトピックに投げる


今はこんなコードです。

```py:GGG.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import json

TOPIC_BASE = 'xxxx'
MQTT_HOST = 'xxxx'
MQTT_PORT = xxxx
MQTT_USERNAME = 'xxxx'
MQTT_PASSWORD = 'xxxx'
HUE_BRIDGE_ID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
HUE_LIGHT_ID = x

def on_connect(client, userdata, flags, rc):
    client.subscribe('{}/itunes/+/current'.format(TOPIC_BASE))

def on_message(client, userdata, msg):
    song = json.loads(msg.payload)
    print('Changed(iTunes): {}'.format(msg.payload.decode('unicode_escape').encode('utf8')))
    color = get_hue_color_for_itunes(song)
    print('Change(Hue): {}'.format(color))
    client.publish('{base}/hue/{bridge}/light/{light}/status'.format(base=TOPIC_BASE, bridge=HUE_BRIDGE_ID, light=HUE_LIGHT_ID), payload=json.dumps(color))

def get_hue_color_for_itunes(song):
    if song['state'] == 'playing':
        if 'track_album' in song and u'勇者王' in song['track_album']:
            return {"on": True, "saturation": 253, "brightness": 218, "hue": 47125}
        if 'track_name' in song and u'勇者王' in song['track_name']:
            return {"on": True, "saturation": 253, "brightness": 218, "hue": 47125}
        if 'track_artist' in song and u'遠藤' in song['track_artist'] and u'正明' in song['track_artist']:
           return {"on": True, "saturation": 253, "brightness": 218, "hue": 47125}
    return {"on": False, "saturation": 253, "brightness": 95, "hue": 47125}



mqtt_client = mqtt.Client()
mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message
mqtt_client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
mqtt_client.connect(MQTT_HOST, MQTT_PORT)

mqtt_client.loop_forever()
```

曲が再生中である場合に、アルバム名かトラック名に「勇者王」が含まれているか、アーティスト名に「遠藤」「正明」が含まれていればHueを点灯します。それ以外は消灯。

これを動かしてみたときの出力例。

```
Changed(iTunes): {"state": "playing", "track_name": "勇者王誕生!", "playlist_name": "ミュージック", "track_album": "勇者王ガオガイガー～オリジナル・サウンドトラック 1", "track_artist": "遠藤正明"}
Change(Hue): {'on': True, 'saturation': 253, 'hue': 47125, 'brightness': 218}
Changed(iTunes): {"state": "playing", "track_name": "かくしん的☆めたまるふぉ〜ぜっ!", "playlist_name": "ミュージック", "track_album": "かくしん的☆めたまるふぉ〜ぜっ! - EP", "track_artist": "土間うまる(CV:田中あいみ)"}
Change(Hue): {'on': False, 'saturation': 253, 'hue': 47125, 'brightness': 95}
```

勇者王誕生！が再生開始されたときだけ、Hueがonになります。勇者王以外が再生開始されたり、あるいは再生自体が停止された場合はoffで。

こんな感じで、**特にそれぞれのプロトコルの詳細を気にすることなく、MQTTのメッセージ購読とメッセージ発行だけで自動的にデバイスが制御されるような感じ**にしてみながら、おうちの機能を開発してみています。
ということで。