Vorgeschichte
Als das SmartHome immer weiter wuchs, kam ich irgendwann an den Punkt, dass ich mir eine einheitliche Vorgehensweise für Benachrichtigungen überlegen musste. Viele Informationen braucht man nur, wenn sie akut sind. Diese über das ganze Dashboard zu verteilen und abzuklappern, wäre unpraktisch, denn Benachrichtigungen entstanden an immer mehr Stellen (der Versuch einer Sammlung):
- Unwetterwarnungen
- Meldung von technischen Problemen bei Schnittstellen
- Meldung von technischen Problemen bei Sensoren
- Update-Benachrichtigungen für Geräte-Firmware
- Batterie-Meldungen von Sensoren
- Indoor und Outdoor Temperaturwarnungen
- Türklingel
- Warnung bei hoher CPU Auslastung
- Verkehrsmeldungen aus dem ÖPNV
- Warnmeldungen zum Warmwasservorrat
- Müll-Entsorgungsankündigungen
- Geöffnete Türen oder Fenster (mit KWL kommt das nicht so häufig vor)
- tägliche Niederschlagsmenge (ab einem bestimmten Grenzwert)
Inhaltsverzeichnis
Der Benachrichtigungsbereich
Herausgekommen ist sowohl im Frontend als auch im Backend ein zentraler Benachrichtigungsbereich, in dem auch die Push-Meldungen ans Smartphone verarbeitet werden.
Benachrichtigungen aggregieren
Um die Benachrichtigungen zu zusammenzufassen, nutze ich das list-Node von node-red-contrib-combine. Es kann eingehende Messages sammeln, auf verschiedene Weise aggregieren und auch selbsttätig nach einer einstellbaren Zeitspanne wieder entfernen.
Compose a table of consecutive incoming topics and payloads. Output as array, csv, html table or html list. Columns and sort order can be configured. Message distinction can be configured to
node-red-contrib-combine / listmsg.topic
ormsg._id
. A timeout can be configured that removes messages from the internal cache. The tables are re-composed when a message times out.
„list“ nimmt mir viel Arbeit ab, denn es generiert automatisch eine Tabelle aller Benachrichtigungen, die ich dann über ein Template-Node ausgebe.
So sammle ich die Benachrichtigungen ein
Benachrichtigungen entstehen in verschiedenen Flows. Diese leite ich über die Link-In-Out-Nodes zum Benachrichtigungsbereich-Flow. Vorher wird jede Benachrichtigung in einheitlicher Form aufbereitet. Bei mir sieht das so aus:
msg.topic | jede Benachrichtigungskategorie erhält ein eindeutiges Topic |
msg.payload | enthält die Meldung |
msg.time | enthält entweder die aktuelle Zeit oder die Zeit, auf die sich die Meldung bezieht, z.B. das Datum beim Entsorgungstermin oder der Zeitraum bei einer Unwetterwarnung |
Der entsprechende „Code“ dazu:
a = new Date(); b = a.getHours(); c = ("00" + a.getMinutes()).slice(-2); msg.topic="batterie"; msg.time=b+':'+c; msg.payload="Bei einem Sensor ist die Batterie bald leer."; return msg;
Da ich msg.time nicht immer für die aktuelle Zeit nutze, sondern teils auch für andere Informationen, wird diese bei jedem Ereignis entsprechend generiert. Bei den Mülltonnen-Leerungen wird zum Beispiel das Datum dort eingesetzt, bei Unwetterwarnungen der Gültigkeitszeitraum der Warnung.
Meldungen entfernen – „Entwarnung“
Um Meldungen zu entfernen oder zu widerrufen, sendet man „false“ als Payload mit gleichem msg.topic, schon wird die Meldung aussortiert. (zumindest wenn man es im list-Node so eingestellt hat)
msg.topic="batterie"; msg.time=false; msg.payload=false; return msg;
Den Benachrichtigungsbereich ein- und ausblenden
Damit das Dashboard schön ist, wollte ich einen leeren Benachrichtigungsbereich vermeiden. Dank des ui-control Widgets kann man diesen problemlos ausblenden, sobald keine Nachrichten mehr vorliegen.
Immer wenn eine Meldung ankommt oder entfernt wird, wird die Gesamt-Zeichenanzahl der Benachrichtigungen in den Kontextdaten gespeichert, sodass sowohl bei Änderungen der Benachrichtigungen als auch beim Aufruf des Tabs schnell geprüft werden kann, ob die UI-Gruppe angezeigt werden soll.
if(msg.payload===true) { msg.payload={"group": {"show": ["Anfang_Meldungen"], "focus": false}}; } else { msg.payload={"group": {"hide": ["Anfang_Meldungen"], "focus": false}}; } return msg;
(mein Tab heißt „Anfang“, die UI-Gruppe „Meldungen“, die Adressierung der Gruppe funktioniert mit einem Unterstrich dazwischen: „Tab_Gruppe“)
Den Benachrichtigungsbereich stylen
Damit der Benachrichtigungsbereich optisch heraussticht, habe ich ihn auffällig gestaltet. Über ein Template-Node in der Head-Sektion habe ich folgende Styles hinterlegt. (Wie die nötige ID der UI-Gruppe zustande kommt, steht ein Absatz weiter oben oder im Quelltext deines Dashboards.)
#Anfang_Meldungen div.nr-dashboard-cardpanel { background-color: rgba(255,227,116,0.4); } #Anfang_Meldungen div.nr-dashboard-cardpanel p { color:red; } #Anfang_Meldungen_cards md-card { background-color: rgba(255,227,116,0.3); }
Ein Problem bleibt – die Höhe
Für ein „Problem“ habe ich noch keine Lösung gefunden. Die Gruppe, bzw. das template-Node hat bei mir eine fixe Höhe von aktuelle zwei Raster-Höheneinheiten. Sind viele Meldungen da, muss man also scrollen. Hinweise und Lösungen dazu gerne in die Kommentare.
Aus UI-Benachrichtigungen werden Push-Meldungen
Der Vorteil, wenn man alle Benachrichtigungen zentral sammelt, ist: man kann aus den UI-Benachrichtigungen auch ganz schnell Push-Meldungen – bei mir per XMPP – aufs Smartphone generieren. Um die Nerven zu schonen, habe ich dabei inzwischen umfangreiche Filter eingebaut, um Push-Terror zu vermeiden. Ich habe sehr individuell abgestimmt, welche Benachrichtigungen ich akut aufs Smartphone bekommen will. Dafür nutze ich mehrere Level:
- alle und immer
- alle, aber nur einmal pro Zeitraum
- alle, aber Dopplungen werden für einen gewissen Zeitraum gefiltert
- erst wenn ein „Warnlevel“ überschritten wird
Anhand des Topics der Benachrichtigung wird bei mir zwischen den unterschiedlichen Leveln variiert. Das sieht aktuell dann so aus:
Nur einmal pro Zeitraum
Nur einmal pro Zeitraum funktioniert ganz einfach über das Delay-Node über „Nachrichtenrate begrenzen“. Wichtig ist hier nur die Aktivierung des Feldes „Zwischennachrichten löschen“.
Erst wenn ein Warnlevel überschritten wird
Beim Warnlevel muss in einem bestimmten Zeitraum eine gewisse Anzahl von Benachrichtigungen eingegangen sein. Nach Ablauf des Zeitraums wird die Zählung genullt. Ich nutze sie vor allem für Sensor- oder Aktor-Erreichbarkeitsprobleme im WLAN oder für Schnittstellen zu externen Datenquellen. Wenn einmal ein Ping z.B. zu einem Shelly nicht zustande kommt, sagt das nicht viel aus. Passiert das innerhalb von drei Stunde mehrfach, liegt vielleicht ein Problem vor.
Mein Code ist nichts besonderes, der Vollständigkeit halber aber trotzdem hier:
msg.warnlevel = flow.get("warnlevel_wlan")+1; flow.set("warnlevel_wlan",msg.warnlevel); if(msg.warnlevel > 10) { return msg; }
Dazu noch das entsprechende „Nullen“ nach Ablauf einer Frist. Hierbei nutze ich einen festen Rhythmus, sodass das Alter der Warnungen nicht berücksichtigt wird. In meinen Augen ist das aber nicht weiter schlimm, da ich die Methode „Warnlevel“ eh nur bei nicht-sicherheitsrelevanten Benachrichtigungen nutze. Außerdem werden alle Benachrichtigungen ja auch im UI angezeigt, sodass man dort auch „drüberstolpern“ kann.
Dopplungen filtern
Bei den Unwetterwarnungen filtere ich Dopplungen heraus. Die Informationen werden regelmäßig abgerufen, daraus ergibt sich, dass ein und die selbe Meldung vielfach abgerufen wird. Um nicht jedes Mal wieder Alarm zu geben, werden identische Meldungen 24 Stunden lang geblockt. Dazu speichere ich bereits gepushte Meldungen in den Kontextdaten und gleiche das entsprechend ab.
Flow als Download
Benötigt werden die Nodes von node-red-contrib-combine.
Genau sowas suche ich :-). Gibt es den Code nicht als Beispiel zum Download??
Hallo Konrad,
ich habe mal ein paar Nodes zusammengestellt und als Download in den Artikel eingefügt. Bei Fragen oder Problemen, melde dich einfach.
Lukas