Last active
July 10, 2025 15:34
-
-
Save jpsutton/d5e742756c4b3077bce59d5b3ec7dd34 to your computer and use it in GitHub Desktop.
Toggle all microphones mute status with on-screen indicator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import pulsectl | |
import dbus | |
import time | |
def show_notification(title, message, icon_name): | |
""" | |
Show a KDE desktop notification using D-Bus directly. | |
Args: | |
title: The notification title | |
message: The notification message body | |
icon_name: Name of the icon to display | |
""" | |
print(f"{title}: {message}") | |
try: | |
# Connect to the D-Bus session bus | |
bus = dbus.SessionBus() | |
# Get the notifications service | |
notify_service = bus.get_object( | |
'org.freedesktop.Notifications', | |
'/org/freedesktop/Notifications' | |
) | |
# Get the notifications interface | |
notify_interface = dbus.Interface( | |
notify_service, | |
'org.freedesktop.Notifications' | |
) | |
# Send the notification | |
notify_interface.Notify( | |
"Mic Toggle", # App name | |
0, # Replace id (0 = new notification) | |
icon_name, # Icon name | |
title, # Title | |
message, # Body | |
[], # Actions | |
{}, # Hints | |
2000 # Timeout in ms (2 seconds) | |
) | |
except Exception as e: | |
# Fall back to print if D-Bus fails | |
print(f"Notification failed: {e}") | |
print(f"{title}: {message}") | |
def toggle_all_mics(): | |
""" | |
Toggle mute status of all input devices (microphones) in the PulseAudio system. | |
Shows an on-screen notification of the action. | |
Returns True if mics are now muted, False if unmuted. | |
""" | |
with pulsectl.Pulse('mic-toggle') as pulse: | |
# Get all source (input) devices that are actually microphones | |
all_sources = pulse.source_list() | |
# Filter for actual microphone devices - exclude monitors and non-mic devices | |
microphones = list(filter(lambda x: "input" in x.name.split(".")[0].lower(), all_sources)) | |
# If no microphones found, show notification and exit early | |
if not microphones: | |
show_notification( | |
"Mic Toggle", | |
"No microphones found in the system", | |
"microphone-sensitivity-muted" | |
) | |
return False | |
# Check if any mics are already unmuted to determine current state | |
any_unmuted = any(mic.mute == 0 for mic in microphones) | |
# If any mic is unmuted, we'll mute all | |
# If all mics are muted, we'll unmute all | |
new_mute = 1 if any_unmuted else 0 | |
# Apply mute/unmute only to microphone sources | |
for mic in microphones: | |
print(f"{mic.description}\n\tMuted: {mic.mute}\n\tDesired state: {new_mute}") | |
if mic.mute == new_mute: | |
continue | |
pulse.source_mute(mic.index, new_mute) | |
print(("\tMuting " if new_mute else "\tUnmuting ") + str(mic.index)) | |
# Fast iteration through mics via the Pulse interface seems to be time-sensitive, so delay a bit to get consistent results | |
time.sleep(0.15) | |
# Show notification based on action | |
icon = 'microphone-sensitivity-muted' if new_mute else 'microphone-sensitivity-high' | |
message = 'All microphones muted' if new_mute else 'All microphones unmuted' | |
show_notification("Mic Toggle", message, icon) | |
return new_mute == 1 # Return True if we muted, False if we unmuted | |
if __name__ == "__main__": | |
status = toggle_all_mics() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Some system update after originally publishing this introduced a race condition when toggling, so I added a small delay when iterating through mics. My own testing showed 1/10 of a second was enough to produce consistent results, so I added 50% and called it a day :)
Also added a check of current mic state to avoid trying to set a mute/unmute flag unncessarily, and added some additional terminal logging to help with debugging.