A Better Zoom Mute Indicator

Swiftbar, bash scripts and a Blink(1)

swiftbar and blink(1) I originaly thought my last attempt at creating a Zoom mute indicator was good enough, but I couldn’t leave well enough alone and had to rebuild the whole thing with something new.

One of the big issues with with using AppleScript and AnyBar is that it’s hard to distribute. Someone would need to get my AppleScript code, paste it into Script Editor, and then save it as an application with the correct boxes checked. Then they would also need to install AnyBar. It’s kind of a lot of hoops to jump through.

I wanted a way to package the application, but didn’t actually want to join the Apple Developer Program. After a bit of googling I found BitBar. BitBar is an application that originally displayed Bitcoin prices in the menu bar (hence the name), but it’s now been extended to display just about anything. There’s a whole repository of user contributed plugins. I figured I would just convert my AppleScript to a BitBar plugin and add it to the repo.

Heir Apparent

It turns out BitBar didn’t work well in BigSur and was increasingly difficult to maintain for the developer. Luckily there is a spiritual successor in SwiftBar. SwiftBar is built in Swift, taps into the existing BitBar Plugin library, and has a much better user experience. There is a plugin browser that lets you install plugins without needing to go to a GitHub repo (which makes sharing my plugin a lot easier).

Shell Scripts

Of course, BitBar/SwiftBar plugins aren’t written in AppleScript, so I had to teach myself how to write a shell script. Luckily the BitBar documentation was thorough, and there was a whole repository of samples I could pull from. A little Googling led me to a Six Colors blog post describing how to use the osascript command to run AppleScript from a shell script.

I was able to use most of my original AppleScript, but I needed to output a variable value instead of telling AnyBar to change to a color. I then wrote a few if statements to output an emoji for the different variables. I displayed a Green dot for unmuted and red dot for muted. I ended up with the script below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash

# <bitbar.title>Mute Status</bitbar.title>

zm_status=$(osascript -e'
tell application "System Events"
  if (get name of every application process) contains "zoom.us" then
    tell application "System Events" to tell application process "zoom.us"
      if menu item "Mute audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists then
        return true
      else
        if menu item "Unmute audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists then
          return false
        else
          return off
        end if
      end if
    end tell
  else
    return off
  end if
end tell
');


if [ "$zm_status" = "true" ]; then
  echo 🟢
fi

if [ "$zm_status" = "false" ]; then
  echo 🔴
fi

if [ "$zm_status" = "off" ]; then
  echofi

Enhancements

I got the basics working and then I added some quality of life enhancements. I added dropdowns to mute and unmute from the menubar icon, I added some logic to account for using telephone audio with Zoom, and I (partially) accounted for Zoom Webinars. Below is the code for anyone that wants to use it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/bin/bash

# <bitbar.title>Zoom Mute Status</bitbar.title>
# <bitbar.allowed for example:>v1.0</bitbar.allowed for example:>
# <bitbar.author>Dustin</bitbar.author>
# <bitbar.author.github>dustincredible</bitbar.author.github>
# <bitbar.desc>Reports the mute status of a Zoom meeting. Relies on menu bar item names, so this will only work if your Zoom app is using english. Only partially works for Zoom webinars.</bitbar.desc>
# <bitbar.image>https://user-images.githubusercontent.com/14987234/104110931-77e27f00-5291-11eb-98fb-47cf1febd84e.png</bitbar.image>

# Check the Enable Global Shortcut for Mute/unmute in your Zoom app settings
if [[ "$1" = "toggle" ]]; then
  osascript -e 'tell application "System Events" to keystroke "a" using {command down, shift down}'
fi

if [[ "$1" = "launch" ]]; then
  osascript -e '
    tell application "zoom.us" 
      activate
    end tell
	'
fi

zm_status=$(osascript -e'
tell application "System Events"
		if (get name of every application process) contains "zoom.us" then
			tell application "System Events" to tell application process "zoom.us"
				if menu item "Join Audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists then
					return 1
				else
					if (menu item "Mute audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) or (menu item "Mute telephone" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) then
						return true
					else
						if (menu item "Unmute audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) or (menu item "Unmute telephone" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) then
							return false
						else
							return off
						end if
					end if
				end if
			end tell
		else
			return off
		end if
end tell
');

if [ "$zm_status" = "true" ]; then
  echo "🟢 Unmuted| color=#00FF00" #emoji allowed for example: echo 🗣
  echo ---
  echo "Mute| bash='$0' param1=toggle terminal=false"
  exit
fi

if [ "$zm_status" = "false" ]; then
  echo "🔴 Muted| color=#FF0000" #emoji allowed for example: echo 🤫
  echo ---
  echo "Unmute| bash='$0' param1=toggle terminal=false"
  exit
fi

if [ "$zm_status" = "off" ]; then
  echo "⚪️ Zoom not running"
  echo ---
  echo "Launch Zoom| bash='$0' param1=launch terminal=false"
  exit
fi

if [ "$zm_status" = "1" ]; then
  echo "Mic not enabled"
  echo ---
  echo "Join Audio| bash='$0' param1=toggle terminal=false"
  exit
fi

I’ll add it to the bitbar repo at some point, but that requires doing a pull request. I barely understand git, so I’m a little terrified of doing that.

Adding in a Blink(1)

Not content with just a digital indicator, I searched for a physical device that I could use to indicate my Zoom mute status. The Blink(1) from ThingM was perfect. Originally a Kickstarter project, the Blink(1) is a USB device with RGB lights. There is a command line tool that can set the color and could be integrated with my shell script.

To get this setup, I had to download the blink1-tool, drop the executable into usr/local/bin, and add the blink1-tool commands to my script.

blink1 and Swiftbar cycling between green, red, and white

The finished product (sorry for the shaky cam)

The full shell script with Blink(1) support:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/bin/bash

# <bitbar.title>Zoom Mute Status</bitbar.title>
# <bitbar.allowed for example:>v1.0</bitbar.allowed for example:>
# <bitbar.author>Dustin Liang</bitbar.author>
# <bitbar.author.github>dustincredible</bitbar.author.github>
# <bitbar.desc>Reports the mute status of a Zoom meeting. Relies on menu bar item names, so this will only work if your Zoom app is using english. Only partially works for Zoom webinars.</bitbar.desc>
# <bitbar.image>https://user-images.githubusercontent.com/14987234/104110931-77e27f00-5291-11eb-98fb-47cf1febd84e.png</bitbar.image>

# Check the Enable Global Shortcut for Mute/unmute in your Zoom app settings
if [[ "$1" = "toggle" ]]; then
  osascript -e 'tell application "System Events" to keystroke "a" using {command down, shift down}'
fi

if [[ "$1" = "launch" ]]; then
  osascript -e '
	  tell application "zoom.us" 
		  activate
	  end tell
	'
fi

zm_status=$(osascript -e'
tell application "System Events"
		if (get name of every application process) contains "zoom.us" then
			tell application "System Events" to tell application process "zoom.us"
				if menu item "Join Audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists then
					return 1
				else
					if (menu item "Mute audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) or (menu item "Mute telephone" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) then
						return true
					else
						if (menu item "Unmute audio" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) or (menu item "Unmute telephone" of menu 1 of menu bar item "Meeting" of menu bar 1 exists) then
							return false
						else
							return off
						end if
					end if
				end if
			end tell
		else
			return off
		end if
end tell
');

if [ "$zm_status" = "true" ]; then
  echo "🟢 Unmuted| color=#00FF00" #emoji allowed for example: echo 🗣
  echo ---
  echo "Mute| bash='$0' param1=toggle terminal=false"
  blink1-tool --rgb 298A08 &> /dev/null
  exit
fi

if [ "$zm_status" = "false" ]; then
  echo "🔴 Muted| color=#FF0000" #emoji allowed for example: echo 🤫
  echo ---
  echo "Unmute| bash='$0' param1=toggle terminal=false"
  blink1-tool --rgb 8a2908 &> /dev/null
  exit
fi

if [ "$zm_status" = "off" ]; then
  echo "⚪️ Zoom not running"
  echo ---
  echo "Launch Zoom| bash='$0' param1=launch terminal=false"
  blink1-tool --off &> /dev/null
  exit
fi

if [ "$zm_status" = "1" ]; then
  echo "Mic not enabled"
  echo ---
  echo "Join Audio| bash='$0' param1=toggle terminal=false"
  blink1-tool --off &> /dev/null
  exit
fi


See also