Since I had such a huge Pipewire headache this weekend, I wrote my findings into a Linux script. Please check it out and run it if you find it safe. It does everything: Installs pipewire, writes an espeakup override if you have it it'll be useful, stops LightDM from spawning its own socket and using the shared /tmp/pulse.soc instead, and just, works to move Bluetooth syncs from HFP to A2DP. Again, verify verify. I had GPT help me write it so I cannot give it 100% trust.
eurpod.com/setup-pipewire-a11y…
Looping in @fireborn and @storm, other Linux geeks, love you all, PLEASE weigh in. :D


Billy
in reply to Tamas G • • •this isn't totally in my field of knowledge, e.g. I use USB headset and nodm as my display manager, but it looks mostly fine. If it works, and you have sound on boot, this may not be helpful.
I noticed that you enable the user services for pipewire:
But unless I missed it linger isn't enabled for $TUSER so sound may not start at boot. If you have problems with no sound, try adding the following around where those services get enabled:
Tamas G
in reply to Billy • • •Tamas G
in reply to Billy • • •ah, argh. you wanna know today? Upgrading pipewire from 1.2.X to 1.4.X for the LEAudio Bluetooth bits I wanted. Well well. Turns out, the Stormux script sets WirePlumber 0.4-style Lua tweaks. On WirePlumber 0.5 those are ignored, so BlueZ endpoints don’t come up until your full user session finishes and policy loads. O, M, G. Also disable seat monitoring so WirePlumber registers BlueZ endpoints without a logged-in seat. (This is the key for pre-login use.) But it's really not about the shared pulse socket. You would just need to move your “no suspend” ALSA tweak to wireplumber.conf.d rules. Oh, and the Bluetooth tweak. so:
"cat > "$xdgPath/wireplumber/bluetooth.conf.d/50-autoconnect.conf" <<'EOF'
# WirePlumber 0.5
bluez5.auto-connect = [ a2dp_sink hfp_hf hsp_hs ]
bluez5.default.profile = "a2dp-sink"
EOF
# Allow BT endpoints without a logged-in seat (pre-login audio)
cat > "$xdgPath/wireplumber/wireplumber.conf.d/10-disable-seat-monitoring.conf" <<'EOF'
wireplumber.profiles = {
main = {
monitor.bluez.seat-monitoring = disabled
}
}
EOF
cat > "$xdgPath/wireplumber/wireplumber.conf.d/50-fenrir-no-suspend.conf" <<'EOF'
monitor.alsa.rules = [
{
matches = [ { node.name = "~alsa_output.*" } ]
actions = { update-props = { session.suspend-timeout-seconds = 0 } }
}
{
matches = [ { node.name = "~alsa_input.*" } ]
actions = { update-props = { session.suspend-timeout-seconds = 0 } }
}
]
EOF"
Tamas G
in reply to Tamas G • • •--no-linger Do not enable systemd lingering for the target user. --mask-lightdm-audio Mask LightDM's PipeWire/WirePlumber/Pulse bridges so the greeter
*only* uses the shared /tmp/pulse.sock (use if you see BlueZ conflicts).
Beyond this, some bug fixes!
• Now, we check better whether ESpeakUp is installed. systemctl status returns non-zero if the unit isn’t active, which made the script print “not installed” even when the unit exists (or is momentarily restarting). We now go based on LoadState.
• Use curl if present, fall back to wget. (Some minimal installs don’t have wget.)
• Don’t hard-code X/Wayland display ":0" - (Broaden the espeakup override so it skips any display, not just :0.)
• Keep the "prefer A2DP" restart tidy, the unit file is reloaded before restart and we don't yank out existing profiles or connections like in the first script.
Enjoy!
Link again:
eurpod.com/setup-pipewire-a11y…