virt-connectorをリリースしました
机をDIYして、机の間接照明をモニタのスリープ・復帰に連動させたい、机の間接照明をシャットダウン時に消灯したい、というのは実現したのだけれど、手順が煩雑なことと、Logi Options+の機能に依存していることが気になっていた。
クイックな使い方
1. Homebrew Caskでインストール
brew tap rioriost/cask https://github.com/rioriost/homebrew-cask
brew install --cask rioriost/cask/virt-connector2. Shortcutsを作成
macOSのショートカット.appで、制御したいHome/Matterデバイス用のShortcutを2つ作成します。
TurnOnLED: 例としてLED StripをオンにするTurnOffLED: 例としてLED Stripをオフにする
作成後、ターミナルから手動実行できることを確認します。
shortcuts run TurnOnLED
shortcuts run TurnOffLED3. VirtConnectorを設定
virt-connector setup --device "LED Strip" --on TurnOnLED --off TurnOffLEDこれで以下が行われます。
~/.config/virt-connector/config.jsonを作成または更新~/Library/LaunchAgents/st.rio.virt-connectord.plistを作成VirtConnectorAgentをユーザーLaunchAgentとして起動- メニューバーにVirtConnectorの電源アイコンを表示
以後、ディスプレイのスリープ/復帰に応じてTurnOffLED/TurnOnLEDが実行されます。
システム終了時に確実にLEDをオフにしたい場合は、Appleメニューの「システム終了...」ではなく、VirtConnectorのメニューバーアイコンから「システム終了...」を選びます。このメニューは、設定済みのpower_off動作を実行してからmacOSのシステム終了を要求します。
以下、色々試した結果をまとめておく。
macOSネイティブアプリはHomeKitが使えない
ホーム.appというアプリがmacOSにはあって、これでMatterデバイスを制御する。同じアプリはiOS / iPadOSにもあるのだけれど、macOS版のUIが「ちょっと変」なのが気になっていた。そして、このことは次のセクションのヒントになっている。
さて、macOSネイティブのアプリ、つまりSwiftUI + AppKitを書くと、HomeKitが使えない。何せ、APIもSDKも無い。従って、ホーム.appで見えているデバイスを、ネイティブアプリからは制御できない。Matterデバイスを直接制御するアプリを書くことは、Matter controller / fabricの実装を意味するので、かなり重い。
一方で、IOKit / NSWorkspaceで電源イベントを検知するには、macOSネイティブのアプリじゃないとできないという制約がある。
じゃあCatalystなのか?
HomeKitを利用するにはMac Catalystを利用する必要がある。Mac CatalystはiOS / iPadOS用のアプリをMac向けに動かす仕組みで、UIKitを使う。これが、ホーム.appがmacOSのアプリとして「ちょっと変」なUIを持っている理由。
ところが、UIKitからだと電源イベントを検知する仕組みが無い。そこでUIはCatalystで作って、ネイティブアプリのヘルパーでイベントを検知したら、Catalyst側に投げてHomeKitでデバイス制御する、というのをやってみた。
でも、このCatalyst - ネイティブアプリのやり取りが遅くて電源イベントを拾っても、Catalyst側にうまく伝えられないことが分かった。
結局どうしたのか
元のシェルスクリプトによる実装が、この「電源イベント→Matterデバイス制御」としては、図らずも正鵠を射てた。
つまり、pmset -gをビジーループ(sleep 5を挟んでいるとはいえ)で監視しておく、という実装。そして、Sandbox内からはpmsetは監視できないので、App Storeから配布する形態では実現できない。
結局、VirtConnectorAgent.appという常駐アプリに含まれる、virt-connectordというデーモンがpmset -gを実行し、それをvirt-connectorというCLIで制御する、という実装になりましたとさ。
