154 lines
4.4 KiB
JavaScript
154 lines
4.4 KiB
JavaScript
/******************************************************
|
||
* 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)
|
||
******************************************************/
|
||
|
||
/*********** 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",
|
||
stateSimple: "sonos.0.root.192_168_178_75.state_simple" // true = spielt, false = kein Playback
|
||
};
|
||
/*************************************/
|
||
|
||
/*********** Helpers ***********/
|
||
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 bereits Boolean: true = Wiedergabe
|
||
return readBool(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}`;
|
||
}
|
||
|
||
function sendMQTT(topic, payloadObj) {
|
||
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 (kein Playback)");
|
||
}
|
||
|
||
/*********** 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 UND Playback aktiv
|
||
keepAliveTmr = setInterval(() => {
|
||
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 playing = isPlaying();
|
||
|
||
// Kein Lied ODER Player nicht im Play-Status → 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 → Keep-Alive neu starten
|
||
if (sig !== currentSig) {
|
||
currentSig = sig;
|
||
log(`🎧 NowPlaying → ${text}`);
|
||
startKeepAlive(text);
|
||
}
|
||
}
|
||
|
||
/*********** Trigger & Initiallauf ***********/
|
||
function scheduleUpdate() {
|
||
if (debounceTmr) clearTimeout(debounceTmr);
|
||
debounceTmr = setTimeout(updateAwtrix, 200);
|
||
}
|
||
|
||
// 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);
|