Eine iPad-Sonos-Steuerapp
Vor ein paar Monden fand ich ein altes iPad Mini, welches ich mir 2014 kaufte. Es funktionierte noch blendend und es wäre ja schade, wenn es weiter sein Dasein im Schrank fristen müsste, bis der Akku sich entscheidet aufzuplatzen, wie bei allen meinen sonstigen alten Apple-Geräten.
Also suchte ich nach einem Verwendungszweck. Ich fand so herumhängender Smart-Home-Steuerzentralen schon immer ganz nett, aber ich will mit kein iPad an die Wand hängen und eigentlich muss ich nichts in meinem Smart Home wirklich steuern.
Was aber ganz cool wäre, wäre ein Ding, mit dem man schnell Musik oder Hörspiele anmachen kann, ohne ewig zu überlegen, ob man es nun bei Spotity hat oder in der Plex-Bibliothek oder wo auch immer. Einfach so ein Speed-Dial für die drei Lieblingsalben, -playlists oder die neuste Folge der Drei Fragezeichen.
Home Assistant
Da ich Home Assistant verwendete und dort auch alle Sonos-Boxen drin hab, konnte ich mir über die API schnell was zusammenbasteln. Ein paar Kacheln mit Covern und die Sache war geritzt. Irgendwann fand ich sogar raus in welchem abstrusen Format die URL, die man an den play_media
-Service schicken muss, sein muss, damit man was aus der Plex-Bibliothek abspielen kann.
Nun könnte man es natürlich einen Day callen und so lassen, aber ich wollte doch noch was einbauen. Kein Management mehr über eine config.php
und eine Auswahlmöglichkeit der Sonos-Box, die angesprochen werden soll, zum Beispiel. Wenn man mal woanders was hören will!
Rund machen
Glücklicherweise wollte ich mich gerade eh etwas weiterbilden in Sachen Laravel, Livewire und TailwindCSS, also nahm ich das zum Anlass um das Ding mal etwas runder zu machen.
Leider fand ich noch keinen wirklich guten Namen, dafür habe ich aber immerhin schon die Funktionalität des Proof of Concepts nachgebaut und ein Management-Interface eingefügt, damit man keine Konfigurationsdateien mehr bearbeiten muss.
Aktuell umfasst das Featureset folgende Sachen:
- Übersicht aller hinzugefügten Media-Items, wobei Artists automatisch “Ordner” sind, die mehrere Alben enthalten
- Erkennung aller Media Player im Home Assistant-System und Auswahl, wo was abgespielt werden soll
- Management-Oberfläche um neue Sachen einzufügen, inklusive Einholen verschiedener Daten von Spotify. Automatischer Import von Alben eines Artists, Cover, etc. Außerdem eine kleine Hilfe um die verrückten Plex-URLs zusammenzubauen
Livewire, Alpine.js und Tailwind
Kommen wir zum technischen Bereich. Ich schaute mir ja Livewire schon damals an, als ich meinen last.fm-Klon baute. So richtig tief hab ich mich damals allerdings nicht damit beschäftigt. Erst in den letzten Wochen schaffte ich das, weil ich ein Freelance-Projekt mit der Technologie bearbeite.
Oberflächlich gesehen gefällt mir das alles ganz gut. Ich habe schon das Gefühl, dass ich schneller voran komme, als würde ich eine API und dazu ein JavaScript-Frontend bauen. Es kommt einfach alles aus einem Guss und geht schnell und ich hatte eine Menge Spaß dabei, zum Beispiel den Sonos-Box-Auswahl-Dialog und diverse andere Sachen, sicher mehr, als nötig gewesen wäre, damit umzusetzen. Hier und da, und da kommt der Wermutstropfen, vor allem, wenn man etwas mehr braucht und AlpineJS dazu nimmt fängt die Fassade allerdings an zu bröckeln.
Plötzlich findet man sich in einer komischen Verwurstelung von Concerns wieder. Manche Sachen passieren im PHP, manch andere in einer JavaScript-Datei, oder vielleicht auch in einem HTML-Attribut im Template. Dies und das funktioniert nur an dem einen Ort, oder teilweise gehen bei bestimmten Kombinationen auch Dinge einfach kaputt, weil es wohl nicht so optimal ist, wenn eine Komponente halb im Back- und Frontend lebt.
Bisher ist es noch so ein Gefühl und ich hab noch zu wenig Erfahrung um es abschließend zu bewerten, aber Livewire fühlt sich noch etwas wackelig an. Ich mag es trotzdem, weil es mir viele Möglichkeiten gibt, die ich vorher nicht hatte, und das ohne eine Zeile JavaScript zu schreiben, aber es gab mir in den ersten Wochen noch nicht das Gefühl, dass ich nie wieder ein Frontend-Framework anfassen werden muss.
Mit Tailwind hatte ich in diesem kleinen Projekt eigentlich keine Probleme. Das gefällt mir weiterhin recht gut, und so langsam muss ich auch immer weniger in die Dokumentation schauen, um zu wissen, wie die Klassen nun heißen. Eine Sache, die ich gerne noch gemacht hätte, wo mir allerdings vollkommen unklar ist, wie das mit Tailwind funktionieren könnte, wäre so eine Art Theme-Chooser. Aktuell sind die Farben alle mehr oder weniger in indigo
. Ich fänds ganz cool, wenns im Management-Interface einfach eine Auswahl aus allen Tailwind-Farben gäbe und man sich damit das Colortheme auswählen kann. Aber dazu müsste ich einerseits alle Farben forciert ins CSS einbacken und andererseits… die Klassennamen dynamisch ersetzen? Keine Ahnung.
iOS 12
Ein Problem bestand allerdings noch. Natürlich ist auf dem 2014er iPad Mini nicht das aktuellste iOS und damit ist auch der Safari schon etwas in die Jahre gekommen. Meine erste Proof-of-Concept-Version des Tools funktionierte darauf super, denn ich benutzte fast kein JavaScript und keine komplizierten CSS-Anweisungen. Doch jetzt bin ich ja quasi auf der bleeding Edge unterwegs!
Leider hatte ich direkt das Problem, dass gar nichts funktionierte, was über das normale Anzeigen der Seite hinaus ging. Bei jedem Klick gab es ein “This page has expired”-Fehlermeldung von Laravel. Meine Debugging-Versuche kamen leider nicht besonders weit und ich hatte einfach auch keine Lust. Das Deaktivieren der CSRF-Tokens löste das Problem. Da es sich hierbei um ein Tool handelt, was eine einzelne Person benutzt, halte ich die Security-Problematik, die sich daraus ergibt für kalkulierbar. Ich wüsste aber weiterhin trotzdem gerne, was der iOS 12 Safari beim Handling der… Session-Cookies(?) falsch macht, sodass halbwegs moderne Laravel/Livewire-Apps nicht zu funktionieren scheinen. Ich fand leider auch nichts dazu bei Google. Naja.
…und auch noch Docker?
Um die Chance zu erhöhen, dass es jemand benutzt, kümmerte ich mich sogar darum, dass sich das ganze leicht installieren lässt.
Witzig ist ja, dass PHP früher als das am Leichtesten zu installierende galt, ein Grund warum ich es damals lernte. Es war einfach überall installiert und man musste den Code nur schnell hochladen. Heutzutage weinen alle rum, wenn man nicht alles als fertige abgeschlossene Binary veröffentlicht!
Ich habe nämlich ein Docker Image erstellt. Wenn ihr also einen Raspberry Pi habt, auf dem eh schon Home Assistant läuft oder so, müsst ihr kurz kurz folgendes machen um dieses Projekt zu starten:
touch db.sqlite
docker run -d \
-p 127.0.0.1:9901:9901 \
-v $(pwd)/db.sqlite:/app/database.sqlite \
ghcr.io/pwaldhauer/kioski:latest
Fertig! Im Webinterface könnt ihr eure HomeAssistant und Spotify-Daten angeben und es kann losgehen.
Tatsächlich ist eine PHP-Anwendung immer etwas, was sich nicht so leicht in einen docker-Container pressen lässt, da man normalerweise ja nginx oder Apache und php-fpm hat und vielleicht noch eine Datenbank oder so. Ich hab es mir hier echt leicht gemacht und nutze einfach das php8.2-alpine
Image und den eingebauten Server von PHP. Da das Tool im Normalfall nur von einer Person benutzt wird, sollte das wohl ausreichen und zu keinen Problemen führen. Die Daten sind in einer SQLite-Datenbank und brauchen daher auch keine weiteren Container. Also, alles schön verpackt und das Endergebnis ist gerade mal 140 MB groß. Natürlich eigentlich immer noch riesig, aber naja.
Wer will kann es natürlich auch normal installieren. Herunterladen und eine nginx Konfiguration anlegen. Ist zumindest etwas Speicherplatz-sparender!
Zukunft
Mal sehen, ich habe sicher noch die ein oder andere Idee, aber erstmal muss man natürlich schauen, ob es performt und man es überhaupt benutzt. Vielleicht benutzt es sogar jemand anderes, da ich die Installation so leicht gemacht habe, das wäre ja schön. Sagt gerne Bescheid!