【作ってみた】オンライン会議中にON AIRライトを点ける

オンライン会議中に「ON AIR」ライトを点灯してみました。雰囲気出ます。
2021.05.05

CX事業本部チームIoTの熊膳です。

リモートワークでのオンライン会議、家族に気を使われることってありませんか?例えば、音を立てないようにとか、カメラに映り込まないようにとか。うちはあります。 でも、私、一日中ヘッドホンしているので、家族からするといつオンライン会議しているのか、すごくわかりにくいです。

というわけで、GWの企画として「オンライン会議中にON AIRライトを点ける」仕組みを作ってみました。

オンライン会議 ON AIRライトシステム

最初に動いている動画を紹介します。

ちょっとだけタイムラグがありますが、会議が始まるとON AIRが点灯し、終了すると消えています。

作ってみた

「オンライン会議中」をどう判断するか?という部分をどうしようかと悩んだのですが、結論としてはWebカメラがONになっている時としました。オンライン会議=カメラONではないのですが、やりたいことはカメラがONになってることを伝えたいということなので問題ないです。

作戦はこうです。

  1. 何らかの仕組みでカメラの状態を検知する。
  2. カメラの状態がOFFからONになったらON AIRライトをつける。ONからOFFなら消す。

ON AIRライトのON/OFFは、スマートプラグを使いました。

参考までに、今回使った機材は以下です。

カメラの状態の検知

まずはカメラの状態をどうやって取得するかです。

私の使用しているWebカメラは「ロジクールStreamCam」で、MacとはUSB-Cケーブルで接続しています。最初はUSBハブ経由で電気的な違いを取得するとか、カメラONのライトをコンピュータビジョンを用いて検出するとか、出来もしないことを考えていたのですが、カメラ起動中は何かのプロセスが動いているはずとの妄想を元に、アクティビティモニタを「cam」で検索したところ、LogiFacecamServiceというプロセスを見つけました。

カメラをON/OFFしたところ、CPUの値が変化していることを発見。これ使えそうです。

以下のコマンドで、CPUの値を取得できました。

ps -o %cpu -o comm -ax|grep LogiFacecam
 14.7 /Library/Application Support/LogiFacecam.bundle/Contents/MacOS/LogiFacecamService

上記の14.7というのがCPUの値です。CPUの値が、0.0より大きい場合をカメラONと判断することにします。

【追記】 実際に何日か運用してみると、カメラONの場合でもタイミングによっては0.0になる場合があるようです。OFFになったと判断する場合は、0.0が2,3回続くという条件にしたほうが良さそうです。

ON AIRライトの点灯

ON AIRライトの点灯、消灯は、スマートプラグを使用します。今回使ったHS105は、tplink-smarthome-apiを用いることで、Macから制御できますのでこれを使います。

セットアップの仕方や、ライブラリの使い方は、山本さんやMiyajimaさんのブログを参考にしました。

ロジックは以下としました。

  • 前回のカメラの状態をファイルから読み込む
  • プロセスを取得し前回のカメラの状態から変更があったら
    • スマートプラグで電源のON/OFFを行う
    • カメラの状態をファイルに書き込む

コード

私、エンジニアじゃないのでコードとしては微妙かもしれないですが、一応動いているコードはこちらになります。

const fs = require('fs');
const childProcess = require('child_process');
const client = new (require('tplink-smarthome-api').Client)();

const CMD ='ps -o %cpu -o comm -ax|grep LogiFacecam';
const FILE = '/<ステータスファイルのパス>/state';
const PLUGIP = '192.168.x.x'; // HS105のIPアドレス tplink-smarthome-api searchで事前に調べておく

const lastStatus = fs.readFileSync(FILE, 'utf-8');
const cmd = childProcess.spawn(CMD, [], {shell: true});

cmd.stdout.on('data', (data) => {
  console.log(data.toString());
  // 1番目の数値をCPU値として取り出す
  const cpu = data.toString().trim().split(' ')[0];
  // cpuが0より大きい場合は、カメラが起動中と判断する
  const cameraStatus = (Number(cpu) > 0 ? 'ON' : 'OFF');
  console.log(cameraStatus);
  // カメラの起動状態が変更したらファイルを更新しONAIR状態を更新する
  if (lastStatus !== cameraStatus) {
    // ファイルを更新
    fs.writeFile(FILE, cameraStatus, (err) => {
        if (err) throw err;
    });
    // ON AIR ライトの変更
    client.getDevice({ host: PLUGIP }).then((device) => {      
        if (device.deviceType === 'plug') {
          device.setPowerState(cameraStatus === 'ON' ? true : false);
        }
      });
  } 
});

スクリプト起動

Macを使っているので上記のスクリプトをlaunchdで定期起動することにします。 以下のplistファイルを~/Library/LaunchAgents/xxx.plistとして配置します。ファイル名はjp.classmethod.seisho.kumazen.onair.plistとしました。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>jp.classmethod.seisho.kumazen.onair</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/node</string>
        <string>/<スクリプトのパス>/onair.js</string>
    </array>
    <key>StartInterval</key>
    <integer>5</integer>
    <key>StandardOutPath</key>
    <string>/<スクリプトのパス>/log.out</string>
    <key>StandardErrorPath</key>
    <string>/<スクリプトのパス>/log.err</string>
</dict>
</plist>

起動間隔は、撮影の都合上5秒にしてますが、そこまでリアルタイムじゃなくていいと考え60秒でもいいかなと思ってます。StartIntervalの値で変更可能です。

ファイルを配置したら、以下のコマンドを実行して完了です。

launchctl load ~/Library/LaunchAgents/jp.classmethod.seisho.kumazen.onair.plist

ちなみに停止は、上記のloadunloadに変更して実行です。

まとめ

GWの自由研究として、「オンライン会議中にON AIRライトを点ける」をやってみました。 この「オンライン会議中に」というイベントをトリガーに何かを行うというのは、色々応用できそうですね。例えば「LEDライトを点灯する」というのは、すぐに出来そうです。

以上、どなたかの参考になれば幸いです。