Skip to content

Instantly share code, notes, and snippets.

@mikeboiko
Created March 31, 2025 10:35
Show Gist options
  • Save mikeboiko/6e2c80ab3de7c35ba8bf0579622c2552 to your computer and use it in GitHub Desktop.
Save mikeboiko/6e2c80ab3de7c35ba8bf0579622c2552 to your computer and use it in GitHub Desktop.
Auto-switching PulseAudio devices using udev rules

The Problem

PulseAudio's default behavior when disconnecting Bluetooth devices can be frustrating. When your Bluetooth headphones disconnect, PulseAudio automatically switches to another available sink - but not necessarily the one you want (like your laptop speakers).

The Solution

This solution uses udev rules to automatically switch audio devices when Bluetooth connects/disconnects:

  1. When Bluetooth headphones connect -> Set them as default input/output
  2. When Bluetooth headphones disconnect -> Force specific fallback devices (e.g., laptop speakers)

Setup Instructions

  1. Create a udev rule at /etc/udev/rules.d/99-bluetooth.rules:
ACTION=="add", SUBSYSTEM=="input", ATTRS{phys}=="XX:XX:XX:XX:XX:XX", RUN+="/path/to/set-pulse-audio-defaults.sh --connect"
ACTION=="remove", SUBSYSTEM=="input", ATTRS{phys}=="XX:XX:XX:XX:XX:XX", RUN+="/path/to/set-pulse-audio-defaults.sh --disconnect"
  1. Save the script below as set-pulse-audio-defaults.sh and make it executable:
BT_SINK="bluez_output.AC_12_2F_7B_D4_E3.1"
BT_SOURCE="bluez_input.AC:12:2F:7B:D4:E3"
BACKUP_SINK="alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Headphones__sink"
BACKUP_SOURCE="alsa_input.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Mic1__source"
PULSE_RUNTIME_DIR="/run/user/1000/pulse" # Default, will be overwritten if XDG is set.
USER="mike"
LOG_FILE="/dev/null"
# LOG_FILE="/tmp/udev.log" # For troubleshooting

# Parse command line arguments
case "$1" in
--connect)
    PULSE_SINK="$BT_SINK"
    PULSE_SOURCE="$BT_SOURCE"
    sleep 2
    ;;
--disconnect)
    PULSE_SINK="$BACKUP_SINK"
    PULSE_SOURCE="$BACKUP_SOURCE"
    ;;
*)
    echo "Usage: $0 [--connect|--disconnect]"
    exit 1
    ;;
esac

# Get XDG_RUNTIME_DIR if available
if [[ -n "$XDG_RUNTIME_DIR" ]]; then
    PULSE_RUNTIME_DIR="$XDG_RUNTIME_DIR/pulse"
fi

# Check if PulseAudio socket exists
if [[ -S "$PULSE_RUNTIME_DIR/native" ]]; then
    date "+%Y-%m-%d %H:%M:%S - Udev: setting default to $PULSE_SINK" >>"$LOG_FILE"
    sudo -u "$USER" PULSE_RUNTIME_PATH="$PULSE_RUNTIME_DIR" /usr/bin/pactl set-default-sink "$PULSE_SINK" >>"$LOG_FILE" 2>&1
    sudo -u "$USER" PULSE_RUNTIME_PATH="$PULSE_RUNTIME_DIR" /usr/bin/pactl set-default-source "$PULSE_SOURCE" >>"$LOG_FILE" 2>&1
else
    date "+%Y-%m-%d %H:%M:%S - PulseAudio socket not found for user $USER" >>"$LOG_FILE"
    echo "PulseAudio socket not found at $PULSE_RUNTIME_DIR/native" >>"$LOG_FILE"
fi

exit 0
  1. Reload udev rules:
sudo udevadm control --reload-rules

Finding Your Device Information

  • To get your Bluetooth device's MAC address and other udev properties:
udevadm monitor --property
  • To list available PulseAudio sinks and sources:
pactl list sinks short
pactl list sources short
pactl get-default-sink

Customization

Update these variables in the script to match your system:

  • BT_SINK: Your Bluetooth headphones' sink name
  • BT_SOURCE: Your Bluetooth headphones' source name
  • BACKUP_SINK: Your preferred fallback output device
  • BACKUP_SOURCE: Your preferred fallback input device
  • USER: Your system username

For troubleshooting, uncomment the alternate LOG_FILE line to enable logging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment