Merge branch 'main' of https://git.mike-lindner.net/mike/public-scripts
This commit is contained in:
5
ioBroker-Skripte/AWTRIX Now Playing Sonos/.gitignore
vendored
Normal file
5
ioBroker-Skripte/AWTRIX Now Playing Sonos/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# (Optional) lokale Notizen / IDE
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.log
|
||||||
7
ioBroker-Skripte/AWTRIX Now Playing Sonos/CHANGELOG.md
Normal file
7
ioBroker-Skripte/AWTRIX Now Playing Sonos/CHANGELOG.md
Normal file
@@ -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
|
||||||
21
ioBroker-Skripte/AWTRIX Now Playing Sonos/LICENSE
Normal file
21
ioBroker-Skripte/AWTRIX Now Playing Sonos/LICENSE
Normal file
@@ -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.
|
||||||
82
ioBroker-Skripte/AWTRIX Now Playing Sonos/README.md
Normal file
82
ioBroker-Skripte/AWTRIX Now Playing Sonos/README.md
Normal file
@@ -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` → `<dein Gerät>` →
|
||||||
|
- `current_title`
|
||||||
|
- `current_artist`
|
||||||
|
- `current_album`
|
||||||
|
- `state_simple`
|
||||||
|
|
||||||
|
Kopiere die Objekt-IDs in `CFG.DP`.
|
||||||
|
|
||||||
|
## AWTRIX MQTT Topics (was das Script sendet)
|
||||||
|
|
||||||
|
- Custom App:
|
||||||
|
- `<prefix>/custom/<APP_NAME>`
|
||||||
|
- Beispiel: `awtrix/custom/NowPlaying`
|
||||||
|
- Optionaler Switch:
|
||||||
|
- `<prefix>/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`.
|
||||||
@@ -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:**
|
|
||||||
```
|
|
||||||
<AWTRIX_PREFIX>/custom/<APP_NAME>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Payload:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "NowPlaying",
|
|
||||||
"text": "🎵 Artist — Title (Album)",
|
|
||||||
"icon": 29944,
|
|
||||||
"color": [255, 255, 255],
|
|
||||||
"textCase": 2,
|
|
||||||
"lifetime": 600
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auf App umschalten (optional)
|
|
||||||
**Topic:**
|
|
||||||
```
|
|
||||||
<AWTRIX_PREFIX>/switch
|
|
||||||
```
|
|
||||||
|
|
||||||
**Payload:**
|
|
||||||
```json
|
|
||||||
{ "name": "NowPlaying" }
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### App entfernen
|
|
||||||
**Topic:**
|
|
||||||
```
|
|
||||||
<AWTRIX_PREFIX>/custom/<APP_NAME>
|
|
||||||
```
|
|
||||||
|
|
||||||
**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 |
|
|
||||||
|
|
||||||
---
|
|
||||||
@@ -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 -> <dein Gerät> -> 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);
|
||||||
@@ -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);
|
|
||||||
Reference in New Issue
Block a user