/*********** Einstellungen ***********/ const MQTT_INSTANCE = "mqtt.0"; const AWTRIX_PREFIX = "awtrix"; // Dein AWTRIX-Prefix const USE_CUSTOM = true; // true = Rotation via /custom, false = Fallback /notify // Trashschedule-Datenpunkte (exakte Pfade laut Screenshot) const DP = { rest: "trashschedule.0.type.hausmuell.nextDate", bio: "trashschedule.0.type.biotonne.nextDate", gelb: "trashschedule.0.type.gelbetonne.nextDate" }; // Labels, AWTRIX Icon-IDs und Farben const BIN = { rest: { label: "Hausmüll", iconId: 12155, color: [255, 255, 255] // Weiß }, bio: { label: "Biomüll", iconId: 12442, color: [198, 133, 53] // Braun }, gelb: { label: "Gelbe Tonne", iconId: 12212, color: [255, 255, 0] // Gelb } }; // Zeitsteuerung / Anzeige const CRON_CHECK = "*/5 * * * *"; // alle 5 Minuten prüfen const ROTATION_NAME = "TrashInfo"; // Name der Custom-App (Topic: awtrix/custom/TrashInfo) const LIFETIME_SEC = 600; // Eintrag verschwindet nach 10 Min ohne Update /************************************/ /*********** Helper-Funktionen ***********/ // robust: unterstützt number (ms), Date-Objekte und Strings function parseDate(val) { if (val === null || val === undefined) return null; if (typeof val === "number") { const d = new Date(val); return isNaN(d) ? null : d; } if (Object.prototype.toString.call(val) === "[object Date]") { return isNaN(val) ? null : val; } const s = String(val).trim(); if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return new Date(s + "T00:00:00"); const d = new Date(s); return isNaN(d) ? null : d; } function startOfDay(d) { return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0,0,0,0); } // Fenster: Vortag 16:00 → Abholtag 10:00 function withinWindow(next) { const day = startOfDay(next); // Abholtag 00:00 const windowStart = new Date(day); windowStart.setDate(windowStart.getDate() - 1); windowStart.setHours(16,0,0,0); // Vortag 16:00 const windowEnd = new Date(day); windowEnd.setHours(10,0,0,0); // Abholtag 10:00 const now = new Date(); return now >= windowStart && now <= windowEnd; } // gibt "morgen" oder "heute" zurück function phaseText(next) { const now = new Date(); return now < startOfDay(next) ? "morgen" : "heute"; } function readNext(dpId) { const st = getState(dpId); return st ? parseDate(st.val) : null; } function sendMQTT(topic, payloadObj) { sendTo(MQTT_INSTANCE, "sendMessage2Client", { topic, message: JSON.stringify(payloadObj), retain: false, qos: 0 }); } /*********** Text & Icon für AWTRIX bauen ***********/ function buildAppData() { const items = [ { key: "rest", dp: DP.rest, meta: BIN.rest }, { key: "bio", dp: DP.bio, meta: BIN.bio }, { key: "gelb", dp: DP.gelb, meta: BIN.gelb } ]; const active = items.filter(it => { const next = readNext(it.dp); if (!next) return false; if (!withinWindow(next)) return false; it.next = next; return true; }); if (active.length === 0) return null; // Eine Zeile anzeigen (erste aktive Tonne) const first = active[0]; return { text: `${first.meta.label} wird ${phaseText(first.next)} abgeholt!`, icon: first.meta.iconId, color: first.meta.color, // <-- Farbe je nach Müllart lifetime: LIFETIME_SEC, textCase: 2, // Behalte Groß-/Kleinschreibung bei }; } /*********** Ausgabe ***********/ let lastPayload = ""; function tick() { const data = buildAppData(); if (data) { const payloadStr = JSON.stringify(data); const topic = `${AWTRIX_PREFIX}/custom/${ROTATION_NAME}`; if (payloadStr !== lastPayload) { if (USE_CUSTOM) { sendMQTT(topic, data); log(`AWTRIX aktualisiert: ${data.text} (Icon ${data.icon}, Farbe ${data.color})`); } else { // Fallback über Notify sendMQTT(`${AWTRIX_PREFIX}/notify`, { text: data.text, icon: data.icon, color: data.color, duration: 15, ticker: true, sound: "notif" }); } lastPayload = payloadStr; } else { // Heartbeat, damit lifetime nicht abläuft if (USE_CUSTOM) sendMQTT(topic, data); } } else { lastPayload = ""; log("Keine Mülltermine im aktuellen Zeitfenster."); } } /*********** Scheduler ***********/ schedule(CRON_CHECK, tick); // Optional: Direkt ausführen (zum Test kurz aktivieren, danach wieder auskommentieren) // tick(); /*********** Optionales Debug (zum Prüfen der Rohwerte) ***********/ // function dumpDps() { // Object.entries(DP).forEach(([k, id]) => { // const st = getState(id); // const raw = st?.val; // const dt = parseDate(raw); // log(`[DP:${k}] ${id} -> raw=${raw} | parsed=${dt ? dt.toISOString() : "null"}`); // }); // } // dumpDps();