diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md b/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md new file mode 100644 index 0000000..89ff4db --- /dev/null +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md @@ -0,0 +1,207 @@ +AWTRIX NowPlaying – Sonos → AWTRIX Custom App (ioBroker) + +Zeigt den aktuell laufenden Song von Sonos als eigene AWTRIX Custom-App an: +🎵 Künstler — Titel (Album) – hält sich per Keep-Alive in der Rotation, solange ein Titel läuft. + +Getestet mit ioBroker (Javascript Adapter) + MQTT Adapter und einer AWTRIX mit MQTT-Support für Custom-Apps. + +Features + +Liest Titel/Artist/Album aus Sonos-Datenpunkten. + +Baut automatisch die Anzeige: 🎵 Künstler — Titel (Album). + +Keep-Alive: hält die App per Refresh aktiv, solange der Titel vorhanden ist. + +Automatisches Entfernen der App, sobald keine Titelinfos mehr da sind. + +Optionales FORCE_SWITCH: bei jedem Refresh auf die App umschalten. + +Debounce-Logik (200 ms) gegen „Datenpunkt-Flattern“ bei Trackwechseln. + +Voraussetzungen + +ioBroker mit: + +Javascript Adapter (zum Ausführen des Skripts) + +MQTT Adapter (als Client zum MQTT-Broker; verwendet sendMessage2Client) + +Sonos Adapter (liefert current_title, current_artist, current_album) + +AWTRIX (mit MQTT-Support für Custom-Apps) + +Ein erreichbarer MQTT-Broker (falls nicht in ioBroker integriert) + +Installation + +In ioBroker den Javascript-Adapter öffnen → neues Script NowPlaying Sonos → AWTRIX anlegen. + +Den Skript-Code aus nowplaying_sonos_awtrix.js einfügen und speichern. + +Prüfen/Anpassen: + +MQTT_INSTANCE, AWTRIX_PREFIX + +Sonos-Datenpunkte unter DP.title / artist / album + +Skript starten. +Beim ersten laufenden Titel sollte auf der AWTRIX die App NowPlaying erscheinen. + +Konfiguration +Konstante Typ Default Beschreibung +MQTT_INSTANCE String "mqtt.0" ioBroker-Instanzname des MQTT-Adapters (für sendTo(..., "sendMessage2Client", ...)). +AWTRIX_PREFIX String "awtrix" MQTT-Prefix deiner AWTRIX (z. B. awtrix → Themen wie awtrix/custom/NowPlaying). +APP_NAME String "NowPlaying" Name der Custom-App (wird als Topic und Anzeigename genutzt). +LIFETIME_SEC Number 600 Lebensdauer der App in Sekunden, falls kein Refresh mehr kommt. +KEEPALIVE_SEC Number 10 Refresh-Intervall in Sekunden, solange Titel vorhanden ist. +FORCE_SWITCH Boolean false Bei jedem Refresh aktiv auf die App schalten (awtrix/switch). +ICON_MUSIC Number 29944 AWTRIX-Icon-ID für die Anzeige. +COLOR_RGB Array [255,255,255] Textfarbe als RGB. +TEXT_CASE Number 2 Textdarstellung lt. AWTRIX (z. B. 0=normal, 1=upper, 2=smart). +DP.title String Beispiel Sonos-Datenpunkt current_title. +DP.artist String Beispiel Sonos-Datenpunkt current_artist. +DP.album String Beispiel Sonos-Datenpunkt current_album. + +Hinweis: Passe die Sonos-DPs an dein Gerät an (IP/Struktur im Sonos-Adapter kann variieren). + +Funktionsweise (Kurz) + +Trigger: on({ id: [title, artist, album], change: "ne" }) +Bei Änderung wird mit Debounce (200 ms) updateAwtrix() aufgerufen. + +Logik: + +Wenn kein Titel/Artist/Album: App entfernen, Keep-Alive stoppen. + +Bei neuem Track: Text bauen → sofort publishen → Keep-Alive-Timer starten. + +Bei gleichem Track: nichts tun; Keep-Alive veröffentlicht regelmäßig neu. + +Keep-Alive: +Alle KEEPALIVE_SEC Sekunden publishCustom(text); sobald title leer ist → App entfernen. + +MQTT-Themen & Payloads + +Custom-App veröffentlichen: +Topic: + +/custom/ + + +Payload (Beispiel): + +{ + "name": "NowPlaying", + "text": "🎵 Artist — Title (Album)", + "icon": 29944, + "color": [255, 255, 255], + "textCase": 2, + "lifetime": 600 +} + + +Auf App umschalten (optional): +Topic: + +/switch + + +Payload: + +{ "name": "NowPlaying" } + + +App entfernen: +Topic: + +/custom/ + + +Payload: + +{ "name": "NowPlaying", "lifetime": 1 } + + +Das Skript sendet per sendTo("mqtt.X", "sendMessage2Client", { topic, message, retain:false, qos:0 }). + +Anpassungen & Tipps +Mehrere Sonos-Player + +Lege das Skript mehrfach an (kopieren) und passe je Instanz: + +APP_NAME (z. B. NowPlaying_Kueche) + +DP.* auf die jeweiligen Player-Datenpunkte + +Kürzere/Längere Anzeige + +Häufigkeit der Refreshs: KEEPALIVE_SEC + +Max. Lebensdauer ohne Refresh: LIFETIME_SEC + +Rotation „hart“ anstoßen + +Setze FORCE_SWITCH = true, wenn viele Apps in der Rotation sind und du bei jedem Refresh zu NowPlaying springen willst. + +Troubleshooting + +Keine Anzeige auf der AWTRIX + +Prüfe, ob der MQTT-Broker erreichbar ist und der ioBroker-MQTT-Adapter verbunden ist. + +AWTRIX_PREFIX korrekt? (z. B. awtrix vs. matrix/awtrix). + +APP_NAME stimmt? (Switch bezieht sich auf denselben Namen) + +Läuft mindestens ein Titel (also DP.title nicht leer)? + +Payload kommt an, aber keine/komische Darstellung + +ICON_MUSIC existiert? Ggf. andere Icon-ID testen. + +TEXT_CASE anpassen (0/1/2 – abhängig von deiner AWTRIX-Firmware). + +App verschwindet zu schnell + +LIFETIME_SEC erhöhen. + +Prüfen, ob KEEPALIVE_SEC zu groß ist (seltener Refresh). + +App „spammt“ die Rotation + +FORCE_SWITCH = false belassen. + +KEEPALIVE_SEC erhöhen (weniger häufige Refreshs). + +Sonos-Datenpunkte ändern sich nicht + +Sonos-Adapter prüfen; korrekte Instanz und Player-IDs? + +Testweise die DPs im Objekt-Baum beobachten. + +Code + +Die vollständige Version liegt in nowplaying_sonos_awtrix.js. + +Sicherheit + +MQTT-Zugangsdaten werden im ioBroker-MQTT-Adapter gepflegt (nicht im Skript). + +Das Skript sendet ohne Retain (retain:false) und mit qos:0. + +Lizenz + +MIT License – siehe LICENSE. + +Credits + +Idee & Umsetzung: Mike + +AWTRIX Projekt & Icons: Respect an die AWTRIX-Community + +ioBroker Adapter: Danke an die Maintainer von Sonos/MQTT/JS + +Changelog + +v0.0.1 – Erste Veröffentlichung \ No newline at end of file diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js b/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js new file mode 100644 index 0000000..8bb119d --- /dev/null +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js @@ -0,0 +1,139 @@ +/****************************************************** + * AWTRIX NowPlaying – Sonos → AWTRIX Custom App + * Version 0.0.1 + * Autor: Mike + * Zweck: Zeigt "🎵 Künstler — Titel (Album)" auf der AWTRIX + * Trigger: Änderungen an Sonos-Datenpunkten (Titel/Artist/Album) + ******************************************************/ + +/*********** Einstellungen ***********/ +const MQTT_INSTANCE = "mqtt.0"; +const AWTRIX_PREFIX = "awtrix"; // Dein Prefix +const APP_NAME = "NowPlaying"; +const LIFETIME_SEC = 600; // Eintrag läuft ab, wenn kein Refresh kommt +const KEEPALIVE_SEC = 10; // alle x Sekunden Refresh solange Titel vorhanden +const FORCE_SWITCH = false; // bei jedem Refresh auf App schalten (falls viele Apps) + +/* Anzeige */ +const ICON_MUSIC = 29944; +const COLOR_RGB = [255, 255, 255]; +const TEXT_CASE = 2; + +/* Sonos-Datenpunkte (ggf. anpassen) */ +const DP = { + title: "sonos.0.root.192_168_178_75.current_title", + artist: "sonos.0.root.192_168_178_75.current_artist", + album: "sonos.0.root.192_168_178_75.current_album" +}; +/*************************************/ + +/*********** Helpers ***********/ +function readVal(id) { + const st = getState(id); + return st ? (st.val ?? "") : ""; +} + +function buildText(artist, title, album) { + let base = ""; + if (artist && title) base = `${artist} — ${title}`; + else if (title) base = title; + else if (artist) base = artist; + if (base && album) base += ` (${album})`; + if (!base) base = "—"; + return `🎵 ${base}`; +} + +function sendMQTT(topic, payloadObj) { + // ioBroker als Broker → sendMessage2Client + sendTo(MQTT_INSTANCE, "sendMessage2Client", { + topic, + message: JSON.stringify(payloadObj), + retain: false, + qos: 0 + }); +} + +function publishCustom(text) { + const payload = { + name: APP_NAME, + text, + icon: ICON_MUSIC, + color: COLOR_RGB, + textCase: TEXT_CASE, + lifetime: LIFETIME_SEC + }; + sendMQTT(`${AWTRIX_PREFIX}/custom/${APP_NAME}`, payload); + if (FORCE_SWITCH) sendMQTT(`${AWTRIX_PREFIX}/switch`, { name: APP_NAME }); +} + +function removeApp() { + sendMQTT(`${AWTRIX_PREFIX}/custom/${APP_NAME}`, { name: APP_NAME, lifetime: 1 }); + log("🛑 NowPlaying entfernt (keine Titelinfos)"); +} + +/*********** Kernlogik ***********/ +let currentSig = ""; // artist|title|album +let keepAliveTmr = null; // setInterval-Handle +let debounceTmr = null; + +function stopKeepAlive() { + if (keepAliveTmr) { + clearInterval(keepAliveTmr); + keepAliveTmr = null; + } +} + +function startKeepAlive(text) { + stopKeepAlive(); + // Erster Push sofort (sichtbar machen) … + publishCustom(text); + // … und dann regelmäßig solange Titel vorhanden ist + keepAliveTmr = setInterval(() => { + // Wenn Titel leer geworden ist, sofort stoppen und App entfernen + const t = String(readVal(DP.title)).trim(); + if (!t) { + stopKeepAlive(); + currentSig = ""; + removeApp(); + return; + } + publishCustom(text); + }, KEEPALIVE_SEC * 1000); +} + +function updateAwtrix() { + const title = String(readVal(DP.title)).trim(); + const artist = String(readVal(DP.artist)).trim(); + const album = String(readVal(DP.album)).trim(); + + // Kein Lied → alles aus + if (!title && !artist && !album) { + stopKeepAlive(); + if (currentSig) removeApp(); + currentSig = ""; + return; + } + + const text = buildText(artist, title, album); + const sig = `${artist}|${title}|${album}`; + + // Neuer/anderer Track → Keep-Alive neu starten + if (sig !== currentSig) { + currentSig = sig; + log(`🎧 NowPlaying → ${text}`); + startKeepAlive(text); + } else { + // Gleicher Track: nichts weiter – Keep-Alive tickt von selbst + } +} + +/*********** Trigger & Initiallauf ***********/ +function scheduleUpdate() { + if (debounceTmr) clearTimeout(debounceTmr); + debounceTmr = setTimeout(updateAwtrix, 200); +} + +on({ id: [DP.title, DP.artist, DP.album], change: "ne" }, scheduleUpdate); + +// Beim Start einmal versuchen +setTimeout(updateAwtrix, 1500);