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 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 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. 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..d77e768 --- /dev/null +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/README.md @@ -0,0 +1,82 @@ +# AWTRIX 3 – Sonos NowPlaying (ioBroker Script) + +Zeigt den aktuell abgespielten Sonos-Titel als **AWTRIX 3 Custom App** via **MQTT** an: + +> 🎵 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 + +- ✅ 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 + +- 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`) + +## 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 + +## Sonos-Datenpunkte finden + +In ioBroker unter **Objekte**: + +`sonos.0` → `root` → `` → +- `current_title` +- `current_artist` +- `current_album` +- `state_simple` + +Kopiere die Objekt-IDs in `CFG.DP`. + +## AWTRIX MQTT Topics (was das Script sendet) + +- Custom App: + - `/custom/` + - Beispiel: `awtrix/custom/NowPlaying` +- Optionaler Switch: + - `/switch` + - Wird nur genutzt, wenn `FORCE_SWITCH=true` + +## Konfiguration (wichtigste Optionen) + +Im `CFG` Block: + +- `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 + +## Troubleshooting + +### 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 + +### Die Uhr springt ständig auf die App +- `FORCE_SWITCH` auf `false` setzen + +## Lizenz + +MIT – siehe `LICENSE`. diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md b/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md deleted file mode 100644 index 2b67af6..0000000 --- a/ioBroker-Skripte/AWTRIX Now Playing Sonos/Readme.md +++ /dev/null @@ -1,201 +0,0 @@ -# 🎵 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 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 - ---- - -## 🧩 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 - -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. - ---- - -## 🔧 Konfiguration - -| 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` | - -> ⚠️ **Hinweis:** Passe die Sonos-Datenpunkte an dein Gerät an – IP und Struktur können variieren. - ---- - -## 🧠 Funktionsweise - -1. **Trigger:** - ```js - on({ id: [DP.title, DP.artist, DP.album], change: "ne" }, scheduleUpdate); - ``` - Bei Änderung wird `updateAwtrix()` mit Debounce (200 ms) aufgerufen. - -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 - -3. **Keep-Alive:** - Alle `KEEPALIVE_SEC` Sekunden wird `publishCustom()` aufgerufen. - Sobald kein Titel mehr vorhanden ist → App wird automatisch entfernt. - ---- - -## 📡 MQTT-Themen & Payloads - -### Custom-App veröffentlichen -**Topic:** -``` -/custom/ -``` - -**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 | - ---- diff --git a/ioBroker-Skripte/AWTRIX Now Playing Sonos/awtrix-sonos-nowplaying.js b/ioBroker-Skripte/AWTRIX Now Playing Sonos/awtrix-sonos-nowplaying.js new file mode 100644 index 0000000..669c1e7 --- /dev/null +++ b/ioBroker-Skripte/AWTRIX Now Playing Sonos/awtrix-sonos-nowplaying.js @@ -0,0 +1,220 @@ +/****************************************************** + * 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 + ******************************************************/ + +/******************** USER CONFIG (ANPASSEN) ********************/ +const CFG = { + /* ioBroker MQTT Adapter-Instanz */ + MQTT_INSTANCE: "mqtt.0", + + /* AWTRIX MQTT Prefix (Standard: "awtrix") */ + AWTRIX_PREFIX: "awtrix", + + /* 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}`); +} + +function readVal(id) { + const st = getState(id); + return st ? (st.val ?? "") : ""; +} + +function readBool(id) { + const st = getState(id); + return st ? !!st.val : false; +} + +function isPlaying() { + // state_simple ist im Sonos-Adapter i.d.R. Boolean: true = Wiedergabe + return readBool(CFG.DP.stateSimple); +} + +function buildText(artist, title, album) { + let 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(CFG.MQTT_INSTANCE, "sendMessage2Client", { + topic, + message: JSON.stringify(payloadObj), + retain: false, + qos: 0 + }); +} + +function publishCustom(text) { + const payload = { + name: CFG.APP_NAME, + text, + icon: CFG.ICON_MUSIC, + color: CFG.COLOR_RGB, + textCase: CFG.TEXT_CASE, + lifetime: CFG.LIFETIME_SEC + }; + + 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() { + // "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; // setTimeout-Handle + +function stopKeepAlive() { + if (keepAliveTmr) { + clearInterval(keepAliveTmr); + keepAliveTmr = null; + dbg("stopKeepAlive"); + } +} + +function startKeepAlive(text) { + stopKeepAlive(); + + // 1) Sofort pushen (sichtbar machen) + publishCustom(text); + + // 2) Dann regelmäßig refreshen solange Titel vorhanden & playing + keepAliveTmr = setInterval(() => { + const title = String(readVal(CFG.DP.title)).trim(); + const playing = isPlaying(); + + 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); + }, CFG.KEEPALIVE_SEC * 1000); + + dbg(`startKeepAlive every ${CFG.KEEPALIVE_SEC}s`); +} + +function updateAwtrix() { + 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(); + + 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(); + currentSig = ""; + return; + } + + const text = buildText(artist, title, album); + const sig = `${artist}|${title}|${album}`; + + // Neuer/anderer Track -> KeepAlive neu starten + if (sig !== currentSig) { + currentSig = sig; + log(`🎧 NowPlaying → ${text}`); + startKeepAlive(text); + } +} + +/******************** Trigger & Initiallauf ******************************/ +function scheduleUpdate() { + if (debounceTmr) clearTimeout(debounceTmr); + debounceTmr = setTimeout(updateAwtrix, CFG.DEBOUNCE_MS); +} + +// 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 (Adapter brauchen manchmal kurz) +setTimeout(updateAwtrix, 1500); 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 deleted file mode 100644 index 8bb119d..0000000 --- a/ioBroker-Skripte/AWTRIX Now Playing Sonos/ioBroker_awtrix_sonos_NowPlaying.js +++ /dev/null @@ -1,139 +0,0 @@ -/****************************************************** - * 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);