Handy scripts you'd like to share

I swap audio output between my speakers and headset all the time for gaming. The audio output systray tool included in Garuda Hyprland edition is nice, but I was getting annoyed at having to click through that thing so often. So I wrote a Python script that will swap between them for me, then bound that script to a key combo.


import re
import logging
import subprocess
from systemd.journal import JournalHandler

# full sink names captured from pactl
SpeakerSink = None
HeadsetSink = None
DefaultSink = None
NewSink = None

# audio sink name printed in notification
SpeakerNiceName = 'PreSonus AudioBox'
HeadsetNiceName = 'Logitech G535 Headset'
NewSinkNiceName = None

# regexes for audio sink names from pactl
SpeakerSinkRe = re.compile('(.*PreSonus_AudioBox.*)')
HeadsetSinkRe = re.compile('(.*Logitech_G535.*)')
DefaultSinkRe = re.compile('Default Sink: (.*)$')

log = logging.getLogger('swapAudioOut')
handler = JournalHandler()
handler.setFormatter(logging.Formatter('[%(levelname)s] %(message)s'))

def sendErrorNotification():
	cmd = f'notify-send -t 5000 \"Audio swap failure!\" \"Check journal for details.\"'
	subprocess.Popen(cmd, shell=True)

def logSinks():
	log.error(f'Speaker sink: {str(SpeakerSink)}')
	log.error(f'Headset sink: {str(HeadsetSink)}')

pactlOutput = subprocess.Popen('pactl list short sinks', shell=True, stdout=subprocess.PIPE).communicate()[0].decode('utf-8')
splitOutput = pactlOutput.split('\t')

for sink in splitOutput:
	SpeakerMatch = SpeakerSinkRe.match(sink)
	if SpeakerMatch:
		SpeakerSink = SpeakerMatch.group()

	HeadsetMatch = HeadsetSinkRe.match(sink)
	if HeadsetMatch:
		HeadsetSink = HeadsetMatch.group()

if not SpeakerSink or not HeadsetSink:
	log.error('One or more sinks not found.')

pactlOutput = subprocess.Popen('pactl info', shell=True, stdout=subprocess.PIPE).communicate()[0].decode('utf-8')
splitOutput = pactlOutput.split('\n')

for line in splitOutput:
	DefaultMatch = DefaultSinkRe.match(line)
	if DefaultMatch:
		DefaultSink = DefaultMatch.group(1)

if DefaultSink is None:
	log.error('Default sink not found.')

if DefaultSink == HeadsetSink:
	NewSink = SpeakerSink
	NewSinkNiceName = SpeakerNiceName
elif DefaultSink == SpeakerSink:
	NewSink = HeadsetSink
	NewSinkNiceName = HeadsetNiceName

if NewSink:
	subprocess.Popen(f'pactl set-default-sink {NewSink}', shell=True)
	cmd = f'notify-send -t 5000 \"Audio output changed\" \"{NewSinkNiceName}\"'
	subprocess.Popen(cmd, shell=True)
	log.error(f'New sink: {str(NewSink)}')

The names of my headset and speaker audio sinks (PreSonus AudioBox and Logitech G535) are hard coded in here. I’ve seen some solutions to this that are more generic and will flip through all your available sinks, but I just wanted the script to toggle between these two. You can change the strings and regexes to match the sinks on your machine - get them with “pactl list short sinks”.

Then in my ~/.config/hypr/hyprland.conf,

bind = $mainMod, F5, exec, python3 ~/swapAudioOut.py