RevComm Tech Blog

コミュニケーションを再発明し 人が人を想う社会を創る

テレワーク中、WEB 会議のひと工夫

こんにちは。はじめまして。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>

ここでは、

  1. WatchPaths で指定したファイルに変化があったら、
  2. 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 会議中に割り込まれることが少なくなって快適
  • (家族からは) 話しかけて大丈夫なタイミングがわかるようになって、ストレス軽減と好評
  • 子どもから、お父さんすごーい!とピュアな尊敬を得られる

一方、抜くのを忘れてて夕ご飯に呼ばれず、一人寂しく食べる羽目になったこともありました。 技術の過信は良くないですね(大げさ)。

*1:RevComm ではフルリモート制度を導入していまして、普段のミーティングは基本的にオンラインで行われます

*2:Tapo P105 はファームウェアバージョンによって API の仕様が異なるらしく、試した他のライブラリは動作しませんでした。