Siri + IRKit + Homebridge でエアコンを操作すると快適

追記

MacじゃなくてRaspberry Pi で動作させるようにしました。
Siri - Raspberry Pi - iRKit でエアコン操作 - rochefort’s blog


概要

赤外線のデータ送信を行える IRKitを買った時から、Siriでエアコンの ON / OFF を行いたいと思っていました。
iPhone 6s 以降、充電中以外も Siri が反応してくれていますので、昨年iPhone7を購入したので実現してみました。
 
やってみたところ、まずまず快適です。なんつーか、楽しいです。
あえて文句を言うなら「Hey, Siri」を「Siriちゃん」に変更したいくらいでしょうか。
 
図にするとこんな感じ。
f:id:rochefort:20170106042045j:plain

IRKit

赤外線を送信するこの手のやつでは、格安の上にAPIが充実しています。Rest APIが叩ける人であれば、買って損はないでしょう。
リモコンで赤外線を送信するタイプの家電であれば、リモコン不要で操作できるようになりますし、インターネット経由での利用も可能です。位置情報やIFTTTなどと組み合わせるのも可能です。

赤外線データの確認

初期設定などは省略して、データの確認方法から。
通常は、公式に記載されている通り(IRKit - Open Source WiFi Connected Infrared Remote Controller)以下を実行すれば、最新のデータ内容が確認できます。

curl -i "http://10.0.1.2/messages" -H "X-Requested-With: curl"

しかし、先日実施したFirmwareのUpdate のせいか、bodyが取得できなくなってしまっていました。

iPhoneのパケットキャプチャ

仕方がないので、WireshirkでiPhoneをCaptureしてみました。
ググると沢山出てきますが、
iPhone端末とかのパケットキャプチャ - Qiita
iPhoneMacにつないで、XcodeでUDIDを確認(Window –> Devices)し、仮装インターフェースを作成し、あとはお好きな方法でキャプチャすればOKです。
こんなに簡単にできるとは知りませんでした。

# 仮装Interfaceの作成方法
rvictl -s <your_iphone_udid>

以降、ここでキャプチャしたデータを使います。

homebridge

こいつがイカしてるのですが、iOS HomeKit API をエミュレートする Nodeのサーバー とのこと。
nfarina/homebridge: HomeKit support for the impatient
いろんなデバイスプラグインに対応しており、roomba、hue などの面白家電達のプラグインも実装されています。
 
ただ、少々不安定。

Installation

npm -g install homebridge

あとは、 ~/.homebridge/config.json を作成(後述)し、homebridge を実行すれば、 iPhoneでHome Kit アプリとして登録することができてしまいます。

homebridge-irkit

IRKitのプラグイン
senyoltw/homebridge-irkit: Supports IRKit on Homebridge Platform. IRKitをSiri(Homekit)で操作するやつ。

Installation

nmp -g install homebridge-irkit

config.json

以下は、githubのsampleコード。 irkit_hostは、dns-sd -B irkit.tcp で調べた値を設定。 on_form、off_form は、パケットキャプチャした内容を設定。

# ~/.homebridge/config.json 
{
    "bridge": {
        "name": "Homebridge",
        "username": "CD:22:3D:E3:CE:30",
        "port": 51826,
        "pin": "031-45-156"
    },
    
    "description": "The Onion!",

    "platforms": [],

    "accessories": [
        {
            "accessory": "IRKit",
            "name": "irkit control device",
            "irkit_host": "irkitxxxxx.local",
            "on_form": {"format":"raw","freq":38,"data":[]},
            "off_form": {"format":"raw","freq":38,"data":[]},
        }
    ]
}

homebridge の launchd 登録

ここまでくると、homebridgeを起動するだけなのですが、今回はMacのサービスとして稼働させるためlaunchd へ登録します。 Install Homebridge on OSX · nfarina/homebridge Wiki
ここに記載の通り、 ~/Library/LaunchAgents/com.homebridge.server.plist を作成

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>Label</key>
    <string>com.homebridge.server</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/rochefort/.nodebrew/current/bin/homebridge</string>
        <string>-D</string>
    </array>
    <key>StandardOutPath</key>
            <string>/var/log/homebridge/homebridge.log</string>
    <key>StandardErrorPath</key>
            <string>/var/log/homebridge/homebridge_error.log</string>
    <key>EnvironmentVariables</key>
    <dict>
            <key>PATH</key>
            <string>PATH=/Users/rochefort/.rbenv/shims:/Users/rochefort/.rbenv/bin:/Users/rochefort/.nodebrew/current/bin:$PATH</string>
    </dict>
</dict>
</plist>

各種パスを適切なものに変更。
ログの場所をどうするか悩んだのですが、とりあえず、/var/log/homebridge 以下に保存。(権限に注意。とりあえずlogrotateは後回し。)
デフォルトだと、-I オプションを指定するようになっていましたが、helpを見る限り必要なさそうなのと、実際になくても動作するので無くしました。代わりに -D のdebugオプションをつけています。

allow unauthenticated requests (for easier hacking)

あとは、launchctlで登録。プロセスが起動していればok

launchctl load ~/Library/LaunchAgents/com.homebridge.server.plist

iPhoneホームアプリへの登録

登録自体は、インストラクションに従うだけですので割愛。
手動でPIN登録(config.jsonの値)すれば homebridge本体とエアコンのアクセサリーが登録されます。

こんな感じ

エアコンをつけて/消して などで操作できるようになりました。
f:id:rochefort:20170106023456p:plain かわいいw。
無駄に何度も点けたり消したりしてしまってます。

その他

iPhoneのホームアプリから、アクセサリを追加できなくなった場合

色々試しているうちに、動作が不安定になったため、一度ホームアプリからアクセサリの削除をしてみたところ、再追加できなくなってしまいました。
ググるとhomebridgeの再インストールで可能などと書かれていたりしましたが、こちらでは不可でした。
正しい対応かどうか不明ですが、とりあえず、~/.homebridge/persist を削除することで対応できました。

homebridge が socket hang up する

連続で実行すると socke hang up というメッセージがログに出力され以降の処理は失敗し続けます。
debugまではできていないのですが、こうなると、homebridge の再起動が必要なようです。

MacをSierraにUpdate

Sierra に Update してみました。
とりあえず問題なく動作していますが、Bartender という メニューバーのアイコンを隠すアプリのみ動作しなかったため、お金で解決しました。

Bartender 1系だとエラーとなる

El Capitan では Bartender 1系でも動作していたのですが、
Sierra に Update すると Bartender 2系でしか動作保障されていないようでしたので
やむなく Upgrade (Upgradeの場合は、50% OFFで914円。)しました。
必要な機能のみであれば、本気出せば作れそうな気もするけど、費用対効果を考えて購入。

IRKit Firmware を Update (v3.0.0)

昨年からほったらかしにしていたIRKit の Firmware updateを行いました。
IRKit v2以下は脆弱性があるので、updateが必要です。
IRKitの脆弱性とファームウェアアップデートのお願い - maaash.jp

やり方

上記URLに記載の通り、updater を使えば良いようですが、エラーになるようです。

# Mac OS 10.12.2 (Sierra)

Downloading v3.0.0 from https://api.github.com/repos/irkit/device/releases/1712146
Successfully downloaded to /var/folders/56/tt94wwzs0cg0z4lwpq8qc6wr0000gn/T/f-11704-7085-1whnyzk
Reading current firmware from IRKit
Will run: /private/var/folders/56/tt94wwzs0cg0z4lwpq8qc6wr0000gn/T/AppTranslocation/B1E59226-19E6-48E7-B9CF-6AD1D66923A9/d/IRKit Updater.app/Contents/Resources/app/bin/pulser_darwin_amd64 /dev/cu.usbmodem1421
......................................Trying again
Will run: /private/var/folders/56/tt94wwzs0cg0z4lwpq8qc6wr0000gn/T/AppTranslocation/B1E59226-19E6-48E7-B9CF-6AD1D66923A9/d/IRKit Updater.app/Contents/Resources/app/bin/pulser_darwin_amd64 /dev/cu.usbmodem1421
......................................Finished with error: Port not found within 10 seconds

仕方がないので、github に記載の irkit/device: Infrared-WiFi-HTTP bidirectional proxy device Arduino IDE を使ったやり方で実施してみました。

注意点

これで良いのか不安ですが、とりあえず動作はするようです。

version.c

srcのcommit versionを記載しているようなので以下のようにします。

const char version[] = "v3.0.0.0.g85190b1";

GSwifi.cpp

最新のソースと行数があっていないようですが、以下の XXXXXXXXXX を変更すれば動作しました。

char* GSwifi::password() {
    // reuse index: 0 area
    // this should be safe if we immediately call `strcpy( target, password() )`
    char *ret = PB("XXXX, XXXXXXXXXX", 0);
    return ret + 5; // we detect ^ this pattern in password replacer
}

USBケーブル

転送用と充電用があるのを忘れてしまっており、小一時間ほど悩んでしまいました。
転送用を使いましょう。

後は

起動後にiOSのアプリで「名前を変更」を一度選んでやると version表記が変わりました。

f:id:rochefort:20170104170339p:plain:w350

確認してみる

以下でIPアドレスを調べます。

$ dns-sd -B _irkit._tcp
Browsing for _irkit._tcp
DATE: ---Wed 04 Jan 2017---
17:00:15.220  ...STARTING...
Timestamp     A/R    Flags  if Domain               Service Type         Instance Name
17:00:15.390  Add        2   4 local.               _irkit._tcp.         IRKit9628

$ dns-sd -G v4 IRKit9628.local
DATE: ---Wed 04 Jan 2017---
17:00:24.860  ...STARTING...
Timestamp     A/R Flags if Hostname                               Address                                      TTL
17:00:25.578  Add     2  4 IRKit9628.local.                       192.168.1.38                                 10
# status 200(bodyが空なのは良いのか?)
$ curl -i "http://192.168.1.38/messages" -H "X-Requested-With: curl"
HTTP/1.0 200 OK
Access-Control-Allow-Origin: *
Server: IRKit/v3.0.0.0.g85190b1
Content-Type: text/plain

# status 400
curl -i "http://192.168.1.38/messages"
HTTP/1.0 400 Bad Request
Access-Control-Allow-Origin: *
Server: IRKit/v3.0.0.0.g85190b1
Content-Type: text/plain