Skip to main content
  1. Posts/

Meeting mute toggle from anywhere on MacOS

·4 mins

Problem #

On a virtual meeting. Have another app or window open. Heard my name called, and needed to respond. Quickly hunted for Teams window, and then for unmute button.

It happens every day, in almost every meeting.

Plus with IDEs and Docker open, and MS Teams running all day, many times the UI is unresponsive and I can’t get off mute.

I want a way to be able to toggle mute for MS Teams from anywhere.

Ideally menu bar icon + keyboard shortcut.

Solution #

Searched Google and the first response from Geeker’s Digest had a great solution: a tool called Hammerspoon.

Installation #

Install with brew:

brew install --cask hammerspoon

Download, unzip, and install the MicMute Spoon from here. Installation should just be double clicking on the MicMute.spoon file.

Configure Hammerspoon #

Create a ~/.hammerspoon/init.lua file which loads MicMute, and sets the appropriate keybinding. The keybinding I chose was 3 modifier keys (command, option, and shift) and then the m key.

mkdir -p ~/.hammerspoon/Spoons
cat << EOF > ~/.hammerspoon/init.lua
local mm = hs.loadSpoon("MicMute")
local mmkeybind = {"toggle"}
mmkeybind.toggle = {[1]={"cmd", "option", "shift"}, [2]="m"}
local mmfunctionkeybind = {"toggle"}
mmfunctionkeybind.toggle = {[1]={}, [2]="f19"}

mm:bindHotkeys(mmkeybind, 1)
mm:bindHotkeys(mmfunctionkeybind, 1)
EOF

If you also want to add another button on the menu bar to automatically switch to the Teams window, add this:

cat << EOF >> ~/.hammerspoon/init.lua

-- Teams Show Button
local teamsShowButton = hs.menubar.new()
local logo = hs.image.imageFromAppBundle("com.microsoft.teams")
logo:size({w=16,h=16})
teamsShowButton:setIcon(logo)

function showTeams() 
  local teams = hs.application.find("com.microsoft.teams")
  if not (teams == null) then
    teams:activate()
  end
end

teamsShowButton:setClickCallback(showTeams)
EOF

At this point, you should now have a .hammerspoon folder with a Spoons subfolder and an init.lua file. The Spoons subfolder should have MicMute.spoon inside. The MicMute.spoon is a folder with another init.lua and a docs.json file inside.

$ ls ~/.hammerspoon         
Spoons          init.lua
$ ls ~/.hammerspoon/Spoons 
MicMute.spoon
$ ls ~/.hammerspoon/Spoons/MicMute.spoon 
docs.json       init.lua

Extend MicMute for Teams support #

Originally, MicMute only supported Zoom. I put in a Github Pull Request to extend MS Teams support to MicMute (v1.1). Depending on when you read this post, that might already be merged in (so no need to do the next step).

If you need to make that update, replace the toggleMicMute function inside the MicMute.spoon/init.lua file (using your editor of choice). You can see it here.

function obj:toggleMicMute()
 local mic = hs.audiodevice.defaultInputDevice()
 local zoom = hs.application'Zoom'
 local teams = hs.application.find("com.microsoft.teams")
 if mic:muted() then
   mic:setInputMuted(false)
   if zoom then
    local ok = zoom:selectMenuItem'Unmute Audio'
    if not ok then
      hs.timer.doAfter(0.5, function()
       zoom:selectMenuItem'Unmute Audio'
      end)
    end
   end
   if teams then
    local ok = teams:selectMenuItem'Unmute'
    if not ok then
      hs.timer.doAfter(0.5, function()
       hs.eventtap.keyStroke({"cmd","shift"}, "m", 0, teams)
      end)
    end
   end
 else
   mic:setInputMuted(true)
   if zoom then
    local ok = zoom:selectMenuItem'Mute Audio'
    if not ok then
      hs.timer.doAfter(0.5, function()
       zoom:selectMenuItem'Mute Audio'
      end)
    end
   end
   if teams then
    local ok = teams:selectMenuItem'Mute'
    if not ok then
      hs.timer.doAfter(0.5, function()
       hs.eventtap.keyStroke({"cmd","shift"}, "m", 0, teams)
      end)
    end
   end
 end
 obj:updateMicMute(-1)
end

Run & Test it #

  1. Launch Hammerspoon (either from Applications, Spotlight, or Alfred)
  2. Click the menu bar Hammerspoon icon and reload the config
  3. Click the menu bar Hammerspoon icon and open the Console to verify that there are no errors. The last line should probably say Done.
  4. Assuming everything before this step went correctly, you should now have another Microphone menu bar item that if you click on it toggles between 🎙 On and 📵 Muted
  5. Test the keyboard shortcut to validate that it toggles
  6. Launch a Zoom or Teams meeting, and validate that the menu bar and keybindings both toggle the meeting correctly

Troubleshooting #

  1. If there are any errors, you should see them on the Hammerspoon console
  2. If you make any changes to your init.lua or the MicMute.spoon/init.lua files and you want to test them, choose the Reload config option in the Hammerspoon menu bar dropdown
  3. If you want to add some log statements for debugging, add something like this to the top of the Lua files:
local log = hs.logger.new("mine", "debug")
log.d("HI")

Refs #