spotify connect und airplay lautsprecher für 100 euro

felix schwenzel, , in artikel    

nachdem wir uns vor ein paar monaten einen neuen receiver von yamaha gekauft haben, hatten wir auch einen mussiccast-lautsprecher dazu gekauft, der regulär 229 euro kostet. die yamaha-lautsprecher haben zwar „multiroom“-fähigkeiten, man kann also die gleiche musik in mehreren räumen spielen, aber sie sind mono und die beifahrferin fand den klang so schlecht, dass sie das teil eigentlich zurückgeben wollte. weil das kind den lautsprecher ok fand, haben wir ihn dann doch behalten, aber das kind durfte ihn nach seinem auszug mitnehmen. das ding funktioniert auch standalone als airplay und spotify-connect-lautsprecher.

jetzt wollte die beifahrerin die musiklücke im kinderzimmer schliessen und neue lautsprecher kaufen. die yamaha-musiccast-lautsprecher waren ihr aber zu teuer und zu mono. die können zwar auch stereo, aber dann müsste man zwei kaufen und ist ruckzuck fast 500 euro los. also entschieden wir uns wegen guter rezensionen für ein paar „dumme“ — genauer: unvernetzte — aktivlautsprecher für 40 euro. ich dachte mir, dass ich mir meinen ersten raspberry-pi kaufe und irgendwas bastle, um die lautsprecher zu vernetzen.

die lautsprecher kamen ein paar tage vor dem raspi und hörten sich für 40 euro wirklich gut an. nur das iphone manuell anzustöpseln zum musikhören fühlte sich enorm unpraktisch und hinterwälderisch an. als der raspberry endlich da war, installierte ich das komplettpaket pimusicbox drauf. das ist wirklich einfach: image downloaden, auf eine SD-karte kopieren, in den raspi stecken, erst mit ethernetkabel booten und dann die wichtigsten einstellungen über eine webseite konfigurieren. wenn der pi danach neu bootet, verbindet er sich per wlan und wird von rechnern und telefonen als airplay-lautsprecher erkannt. über eine weboberfläche kann man auch playlisten erstellen, lokal auf dem pi vorhandene musik abspielen oder von netzlaufwerken musik abspielen. es gibt auch eine spotify-integration, die aber eher umständlich über die weboberfläche gesteuert werden will.

in zeiten von spotify-connect will das aber eigentlich keiner, also wir zumindest nicht. wenn der spotify-client auf dem telefon oder dem rechner läuft, will die beifahrerin den lautsprecher am liebsten per spotify-connect ansteuern, vor allem weil sie meint, dass über airplay immer wieder ruckler (alle 1-2 stunden) zu bemerken wären. spotify-connect können der yamaha-receiver und der echo dot in der küche auch und es fühlt sich an, wie das perfekte, reibungslose bedienkonzept.

richtig befriedigende lösungen spotify-connect auf den pi zu bringen gibt’s nicht so viele. wenn man das thema googelt, schlagen viele vor, einen linux-spotify-client auf dem pi zu installieren, komplett mit x-windows und gedöns. andere lösungen wollen einen spotify-API-key, den spotify aber nicht mehr rausrückt. dann fand ich irgendwo einen hinweis auf librespot, einen reverse engineerten spotify-connect-client. die kompilierung von librespot schien mir aber ein ticken zu schwer, weshalb ich froh war einen hinweis auf den fork von @herrernst zu finden, der vorkompilierte binärdatein von librespot zum download anbietet.

einfach runterladen, auspacken und starten:

./librespot --name musikkind --cache /tmp

damit taucht der raspberry-pi-lautsprecher schon als spotify-connect lautsprecher in der spotify-app auf.

damit waren 80% der arbeit an einem abend erledigt: der pi hatte ein betriebsystem, lief rund und war mit dem wlan verbunden, ohne dass ich auch nur eine zeile in die kommandozeile schreiben musste, die lautsprecher waren per airplay und spotify-connect erreichbar und alles klang gut.

na gut ein bisschen musste ich doch in der kommandozeile rumwerkeln, weil airplay nicht auf anhieb funktionierte. ich folgte dieser anweisung um das problem zu beheben und deaktivierte den firewall von pimusicbox indem ich die erste (bzw. zweite) zeile der datei /etc/network/if-up.d/iptables:iptables-restore auskommentierte.

die restlichen 20% arbeit, librespot automatisch zu starten und am laufen zu halten, hielten mich dann die folgenden zwei wochen auf trab.

* * *

die pimusicbox startet die konfigurierten dienste, wie bei linux üblich, über init-daemons und sorgt mit einen monit-server-dienst daür, das ein paar der beim booten gestartetet prozesse überwacht und im zweifel neu gestartet werden. für librespot habe ich mir das startscript erstmal selbst zusammengestöpselt und dabei ist das rausgekommen:

/etc/init.d/librespot

#!/bin/sh
#
# librespot daemon start/stop script.
#
# Written by Alexei Vladishev <alexei.vladishev@zabbix.com>.
# changed by ix
#
### BEGIN INIT INFO
# Provides: librespot
# Required-Start: $network $remote_fs
# Required-Stop: $network $remote_fs
# Should-Start: $named alsa-utils avahi dbus pulseaudio
# Should-Stop: $named alsa-utils avahi dbus pulseaudio
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: librespot server
### END INIT INFO
#
NAME=librespot
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/:/usr/bin
DAEMON=/usr/bin/${NAME}
DESC="librespot server daemon"
PIDFILE=/var/run/$NAME.pid
#
# Define LSB log_* functions.
. /lib/lsb/init-functions
#
test -f $DAEMON || exit 0
set -e
case "$1" in
start)
echo "Starting $DESC: $NAME"
#start-stop-daemon --oknodo --start --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- --name musikkind --cache /tmp --onstart /root/librespot_play.py --onstop /root/librespot_stop.py #> /dev/null 2>&1 &
start-stop-daemon --oknodo --start --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- --name musikkind --cache /tmp > /dev/null 2>&1 &
;;
stop)
echo "Stopping $DESC: $NAME"
start-stop-daemon --oknodo --stop --pidfile $PIDFILE --exec $DAEMON -- --name musikkind --cache /tmp > /dev/null 2>&1 &
;;
restart|force-reload)
$0 stop
sleep 3
$0 start
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0

damit lässt sich dann /usr/bin/librespot (dahin habe ich die ausführbare binärdatei verschieben) starten, stoppen und abfragen:
/etc/init.d/librespot start
/etc/init.d/librespot stop
/etc/init.d/librespot status

damit monit sich um den dienst kümmert habe ich noch diese kleine monit-startdatei in /etc/monit/conf.d/librespot gelegt:


check process librespot with pidfile /var/run/librespot.pid
      start program = "/etc/init.d/librespot start"
      stop program = "/etc/init.d/librespot stop"
      restart program = "/etc/init.d/librespot restart"

damit librespot direkt nach dem startvorgang startet (und nicht erst wenn monit den dienst startet), habe ich diese letzte zeile in der datei /opt/musicbox/startup.sh hinzugefügt:

/etc/init.d/librespot start

* * *

oben habe ich es auskommentiert, aber ich nutze noch eine option von librespot um den abspielstatus in meinem home-assistant anzuzeigen. gibt man librespot die startoptionen --onstart und --onstop mit, kann es zum start und stop des playbacks eine datei ausführen. ich lasse librespot je zwei kleine python-scripte ausführen, die den status per mqtt veröffentlichen, was ich dann im home-assitant anzeigen lassen kann. in /root/librespot_play.py steht:


#!/usr/bin/env python
import paho.mqtt.publish as publish
import sys
 
def publish_message(topic, message):
print("Publishing to MQTT topic: " + topic)
print("Message: " + message)
auth = {
'username' : 'xxx',
'password' : 'xxx'
}
publish.single(topic, message, hostname="192.168.1.57", port=xxx, auth=auth)
 
if __name__ == '__main__':
publish_message("musikkind/librespot/status", "play")

/root/librespot_stop.py unterscheidet sich lediglich im payload der mqtt-nachricht. das python-script funktioniert natürlich nur, wenn man die entsprechenden abhängigkeiten vorher installiert, also mindestens python und paho-mqtt.

* * *

librespot funktioniert eigentlich ganz gut. manchmal, nach längerem leerlauf stürzt das programm aber ab, vermutlich weil eine der verbindungen zu spotify abgebrochen ist und das programm sich lieber panisch abbricht, als die verbindung neu aufzubauen. das fängt dann aber monit ab, das den librespot-prozess wieder neustartet, in der standardeinstallung allerdings unter umständen erst nach zwei minuten, weil der prüfzyklus von monit standardmässig auf zwei minuten gestellt ist. das lässt sich aber in /etc/monit/monitrc anpassen, indem man set daemon 30 statt set daemon 120 einträgt.

dazu kommt, dass spotify das reverse-engineerte librespot wohl nicht mehr so gerne sieht und immer wieder sperrt oder änderungen an der (undokumentierten) API vornimmt. @plietar bessert librespot zwar immer schnell nach, aber bei den letzten änderungen ist @herrenst mit seinen binaries nicht immer nachgekommen.

zuerst habe ich versucht librespot selbst auf dem pi zu kompilieren, bin aber daran gescheitert die entsprechenden abhängigkieten (hunderte von megabyte) zu installieren, bzw. zum laufen zu bringen. ich habe mit raspotify von @dtcooper aber einen einfachen weg gefunden, librespot selbst zu kompilieren: per docker auf dem mac. nachdem docker installiert ist, reicht es folgendes in der kommandozeile auszuführen und ein bisschen zu warten:


git clone --recursive https://github.com/dtcooper/raspotify
cd raspotify
./build_raspotify.sh

am ende spuckt das script eine .deb-datei aus, die man auf dem pi instalieren kann: einfach in /tmp kopieren und auf dem pi folgendes in der kommandozeile ausführen (je nachdem wie die .deb-datei benannt ist):


cd /tmp
sudo dpkg -i raspotify_0.4~librespot-6f24e3b-1_armhf.deb
rm raspotify_0.4~librespot-6f24e3b-1_armhf.deb

damit landet librespot in /usr/bin, also da wo das start-script die datei auch erwartet.

den letzten librespot-fix von gestern habe ich einfach in raspotify/librespot/src/connection/mod.rs selbst gepatcht und dann alles neu kompiliert. bis jetzt funktioniert es.

* * *

tl;dr: statt knapp 500 euro auszugeben, habe ix einfach sehr günstige, dumme, unvernetzte [-werbelink] aktivlautsprecher mit einem [-werbelink] raspbery pi vernetzt und damit nur knapp 100 euro ausgegeben und viel bastelvergnügen gehabt.

unsere zweit-„anlage“