Ziqi (Katrina) Ding

Back

The Problem#

Home Assistant can generate audio (TTS, alerts), but getting it to actually come out of a USB speaker on a Linux box can be weirdly painful—mostly because audio services love fighting over devices and permissions.

In this post, I’ll show how I routed HA audio through a USB speaker using MPD, how to verify the ALSA device, and how to fix the classic “device busy” / “PipeWire connection refused” traps so sound finally plays where it should.

The Solution#

How to Do It#

1. Ensure your speaker is visible by the system#

katrina@homeserver:~$ aplay -l
**** List of PLAYBACK Hardware Devices ****
...    # Other devices
card 2: Device [USB Audio Device], device 0: USB Audio [USB Audio]     # <== This one
  Subdevices: 0/1
  Subdevice #0: subdevice #0
sh

In my case, the USB speaker is the card 2. We can also verify it by cat /proc/asound/cards.

katrina@homeserver:~$ cat /proc/asound/cards
...    # Other devices
 2 [Device         ]: USB-Audio - USB Audio Device    # <== This one
                      GeneralPlus USB Audio Device at usb-0000:00:14.0-1, full speed
sh

2. Ensure MPD is installed and Verify it’s working#

Check MPD system daemon status by systemctl status mpd

katrina@homeserver:~$ systemctl status mpd
 mpd.service - Music Player Daemon
     Loaded: loaded (/lib/systemd/system/mpd.service; enabled; preset: enabled)
     Active: active (running) since Fri 2025-08-22 17:27:41 AEST; 14min ago
	 ...
sh

We can verify that is running as it’s saying Active: active (running). If not, enable and start MPD by

sudo systemctl enable mpd
sudo systemctl start mpd
sh

Then we can check its status using mpc

katrina@homeserver:~$ mpc status
volume:100%   repeat: off   random: off   single: off   consume: off
ERROR: Failed to decode http://192.168.0.208:8123/api/tts_proxy/ffxnuIRPgDEXKSy1qKMovg.mp3; CURL failed: The requested URL returned error: 404
sh

Uh oh, it’s currently off, and the speaker-test fails as well.

katrina@homeserver:~$ speaker-test -D hw:2,0 -c 2 

speaker-test 1.2.8

Playback device is hw:2,0
Stream parameters are 48000Hz, S16_LE, 2 channels
Using 16 octaves of pink noise
Playback open error: -16,Device or resource busy
sh

Since the output indicates Device or resource busy, it maybe occupied by other process.

2.1 Debug - Check Who is Using PulseAudio#

Execute command:

## Check who is using audio devices
sudo fuser -v /dev/snd/*

## Check audio processes
ps aux | grep -E 'pulse|pipewire'

## If above indicates that PulseAudio is occupied, try temporarily stop it
systemctl --user stop pulseaudio.socket
systemctl --user stop pulseaudio.service
sh

3. Configure and Test MPD#

Open MPD config file with nano sudo nano /etc/mpd.conf Edit:

## Ensure MPD listen to localhost
bind_to_address                 "0.0.0.0"

## Ensure MPD listen  port 6600
port                            "6600"
sh

Then, restart MPD

sudo systemctl restart mpd 

## Check status 
systemctl status mpd 

## Ensure the port is listened 
ss -tuln | grep 6600
sh

Play a sample mp3 to test the MPD

## Clear old playlist
mpc clear 

## Play a sample mp3
mpc add https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3 
mpc play 

## Check status
mpc status 

## Check volumn
mpc volume

## List all audio output devices
pactl list short sinks
sh

In my case, I see this output:

katrina@homeserver:~$ mpc status
https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3
[paused]  #1/1   0:00/6:13 (0%)
volume: n/a   repeat: off   random: off   single: off   consume: off
ERROR: Failed to enable output "PipeWire Output" (pulse); pa_context_connect() has failed: Connection refused

katrina@homeserver:~$ pactl list short sinks
43      alsa_output.usb-GeneralPlus_USB_Audio_Device-00.analog-stereo   PipeWire        s16le 2ch 48000Hz       RUNNING
...
sh

The first mpc status gives Connection refused error, while pactl list short sinks indicates that our USB speaker is running as device 43.

3.1 Debug - Speaker is running, but PipeWire Output says “Connection refused”#

Observation: We could list our speaker through pactl list short sinks, which was running with user-level permission; but MPD service could not connect to PipeWire (showed “Connection refused” error).

This might be because MPD service is running as a mpd user, so it can’t connect to the user-level (katrina) PipeWire session.

We can verify this conflicts:

katrina@homeserver:~$ ps aux | grep -E "mpd|pipewire"
katrina  3832551  3.4  0.1 109980 29416 ?        Ssl  17:10   6:07 /usr/bin/pipewire        # <== PipeWire running with "katrina"
katrina  3832553  2.3  0.5 216200 85196 ?        SLsl 17:10   4:14 /usr/bin/pipewire-pulse  # An adapter layer
mpd      3923115  0.1  0.4 727684 72492 ?        SLsl 18:00   0:11 /usr/bin/mpd --systemd   # <== MPD running with "mpd" user
katrina  4060487  0.0  0.0   6336  2064 pts/5    S+   20:09   0:00 grep -E mpd|pipewire     # The grep itself
sh

The problem is clear now. MPD running as an “mpd” user, so it doesn’t have permission to access PipeWire’s socket.

Solution: Let’s check MPD config file

  1. Stop MPD service
    sudo systemctl stop mpd 
    sudo systemctl disable mpd
    sh
  2. Open MPD config file, and find this line
    # This setting specifies the user that MPD will run as. MPD should never run as
    # root and you may use this setting to make MPD change its user ID after
    # initialization. This setting is disabled by default and MPD is run as the
    # current user.
    #
    user                           "mpd"
    plaintext
  3. Ah, we can see that user is set to “mpd”. We just need to comment this line to let MPD run as the current user
    # Comment this line
    # user                           "mpd" 
    sh
  4. Start MPD service
    mpd
    sh
  5. Verify permission — All running with current user
    katrina@homeserver:~$ ps aux | grep -E "mpd|pipewire"
    katrina  3832551  3.4  0.1 109980 30184 ?        Ssl  17:10   7:40 /usr/bin/pipewire
    katrina  3832553  2.4  0.5 216200 90956 ?        SLsl 17:10   5:18 /usr/bin/pipewire-pulse
    katrina  4122244  1.9  0.3 932160 53944 ?        Ssl  20:51   0:00 mpd
    katrina  4122673  0.0  0.0   6336  2144 pts/5    S+   20:51   0:00 grep -E mpd|pipewire
    sh
  6. If not, try killing mpd process completely then start again
katrina@homeserver:~$ ps aux | grep mpd | grep -v grep                  # Find the process
katrina  4122244  0.3  0.3 932160 53944 ?        Ssl  20:51   0:00 mpd
katrina@homeserver:~$ kill -9 4122244                                   # The process ID
katrina@homeserver:~$ ps aux | grep mpd | grep -v grep                  # Verify the process is gone
katrina@homeserver:~$ mpd                                               # Start MPD
sh

Then, repeat the steps to play sample mp3 to test the MPD. The USB speaker should now be able to play some sound.

Summary#

Home Assistant audio now plays through a USB speaker by routing output via MPD and fixing PipeWire/permission conflicts.

Use USB Speaker as Audio Output
https://katrina-ziqi-ding.com/blog/use-usb-speaker-as-audio-output
Author Ziqi (Katrina) Ding
Published at 22-08-2025
Comment seems to stuck. Try to refresh?✨