Initial commit
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
/*********** 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();
|
||||
Reference in New Issue
Block a user