173 lines
4.8 KiB
JavaScript
173 lines
4.8 KiB
JavaScript
/*********** 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();
|