From 7ff6e34f671fa7e8eff4bc0a7a4543f84daa4351 Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:04:10 +0000 Subject: [PATCH 1/8] ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js aktualisiert Version 0.0.2 --- .../ioBroker_awtrix_sonos_NowPlaying.js | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) 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 index 8bb119d..a61ee8b 100644 --- a/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js @@ -1,18 +1,18 @@ /****************************************************** * AWTRIX NowPlaying – Sonos → AWTRIX Custom App - * Version 0.0.1 + * Version 0.0.2 * Autor: Mike * Zweck: Zeigt "🎵 Künstler — Titel (Album)" auf der AWTRIX - * Trigger: Änderungen an Sonos-Datenpunkten (Titel/Artist/Album) + * Trigger: Änderungen an Sonos-Datenpunkten (Titel/Artist/Album/State) ******************************************************/ /*********** 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) +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; @@ -21,9 +21,10 @@ 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" + 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", + stateSimple: "sonos.0.root.192_168_178_75.state_simple" // true = spielt, false = kein Playback }; /*************************************/ @@ -33,6 +34,16 @@ function readVal(id) { return st ? (st.val ?? "") : ""; } +function readBool(id) { + const st = getState(id); + return st ? !!st.val : false; +} + +function isPlaying() { + // state_simple ist bereits Boolean: true = Wiedergabe + return readBool(DP.stateSimple); +} + function buildText(artist, title, album) { let base = ""; if (artist && title) base = `${artist} — ${title}`; @@ -44,7 +55,6 @@ function buildText(artist, title, album) { } function sendMQTT(topic, payloadObj) { - // ioBroker als Broker → sendMessage2Client sendTo(MQTT_INSTANCE, "sendMessage2Client", { topic, message: JSON.stringify(payloadObj), @@ -68,7 +78,7 @@ function publishCustom(text) { function removeApp() { sendMQTT(`${AWTRIX_PREFIX}/custom/${APP_NAME}`, { name: APP_NAME, lifetime: 1 }); - log("🛑 NowPlaying entfernt (keine Titelinfos)"); + log("🛑 NowPlaying entfernt (kein Playback)"); } /*********** Kernlogik ***********/ @@ -85,29 +95,34 @@ function stopKeepAlive() { function startKeepAlive(text) { stopKeepAlive(); - // Erster Push sofort (sichtbar machen) … + // Erster Push sofort (sichtbar machen) publishCustom(text); - // … und dann regelmäßig solange Titel vorhanden ist + + // … und dann regelmäßig solange Titel vorhanden UND Playback aktiv keepAliveTmr = setInterval(() => { - // Wenn Titel leer geworden ist, sofort stoppen und App entfernen - const t = String(readVal(DP.title)).trim(); - if (!t) { + const t = String(readVal(DP.title)).trim(); + const playing = isPlaying(); + + // Wenn kein Titel oder nicht mehr playing → sofort stoppen und App entfernen + if (!t || !playing) { 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(); + const title = String(readVal(DP.title)).trim(); + const artist = String(readVal(DP.artist)).trim(); + const album = String(readVal(DP.album)).trim(); + const playing = isPlaying(); - // Kein Lied → alles aus - if (!title && !artist && !album) { + // Kein Lied ODER Player nicht im Play-Status → alles aus + if ((!title && !artist && !album) || !playing) { stopKeepAlive(); if (currentSig) removeApp(); currentSig = ""; @@ -122,8 +137,6 @@ function updateAwtrix() { currentSig = sig; log(`🎧 NowPlaying → ${text}`); startKeepAlive(text); - } else { - // Gleicher Track: nichts weiter – Keep-Alive tickt von selbst } } @@ -133,7 +146,8 @@ function scheduleUpdate() { debounceTmr = setTimeout(updateAwtrix, 200); } -on({ id: [DP.title, DP.artist, DP.album], change: "ne" }, scheduleUpdate); +// Triggert jetzt auch auf state_simple +on({ id: [DP.title, DP.artist, DP.album, DP.stateSimple], change: "ne" }, scheduleUpdate); // Beim Start einmal versuchen setTimeout(updateAwtrix, 1500); From 1b5d0aa3f3d6d3765f0d712480ec7204a1038c5a Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:16:51 +0000 Subject: [PATCH 2/8] ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md aktualisiert --- .../AWTRIX Now Playing Sonos/Readme.md | 235 +++++------------- 1 file changed, 58 insertions(+), 177 deletions(-) diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md b/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md index 2b67af6..d77e768 100644 --- a/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md @@ -1,201 +1,82 @@ -# 🎵 AWTRIX NowPlaying – Sonos → AWTRIX Custom App (ioBroker) +# AWTRIX 3 – Sonos NowPlaying (ioBroker Script) -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. +Zeigt den aktuell abgespielten Sonos-Titel als **AWTRIX 3 Custom App** via **MQTT** an: -> Getestet mit ioBroker (Javascript Adapter) + MQTT Adapter und einer AWTRIX mit MQTT-Support für Custom-Apps. +> 🎵 Künstler — Titel (Album) ---- +Das Script nutzt einen **Keep-Alive Refresh**, damit die App in der Rotation bleibt, solange Sonos spielt. Sobald Playback stoppt/pausiert oder kein Titel mehr vorhanden ist, wird die App automatisch entfernt. -## 🚀 Features +## Features -- Liest **Titel / Artist / Album** aus Sonos-Datenpunkten -- Baut automatisch die Anzeige: `🎵 Künstler — Titel (Album)` -- **Keep-Alive**: hält die App aktiv, solange ein 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 Flattern bei Trackwechseln +- ✅ Anzeige: `🎵 Künstler — Titel (Album)` +- ✅ Keep-Alive Refresh (damit die App nicht „rausfällt“) +- ✅ Entfernt die App automatisch bei Stop/Pause/kein Titel +- ✅ Debounce gegen Event-Spam vom Sonos-Adapter +- ✅ Alles in **einer Datei** (ioBroker-friendly) ---- +## Voraussetzungen -## 🧩 Voraussetzungen +- ioBroker **JavaScript-Adapter** +- ioBroker **Sonos-Adapter** +- ioBroker **MQTT-Adapter** (als Client/Publisher nutzbar über `sendMessage2Client`) +- AWTRIX 3 mit aktivierter MQTT-Anbindung (Prefix bekannt, i.d.R. `awtrix`) -- **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 (Quick Start) ---- +1. Datei `nowplaying.js` öffnen und den Block **USER CONFIG** anpassen: + - `AWTRIX_PREFIX` (meist `awtrix`) + - `DP.*` (deine Sonos-Datenpunkte) +2. In ioBroker → **JavaScript** → neues Script anlegen → Inhalt von `nowplaying.js` einfügen +3. Script starten +4. Sonos abspielen → nach spätestens wenigen Sekunden sollte es auf der AWTRIX erscheinen -## ⚙️ Installation +## Sonos-Datenpunkte finden -1. In ioBroker den **Javascript-Adapter** öffnen → neues Script `NowPlaying Sonos → AWTRIX` anlegen -2. Den Code aus `nowplaying_sonos_awtrix.js` einfügen -3. Folgende Werte anpassen: - - `MQTT_INSTANCE`, `AWTRIX_PREFIX` - - Sonos-Datenpunkte unter `DP.title / artist / album` -4. Skript starten - → Beim ersten laufenden Titel sollte auf der AWTRIX die App **NowPlaying** erscheinen. +In ioBroker unter **Objekte**: ---- +`sonos.0` → `root` → `` → +- `current_title` +- `current_artist` +- `current_album` +- `state_simple` -## 🔧 Konfiguration +Kopiere die Objekt-IDs in `CFG.DP`. -| Variable | Typ | Standardwert | Beschreibung | -|--------------------|---------|---------------|--------------| -| `MQTT_INSTANCE` | String | `"mqtt.0"` | ioBroker-Instanzname des MQTT-Adapters | -| `AWTRIX_PREFIX` | String | `"awtrix"` | MQTT-Prefix der AWTRIX (z. B. `awtrix/custom/NowPlaying`) | -| `APP_NAME` | String | `"NowPlaying"`| Name der Custom-App | -| `LIFETIME_SEC` | Number | `600` | Lebensdauer der App in Sekunden (wenn kein Refresh kommt) | -| `KEEPALIVE_SEC` | Number | `10` | Intervall für den Refresh in Sekunden | -| `FORCE_SWITCH` | Boolean | `false` | Bei jedem Refresh auf die App umschalten | -| `ICON_MUSIC` | Number | `29944` | AWTRIX-Icon-ID | -| `COLOR_RGB` | Array | `[255,255,255]` | RGB-Farbe für Text | -| `TEXT_CASE` | Number | `2` | Textdarstellung (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` | +## AWTRIX MQTT Topics (was das Script sendet) -> ⚠️ **Hinweis:** Passe die Sonos-Datenpunkte an dein Gerät an – IP und Struktur können variieren. +- Custom App: + - `/custom/` + - Beispiel: `awtrix/custom/NowPlaying` +- Optionaler Switch: + - `/switch` + - Wird nur genutzt, wenn `FORCE_SWITCH=true` ---- +## Konfiguration (wichtigste Optionen) -## 🧠 Funktionsweise +Im `CFG` Block: -1. **Trigger:** - ```js - on({ id: [DP.title, DP.artist, DP.album], change: "ne" }, scheduleUpdate); - ``` - Bei Änderung wird `updateAwtrix()` mit Debounce (200 ms) aufgerufen. +- `LIFETIME_SEC`: Wie lange ein Eintrag ohne Refresh überlebt +- `KEEPALIVE_SEC`: Alle wieviel Sekunden refreshed wird +- `FORCE_SWITCH`: Wenn `true`, schaltet AWTRIX bei jedem Refresh aktiv auf die App (meist **false** lassen) +- `ICON_MUSIC`, `COLOR_RGB`, `TEXT_CASE`: Darstellung +- `DEBUG`: Zusätzliche Logs -2. **Logik:** - - Kein Titel → App entfernen, Keep-Alive stoppen - - Neuer Track → Text bauen, sofort publishen, Keep-Alive starten - - Gleicher Track → Keep-Alive aktualisiert regelmäßig selbst +## Troubleshooting -3. **Keep-Alive:** - Alle `KEEPALIVE_SEC` Sekunden wird `publishCustom()` aufgerufen. - Sobald kein Titel mehr vorhanden ist → App wird automatisch entfernt. +### Es wird nichts angezeigt +- Stimmt `AWTRIX_PREFIX`? +- Ist MQTT auf der AWTRIX aktiv? +- Funktioniert dein MQTT Adapter (und kann publishen)? +- Stimmen die Sonos-Datenpunkte? ---- +### App verschwindet nach kurzer Zeit +- `KEEPALIVE_SEC` ggf. kleiner setzen (z.B. 5–10) +- `LIFETIME_SEC` größer setzen (z.B. 600–1200) +- Prüfen ob `state_simple` wirklich `true` während Playback ist -## 📡 MQTT-Themen & Payloads +### Die Uhr springt ständig auf die App +- `FORCE_SWITCH` auf `false` setzen -### Custom-App veröffentlichen -**Topic:** -``` -/custom/ -``` +## Lizenz -**Payload:** -```json -{ - "name": "NowPlaying", - "text": "🎵 Artist — Title (Album)", - "icon": 29944, - "color": [255, 255, 255], - "textCase": 2, - "lifetime": 600 -} -``` - ---- - -### Auf App umschalten (optional) -**Topic:** -``` -/switch -``` - -**Payload:** -```json -{ "name": "NowPlaying" } -``` - ---- - -### App entfernen -**Topic:** -``` -/custom/ -``` - -**Payload:** -```json -{ "name": "NowPlaying", "lifetime": 1 } -``` - ---- - -> Das Skript sendet über: -> ```js -> sendTo("mqtt.X", "sendMessage2Client", { topic, message, retain: false, qos: 0 }); -> ``` - ---- - -## 🛠️ Anpassungen & Tipps - -### Mehrere Sonos-Player -- Skript **duplizieren** -- `APP_NAME` (z. B. `NowPlaying_Kueche`) und `DP.*` anpassen - -### Anzeige-Dauer / Refresh -- Anzeige-Frequenz → `KEEPALIVE_SEC` -- Ablaufzeit ohne Refresh → `LIFETIME_SEC` - -### Direkter Wechsel auf NowPlaying -- `FORCE_SWITCH = true` setzen, um bei jedem Refresh direkt zu wechseln - ---- - -## 🧰 Troubleshooting - -| Problem | Mögliche Ursache / Lösung | -|----------|----------------------------| -| Keine Anzeige | MQTT-Broker nicht erreichbar, falsches Prefix oder kein Song aktiv | -| App verschwindet zu schnell | `LIFETIME_SEC` erhöhen | -| Zu häufige Refreshs | `KEEPALIVE_SEC` erhöhen | -| Falsches Icon oder Textformat | `ICON_MUSIC` oder `TEXT_CASE` anpassen | -| App spammt Rotation | `FORCE_SWITCH = false` lassen | -| Keine Daten von Sonos | Sonos-Adapter prüfen (korrekte Player-IDs?) | - ---- - -## 💾 Code - -Die vollständige Version liegt in: -`nowplaying_sonos_awtrix.js` - ---- - -## 🔒 Sicherheit - -- MQTT-Zugangsdaten werden im ioBroker-MQTT-Adapter verwaltet -- Kein Retain (`retain:false`), QoS = 0 → kein Risiko veralteter Nachrichten - ---- - -## 📜 Lizenz - -**MIT License** – siehe `LICENSE` - ---- - -## 👏 Credits - -- **Autor:** Mike -- **AWTRIX-Projekt:** Community & Entwickler -- **ioBroker Adapter:** Sonos, MQTT & Javascript - ---- - -## 🧾 Changelog - -| Version | Datum | Änderungen | -|----------|--------|------------| -| v0.0.1 | 2025-10-27 | Erste Veröffentlichung | - ---- +MIT – siehe `LICENSE`. From df81c528c83b901803117d5949c8cd58e4cdeb90 Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:18:17 +0000 Subject: [PATCH 3/8] ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js aktualisiert --- .../ioBroker_awtrix_sonos_NowPlaying.js | 193 ++++++++++++------ 1 file changed, 130 insertions(+), 63 deletions(-) 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 index a61ee8b..669c1e7 100644 --- a/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js @@ -1,34 +1,73 @@ /****************************************************** - * AWTRIX NowPlaying – Sonos → AWTRIX Custom App - * Version 0.0.2 - * Autor: Mike - * Zweck: Zeigt "🎵 Künstler — Titel (Album)" auf der AWTRIX - * Trigger: Änderungen an Sonos-Datenpunkten (Titel/Artist/Album/State) + * AWTRIX NowPlaying – Sonos → AWTRIX Custom App (ioBroker) + * Version: 0.2.0 + * Autor: Mike (Repo-ready Version) + * + * Zweck: + * - Zeigt "🎵 Künstler — Titel (Album)" auf der AWTRIX 3 (via MQTT Custom App) + * - Keep-Alive Refresh hält die App in der Rotation, solange Sonos spielt & Titel vorhanden ist + * - Entfernt die App sofort, wenn Playback stoppt/pausiert oder kein Titel vorhanden ist + * + * Voraussetzungen: + * - ioBroker JavaScript-Adapter + * - ioBroker MQTT-Adapter (als Client/Publisher) -> sendTo(..., "sendMessage2Client", ...) + * - ioBroker Sonos-Adapter + * - AWTRIX 3 mit MQTT aktiviert ******************************************************/ -/*********** 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) +/******************** USER CONFIG (ANPASSEN) ********************/ +const CFG = { + /* ioBroker MQTT Adapter-Instanz */ + MQTT_INSTANCE: "mqtt.0", -/* Anzeige */ -const ICON_MUSIC = 29944; -const COLOR_RGB = [255, 255, 255]; -const TEXT_CASE = 2; + /* AWTRIX MQTT Prefix (Standard: "awtrix") */ + AWTRIX_PREFIX: "awtrix", -/* 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", - stateSimple: "sonos.0.root.192_168_178_75.state_simple" // true = spielt, false = kein Playback + /* Name der Custom App auf der AWTRIX */ + APP_NAME: "NowPlaying", + + /* Wie lange darf der AWTRIX-Eintrag ohne Refresh leben (Sekunden) */ + LIFETIME_SEC: 600, + + /* Alle x Sekunden ein Refresh, solange Titel vorhanden UND Sonos spielt */ + KEEPALIVE_SEC: 10, + + /* Wenn true: bei jedem Refresh wird aktiv auf die App gewechselt (kann nerven) */ + FORCE_SWITCH: false, + + /* Anzeige */ + ICON_MUSIC: 29944, + COLOR_RGB: [255, 255, 255], + TEXT_CASE: 2, // 0=none, 1=upper, 2=lower (AWTRIX Setting, je nach Firmware) + + /* Sonos-Datenpunkte (ANPASSEN!) + * Tipp: In ioBroker Objekte -> sonos.0 -> root -> -> current_title / current_artist / current_album / state_simple + */ + 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", + stateSimple: "sonos.0.root.192_168_178_75.state_simple" // true=spielt, false=kein Playback + }, + + /* Textformat */ + PREFIX_EMOJI: "🎵", + SEP_ARTIST_TITLE: " — ", + SHOW_ALBUM_IN_PARENS: true, + + /* Debounce für schnelle Sonos-Updates (ms) */ + DEBOUNCE_MS: 200, + + /* Optional: Debug-Logs */ + DEBUG: false }; -/*************************************/ +/******************** /USER CONFIG ******************************/ + +/******************** Helpers ******************************/ +function dbg(msg) { + if (CFG.DEBUG) log(`🐞 ${msg}`); +} -/*********** Helpers ***********/ function readVal(id) { const st = getState(id); return st ? (st.val ?? "") : ""; @@ -40,22 +79,29 @@ function readBool(id) { } function isPlaying() { - // state_simple ist bereits Boolean: true = Wiedergabe - return readBool(DP.stateSimple); + // state_simple ist im Sonos-Adapter i.d.R. Boolean: true = Wiedergabe + return readBool(CFG.DP.stateSimple); } 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}`; + + const a = (artist || "").trim(); + const t = (title || "").trim(); + const al = (album || "").trim(); + + if (a && t) base = `${a}${CFG.SEP_ARTIST_TITLE}${t}`; + else if (t) base = t; + else if (a) base = a; + + if (base && CFG.SHOW_ALBUM_IN_PARENS && al) base += ` (${al})`; + if (!base) base = "—"; + + return `${CFG.PREFIX_EMOJI} ${base}`; } function sendMQTT(topic, payloadObj) { - sendTo(MQTT_INSTANCE, "sendMessage2Client", { + sendTo(CFG.MQTT_INSTANCE, "sendMessage2Client", { topic, message: JSON.stringify(payloadObj), retain: false, @@ -65,63 +111,81 @@ function sendMQTT(topic, payloadObj) { function publishCustom(text) { const payload = { - name: APP_NAME, + name: CFG.APP_NAME, text, - icon: ICON_MUSIC, - color: COLOR_RGB, - textCase: TEXT_CASE, - lifetime: LIFETIME_SEC + icon: CFG.ICON_MUSIC, + color: CFG.COLOR_RGB, + textCase: CFG.TEXT_CASE, + lifetime: CFG.LIFETIME_SEC }; - sendMQTT(`${AWTRIX_PREFIX}/custom/${APP_NAME}`, payload); - if (FORCE_SWITCH) sendMQTT(`${AWTRIX_PREFIX}/switch`, { name: APP_NAME }); + + sendMQTT(`${CFG.AWTRIX_PREFIX}/custom/${CFG.APP_NAME}`, payload); + dbg(`publishCustom -> ${text}`); + + if (CFG.FORCE_SWITCH) { + sendMQTT(`${CFG.AWTRIX_PREFIX}/switch`, { name: CFG.APP_NAME }); + dbg("FORCE_SWITCH -> switch"); + } } function removeApp() { - sendMQTT(`${AWTRIX_PREFIX}/custom/${APP_NAME}`, { name: APP_NAME, lifetime: 1 }); + // "lifetime: 1" sorgt dafür, dass die App praktisch sofort aus der Rotation fällt + sendMQTT(`${CFG.AWTRIX_PREFIX}/custom/${CFG.APP_NAME}`, { + name: CFG.APP_NAME, + lifetime: 1 + }); + dbg("removeApp"); log("🛑 NowPlaying entfernt (kein Playback)"); } -/*********** Kernlogik ***********/ -let currentSig = ""; // artist|title|album -let keepAliveTmr = null; // setInterval-Handle -let debounceTmr = null; +/******************** Kernlogik ******************************/ +let currentSig = ""; // artist|title|album +let keepAliveTmr = null; // setInterval-Handle +let debounceTmr = null; // setTimeout-Handle function stopKeepAlive() { if (keepAliveTmr) { clearInterval(keepAliveTmr); keepAliveTmr = null; + dbg("stopKeepAlive"); } } function startKeepAlive(text) { stopKeepAlive(); - // Erster Push sofort (sichtbar machen) + + // 1) Sofort pushen (sichtbar machen) publishCustom(text); - // … und dann regelmäßig solange Titel vorhanden UND Playback aktiv + // 2) Dann regelmäßig refreshen solange Titel vorhanden & playing keepAliveTmr = setInterval(() => { - const t = String(readVal(DP.title)).trim(); - const playing = isPlaying(); + const title = String(readVal(CFG.DP.title)).trim(); + const playing = isPlaying(); - // Wenn kein Titel oder nicht mehr playing → sofort stoppen und App entfernen - if (!t || !playing) { + if (!title || !playing) { + dbg(`keepAlive stop: title="${title}" playing=${playing}`); stopKeepAlive(); currentSig = ""; removeApp(); return; } + // Text bleibt bewusst gleich, solange der Track gleich ist. publishCustom(text); - }, KEEPALIVE_SEC * 1000); + }, CFG.KEEPALIVE_SEC * 1000); + + dbg(`startKeepAlive every ${CFG.KEEPALIVE_SEC}s`); } function updateAwtrix() { - const title = String(readVal(DP.title)).trim(); - const artist = String(readVal(DP.artist)).trim(); - const album = String(readVal(DP.album)).trim(); + const title = String(readVal(CFG.DP.title)).trim(); + const artist = String(readVal(CFG.DP.artist)).trim(); + const album = String(readVal(CFG.DP.album)).trim(); const playing = isPlaying(); - // Kein Lied ODER Player nicht im Play-Status → alles aus + dbg(`updateAwtrix: playing=${playing} title="${title}" artist="${artist}" album="${album}"`); + + // Kein Lied ODER Player nicht playing -> alles aus if ((!title && !artist && !album) || !playing) { stopKeepAlive(); if (currentSig) removeApp(); @@ -130,9 +194,9 @@ function updateAwtrix() { } const text = buildText(artist, title, album); - const sig = `${artist}|${title}|${album}`; + const sig = `${artist}|${title}|${album}`; - // Neuer/anderer Track → Keep-Alive neu starten + // Neuer/anderer Track -> KeepAlive neu starten if (sig !== currentSig) { currentSig = sig; log(`🎧 NowPlaying → ${text}`); @@ -140,14 +204,17 @@ function updateAwtrix() { } } -/*********** Trigger & Initiallauf ***********/ +/******************** Trigger & Initiallauf ******************************/ function scheduleUpdate() { if (debounceTmr) clearTimeout(debounceTmr); - debounceTmr = setTimeout(updateAwtrix, 200); + debounceTmr = setTimeout(updateAwtrix, CFG.DEBOUNCE_MS); } -// Triggert jetzt auch auf state_simple -on({ id: [DP.title, DP.artist, DP.album, DP.stateSimple], change: "ne" }, scheduleUpdate); +// Triggert auf Titel/Artist/Album/State +on( + { id: [CFG.DP.title, CFG.DP.artist, CFG.DP.album, CFG.DP.stateSimple], change: "ne" }, + scheduleUpdate +); -// Beim Start einmal versuchen +// Beim Start einmal versuchen (Adapter brauchen manchmal kurz) setTimeout(updateAwtrix, 1500); From 1182c21ab055d9348f14363eb45f04a46357d4fa Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:20:58 +0000 Subject: [PATCH 4/8] =?UTF-8?q?ioBroker-Skripte/AWTRIX=20Now=20Playing=20S?= =?UTF-8?q?onos/.gitignore=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ioBroker-Skripte/AWTRIX Now Playing Sonos/.gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ioBroker-Skripte/AWTRIX Now Playing Sonos/.gitignore diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/.gitignore b/ioBroker-Skripte/AWTRIX Now Playing Sonos/.gitignore new file mode 100644 index 0000000..4cf29ec --- /dev/null +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/.gitignore @@ -0,0 +1,5 @@ +# (Optional) lokale Notizen / IDE +.DS_Store +.idea/ +.vscode/ +*.log From 217b9c7fa0c9b94aa74bbd3db20b487b6d3133de Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:21:48 +0000 Subject: [PATCH 5/8] =?UTF-8?q?ioBroker-Skripte/AWTRIX=20Now=20Playing=20S?= =?UTF-8?q?onos/CHANGELOG.md=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ioBroker-Skripte/AWTRIX Now Playing Sonos/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ioBroker-Skripte/AWTRIX Now Playing Sonos/CHANGELOG.md diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/CHANGELOG.md b/ioBroker-Skripte/AWTRIX Now Playing Sonos/CHANGELOG.md new file mode 100644 index 0000000..fe3955c --- /dev/null +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## 0.2.0 +- Erste GitHub/Gitea-ready Version (Single-File ioBroker Script) +- Keep-Alive Refresh + Auto-Remove bei Stop/Pause +- Debounce für Sonos Events +- README + MIT License From 1bd18b7cdf5ead5e074172a21fbe988aaa79d330 Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:27:06 +0000 Subject: [PATCH 6/8] ioBroker-Skripte/AWTRIX Now Playing Sonos/README.md aktualisiert --- .../AWTRIX Now Playing Sonos/{Readme.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ioBroker-Skripte/AWTRIX Now Playing Sonos/{Readme.md => README.md} (100%) diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md b/ioBroker-Skripte/AWTRIX Now Playing Sonos/README.md similarity index 100% rename from ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md rename to ioBroker-Skripte/AWTRIX Now Playing Sonos/README.md From 878c25dce5ddaf8133418ec0192c4dc2be185fc0 Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:27:38 +0000 Subject: [PATCH 7/8] ioBroker-Skripte/AWTRIX Now Playing Sonos/awtrix-sonos-nowplaying.js aktualisiert --- ...oker_awtrix_sonos_NowPlaying.js => awtrix-sonos-nowplaying.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ioBroker-Skripte/AWTRIX Now Playing Sonos/{ioBroker_awtrix_sonos_NowPlaying.js => awtrix-sonos-nowplaying.js} (100%) diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js b/ioBroker-Skripte/AWTRIX Now Playing Sonos/awtrix-sonos-nowplaying.js similarity index 100% rename from ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js rename to ioBroker-Skripte/AWTRIX Now Playing Sonos/awtrix-sonos-nowplaying.js From 769eaadd463e938486561236f4899b9b2863b897 Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 3 Jan 2026 13:29:51 +0000 Subject: [PATCH 8/8] =?UTF-8?q?ioBroker-Skripte/AWTRIX=20Now=20Playing=20S?= =?UTF-8?q?onos/LICENSE=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AWTRIX Now Playing Sonos/LICENSE | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 ioBroker-Skripte/AWTRIX Now Playing Sonos/LICENSE diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/LICENSE b/ioBroker-Skripte/AWTRIX Now Playing Sonos/LICENSE new file mode 100644 index 0000000..3cef8a0 --- /dev/null +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.