Skip to content

GNOME Auto switch to new connected BT Device + try setting it to Headphone (A2DP) and avoid Handsfree #268

@CodingKoalaGeneral

Description

@CodingKoalaGeneral

When I connect a Bluetooth device, it isn't automatically selected as the new output device in GNOME 49.
Solution: Auto switch to new connected BT Device

Tested:

mkdir -p ~/.config/pipewire/pipewire-pulse.conf.d/
echo 'pulse.cmd = [{ cmd = "load-module" args = "module-switch-on-connect" }]' > ~/.config/pipewire/pipewire-pulse.conf.d/50-bt-switch.conf
systemctl --user restart pipewire pipewire-pulse wireplumber

The module now automatically switches to the headset upon Bluetooth connection (A2DP preferred, if available).

Also the bug is related to the following issue: If you connect a bt device and last volume was set above mute, the selected audio output is not changed (my monitor in that case) but it uses the sound volume of newly connected device / 100 or what ever. Blew me literally away multiple times until I muted the monitors with their volume buttons.


Part 2 - totally optional

Sometimes only handsfree mode is available than a simple reconnect often fixed the issue.

crap below dont waste time reading - will comeback once i have to time to focus on this.

The idea was that a udev rule is triggered when the device connects,
it checks the mode and reconnects if headset is not available.

sudo echo 'SUBSYSTEM=="bluetooth", ACTION=="add", RUN+="/home/$USER/bt-reconnect.sh"' > /etc/udev/rules.d/99-bt-headset.rules      

since udev does not work in this case (if it does let me know)
using a systemd service which checks the path '/sys/class/bluetooth/*/device' for new bt devices
and then executes as soon as a change is detected. But I couldn't get it to work.
Maybe / hopefully I test this later - be aware

sudo nano /etc/systemd/system/bt-device.path

[Unit]
Description=Watch for Bluetooth device appearance

[Path]
# Watch the Bluetooth device directory (adjust if your device shows up elsewhere)
PathExistsGlob=/sys/class/bluetooth/*/device
Unit=bt-device.service

[Install]
WantedBy=multi-user.target

Updated: Initial version did not work - I got 'Could not connect to PipeWire' as response.
I changed it to restart user level pipewire services and it did work.

systemctl --user restart pipewire
systemctl --user restart pipewire-pulse

bt-reconnect.sh

#!/bin/bash
sleep 3
NEW_DEVICE=$(bluetoothctl devices Connected | grep -o '[0-9A-Fa-f:]\{17\}' | head -1)
STATE_FILE="/tmp/bt-reconnect-$NEW_DEVICE"

if [ -n "$NEW_DEVICE" ]; then
    echo "$(date) Checking $NEW_DEVICE..." >> /tmp/bt-reconnect.log
    
    # Skip if already set as default
    CURRENT_SINK=$(wpctl status | grep '\*' | grep -o '[0-9]\+' | head -1)
    CURRENT_SINK_NAME=$(wpctl inspect "$CURRENT_SINK" 2>/dev/null | grep -i bluez | grep -o '[0-9A-Fa-f:]\{17\}' || echo "none")
    [[ "$CURRENT_SINK_NAME" == "$NEW_DEVICE" ]] && { echo "Already default → Skipping"; exit 0; }
    
    # Check if only Handsfree (HSP/HFP) is available
    INFO=$(bluetoothctl info "$NEW_DEVICE")
    HAS_A2DP=$(echo "$INFO" | grep -q "UUID: Audio Sink" && echo 1 || echo 0)
    
    if [ "$HAS_A2DP" = "1" ]; then
        # Set as default immediately
        NEW_SINK=$(wpctl status | sed 's/│/|/g' | grep -i "$NEW_DEVICE" | grep -v source | sed 's/|/ /g' | awk '{for(i=1;i<=NF;i++) if($i ~ /^[0-9]+$/) {print $i; exit}}' | head -1)
        [[ "$NEW_SINK" =~ ^[0-9]+$ ]] && wpctl set-default "$NEW_SINK"
        exit 0
    fi
    
    # AUTO-RECONNECT LOGIC (3x max)
    ATTEMPTS=$(cat "$STATE_FILE" 2>/dev/null || echo "0")
    ATTEMPTS=$((ATTEMPTS + 1))
    
    if [ "$ATTEMPTS" -le 3 ]; then
        echo "$(date) -> Attempt ${ATTEMPTS}/3..." >> /tmp/bt-reconnect.log
        echo "$ATTEMPTS" > "$STATE_FILE"
        
        bluetoothctl disconnect "$NEW_DEVICE"
        systemctl --user restart pipewire
        systemctl --user restart pipewire-pulse
        sleep 1
        bluetoothctl connect "$NEW_DEVICE"
        sleep 3
        
        # Check success after reconnect
        NEW_INFO=$(bluetoothctl info "$NEW_DEVICE")
        if echo "$NEW_INFO" | grep -q "UUID: Audio Sink"; then
            echo "A2DP successful after ${ATTEMPTS} attempts!" >> /tmp/bt-reconnect.log
            rm -f "$STATE_FILE"
            # Set sink logic would follow here if needed...
        fi
        
    else
        echo "$(date) MAX 3 attempts reached!" >> /tmp/bt-reconnect.log
        rm -f "$STATE_FILE"
        
        # GNOME Notification
        notify-send "Bluetooth Headphone Mode unavailable" "Please turn your Bluetooth Device off and on to reset connection" -u critical -t 10000
        
        bluetoothctl disconnect "$NEW_DEVICE"
    fi
fi

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/enhancementNew feature, don't implement without a spec and consensus

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions