はじめに
NRI OpenStandiaのアドベントカレンダーですが、業務に関連する話ではなく、完全に趣味の話で、Homebridgeのプラグインの一つであるhomebridge-cmd4を紹介します。
HomebridgeはHomeKitに対応していない製品とHomeKitを仲介してくれるサーバアプリです。プラグイン形式で様々なIoT製品とHomeKitの仲介をしてくれます。HomeKitはAppleのIoTシステムです。HomeKitに対応しているとApple製品のホームアプリやSiriから操作できるようになります。
Homebridgeについては多くの紹介記事等が公開されているので、QiitaやGoogleで検索してみてください。
homebridge-cmd4とは
hoembridge-cmd4はホームアプリの操作等に合わせてHomebridgeを実行しているサーバ上のコマンドを実行するプラグインの一つです。
HomeKitの様々なアクセサリタイプ(スイッチやモーションセンサ、テレビ等)に対応しているのが特徴です。READMEのはじめではすべてのアクセサリタイプをサポートしていると書かれています。実際はカメラは対応してないようなので、完全なすべてではないですがほぼ全てサポートしているようです。
Nature Remoのエアコン操作用プラグイン(NatureRemoAircon)やセンサ用プラグイン(remo-sensor)のような専用のプラグインが公開されていないものに、homebridge-cmd4を使うことで個別のプラグイン開発までいかずに対応できます。
今回は私が実際に使っている設定をいくつか紹介します。
設定例
Switch
まずは一番基本的と思われるSwitchの紹介を通して、homebridge-cmd4の設定の全体感を説明します。なお、Switchは名前の通りスイッチアクセサリです。単純なオンオフに対応しています。
私はWoLとSSHでの作業用サーバ(PC)の電源オンオフや床暖房のオンオフ等に使っています。今回はサーバのオンオフする設定を元に設定方法をご紹介します。
ホームアプリ上の見た目は以下です。
早速設定ですが、まずはHomebridgeのconfig.jsonからです。
Switchの設定例は以下のとおりです。
"platforms": [
{
"platform": "Cmd4",
"name": "Cmd4",
"accessories": [
{
"type": "Switch",
"name": "Server",
"displayName": "server",
"on": "FALSE",
"polling": [
{
"characteristic": "on",
"interval": 5,
"timeout": 10000
}
],
"state_cmd": "/patch/to/server.sh"
}
]
}
]
platforms
中のplatform
やname
はHomebridgeのプラグインの基本的な設定内容なので、解説は割愛します。
accessories
の要素についての説明を以下表にまとめます。
項目名 | 内容 |
---|---|
type | アクセサリタイプを指定します。 |
name | ホームアプリ上で表示される名前等に利用されます。指定がない場合displayName の値が利用されます。 |
displayName |
state_cmd で指定されたコマンドの実行時にアクセサリ名としてコマンドに引数で渡されたり、ログ出力等に利用されます。指定がない場合name の値が利用されます。 |
on |
characteristic と呼ばれるアクセサリのステータスのようなものとなり、利用するものを宣言しておくイメージの設定となります。アクセサリタイプによって利用できるcharacteristic は異なり、アクセサリタイプにより必須のものと任意のものがあります。何が必須なのかなどは設定値の解説等が公開されているhomebridge-cmd4のGitHub Pagesが参考になると思います。 |
polling |
characteristic の状態を確認するポーリングの設定です。ホームアプリでの操作以外でアクセサリの状態が変わっていることを検知します。interval はポーリング間隔(秒)、timeout はポーリングリクエストのタイムアウト時間(ミリ秒)の指定です。interval とtimeout は指定がない場合のデフォルト値は60秒です。配列で複数のcharacteristic のポーリング設定を記載できます。また、配列とせず、poling: true という指定もでき、この場合デフォルトのcharacteristic に対し、ポーリング間隔等もデフォルト値でポーリングを行います。デフォルトのcharacteristic はhomebridge-cmd4のGitHub Pagesを参照ください。 |
state_cmd | ホームアプリからの操作やポーリング時に実行するコマンドの指定です。 実行するコマンドはhomebridge-cmd4では1つのアクセサリに対して一つのコマンドを指定します。呼び出しの場面によって4つの引数が渡されてそのコマンドが実行されます。引数の詳細については次に説明するコマンドの実装の中で紹介します。 |
次にhomebridge-cmd4から呼び出されるコマンドについてです。
config.json
のstate_cmd
の説明で紹介したとおり、homebridge-cmd4は4つの引数でコマンドを実行します。第1引数はGet
またはSet
が入ります。Get
はポーリングでのアクセサリのステータス確認時に渡され、Set
はスイッチオン等のステータス変更時に渡されます。第2引数はアクセサリのdisplayName
が入ります。一つのコマンドで複数のアクセサリに対応したい場合や、後ほど説明するlinkedType
というアクセサリタイプを入れ子にする設定を利用する際に使用します。第3引数はcharacteristic
が入ります。確認や変更をする対象のcharacteristic
が何なのかの判定に利用します。config.jsonの説明の中でも触れたように、アクセサリごとに対応しているcharacteristic
は異なります。第4引数は第1引数にSet
が入る場合に変更後のステータスが入ります。
上述のconfig.jsonの設定内容でスイッチオンされた場合のコマンドは次のようになります。
/path/to/server.sh Set server On 1
オフの場合は第4引数が0
となり、ポーリングでは第1引数がGet
となることになります。
この引数に合わせてコマンドを用意します。私は以下のようにしています。サンプルでBashが使われていたこともあり、Bashで実装しています。
なお、Get
に対しては標準出力で結果を返す形になります
#!/bin/bash
if [ "$1" = "Get" ]; then
case "$3" in
"On")
ping -c3 server > /dev/null
if [ $? = 0 ]; then
echo 1
else
echo 0
fi
;;
esac
exit 0
fi
if [ "$1" = "Set" ]; then
case "$3" in
"On")
if [ "$4" = "1" ]; then
awake xx:xx:xx:xx:xx:xx
exit 0
else
ssh server 'sudo shutdown -P now'
exit 0
fi
;;
esac
fi
電源の状態はpingで確認しています。結果を標準出力で返す必要があるため、ping自体の出力は捨てています。
AirPurifier
次はAirPurifierの設定を紹介します。AirPurifierは空気清浄機用のアクセサリタイプです。
私はSwitchBotを利用して強モードにする操作をできるようにし、ホームアプリのオートメーション設定と組み合わせて外出後に強モードにするようにしています。
1つのスイッチのオンオフでの利用のため、Switchでも良いのですが、せっかく丁度いいアクセサリタイプがあるので、これを利用しています。
ホームアプリ上の見た目は以下です。
"platforms": [
{
"platform": "Cmd4",
"name": "Cmd4",
"accessories": [
{
"type": "AirPurifier",
"name": "Air Purifier",
"displayName": "air-purifier",
"active": "ACTIVE",
"currentAirPurifierState": "INACTIVE",
"targetAirPurifierState": "MANUAL",
"polling": [
{
"characteristic": "active"
},
{
"characteristic": "currentAirPurifierState"
}
],
"state_cmd": "/path/to/air-purifier.sh"
}
]
}
]
config.jsonはSwitchの設定と大きくは変わりません。複数のcharacteristic
の利用とポーリングする設定になっています。
#!/bin/bash
if [ "$1" = "Get" ]; then
case "$3" in
"Active")
if [ `curl -s http://xxxxx/status | jq .data.status` = 1 ]; then
echo 1
else
echo 0
fi
;;
"CurrentAirPurifierState")
if [ `curl -s http://xxxxx/status | jq .data.status` = 1 ]; then
echo 2
else
echo 0
fi
;;
"TargetAirPurifierState")
echo 0
;;
esac
exit 0
fi
if [ "$1" = "Set" ]; then
case "$3" in
"Active")
if [ "$4" = 1 ]; then
curl -s http://xxxxx/jetmode
fi
exit 0
;;
esac
fi
コマンドの実装もSwitchと同様です。characteristic
によって処理を分けています。
今回は詳細は割愛しますが、SwitchBotをBluetoothで制御するアプリを別に用意して、シェルスクリプトからはそれを呼び出すことでSwitchBotを動かす構成としています。
なお、AirPurifierではActive
が1 (ACTIVE)
でCurrentAirPurifierState
が1 (IDEL)
という組み合わせで待機中という状態を表現できるようですが、私の用途では不要のため、0 (INACTIVE)
/0 (INACTIVE)
のオフと1 (ACTIVE)
/2 (PURIFYING_AIR)
のオンの2つの状態で利用しています。また、上述の画像ではモードというのもありますが、それも利用していません。
BatteryService
次にlinkedTypes
という設定についてBatteryServiceというアクセサリタイプの利用例を用いて紹介します。
BatteryServiceはバッテリー残量を示すアクセサリタイプです。バッテリーや電池駆動のアクセサリタイプに対して組み込んで利用するのが通常の使い方かと思います。この組み込むという設定がlinkedTyeps
になります。
私はマンションの呼び出し式オートロック共同玄関のモニター側の操作を行うアクセ
サリの設定内でBatteryServiceを利用しています。
まずはこの遠隔操作を行う仕組みの全体感を紹介します。
基本はSwitchBotを利用した遠隔の解錠操作です。それに加えて、SensorTagの照度センサを利用して、呼び出し時のモニターの点灯を検知して自動でSwitchBotでの解錠操作を行うようにしています。このSensorTagの電池がそこまで長期間持たないということもあり、BatteryServiceを利用してホームアプリから残量がわかるようにしています。
なお、いつでもモニターの点灯で自動解錠してしまうと誰でも入れるようになってしまうため、ホームアプリのオートメーションで登録された人が家に近づいたときに照度センサのスキャンを開始するようにしています。
続いてconfig.jsonの説明に入ります。
"platforms": [
{
"platform": "Cmd4",
"name": "Cmd4",
"accessories": [
{
"type": "LockMechanism",
"name": "Entrance",
"displayName": "open-entrance",
"lockCurrentState": "UNSECURED",
"lockTargetState": "UNSECURED",
"linkedTypes": [
{
"type": "Switch",
"name": "Scan Entrance",
"displayName": "scan-entrance",
"on": "FALSE",
"polling": [
{
"characteristic": "on",
"interval": 5,
"timeout": 10000
}
],
"state_cmd": "/path/to/entrance.sh"
},
{
"type": "BatteryService",
"name": "SensorTag Battery",
"displayName": "sensortag-battery",
"batteryLevel": 50,
"chargingState": "NOT_CHARGEABLE",
"statusLowBattery": "BATTERY_LEVEL_NORMAL",
"polling": [
{
"characteristic": "batteryLevel"
},
{
"characteristic": "statusLowBattery"
}
],
"state_cmd": "/path/to/entrance.sh"
}
],
"polling": [
{
"characteristic": "lockCurrentState"
},
{
"characteristic": "lockTargetState"
}
],
"state_cmd": "/path/to/entrance.sh"
}
]
}
]
基本のSwitchBotの操作部分はLockMechanismという鍵用のアクセサリタイプを利用しています。
そして、linkedTypes
でBatteryServiceとSwitchを子アクセサリタイプとして登録しています。linkedTypes
内でのアクセサリの設定内容は通常の設定と同じです。Switchはオートメーションで登録した人が家に近づいたときにスキャンを開始するスイッチに利用しています。Switchなのでオートメーションだけでなく手動でのオンオフも可能です。
コマンドについては、渡されたcharacteristic
に合わせて結果を返せばいい点は、上述の他のアクセサリタイプと変わらないため、紹介は割愛します。
ホームアプリ上の見た目は以下です。
linkedTypes
で定義しているSwitchのボタンが横並びで表示されていますが、ホームアプリの設定で、以下のように一つのボタンとして表示することも可能です。
statusLowBattery
が1 (BATTERY_LEVEL_LOW)
を返すようになると以下のようになります。
Television
最後に少し特殊なアクセサリタイプのTelevisionを紹介します。Televisionアクセサリはホームアプリ上からだけでなく、リモートアプリ上からも操作できるようになります。
私はテレビはApple TVのディスプレイ専用機になっているので、基本的な操作はApple TVのリモコンやリモートアプリでのApple TVの操作で行っており、Televisionアクセサリではホームアプリ上からの電源のオンオフと入力切替、リモートアプリ上からの音量調整をできるようにしています。
なお、音量調整はリモートアプリ上でiPhone本体の音量ボタンを操作する形となっています。最近のiOSでは上の画像のようにナビゲーションしてくれていますが、以前のバージョンではこれが表示されなかったため、どのように操作すれば音量操作になるのかがわかりにくかったです。。
config.jsonは以下のようになります。
"platforms": [
{
"platform": "Cmd4",
"name": "Cmd4",
"accessories": [
{
"type": "Television",
"configuredName": "TV",
"displayName": "tv",
"active": "ACTIVE",
"activeIdentifier": 1,
"sleepDiscoveryMode": "ALWAYS_DISCOVERABLE",
"currentMediaState": "STOP",
"targetMediaState": "STOP",
"remoteKey": "SELECT",
"publishExternally": true,
"category": "TELEVISION",
"linkedTypes": [
{
"type": "InputSource",
"configuredName": "Input",
"displayName": "Input1",
"currentVisibilityState": "SHOWN",
"inputSourceType": "HDMI",
"isConfigured": "CONFIGURED",
"identifier": 1,
"targetVisibilityState": "SHOWN",
"name": "Input1",
"polling": true,
"state_cmd": "/path/to/tv.sh"
},
{
"type": "InputSource",
"configuredName": "Input",
"displayName": "Input2",
"currentVisibilityState": "SHOWN",
"inputSourceType": "HDMI",
"isConfigured": "CONFIGURED",
"identifier": 2,
"targetVisibilityState": "SHOWN",
"name": "Input2",
"polling": true,
"state_cmd": "/path/to/tv.sh"
},
{
"type": "TelevisionSpeaker",
"name": "TVSpeaker",
"displayName": "TVSpeaker",
"mute": "FALSE",
"volumeControlType": "RELATIVE_WITH_CURRENT",
"volumeSelector": "INCREMENT",
"polling": [
{
"characteristic": "volumeSelector"
}
],
"state_cmd": "/path/to/tv.sh"
}
],
"polling": [
{
"characteristic": "active",
"interval": 5,
"timeout": 10000
},
{
"characteristic": "remoteKey"
},
{
"characteristic": "activeIdentifier"
}
],
"state_cmd": "/path/to/tv.sh"
}
]
}
]
Name
の変わりにconfiguredName
が必須となっていますが、使われ方としてはName
と同じようです。また、"publishExternally": true
と"category": "TELEVISION"
が必須になっています。
その他については基本的な記載方法は他のアクセサリタイプと同様ですが、入力切替と音量操作については、それぞれInputSource
とTelevisionSpeaker
をlinkedTypes
で設定する形になります。
InputSource
はコマンド実行部分も特殊で、以下のように親のTelevision
のactiveIdentifier
として、コマンド実行されます。
/path/to/tv.sh Set tv ActiveIdentifier 1
なお、リモートアプリ上の音量以外の操作はremoteKey
となっています。
コマンドについては、渡されたcharacteristic
に合わせて結果を返せばいい点は、上述の他のアクセサリタイプと変わらないため、紹介は割愛します。
おわりに
Homebridgeの専用プラグインがない製品に汎用的に対応できるプラグインのhomebridge-cmd4の設定例の紹介でした。
今回紹介したアクセサリタイプ以外にも様々アクセサリタイプがあります。READMEにあるInstallation Detailsに従ってインストールすると、サンプルの設定が行われ、以下のように、様々なアクセサリタイプを見てみることができます。
設定は、Installation Detailsで使われているサンプルの設定ファイルや、あまり詳細は書かれていませんが設定サンプルや設定値の解説等を公開しているhomebridge-cmd4のGitHub Pagesが参考になると思います。本記事で紹介しているアクセサリタイプでも説明しているもの以外の様々な設定項目があります。
コマンドの実装については、あまり種類は多くないですがGitHub上で公開されているサンプルも参考になると思います。