こんにちは。はじめまして。PBX チーム所属の山崎です。
はじめに
さてみなさま、夏休みはいかがお過ごしでしょうか。 夏休みで子どもがずっと家にいると、WEB 会議に乱入してくる可能性が高くなります *1。 「おとーさーん!おとーーさーーん!あれ〜いないの〜〜?」「ちょっと静かにしててー💢」みたいなご経験、ないでしょうか。
もちろん乱入してきたからといってどうということはなく、同僚のそんな様子はむしろ和むのでウェルカムですが、自分がその立場になるとやはり気まずいものがあります。
そんな折、同僚が ON AIR ライト を買っていて、良さそうだったので自分も買ってみました。
しかしながら、日に数回とはいえ都度付け消しするのは面倒です。そこで、自動で on/off するようにしてみました。
前書きが長くなりましたが、今回の記事は
- MacBook のヘッドセットの接続/切断イベントを契機に、
- ライトを自動で on/off して、
- 家族にミーティングの開始/終了を伝える
というものを作ってみた、というご紹介になります。
お買い物
今回、製作するにあたって以下を使用しました。
- スマートプラグ
- TP-Link Tapo P105 を使用しましたが、API が公開されていません
- もしこれから購入するのであれば、API ドキュメントが充実している SwitchBot プラグミニ が良さそうに思います
- ON AIR ライト
- 常時スイッチオンの状態にできる (スマートプラグで操作できる) ものであれば、なんでも OK です
- ヘッドセット
- MacBook (Monterey)
- 無線LAN
今回はスマートプラグと ON AIR ライトの組み合わせで製作しましたが、macOS からコマンドで操作できるものであればなんでも構いません。
ヘッドセット接続・切断イベントの検知
自動化するにあたって悩ましいのは、何をトリガーにするかという点です。 私はミーティング時のみ Apple EarPods with 3.5 mm Headphone Plug を MacBook に挿しているので、これをトリガーにすることにしました。
実はここが一番苦労したところでして、Apple 社のドキュメントのどこを見ても、オーディオデバイスの接続・切断の検知方法が書いてありません (調べきれていないだけかも) 。
ブラウザなら MediaDevices の devicechange イベント を拾えばサクッと終わりますが、これだけのために1プロセス常駐させるのももったいない話です(貧乏性なんです)。
また、system_profiler
コマンドで接続中のデバイス一覧がとれるので、これを例えば 「5 分に 1 回実行して状態を検出」でもいいのですが、これもなんだかもったいないですし、最大5分遅延してしまうので、嬉しくありません。
それっぽいアプリもなく煩悶としていたところ、システム環境設定の「サウンド」の項目が変化することに気付きました。
システム環境設定は /Library/Preferences/
以下に設定を保存するので、それっぽいファイルを漁ってみます。
すると、抜き差しのタイミングで /Library/Preferences/Audio/com.apple.audio.SystemSettings.plist
が更新されることがわかりました。そして、ファイルの変更イベントは launchd で検出できます。これで勝ち筋が見えました。
やってみよう
ファイルを2つ、作成します。実際のファイル名は適宜環境に合わせて読み替えてください。
~/Library/LaunchAgents/com.example.on-air.plist
/path/to/on-air.sh
まず ~/Library/LaunchAgents/com.example.on-air.plist
を作成します:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.example.on-air</string> <key>WatchPaths</key> <array> <string>/Library/Preferences/Audio/com.apple.audio.SystemSettings.plist</string> </array> <key>ProgramArguments</key> <array> <string>/path/to/on-air.sh</string> </array> </dict> </plist>
ここでは、
WatchPaths
で指定したファイルに変化があったら、ProgramArguments
を実行する
という処理をしています。
次に、on-air.sh
を実装していきます。このファイルには実行権限の付与( chmod +x on-air.sh
)をしておきます。
#!/bin/sh # "system_profiler -json SPAudioDataType" コマンドで表示される、お手元のデバイスの名前にあわせてください AUDIO_DEVICE_NAME='外部マイク' JQ=/opt/homebrew/bin/jq connected () { osascript -e 'display notification "ON AIR: connected"' } disconnected () { osascript -e 'display notification "ON AIR: disconnected"' } # 切断中 or 接続中を判断する system_profiler -json SPAudioDataType | \ $JQ -e ".SPAudioDataType[]._items[] | select(._name == \"$AUDIO_DEVICE_NAME\")" > /dev/null if [ $? == 0 ]; then connected else disconnected fi
launchd からは接続・切断を見ずに、単に設定ファイルの更新イベントだけを拾って on-air.sh
を呼びます。
そこで、on-air.sh
の中で接続 or 切断を判断しています。
2つのファイルを作成できたら、以下のコマンドで launchd に設定を読み込みます:
$ launchctl load ~/Library/LaunchAgents/com.example.on-air.plist
この状態でヘッドセットを抜き差しすると、通知センターに connected/disconnected が出力されます(数秒のタイムラグがあります)。
あとは connected()/disconnected()
の部分を実装して、ライトの on/off を制御できるようになったら完成です。
ライトのon/off
実際に on/off する処理を書いていきます。
今回使用したスマートプラグ Tapo P105 は API 仕様が公開されていません。 適当にググっていくつか試したところ、fishbigger/TapoP100 がいい感じに動いたので、ありがたくこれを利用することにします。 *2
3つ目のファイル、tapo-p105.py
を作ります:
#!/usr/bin/env python3 import sys from PyP100 import PyP100 ipaddress, email, password, onoff = sys.argv[1:] p105 = PyP100.P100(ipaddress, email, password) p105.handshake() p105.login() if onoff == "on": p105.turnOn() elif onoff == "off": p105.turnOff()
この Python スクリプトを使って、先ほどの connected()/disconnected()
の部分を以下のように書き換えます。
SCRIPT_DIR=/path/to/tapo-p105 PYTHON="$SCRIPT_DIR/venv/bin/python3" IP=192.168.0.2 # スマートプラグの IP アドレス P105_EMAIL="your-account@example.com" P105_PASSWD="your password" connected () { $PYTHON "$SCRIPT_DIR/tapo-p105.py" "$IP" "$P105_EMAIL" "$P105_PASSWD" on } disconnected () { $PYTHON "$SCRIPT_DIR/tapo-p105.py" "$IP" "$P105_EMAIL" "$P105_PASSWD" off }
最後に、TapoP100 ライブラリをインストールします:
cd /path/to/tapo-p105 python3 -m venv venv source venv/bin/activate pip3 install PyP100
これで完成です。
むすびに
実は春休みに作って 4 ヶ月ほど運用してきました。実際に使ってみると、いい感じに QoL が上がりました。
- WEB 会議中に割り込まれることが少なくなって快適
- (家族からは) 話しかけて大丈夫なタイミングがわかるようになって、ストレス軽減と好評
- 子どもから、お父さんすごーい!とピュアな尊敬を得られる
一方、抜くのを忘れてて夕ご飯に呼ばれず、一人寂しく食べる羽目になったこともありました。 技術の過信は良くないですね(大げさ)。