オンライン会議（Teams）の参加・退出をローカルで検知する

:::note info
2024/4/2 「新しいTeams」に対応した内容を追記しました。
:::

在宅勤務あるあるの「オンライン会議中に家族が割り込んでくる」を避けるため、
会議はじまった・おわった通知を自動化しました。

その際に、Teams会議の参加・退出をローカル（PC上）で検知する方法を調べたので、
まとめておきたいと思います。

# 1. 目指すもの
会議割り込み防止としてWeb上ではOnAirランプを使う方法が有名です。
・[リモート会議中の乱入を防ぐ「ON AIR サインランプ」](https://k-tai.watch.impress.co.jp/docs/column/todays_goods/1313705.html)
・[『ただ今オンライン会議中』を自動でお知らせする仕組み](https://qiita.com/takeshi_ohsawa/items/d2cd75c0d3d76a177920)
・[家庭内でビデオ通話中を知らせる仕組みを作る](https://qiita.com/ooharabucyou/items/ec4f8efd9a11c8b84e2c)

Teamsで会議に参加したらランプ点灯、会議が終了したらランプ消灯というのを
「自動で」行えるようにすることを目指します。

# 2. 会議中かどうかの判定方法
「会議中かどうか」をどう判断するかが鍵になりますが、「マイク利用中＝会議中」ということにしました。
今のところ、会議でカメラはほぼ使っていませんが、マイクは確実に利用するから、という考えです。

※アイディアのヒントは以下の記事からいただきました。
　（残念ながらカメラをほとんど使わない＆Webカメラのメーカーに左右される方法だっため見送り）

https://dev.classmethod.jp/articles/onair-at-meeting/

　
しらべてみたところ、Windows 10ではカメラやマイクの最終利用日がレジストリに記録されているようです。

https://soji256.hatenablog.jp/entry/2021/01/28/070000

上記の記事では「HKEY_LOCAL_MACHINE」に記録されているとありますが、
自分の端末ではTeamsの記録は「HKEY_LOCAL_USER」にありました。
（Teamsクライアントのインストール方法にもよるかもしれません）


<font color="red">2024/4/2 「新しいTeams」に対応した内容を追記</font>

```
### Teams Classic
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged\C:#Users#xxxxx#AppData#Local#Microsoft#Teams#current#Teams.exe

<font color="red">### New Teams</font>
HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\MSTeams_8wekyb3d8bbwe
```

どうやらマイク使用中は`LastUsedTimeStop`が0になるようなので、基本的にはこれを会議中として扱うことにします。

# 3.実装
なるべくシンプル（余計なものをインストールせず）に実装したいのでPowershellを使うことにしました。
出来上がったコードは以下の通りです。
<font color="red">※3行目の$regkeyはTeamsのバージョン（Classic/New）に合わせて選択してください</font>

```diff_powershell: Check-Meeting.ps1
$logfile="C:\xxxxx\Check-Meeting.log"
$csvfile="C:\xxxxx\Teams_Activity.csv"
- $regkey="HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged\C:#Users#xxxxx#AppData#Local#Microsoft#Teams#current#Teams.exe"
+ $regkey="HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\MSTeams_8wekyb3d8bbwe"

# ログ出力用関数
function writelog($msg){
	$timetxt = Get-Date -Format "yyyyMMdd HH:mm:ss"
	$msg = $timetxt + " " + $msg
	$msg | Out-File -Append -FilePath $logfile
}

# csvファイルが無い時の処理
If((Test-Path $csvfile) -eq $False) {
	Get-ItemProperty $regkey | Export-csv $csvfile
}

while($true){
	$log_val=Import-Csv $csvfile
	$reg_val=Get-ItemProperty $regkey

	$last_start=$log_val.LastUsedTimeStart
	$reg_start=$reg_val.LastUsedTimeStart

	$last_stop=$log_val.LastUsedTimeStop
	$reg_stop=$reg_val.LastUsedTimeStop

	if (($reg_stop -eq 0) -and ($last_start -ne $reg_start)){
		writelog("Meeting started at " + [datetime]::FromFileTime($reg_start))
		# 会議が始まったときに実行したい処理を書く
	}
	if (($reg_stop -ne 0) -and ($last_stop -ne $reg_stop)){
		writelog("Meeting stopped at " + [datetime]::FromFileTime($reg_stop))
		# 会議が終わったときに実行したい処理を書く
	}

	if (($last_start -ne $reg_start) -or ($last_stop -ne $reg_stop)){
		Write-Output $reg_val | Export-Csv "C:\software\TaskSchedule\Teams_Activity.csv"
	}
	
	Start-Sleep 15
}
```

レジストリ値を`Teams_Activity.log`に保存し、15秒おきに保存した値とレジストリ値を比較しています。

会議中（マイク利用中）かどうかは`LastUsedTimeStop=0`で判定できると書きましたが、
念のため`LastUsedTimeStart`の値が変動しているかどうかもチェックしています。

あとはこのps1をスタートアップやタスクスケジューラで実行すれば、
会議の開始時と終了時に処理を呼び出すことができます。

うまく動けばこういったログファイルが出力されます。

```Check-Meeting.log
20230213 09:57:21 Meeting started at 02/13/2023 09:57:17
20230213 11:08:39 Meeting stopped at 02/13/2023 11:08:35
```

ちなみに、ps1をそのまま呼び出すとPowershellのウインドウが残ってしまうので、
VBScriptから呼び出す方法がおすすめです。

以下のようなVBScriptを作成し、引数としてps1のパスを渡してください。

```TaskSchedule.vbs
Option Explicit

Dim objFSO
Dim objWshShell
Dim res

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objWshShell = WScript.CreateObject("WScript.Shell")

res = objWshShell.Run("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File " & Wscript.Arguments(0) , 0)

Set objFSO = Nothing
Set objWshShell = Nothing

Wscript.Quit(res)
```

# 4. まとめ
今回はTeamsとマイクの組み合わせでしたが、組み合わせ次第では他にも応用が利きそうです。

なお、家族への通知については場所・配線の制約やOn/Offの容易さ（API呼び出し）から
冒頭のOnAirランプは使わずに **[SwitchBotのLEDテープライト](https://www.switchbot.jp/pages/strip-light)** を利用しています。

家族からは「分かりやすくなった」と評価をもらったので成功です！

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919396/9eb3fe19-b207-5c93-4d1a-3949cef2cec2.jpeg">







# （おまけ） マイク以外の方法
最終的にはマイク利用を検知することにしましたが、それ以外の実装も調べてみたので書いておきます。

（１）PC上のプロセスを検知する
　Cisco WebExの場合は会議は専用プロセスが起動するようです。
　残念ながらTeamsの場合はチャットなどと同じTeams.exeが起動するだけでした。
　・[【Windows】CiscoWebEXで会議中か否かを自動判定する方法](https://www.mtioutput.com/entry/webex-upordown-check)

（２）CPU利用率を監視する
　会議はわりとCPUを消費するので、それを利用した~~無理やりな~~実装です。（嫌いではないですw）
　・[「只今 Zoom 中」を知らせたい](https://note.com/ardbeg/n/n7fb0e5bc3d3f)
>複雑そうに見えますが、やっていることは単純で、プロセスの一覧を得る ps というプログラムの中から Zoom プログラムが使用している CPU 利用率を抽出して、それが 6.0（%）より大きかったら Zoom 会議本体が始まっていると判断しています。