24
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

NRI OpenStandiaAdvent Calendar 2021

Day 4

homebridge-cmd4 設定例

Last updated at Posted at 2021-12-04

はじめに

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)の電源オンオフや床暖房のオンオフ等に使っています。今回はサーバのオンオフする設定を元に設定方法をご紹介します。
ホームアプリ上の見た目は以下です。

Server.png

早速設定ですが、まずはHomebridgeのconfig.jsonからです。
Switchの設定例は以下のとおりです。

config.json
"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中のplatformnameはHomebridgeのプラグインの基本的な設定内容なので、解説は割愛します。
accessoriesの要素についての説明を以下表にまとめます。

項目名 内容
type アクセサリタイプを指定します。
name ホームアプリ上で表示される名前等に利用されます。指定がない場合displayNameの値が利用されます。
displayName state_cmdで指定されたコマンドの実行時にアクセサリ名としてコマンドに引数で渡されたり、ログ出力等に利用されます。指定がない場合nameの値が利用されます。
on characteristicと呼ばれるアクセサリのステータスのようなものとなり、利用するものを宣言しておくイメージの設定となります。アクセサリタイプによって利用できるcharacteristicは異なり、アクセサリタイプにより必須のものと任意のものがあります。何が必須なのかなどは設定値の解説等が公開されているhomebridge-cmd4のGitHub Pagesが参考になると思います。
polling characteristicの状態を確認するポーリングの設定です。ホームアプリでの操作以外でアクセサリの状態が変わっていることを検知します。intervalはポーリング間隔(秒)、timeoutはポーリングリクエストのタイムアウト時間(ミリ秒)の指定です。intervaltimeoutは指定がない場合のデフォルト値は60秒です。配列で複数のcharacteristicのポーリング設定を記載できます。また、配列とせず、poling: trueという指定もでき、この場合デフォルトのcharacteristicに対し、ポーリング間隔等もデフォルト値でポーリングを行います。デフォルトのcharacteristichomebridge-cmd4のGitHub Pagesを参照ください。
state_cmd ホームアプリからの操作やポーリング時に実行するコマンドの指定です。 実行するコマンドはhomebridge-cmd4では1つのアクセサリに対して一つのコマンドを指定します。呼び出しの場面によって4つの引数が渡されてそのコマンドが実行されます。引数の詳細については次に説明するコマンドの実装の中で紹介します。

次にhomebridge-cmd4から呼び出されるコマンドについてです。
config.jsonstate_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に対しては標準出力で結果を返す形になります

server.sh
#!/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でも良いのですが、せっかく丁度いいアクセサリタイプがあるので、これを利用しています。
ホームアプリ上の見た目は以下です。

AirPurifier.png

config.json
"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の利用とポーリングする設定になっています。

air-purifie.sh
#!/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ではActive1 (ACTIVE)CurrentAirPurifierState1 (IDEL)という組み合わせで待機中という状態を表現できるようですが、私の用途では不要のため、0 (INACTIVE)/0 (INACTIVE)のオフと1 (ACTIVE)/2 (PURIFYING_AIR)のオンの2つの状態で利用しています。また、上述の画像ではモードというのもありますが、それも利用していません。

BatteryService

次にlinkedTypesという設定についてBatteryServiceというアクセサリタイプの利用例を用いて紹介します。
BatteryServiceはバッテリー残量を示すアクセサリタイプです。バッテリーや電池駆動のアクセサリタイプに対して組み込んで利用するのが通常の使い方かと思います。この組み込むという設定がlinkedTyepsになります。

私はマンションの呼び出し式オートロック共同玄関のモニター側の操作を行うアクセ
サリの設定内でBatteryServiceを利用しています。
まずはこの遠隔操作を行う仕組みの全体感を紹介します。

autolock.png

基本はSwitchBotを利用した遠隔の解錠操作です。それに加えて、SensorTagの照度センサを利用して、呼び出し時のモニターの点灯を検知して自動でSwitchBotでの解錠操作を行うようにしています。このSensorTagの電池がそこまで長期間持たないということもあり、BatteryServiceを利用してホームアプリから残量がわかるようにしています。
なお、いつでもモニターの点灯で自動解錠してしまうと誰でも入れるようになってしまうため、ホームアプリのオートメーションで登録された人が家に近づいたときに照度センサのスキャンを開始するようにしています。

続いてconfig.jsonの説明に入ります。

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に合わせて結果を返せばいい点は、上述の他のアクセサリタイプと変わらないため、紹介は割愛します。

ホームアプリ上の見た目は以下です。

Battery.png

linkedTypesで定義しているSwitchのボタンが横並びで表示されていますが、ホームアプリの設定で、以下のように一つのボタンとして表示することも可能です。

Battery_Tile.png

statusLowBattery1 (BATTERY_LEVEL_LOW)を返すようになると以下のようになります。

Battery_Low.png

Television

最後に少し特殊なアクセサリタイプのTelevisionを紹介します。Televisionアクセサリはホームアプリ上からだけでなく、リモートアプリ上からも操作できるようになります。

TV.png

私はテレビはApple TVのディスプレイ専用機になっているので、基本的な操作はApple TVのリモコンやリモートアプリでのApple TVの操作で行っており、Televisionアクセサリではホームアプリ上からの電源のオンオフと入力切替、リモートアプリ上からの音量調整をできるようにしています。
なお、音量調整はリモートアプリ上でiPhone本体の音量ボタンを操作する形となっています。最近のiOSでは上の画像のようにナビゲーションしてくれていますが、以前のバージョンではこれが表示されなかったため、どのように操作すれば音量操作になるのかがわかりにくかったです。。

config.jsonは以下のようになります。

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"が必須になっています。
その他については基本的な記載方法は他のアクセサリタイプと同様ですが、入力切替と音量操作については、それぞれInputSourceTelevisionSpeakerlinkedTypesで設定する形になります。
InputSourceはコマンド実行部分も特殊で、以下のように親のTelevisionactiveIdentifierとして、コマンド実行されます。

/path/to/tv.sh Set tv ActiveIdentifier 1

なお、リモートアプリ上の音量以外の操作はremoteKeyとなっています。

コマンドについては、渡されたcharacteristicに合わせて結果を返せばいい点は、上述の他のアクセサリタイプと変わらないため、紹介は割愛します。

おわりに

Homebridgeの専用プラグインがない製品に汎用的に対応できるプラグインのhomebridge-cmd4の設定例の紹介でした。

今回紹介したアクセサリタイプ以外にも様々アクセサリタイプがあります。READMEにあるInstallation Detailsに従ってインストールすると、サンプルの設定が行われ、以下のように、様々なアクセサリタイプを見てみることができます。

Homekit_screenshot.png
homebridge-cmd4のREADMEより

設定は、Installation Detailsで使われているサンプルの設定ファイルや、あまり詳細は書かれていませんが設定サンプルや設定値の解説等を公開しているhomebridge-cmd4のGitHub Pagesが参考になると思います。本記事で紹介しているアクセサリタイプでも説明しているもの以外の様々な設定項目があります。
コマンドの実装については、あまり種類は多くないですがGitHub上で公開されているサンプルも参考になると思います。

24
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?