Sno Station
Sno Station is your agents' workstation: open-source software that turns the AI agents you already run into one squad on your own machine. Two heads are better than one, and they get better with every
A Hammerspoon Spoon that toggles the Microsoft Teams meeting microphone from a single hotkey, from any app.
PluginsLua · PklGitHub @hugohOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Hammerspoon Spoon: toggle the Microsoft Teams meeting mic from a hotkey globally, from any app
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A Hammerspoon Spoon that toggles the Microsoft Teams meeting microphone from a single hotkey, from any app, and tells you whether it worked.
• One hotkey toggles mute from anywhere: if Teams isn't frontmost it's activated, the toggle is sent, then focus returns to the app you were in
• Sends Cmd+Shift+M, then confirms the toggle.
• On-screen alert for the result: 🔶 Teams Muted / 🎤 Teams Unmuted on success, or a 🛑 alert (No active Teams call, Teams did not activate in time, Mute toggle did not register, STILL MUTED/STILL UNMUTED) on failureRead the full README ↗A Hammerspoon Spoon that toggles the Microsoft Teams meeting microphone from any app, tells you whether it worked, and shows your mute state in the menu bar.
Repository: https://github.com/hugoh/TeamsControl.spoon
🔶 Teams Muted / 🎤 Teams Unmuted, or a 🛑 alert explaining what went wrongEnsure you have Hammerspoon installed, then choose a method:
TeamsControl.spoon.zip from the latest releaseTeamsControl.spoon folder~/.hammerspoon/Spoons/hs.reload() in the console)spoon.SpoonInstall:installSpoonFromZip(
"https://github.com/hugoh/TeamsControl.spoon/releases/latest/download/TeamsControl.spoon.zip"
)
cd ~/.hammerspoon/Spoons
git clone https://github.com/hugoh/TeamsControl.spoon.git
Bind a hotkey and start the menu bar indicator:
hs.loadSpoon("TeamsControl")
:bindHotkeys({ toggleMute = { { "ctrl", "alt", "cmd" }, "m" } })
:start()
stop() removes the indicator and unbinds the hotkeys.
Or call toggleMute() yourself from your own hotkey wiring:
local teamsControl = hs.loadSpoon("TeamsControl")
hs.hotkey.bind({ "ctrl", "alt", "cmd" }, "m", function() teamsControl:toggleMute() end)
toggleMute() accepts an optional callback, invoked once the toggle has settled
(or the call was ignored as re-entrant), for driving a busy indicator.
Tune behaviour with configure() (all optional):
hs.loadSpoon("TeamsControl"):configure({
teamsBundleID = "com.microsoft.teams2", -- Teams app bundle identifier
activationTimeout = 5, -- seconds to wait for Teams to come forward when retrying
clickSettleDelay = 0.05, -- seconds between checks that the toggle registered
clickSettleMaxRetries = 10, -- checks before retrying, then before giving up
showMenubar = true, -- show the menu bar indicator
menubarStatusDot = true, -- show a 🟢/🟡 dot next to the menu bar icon
menubarPollInterval = 1, -- seconds between menu bar indicator refreshes (only while Teams runs)
useApi = true, -- use Teams' local API when available
apiPort = 8124, -- port of Teams' local API
apiRetryInterval = 30, -- seconds between attempts to reach the API
apiMaxRetries = 3, -- attempts per Teams launch before giving up
apiConfirmTimeout = 0.5, -- seconds to wait for the API to confirm a toggle before the keystroke fallback
}):start()
TeamsControl reads Teams' accessibility tree and sends synthetic keystrokes and clicks, so Hammerspoon needs Accessibility permission (System Settings → Privacy & Security → Accessibility). It never launches anything but Teams and never shells out.
TeamsControl uses Teams' local third-party API when it can, which mutes without keystrokes or bringing Teams forward, and shows the menu bar state instantly. It falls back to the keystroke and accessibility path whenever the API is off, unpaired, or doesn't answer, so nothing here is required.
To enable it:
~/Library/Containers/com.microsoft.teams2/Data/Library/Application Support/Microsoft/MSTeams/configuration.json containing {"thirdPartyDevices/thirdPartyDevicesManagerEnabled": true}, then restart Teams.The log (info level) shows which path each toggle takes. TeamsControl retries the connection apiMaxRetries times after Teams starts, so enable the API before launching Teams. Set useApi = false to turn it off.
The accessibility-tree mute-button lookup (findButton in ui.lua) is adapted from _teamsFindButtonByLabel in RobvH/teams-mac-hotkeys.
The Teams local API client (connection URL, pairing flow, token refresh and request format) follows asp55/MSTeams.spoon2 (MIT, © 2026 asp55), which in turn credits svrooij's documentation of the API.
Full API reference is generated from the docstrings in init.lua (mise run docs).