Initial commit
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
\# AWTRIX Müllkalender (ioBroker)
|
||||
|
||||
|
||||
|
||||
Zeigt bevorstehende Müllabfuhrtermine auf einer AWTRIX 3 Matrix-Uhr über MQTT an.
|
||||
|
||||
|
||||
|
||||
Das Skript nutzt den ioBroker Trashschedule-Adapter und veröffentlicht automatisch Hinweise für:
|
||||
|
||||
|
||||
|
||||
\* Hausmüll
|
||||
|
||||
\* Biomüll
|
||||
|
||||
\* Gelbe Tonne
|
||||
|
||||
|
||||
|
||||
Die Anzeige erfolgt als AWTRIX Custom App inklusive:
|
||||
|
||||
|
||||
|
||||
\* passendem Icon
|
||||
|
||||
\* individueller Farbe je Müllart
|
||||
|
||||
\* automatischer Rotation
|
||||
|
||||
\* Keep-Alive Refresh
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# Funktionen
|
||||
|
||||
|
||||
|
||||
\* Anzeige der nächsten Müllabfuhr auf AWTRIX 3
|
||||
|
||||
\* Automatische Anzeige:
|
||||
|
||||
|
||||
|
||||
  \* ab Vortag 16:00 Uhr
|
||||
|
||||
  \* bis zum Abholtag 10:00 Uhr
|
||||
|
||||
\* Unterschiedliche Farben je Müllart
|
||||
|
||||
\* Eigene AWTRIX-Icons
|
||||
|
||||
\* MQTT-basierte Kommunikation
|
||||
|
||||
\* Keep-Alive Refresh gegen Ablauf der AWTRIX-App
|
||||
|
||||
\* Fallback auf `/notify` möglich
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# Voraussetzungen
|
||||
|
||||
|
||||
|
||||
\## ioBroker Adapter
|
||||
|
||||
|
||||
|
||||
Benötigt werden:
|
||||
|
||||
|
||||
|
||||
\* JavaScript-Adapter
|
||||
|
||||
\* MQTT-Adapter
|
||||
|
||||
\* Trashschedule-Adapter
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# Verwendete Datenpunkte
|
||||
|
||||
|
||||
|
||||
```text
|
||||
|
||||
trashschedule.0.type.hausmuell.nextDate
|
||||
|
||||
trashschedule.0.type.biotonne.nextDate
|
||||
|
||||
trashschedule.0.type.gelbetonne.nextDate
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# AWTRIX Voraussetzungen
|
||||
|
||||
|
||||
|
||||
\* AWTRIX 3
|
||||
|
||||
\* MQTT-Anbindung aktiviert
|
||||
|
||||
\* Custom Apps aktiviert
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# MQTT Topic
|
||||
|
||||
|
||||
|
||||
```text
|
||||
|
||||
awtrix/custom/TrashInfo
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# Konfiguration
|
||||
|
||||
|
||||
|
||||
Im oberen Bereich des Skripts können folgende Werte angepasst werden:
|
||||
|
||||
|
||||
|
||||
\## MQTT
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
|
||||
const MQTT\_INSTANCE = "mqtt.0";
|
||||
|
||||
const AWTRIX\_PREFIX = "awtrix";
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
\## Anzeige
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
|
||||
const ROTATION\_NAME = "TrashInfo";
|
||||
|
||||
const LIFETIME\_SEC = 600;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
\## Zeitfenster
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
|
||||
const CRON\_CHECK = "\*/5 \* \* \* \*";
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
Standard:
|
||||
|
||||
|
||||
|
||||
\* Prüfung alle 5 Minuten
|
||||
|
||||
\* Anzeige:
|
||||
|
||||
|
||||
|
||||
  \* ab Vortag 16:00 Uhr
|
||||
|
||||
  \* bis Abholtag 10:00 Uhr
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# Unterstützte Müllarten
|
||||
|
||||
|
||||
|
||||
| Typ | Farbe |
|
||||
|
||||
| ----------- | ----- |
|
||||
|
||||
| Hausmüll | Weiß |
|
||||
|
||||
| Biomüll | Braun |
|
||||
|
||||
| Gelbe Tonne | Gelb |
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# Besonderheiten
|
||||
|
||||
|
||||
|
||||
Das Skript nutzt einen regelmäßigen Keep-Alive Refresh, damit die AWTRIX-App nicht aus der Rotation verschwindet.
|
||||
|
||||
|
||||
|
||||
Wenn kein relevanter Mülltermin mehr aktiv ist, wird die Anzeige automatisch entfernt.
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\# Datei
|
||||
|
||||
|
||||
|
||||
```text
|
||||
|
||||
awtrix3-abfallkalender.js
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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