virt-connectorをリリースしました

机をDIYして、机の間接照明をモニタのスリープ・復帰に連動させたい机の間接照明をシャットダウン時に消灯したい、というのは実現したのだけれど、手順が煩雑なことと、Logi Options+の機能に依存していることが気になっていた。

クイックな使い方

1. Homebrew Caskでインストール

brew tap rioriost/cask https://github.com/rioriost/homebrew-cask
brew install --cask rioriost/cask/virt-connector

2. Shortcutsを作成

macOSのショートカット.appで、制御したいHome/Matterデバイス用のShortcutを2つ作成します。

  • TurnOnLED: 例としてLED Stripをオンにする
  • TurnOffLED: 例としてLED Stripをオフにする

作成後、ターミナルから手動実行できることを確認します。

shortcuts run TurnOnLED
shortcuts run TurnOffLED

3. 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で制御する、という実装になりましたとさ。

GitHubのレポジトリ