Thomas makes, writes

2021-09-09

Home Assistant Multi room audio

concept

I was able to cook up a fun audio setup controlled and automated with Home Assistant!

  • Multiple room synchronised audio speakers (fixed, portable Bluetooth and phone/tablet app)
  • Internet radio, Spotify and local music playback
  • Sound notifications for Doorbell, garden gate sensor and home alarm.

Overview

Adding sound to a smart home used to be a thing of fancy smart-home solutions sold with their own audio server appliances that are wired up to multi-room speaker set-ups. Nowadays cloud connected voice assistants bring music and notifications to peoples homes.

If you can live without the fancy voice control and enjoy setting up some Linux services, then adding some of the more classic audio features is a doable task, soon to become easier due to a home assistant add-on.

During 2020's work-from-home regime I discovered radio, I listen extensively to radio and Spotify while at home. I wanted to mix in audio notifications like the door bell and garden gate sensor. These turned out to be easily mixed using Snapcast.

To listen to music in the rooms next to our living room, I bought a Bluetooth speaker, figuring I could feed it music via a raspberry pi while at home, and by phone when in the garden or on the road.

concept

Home assistant integration

Being able to move the speaker along around the house while it's not tied to a particular smart phone (that sometimes walks off) is lovely.

I just had to install and configure existing software, a docker container runs the S6 service supervisor using s6-overlay, like home assistant add-ons. It manages

  • the Snapcast audio server, which starts librespot for Spotify playback
  • a MPD daemon to play music and internet radio
  • a MPD daemon to play notifications

The Snapcast server has a meta source type that will switch between audio sources based on priority which is very powerfull yet simple concept. I set it up to prefer notifications over Spotify over music,

The Bluetooth speaker is integrated into home assistant using scripts and a MQTT template 'switch', showing its connection status in home assistant and providing Bluetooth disconnect and connect triggers when operating the switch in home assistant.

Snapcast

The Snapcast website explains it best:

Snapcast is a multi-room client-server audio player, where all clients are time synchronized with the server to play perfectly synced audio. It's not a standalone player, but an extension that turns your existing audio player into a Sonos-like multi-room solution.

concept

Snapcast overview (from the snapcast github page)

Audio is captured by the server and routed to the connected clients. Several players can feed audio to the server in parallel and clients can be grouped to play the same audio stream. One of the most generic ways to use Snapcast is in conjunction with the music player daemon (MPD) or Mopidy.

The Home Assistant Snapcast platform allows you to control Snapcast from Home Assistant.

To add Snapcast to your installation, add the following to your configuration.yaml file:

# Example configuration.yaml entry
media_player:
  - platform: snapcast
        host: YOUR_IP_ADDRESS

Configuration

The Snapserver is configured to accept audio from the MPD's provided at the same sample format of librespot.

The meta source does magic, it switches between sources based on the listed order.

[stream]
source = pipe:///tmp/snapfifo?name=Music&sampleformat=44100:16:2
source = pipe:///tmp/mpd-notify?name=Notify&sampleformat=44100:16:2
source = spotify:///librespot?name=Spotify&bitrate=320&enable-volume-normalisation&sampleformat=44100:16:2
source = meta:///Notify/Spotify/Music?name=Mixed&sampleformat=44100:16:2

MPD Music player daemon

The musicPD is an old-school tool, I believe I was already using it ~15 years ago. It is remarkably elegant, has myriad applications, a nice network protocol and support for lots of stuff.

Playing notifications

To play a notification, the usual home assistant service can be called to play audio on the dedicated MPD:

service: media_player.play_media
data:
  entity_id: media_player.mpd_notification
    media_content_type: music
      media_content_id: bark.wav

After adding the audio files to the music directory, don't forget to update the MPD database.

I had some difficulties with the first few seconds of audio disappearing, which I worked around until digging deeper. I used sox to add a few seconds of silence:

sox -n -r 44100 -c 2 silence.wav trim 0.0 1.5
sox silence.wav bark.wav delayedbark.wav

MPD Configuration

The MPD's are configured to have unique state, database and music paths. They feed their data into their own FIFO (named pipe), and are set to the same (non-standard) sample frequency librespot uses to avoid resampling by the Snapcast server.

audio_output {
    type            "fifo"
    name            "my pipe"
    path            "/tmp/snapfifo"
    #format          "48000:16:2"
    format          "44100:16:2"
    mixer_type      "software"
}

MPD Home Assistant integration

Both MPD's have an entry in the Home Assistant configuration.yaml:

# Example configuration.yaml entry
media_player:
  - platform: mpd
    host: IP_ADDRESS
    port: PORT

They run on the same host, but a different port.

Librespot

Librespot allows the Snapcast source to appear as a Spotify speaker on the local network. It announces itself using multicast DNS and automatically appears to local Spotify players.

Very little configuration at all (just the name it has to announce).

Wonderful piece of kit <3.

Raspberry pi with fixed speaker

The only remarkable thing here is that I use the hardware mixer, this allows control of the master volume via Snapcast.

pi@living:~ $ cat /etc/default/snapclient
# Start the client, used only by the init.d script
START_SNAPCLIENT=true

# Additional command line options that will be passed to snapclient
# note that user/group should be configured in the init.d script or the systemd unit file
# For a list of available options, invoke "snapclient --help"
SNAPCLIENT_OPTS="-h 192.168.x.xxx --hostID living --mixer hardware"

On Raspberry pi 2 with Ubuntu 21.01 with a HDMI screen connected, some extra config was needed:

SNAPCLIENT_OPTS="-h 192.168.x.xxx --hostID living --mixer hardware:Headphone -s plughw:CARD=Headphones,DEV=0"

Raspberry pi with Bluetooth speaker

I found a nice guide (dutch only) on using a bluetooth speaker with snapcast on the raspberry pi, which guided me to install https://github.com/bablokb/pi-btaudio .

My asound.conf ended up like this:

pi@vestel:~/pi-btaudio $ cat /etc/asound.conf
pcm.!default "bluealsa"
ctl.!default "bluealsa"
defaults.bluealsa.service "org.bluealsa"
defaults.bluealsa.device "xx:xx:xx:xx:xx:xx"
defaults.bluealsa.profile "a2dp"
defaults.bluealsa.delay 10000

I ended up using my own helper scripts: https://github.com/thouters/bluetoothspeaker2mqtt Those support start/stopping snapclient and a bluetooth button event watcher.

Home assistant add-on

I had a bit of a bad experience trying to create a Snapcast Home Assistant add-on. I built a container image based on the example and added the Snapcast server.

My local Add-on did not show up on the Supervisor tab (using { "image": }).

When I omitted the local image reference, the supervisor tried to build it for the wrong architecture (ARM7 instead of aarch64/arm8).

A long-term goal is to package this as an add-on, for now I run everything as a container on my main server.

More information

I still haven't published info on my snapclient-server-side docker container. TODO...