Raspberry Pi Zero W als media_player für den Home Assistant

Ein eigener Smart Speaker im Eigenbau!

1765 Wörter
9 Minuten

Das Ziel

Ich interessiere mich schon eine ganze Weile für Heimautomationen mit Home Assistant und mein Ziel war es immer ein Smart Home zu haben, das wirklich “smart” ist. Für mich heißt das, dass möglichst wenig Interaktion mit dem ganzen System notwendig ist – schon gar nicht in dem Sinne, dass ich zum Einschalten meiner Lampe mein Handy zücken muss.

Auf der anderen Seite finde ich aber “Interaktion in die andere Richtung” – also vom Smart Home System zu mir – super wichtig. Ich will Feedback von meinem Smart Home System und wollte schon immer ein klassisches Daily Briefing direkt nach dem Aufstehen bekommen – so richtig mit Wettervorhersage und der aktuellen Folge von Tagesschau in 100 Sekunden! Darum soll es heute gehen.

Hier kommt die media_player Domain von Home Assistant ins Spiel: Sie stellt abstrakt ein Gerät dar, welches Ton wiedergeben kann – sowohl Text-to-Speech (“TTS”) als auch Mediendatein. Für “fertige” Lautsprecher und Sound Bars wie die von Sonos, aber auch für Amazons, Googles und Apples Smart Speaker gibts es teilweise fertige Integrationen, die eine media_player Entity an den Home Assistant exposen. Aber hier soll es darum gehen, das Ganze selbst zu bauen!

Der Plan

Ich hatte zuhause noch einen Raspberry Pi 3 B+ und einen Raspberry Pi Zero W (erste Generation) rumliegen. Meine Wahl fiel auf den Zero W, da – naja, ich weiß es ehrlich gesagt nicht. Vermutlich weil er kleiner ist und der große 3 B+ “zu viel” ist für diesen einfachen Use-case. So Raspberry Pi Zero W it is!

Als Ausgabegerät hatte ich die Idee, einfach meine alte Bose SoundLink Revolve zu nehmen. Sie hat einen AUX-Eingang, einen guten Klang und ich benutze sie nur noch sehr selten. Vor allem benutze ich sie nur dann, wenn ich nicht zuhause bin, also kann ich sie auch für beides parallel verwenden. Da der Raspberry Pi Zero W keinen AUX-Ausgang hat, benötige ich noch eine USB Soundkarte.

Kurzer Hinweis für die Verwendung der AUX-Ausgänge am Raspberry Pi 3 oder 4: Ich rate davon ab, diese zu benutzen! Sie sind qualitativ nicht besonders hochwertig und erzeugen bei Stille ein leises Grundrauschen. Für Smart Speaker, welche eben auch nachts aktiv sind, ist das störend. Deswegen lieber wie ich direkt auf USB-Audio ausweichen!

Jetzt bleibt aber noch die große Frage: Wie bekomme ich den Pi Zero als media_player in den Home Assistant?

Zunächst: Das was ich will, ist ein einfacher simpler Smart Speaker. Ich brauche aktuell kein Multiroom Support à la AirPlay 2 oder will irgendwas über Spotify streamen.

Die erste Möglichkeit wäre die Verwendung des Universal Media Players: Er erlaubt die Eigendefinition einer media_player Entity, bei der alle Service Calls (z.B. media_player.volume_up) selbst wieder einen selbst definierten Service callen können. Das ist bestimmt praktisch für z.B. Infrarot-gesteuerte Endstufen, bei denen ein Service Call einfach einen Infrarot Befehl emuliert. Für unseren Anwendungsfall aber maßlos over-powered und zu komplex.

Ich hab mich also auf der Integrationsliste von Home Assistant nach Integrations umgeschaut, welche nicht für proprietäre Hard-/Software gebaut wurden. Dabei waren noch einige, welche Multimedia Server wie Jellyfin, Plex oder Kodi steuern können. Diese sind aber eigentlich fürs Web-Streaming gedacht – also nicht als permanenter Audioausgang. Daher lasse ich diese auch aus. Übrig geblieben sind:

Was mir beim Research dann aufgefallen ist: Sowohl Volumio als auch OwnTone haben den Music Player Daemon (MPD) built-in! Außerdem sind beide Tools fully-featured Audio Plattformen, mit Web Frontend und allem drum und dran – also mit allem was ich zusätzlich nicht brauche (und was ein Raspberry Pi Zero W (1. Gen) wahrscheinlich auch nicht aushält). So MPD it is!

Die Umsetzung

Hardware

Fast alle Anleitungen, die ich online dazu gefunden habe, machen einige unnötige Sachen. Wie oben erwähnt, benötigen wir eine USB Soundkarte, da der USB Controller vom Raspberry Pi Zero W keinen Audio Controller auf dem USB Bus hat. Außerdem wollen wir sowieso AUX-Audio haben, wofür der Pi Zero keine Buchse hat, die wir aber andernfalls auch nicht verwenden wollen würden, wegen der oben genannten Störgeräusche. Die anderen Anleitungen setzen hier also auf Pi Hat Soundkarten! Diese sind meist recht teuer (beginnen ab 14$) und haben horende Lieferzeiten, bzw. liefern gar nicht nach Deutschland. Ich habe bis heute nicht verstanden, warum alle auf Pi Hats setzen. Ich hab mir einfach eine ganz billige USB Soundkarte von UGREEN besorgt.

Wie oben schon erwähnt, benutze ich als Speaker einfach meine Bose Soundlink Revolve im AUX-Modus. Sieht gut aus, ist unscheinbar und der Klang ist auf jeden Fall gut genug.

Der Raspberry Pi Zero W ist bei mir im originalen rot-weißen Case, welches man aber sowieso nicht sieht, da ich ihn einfach neben meine Musikanlage im Schrank gelegt habe, auf dem die Soundlink Box steht.

Raspberry Pi Zero W im Originalgehäuse neben meinem Verstärker mit USB Soundkarte
Raspberry Pi Zero W im Originalgehäuse neben meinem Verstärker mit USB Soundkarte


Bose Soundlink Revolve als Lautsprecher für den Raspberry Pi
Bose Soundlink Revolve als Lautsprecher für den Raspberry Pi

Installation von MPD

Jetzt kommt der spannende Teil… nein doch nicht. Tatsächlich ist die Installation so trivial wie sie nur sein kann: MPD ist in den offiziellen Paketquellen von Raspberry Pi OS – von dem ich ausgehe, dass es bereits installiert ist. Also ein bisschen apt update && apt install alsa-utils mpd und noch ein bisschen Config hier und ein bisschen Config da…

Zur Erklärung: Bei “Alsa” handelt es sich um den Standard Soundmixer auf dem Raspberry Pi (und vielen anderen Linux Geräten). Er wird von MPD u.a. für die Ausgabe und Lautstärkeregulierung verwendet.

Folgendes muss in die /etc/mpd.conf nach der Installation (Anfügen! Das was schon in der File ist, sollte drin bleiben):

audio_output {
    type            "alsa"
    name            "USB Soundkarte"
    device          "hw:1,0"
    mixer_type      "software"
}

Der Wert für name ist eigentlich egal für uns und kann frei gewählt werden. Den Wert für device muss man über den Befehl aplay -l herausfinden. Dann sucht man die entsprechende Soundkarte im Output. Die Zeile sieht z.B. so aus:

card 1: Device [USB Audio Device], device 0: USB Audio [USB Audio]

Aus der Zahl hinter card und der Zahl hinter device bildet man nun die Angabe für MPD, in dem Fall hw:1,0 für card 1, device 0.

Nachdem die Config Datei angepasst wurde, einmal sicherstellen, dass der MPD Service läuft und automatisch startet:

systemctl enable mpd
systemctl start mpd

Thats it!

Integration in Home Assistant

Die MPD Integration kann leider nicht über die UI konfiguriert werden. Aber schwer ist es tortzdem nicht. Das hier muss in die configuration.yaml (oder eine andere File, die mittels !include dort eingebunden ist):

media_player:
  - platform: mpd
    name: "Leons Smart Speaker"
    host: 10.0.30.99

Den Port habe ich nicht geändert, also muss ich ihn nicht angeben. Theoretisch könnte man noch ein Passwort für MPD setzen, worauf ich aber verzichtet habe, da ich das Gerät in meinem eigenen Subnetzt platziert habe und den Home Assistent sowieso jeder in meinem WG benutzen kann – ein Plaintext Passwort an dieser Stelle wäre also überflüssig. Das Einzige, was also notwendig ist, ist die IP des Raspberry Pis!

Dann noch einmal Home Assistant neu starten, et voila: media_player Entity!

Finale media_player Entity
Finale media_player Entity

Da die media_player Domain vergleichsweise komplex ist, kann es sein, dass nicht alle Services für MPD verfügbar sind. Die die ich aber benötige und als sinnvoll erachte, sind aber voll funktionsfähig:

  • Play Media via URL (oder Playlist Name)
  • Play Text-to-Speech Audio
  • Start/Stop/Pause
  • Leiser/Lauter/Set Volume
  • Seek
  • An/Aus

Generell habe ich mich nicht so sehr mit den Möglichkeiten von MPD befasst, aber für diesen Fall ist es genau das, was ich gesucht habe. Bisher läuft der Dienst seit ca. einem Monat ohne Problem und mein morgentliches Daily Briefing begleitet mich jeden Morgen!

Appendix: Tagesschau in 100 Sekunden im Home Assistant

Ich habs am Anfang schon erwähnt: Mein Ziel mit dem Smart Speaker war, dass ich morgens nach meinem Wecker von einer Folge “Tagesschau in 100 Sekunden” geweckt werden will. Das hat sich als gar nicht so trivial herausgestellt, vor allem da neue Folgen im Stundentakt released werden. Außerdem hab ich auch online nicht wirklich viel dazu gefunden. Deswegen möchte ich hier noch darauf eingehen.

Der media_player.play_media Service von MPD nimmt entweder eine Playlist oder eine Audio-URL entgegen. Ersteres würde bedeuten, dass ich auf dem Raspberry Pi selbst eine Repository mit aktuellen Folgen pflegen müsste. Ich meine, theoretisch geht das – wahrscheinlich sogar relativ einfach mittels Cronjobs – aber ist irgendwie gegen die Philosophie des Ganzen: Der Home Assistant soll der “Single Point of Thruth” sein und entscheiden, was der Smart Speaker – welcher ja nur als Audio Satellit agierten soll – abspielt. Deswegen möchte ich gerne auf das komplette Playlist Feature von MPD verzichten.

Also ist die nächste Frage: Wie komme ich an eine URL der aktuellsten Taggesschau Folge im Home Assistant?

Dafür habe ich zwei Wege gefunden:

Ersteres halte ich schlicht für illegal. RSS-Feeds haben nicht umsonst eine Geschichte jahrelanger Standardisierung hinter sich. Allerdings muss man sagen, dass der Umgang mit der Scrape Integration etwas einfacher war, als der Feedreader: Scrape kann 1. über die UI konfiguriert werden und exposed 2. einen Text Sensor, welcher den Scrapeinhalt (also optimalerweise unsere MP3 URL zu aktuellen Folge) beinhaltet. Das ist in einem Automationsszenario sinnvoll: Wir müssen lediglich den State dieses Sensors an den media_player.play_media Service übergeben und unser Smart Speaker spielt die Folge ab.

Feedparser macht das etwas anders: Diesen müssen wir via YAML konfigurieren. Meine Konfiguration sieht so aus:

feedreader:
  urls:
    - https://www.tagesschau.de/multimedia/sendung/tagesschau_in_100_sekunden/podcast-ts100-audio-100~podcast.xml

Die URL wird nun regelmäßig (ist auch anpassbar, siehe hier) nach neuen RSS-Entries abgefragt. Diese werden (unpraktischerweise, meiner Meinung nach) als Event an den Home Assistant Event Bus geschickt, worauf ich eine Automation bauen muss:

Feedreader Auslöser
Feedreader Auslöser

Die Automation filtert aus dem Feed Item (übertragen als Event Payload in trigger.event.data) das <enclosure> Tag mit dem Attribut type=audio/mpeg und speichert das url= Attribut im gleichen Tag in einen input_boolean Helper. Wie genau so ein RSS-Entry von der Tagesschau aussieht, kann man auch einfach direkt im Feed ansehen.

Feedreader Aktion
Feedreader Aktion

Als Ergebnis haben wir jetzt einen input_boolean, welcher immer die aktuellste Folge beinhaltet und welchen wir genauso wie den Text Sensor aus der Scrape Integration benutzten können, um sie an unseren Smart Speaker zu übermitteln. Ein Service Call sieht dann so aus:

Service Call zum Smart Speaker, um die aktuelle Folge Tageschau in 100 Sekunden abzuspielen
Service Call zum Smart Speaker, um die aktuelle Folge Tageschau in 100 Sekunden abzuspielen

Ich hoffe ich konnte mit diesem Artikel zum Basteln anregen. Ich habe selbst ziemlich wenig zu diesem Thema gefunden, weswegen ich eine Weile gebraucht habe, um dieses eigenlich recht simple Problem so zu lösen. Vielleicht ist dieser Artikel für den ein oder anderen eine gute Anlaufstelle gewesen :)


Referenzen