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:
#!/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"];thenecho 🟢
fiif["$zm_status"="false"];thenecho 🔴
fiif["$zm_status"="off"];thenecho ⚪
fi
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.
#!/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 settingsif[["$1"="toggle"]];then osascript -e 'tell application "System Events" to keystroke "a" using {command down, shift down}'fiif[["$1"="launch"]];then osascript -e '
tell application "zoom.us"
activate
end tell
'fizm_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"];thenecho"🟢 Unmuted| color=#00FF00"#emoji allowed for example: echo 🗣echo ---
echo"Mute| bash='$0' param1=toggle terminal=false"exitfiif["$zm_status"="false"];thenecho"🔴 Muted| color=#FF0000"#emoji allowed for example: echo 🤫echo ---
echo"Unmute| bash='$0' param1=toggle terminal=false"exitfiif["$zm_status"="off"];thenecho"⚪️ Zoom not running"echo ---
echo"Launch Zoom| bash='$0' param1=launch terminal=false"exitfiif["$zm_status"="1"];thenecho"Mic not enabled"echo ---
echo"Join Audio| bash='$0' param1=toggle terminal=false"exitfi
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.
#!/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 settingsif[["$1"="toggle"]];then osascript -e 'tell application "System Events" to keystroke "a" using {command down, shift down}'fiif[["$1"="launch"]];then osascript -e '
tell application "zoom.us"
activate
end tell
'fizm_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"];thenecho"🟢 Unmuted| color=#00FF00"#emoji allowed for example: echo 🗣echo ---
echo"Mute| bash='$0' param1=toggle terminal=false" blink1-tool --rgb 298A08 &> /dev/null
exitfiif["$zm_status"="false"];thenecho"🔴 Muted| color=#FF0000"#emoji allowed for example: echo 🤫echo ---
echo"Unmute| bash='$0' param1=toggle terminal=false" blink1-tool --rgb 8a2908 &> /dev/null
exitfiif["$zm_status"="off"];thenecho"⚪️ Zoom not running"echo ---
echo"Launch Zoom| bash='$0' param1=launch terminal=false" blink1-tool --off &> /dev/null
exitfiif["$zm_status"="1"];thenecho"Mic not enabled"echo ---
echo"Join Audio| bash='$0' param1=toggle terminal=false" blink1-tool --off &> /dev/null
exitfi