Scripting based on active zoom or Teams or Webex meeting

I'm wondering if there is a way to have a given trigger do different actions based on whether a on-line meeting is active or not. i.e. I'd like to "Mute" if a meeting is on, and "Play/Pause" if not. There are 'Conditions' for active window/running apps, but the meeting may be in the background. MS Teams has chat windows, so there's always a running process, similar with zoom.

FWIW- my trigger is an old Griffen PowerMate, configured based on this thread.

I've been doing this recently:

  1. Python script that

    • runs a shell script query (pmset -g) every N seconds
    • Checks the output to see if the "meeting state" has changed
    • If the meeting state has changed, print some status information and update the BTT variable "meeting_app" so that its value is one of '', 'MSTeams' 'Webex', 'zoom.us', where the empty value indicates no meeting in progress.
  2. BTT Trigger on Variable Value did change / variable "meeting_app".

    • Using this to run actions that change the set of buttons visible on my StreamDeck.

This are lots of ways to improve this*, but it does reliably work to change the streamdeck buttons soon after a meeting starts, and change them back when the meeting finishes. I'm on Sonoma 14.6 and have not tested this on other OS versions. The frequency with which you run pmset -g is related to how long this takes to notice that a meeting has started/stopped, and how much extra load you want to put on your mac in doing the monitoring. I found 5 sec to be reasonable.

I'd be happy to have some discussion about how other people are doing this; I found the pmset -g method by doing a lot of googling.

(*for instance, I'm just starting the py script in a terminal window rather than running it as a daemon. It dies occasionally for reasons I haven't tried to figure out yet, then I just restart it...)

The "upload" button doesn't seem to allow me to upload a python script, so I'll paste it in below...

# TODO add shebang
# meetingMonitor.py

# code to check whether we are in a Teams or Webex or Zoom Meeting

import re, argparse, subprocess, time

sleep_CRE = re.compile('sleep prevented by ([^()]+)')

# return the meeting app preventing sleep or None
def check_status():
    #output = subprocess.check_output(['pmset', '-g']
    # pmset -g for Teams:   sleep                0 (sleep prevented by MSTeams, ...
    # pmset -g for zoom :   displaysleep         180 (display sleep prevented by zoom.us)
    # Note, this is a separate line from ' sleep'  (zoom only shows up in this line on Sonoma 14.6)
    #pmsetCmd = 'pmset -g | grep "^ sleep"'
    # Note, will return several lines
    pmsetCmd = 'pmset -g | grep "^ [[:alpha:]]*sleep"'
    output = subprocess.check_output(pmsetCmd, shell=True, encoding='UTF-8')
    # The output of this will be more than one line
    for line in output.splitlines():
        m = sleep_CRE.search(line)
        if m is not None:
            # m.group(1) will be the list of things preventing sleep, i.e. : 'coreaudiod, WebexHelper'
            unsleepers = m.group(1)
            for meetingApp in ['MSTeams', 'Webex', 'zoom.us']:
                if meetingApp in unsleepers:
                    return meetingApp
    return None                                

# TODO maybe notify btt of the app the first time through the loop
def notify_btt(meetingApp=''):
    if meetingApp is None:
        meetingApp = ''
    btt_args = ['open']
    btt_url = f'"btt://set_persistent_string_variable/?variableName=meeting_app&to={meetingApp}"'
    btt_args.append(btt_url)
    if True:
        print(f'[notify btt subproc args] {btt_args}')
    try:
        btt_args = " ".join(btt_args)
        subprocess.check_call(btt_args, shell=True)
    except:
        # TODO, what to do on fail, for instance, if btt not open?  TEST?
        print('unable to update btt with meeting status')

def monitor_status(interval_sec=10, print_updates=True):
    was_meeting = False
    while True:
        meetingApp = check_status()
        is_meeting = meetingApp is not None
        if was_meeting != is_meeting:
            if print_updates:
                print(f'Now Meeting [{is_meeting}] in app {meetingApp}')
            was_meeting = is_meeting
            notify_btt(meetingApp)
        time.sleep(interval_sec) 

if __name__ == '__main__':
    parser = argparse.ArgumentParser(usage='MacOS monitor status of MSTeams and Webex and Zoom meetings')
    # TODO maybe add print/noprint option
    parser.add_argument('-i', '--interval', help='polling interval in sec', default=5, type=int)
    args = parser.parse_args()

    interval_sec = args.interval
    if interval_sec < 1:
        interval_sec = 1
    try:
        monitor_status(interval_sec)
    except KeyboardInterrupt:
        # OK, user pressed ctrl-c, clean exit
        print('  Done.')