NEWS
[erledigt]Skript zum Einsammeln von Batteriestatus von ...
-
@paul53
nicht das ich wüsste - die Geräte werden in Zigbee gepaired und bekommen diese Object ID Zuweisung. Ich wollte es mir einfach machen und quasi nen Platzhalter verwenden. Klappt aber irgendwie nicht -
@mguenther sagte: Zu denen ich bis jetzt etwas gemacht habe, ja - da gibts auch nen Alias.
Dann verwende den Alias, wenn die ID-Struktur einheitlich ist.
-
@paul53
ja, aber dann habe ich nicht alle zigbee geräte. ich wollte je eigentlich gerne alle zigbee geräte auswerten. -
@mguenther sagte in Skript zum Einsammeln von Batteriestatus von Zigbeegeräten?:
ich wollte je eigentlich gerne alle zigbee geräte auswerten.
und zu denen gibt es keine alias?
-
@homoran
hi homoran,
nein, nicht zu allen. ich habe so etwas:unter Sensordaten finde ich auch für einige Zigbeegeräte den "Batteriestatus"
-
@mguenther sagte: nein, nicht zu allen.
Das ist schlecht.
Du zeigst uns nie die Namen. Ist die Bildschirmauflösung so niedrig?Teste mal, was damit im Log erscheint:
const ids = $('zigbee.0.*.battery').toArray(); function batterien() { for(let id of ids) { const val = getState(id).val; id = id.substring(0, id.lastIndexOf('.')); // Geräte-ID log(getObject(id).common.name + ': ' + val); } } schedule('0 18 * * *', batterien); batterien();
-
@paul53
dann habe ich noch nicht verstanden, was du mit "Namen" meinst. Anbei der Log-Auszug:javascript.0 2025-09-30 14:33:24.904 info script.js.System.TEST2Batterie: registered 0 subscriptions, 1 schedule, 0 messages, 0 logs and 0 file subscriptions javascript.0 2025-09-30 14:33:24.903 info script.js.System.TEST2Batterie: Taster_Sonnenrollo: 16 javascript.0 2025-09-30 14:33:24.903 info script.js.System.TEST2Batterie: Zimmer1_Fernsehtaster_Paulina: 60 javascript.0 2025-09-30 14:33:24.902 info script.js.System.TEST2Batterie: Zimmer1_Fernsehtaster_Marcus: 60 javascript.0 2025-09-30 14:33:24.902 info script.js.System.TEST2Batterie: Garagenwand_Heizung: 83 javascript.0 2025-09-30 14:33:24.901 info script.js.System.TEST2Batterie: Garagenwand_Tor_Sued: 70 javascript.0 2025-09-30 14:33:24.901 info script.js.System.TEST2Batterie: Garagenwand_Raum_Sued: 100 javascript.0 2025-09-30 14:33:24.900 info script.js.System.TEST2Batterie: Garagenwand_Briefkasten: 0 javascript.0 2025-09-30 14:33:24.899 info script.js.System.TEST2Batterie: Tuerklingel: 0 javascript.0 2025-09-30 14:33:24.897 info script.js.System.TEST2Batterie: Technikraum: 0 javascript.0 2025-09-30 14:33:24.897 info script.js.System.TEST2Batterie: Garage: 100 javascript.0 2025-09-30 14:33:24.896 info script.js.System.TEST2Batterie: Server: 37 javascript.0 2025-09-30 14:33:24.896 info script.js.System.TEST2Batterie: Arbeitszimmer: 0 javascript.0 2025-09-30 14:33:24.893 info script.js.System.TEST2Batterie: Bad_klein: 97 javascript.0 2025-09-30 14:33:24.893 info script.js.System.TEST2Batterie: Bad_klein: 0 javascript.0 2025-09-30 14:33:24.892 info script.js.System.TEST2Batterie: WC: 83 javascript.0 2025-09-30 14:33:24.891 info script.js.System.TEST2Batterie: Garderobe: 57 javascript.0 2025-09-30 14:33:24.891 info script.js.System.TEST2Batterie: Bad_gross: 23 javascript.0 2025-09-30 14:33:24.890 info script.js.System.TEST2Batterie: Aussen: 43 javascript.0 2025-09-30 14:33:24.890 info script.js.System.TEST2Batterie: Zimmer3: 30 javascript.0 2025-09-30 14:33:24.889 info script.js.System.TEST2Batterie: EG: 17 javascript.0 2025-09-30 14:33:24.889 info script.js.System.TEST2Batterie: Zimmer2: 43 javascript.0 2025-09-30 14:33:24.889 info script.js.System.TEST2Batterie: Bad_gross: 3 javascript.0 2025-09-30 14:33:24.888 info script.js.System.TEST2Batterie: Zimmer1: 0 javascript.0 2025-09-30 14:33:24.888 info script.js.System.TEST2Batterie: Zimmer3: 100 javascript.0 2025-09-30 14:33:24.888 info script.js.System.TEST2Batterie: Zimmer2: 100 javascript.0 2025-09-30 14:33:24.887 info script.js.System.TEST2Batterie: Zimmer1_rechts: 90 javascript.0 2025-09-30 14:33:24.887 info script.js.System.TEST2Batterie: Zimmer1_links: 100 javascript.0 2025-09-30 14:33:24.802 info Start JavaScript script.js.System.TEST2Batterie (Javascript/js)
-
@mguenther sagte: Anbei der Log-Auszug:
Der sieht gut aus. Also haben die Zigbee-Geräte sinnvolle Namen. Manche Namen sind allerdings doppelt vorhanden.
Die Namen kann man auch im Tab "Objekte" zeigen: -
@paul53
aber Paul, dein Ansatz mit const ids = $('zigbee.0.*.battery').toArray(); hat geholfen. ich bastle weiter und poste dann hier das Ergebnis.danke
-
hier mein Skript - chatgpt hat mit draufgeschaut - ich schließe hier.
// ===== Einstellungen ===== const OUT = "0_userdata.0.vis.BatterieListeDebug"; // Datenpunkt für VIS const THRESH_LOW = 30; const THRESH_MED = 60; const MAX_HEIGHT = 660; const WIDTH = 405; // Gesamtbreite const STALE_HOURS = 168; // >7 Tage ohne Meldung = "nicht aktiv" // === Helfer === function colorByVal(v) { if (!isFinite(v)) return "#9e9e9e"; if (v < THRESH_LOW) return "#e53935"; // rot if (v < THRESH_MED) return "#fdd835"; // gelb return "#43a047"; // grün } function devRoot(id) { const p = id.split("."); return [p[0], p[1], p[2]].join("."); } function niceNameFromRoot(rootId) { const obj = getObject(rootId); return obj?.common?.name || rootId; } function voltageToPercent(v) { if (!isFinite(v)) return null; const min = 2.0, max = 3.0; let pct = ((v - min) / (max - min)) * 100; return Math.max(0, Math.min(100, Math.round(pct))); } // Liefert den letzten Änderungszeitpunkt aller States eines Geräts function lastSeenDevice(root) { let last = 0; $('' + root + '.*').each(id => { const st = getState(id); if (st && st.lc && st.lc > last) last = st.lc; }); return last; } // === Rendering === function render() { const map = {}; // Zigbee: echte % $('zigbee.0.*.battery').each(id => { const root = devRoot(id); const name = niceNameFromRoot(root); const raw = getState(id)?.val; const val = isFinite(raw) ? Math.max(0, Math.min(100, Math.round(Number(raw)))) : null; if (val !== null) map[root] = { type: "percent", name, val, sys: "Zigbee" }; }); // HmIP: Operating Voltage -> % $('hm-rpc.0.*.OPERATING_VOLTAGE').each(id => { const root = devRoot(id); const name = niceNameFromRoot(root); const val = voltageToPercent(getState(id)?.val); if (val !== null) map[root] = { type: "percent", name, val, sys: "HmIP" }; }); // Homematic: LOWBAT $('hm-rpc.0.*.LOWBAT').each(id => { const root = devRoot(id); const name = niceNameFromRoot(root); const lowbat = !!(getState(id)?.val); const unreach = !!(getState(root + ".0.UNREACH")?.val); const last = lastSeenDevice(root); const ageH = (Date.now() - last) / (1000 * 60 * 60); if (unreach) { map[root] = { type: "unreach", name, sys: "HM" }; } else if (ageH > STALE_HOURS) { map[root] = { type: "stale", name, sys: "HM" }; } else { map[root] = { type: "lowbat", name, lowbat, sys: "HM" }; } }); // Homematic: LOW_BAT $('hm-rpc.0.*.LOW_BAT').each(id => { const root = devRoot(id); const name = niceNameFromRoot(root); const lowbat = !!(getState(id)?.val); const unreach = !!(getState(root + ".0.UNREACH")?.val); const last = lastSeenDevice(root); const ageH = (Date.now() - last) / (1000 * 60 * 60); if (unreach) { map[root] = { type: "unreach", name, sys: "HmIP" }; } else if (ageH > STALE_HOURS) { map[root] = { type: "stale", name, sys: "HmIP" }; } else { map[root] = { type: "lowbat", name, lowbat, sys: "HmIP" }; } }); // Liste bauen let list = Object.values(map); // Sortierung (angepasst) list.sort((a, b) => { function weight(it) { if (it.type === "unreach") return 0; // HM keine Meldung if (it.type === "percent" && it.sys === "Zigbee" && it.val === 0) return 1; // Zigbee 0% if (it.type === "stale") return 2; // HM nicht aktiv if (it.type === "percent" && it.sys === "Zigbee" && it.val < THRESH_LOW) return 3; // Zigbee rot if (it.type === "lowbat" && it.lowbat === true) return 4; // HM low if (it.type === "percent" && it.sys === "Zigbee" && it.val < THRESH_MED) return 5; // Zigbee gelb if (it.type === "percent" && it.sys === "Zigbee" && it.val >= THRESH_MED) return 6; // Zigbee grün if (it.type === "lowbat" && it.lowbat === false) return 7; // HM grün return 99; } const wa = weight(a), wb = weight(b); if (wa !== wb) return wa - wb; // Falls gleiche Kategorie: nach Wert sortieren if (a.type === "percent" && b.type === "percent") { if (a.val == null) return 1; if (b.val == null) return -1; return a.val - b.val; } return 0; }); // Zeilen bauen const rows = list.map(it => { if (it.type === "percent") { return ` <tr style="border-bottom:1px solid #444;"> <td style="width:55px; color:#f5f5f5;">${it.sys}</td> <td style="width:120px; color:#f5f5f5; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${it.name}">${it.name}</td> <td style="width:55px; text-align:right; color:#f5f5f5;">${it.val == null ? "-" : it.val + " %"}</td> <td style="width:175px;"> <div style="width:100%; height:10px; border-radius:5px; background:#333; overflow:hidden;"> <div style="height:100%; width:${it.val ?? 0}%; background:${colorByVal(it.val)};"></div> </div> </td> </tr>`; } if (it.type === "lowbat") { const ok = it.lowbat === false; const color = ok ? "#43a047" : "#e53935"; const text = ok ? "OK" : "LOW"; return ` <tr style="border-bottom:1px solid #444;"> <td style="width:55px; color:#f5f5f5;">${it.sys}</td> <td style="width:120px; color:#f5f5f5; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${it.name}">${it.name}</td> <td style="width:55px; text-align:right; color:#f5f5f5;">–</td> <td style="width:175px; color:${color}; font-weight:bold;">${text}</td> </tr>`; } if (it.type === "unreach") { return ` <tr style="border-bottom:1px solid #444;"> <td style="width:55px; color:#f5f5f5;">${it.sys}</td> <td style="width:120px; color:#f5f5f5; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${it.name}">${it.name}</td> <td style="width:55px; text-align:right; color:#f5f5f5;">–</td> <td style="width:175px; color:#ff9800; font-weight:bold;">KEINE MELDUNG</td> </tr>`; } if (it.type === "stale") { return ` <tr style="border-bottom:1px solid #444;"> <td style="width:55px; color:#f5f5f5;">${it.sys}</td> <td style="width:120px; color:#f5f5f5; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${it.name}">${it.name}</td> <td style="width:55px; text-align:right; color:#f5f5f5;">–</td> <td style="width:175px; color:#ff5722; font-weight:bold;">NICHT AKTIV</td> </tr>`; } }).join(""); // HTML mit sticky Header const html = ` <div style="font-family: system-ui, Segoe UI, Roboto, Arial; font-size:13px; width:${WIDTH}px; max-height:${MAX_HEIGHT}px; overflow-y:auto; padding:0; background:transparent;"> <table style="width:${WIDTH}px; border-collapse:collapse; background:transparent;"> <thead style="background:#212121;"> <tr style="border-bottom:1px solid #666;"> <th style="width:55px; text-align:left; color:#f5f5f5; position:sticky; top:0; background:#212121;">Typ</th> <th style="width:120px; text-align:left; color:#f5f5f5; position:sticky; top:0; background:#212121;">Gerät</th> <th style="width:55px; text-align:right; color:#f5f5f5; position:sticky; top:0; background:#212121;">Batterie</th> <th style="width:175px; text-align:left; color:#f5f5f5; position:sticky; top:0; background:#212121;">Status</th> </tr> </thead> <tbody>${rows}</tbody> </table> </div>`; setState(OUT, html, true); } // === Setup === createState(OUT, "", { type: "string", role: "html", read: true, write: false }, () => { render(); $('zigbee.0.*.battery').each(id => on({ id, change: "ne" }, render)); $('hm-rpc.0.*.OPERATING_VOLTAGE').each(id => on({ id, change: "ne" }, render)); $('hm-rpc.0.*.LOWBAT').each(id => on({ id, change: "ne" }, render)); $('hm-rpc.0.*.LOW_BAT').each(id => on({ id, change: "ne" }, render)); $('hm-rpc.0.*.UNREACH').each(id => on({ id, change: "ne" }, render)); // Refresh alle 12 Stunden schedule("0 */12 * * *", render); });
Tabelle ist html und lässt sich scrollen in meiner VIS