Flot mit min / max / mittel nutzen

Ich schätze Flot als schlankes Tool um ansehnliche Diagramme zu zeichnen. Diese binde ich per iframe in Node-RED Dashboard ein. Bisher war es mir jedoch nicht gelungen, die Daten abseits der Glättung zu aggregieren und die Eingangsdaten-Arten min, max und mittel zu nutzen. Dabei ist es eigentlich einfach und logisch. Im Reiter „Zeit“ muss nur unter „Aggregation“ der Schritttyp auf Sekunden und die Sekunden entsprechend angepasst werden. Für eine tageweise Aggregation zum Beispiel 86400.

Wetter selbst von yr.no abrufen

Update vom 12.01.2022

Jetzt mit Details zu Dashboard-Ausgabe, inkl. Vorhersage-Chart.

Wetterdaten fürs SmartHome

Rund ums Wetter sammle ich mir eine Vielzahl an Daten aus verschiedenen Quellen. Die Wettervorhersage beziehe ich vom Norwegischen Wetterdienst yr.no, Live-Wetterdaten erhebe ich selbst oder hole sie mir über openSenseMap und Weather.com (Weather Underground) von Wetterstationen aus der Nachbarschaft. Wetterwarnungen kommen vom DWD und ein schickes Niederschlagsradar vom Wetterdienst eines (ehemals) bekannten Wetter-Moderators.

Vorgeschichte

Bisher habe ich für die Wettervorhersage via yr.no den ioBroker-Adapter genutzt. Doch seit der Umstellung der API von xml auf json ist die Anzahl der verfügbaren Werte und damit auch die Anzahl der ioBroker-Objekte explodiert – vieles davon ohne Mehrwert für mich. Dies war mir ein Dorn im Auge, versuche ich doch die Anzahl der Schreibvorgänge auf die SD-Karte klein zu halten.

Also wollte ich die API von yr.no/met.no selbst anzapfen, mit Node-RED ja kein Problem.

So mach ich es

Wenn man die API-Dokumentation aufmerksamer ließt als ich, kann man das ganze recht schnell durchschauen. (Ich brauchte dementsprechend eine Weile.) Man hat die Wahl zwischen dem classic, compact und complete Bericht. Mich interessieren die Temperaturen, die Wetterlage und die erwarteten Niederschlagsmengen. Diese bekomme ich im complete-Bericht.

Man benötigt seine Position als lat/lon. Google Maps verrät beim Rechtsklick auf jeden beliebigen Punkt der Erde die entsprechenden Werte, der erste ist dabei der Breitengrad (lat), der zweite der Längengrad (lon). Ergänzen kann man noch die Höhe über Meeresspiegel – dies ist jedoch optional.

Das Query-String für den Erfurter Dom wäre dann

https://api.met.no/weatherapi/locationforecast/2.0/complete?altitude=194&lat=50.97578937177409&lon=11.023335426472794

Es ist nötig, einen eigenen User-Agent als Identifikation bei der Anfrage mitzusenden, sonst bekommt man einen Fehler „403 Forbidden“. Hier kann man beliebig kreativ sein. Das setzen des Headers geht ganz einfach mit einem Function-Node vor dem HTTP-Request-Node:

msg.headers = {
    "User-Agent": "erfurter-dom-99084"
}
return msg;

Wenn man alles richtig gemacht hat, erhält man als Rückgabe einen wirklich umfangreichen JSON-Datensatz. Dieser enthält Vorhersagedaten für die nächsten 9 Tage. Mich interessieren tatsächlich nur die nächsten 48 Stunden. Auf weiterreichende Wetterentwicklung reagieren meine Flows nicht, und ich habe nicht den Elan, ein allzu umfangreiches Wetter-Dashboard zu bauen. Außerdem ist mir eine weitere Vorhersage eh zu sehr Wahrsagerei.

Die Auswahl der Daten

Da man die API nicht zu oft abfragen darf (und auch nicht will), sollte man die Vorhersagedaten bis zum nächsten API-Abruf zwischenspeichern. Dazu muss man sich überlegen, welche Daten man benötigt.

Beim Stöbern im JSON-Datensatz bin ich darauf gekommen, dass die interessantesten Daten für mich im Bereich der „next_6_hours“ stecken. Sowohl der Symbolcode (= die Wetterlage), als auch die Min-/Max-Temperaturen und die Niederschlagsmenge werden für diese Zeit-Korridore mitgeliefert – und das anfangs stündlich.

{
        "time": "2021-08-30T15:00:00Z",
        "data": {
          "instant": {
            "details": {
              "air_pressure_at_sea_level": 1018.5,
              "air_temperature": 13.8,
              "cloud_area_fraction": 100,
              "cloud_area_fraction_high": 57.8,
              "cloud_area_fraction_low": 100,
              "cloud_area_fraction_medium": 28.1,
              "dew_point_temperature": 12.9,
              "fog_area_fraction": 0,
              "relative_humidity": 94.4,
              "ultraviolet_index_clear_sky": 0,
              "wind_from_direction": 355.9,
              "wind_speed": 2.8
            }
          },
          "next_12_hours": {
            "summary": {
              "symbol_code": "lightrain"
            }
          },
          "next_1_hours": {
            "summary": {
              "symbol_code": "rain"
            },
            "details": {
              "precipitation_amount": 0.3
            }
          },
          "next_6_hours": {
            "summary": {
              "symbol_code": "lightrain"
            },
            "details": {
              "air_temperature_max": 13.9,
              "air_temperature_min": 13.1,
              "precipitation_amount": 0.6
            }
          }
        }
      },

(Nicht verwirren lassen! Alle Zeitangaben sind in UTC)

Die Logik dabei ist:

Zum Zeitpunkt 30.08.2021 17:00 Uhr schaue ich mit „next_6_hours“ auf den Zeitraum zwischen 17:00 und 23:00 Uhr. Auch für den Zeitpunkt 30.08.2021 18:00 Uhr gibt es die „next_6_hours“, welche sich dann auf den Zeitraum 18:00 bis 0:00 Uhr beziehen.

Auf Kosten der Genauigkeit (die bei einer Vorhersage eh relativ ist) und zugunsten der Datensparsamkeit speichere und verarbeite ich also nur jeden sechsten Datensatz bis 42 Stunden in die Zukunft (+6 hours ergibt die 48 Stunden die ich betrachten möchte).

Für diese acht Datensätze (8*6=48 Stunden) habe ich acht Objekte im ioBroker angelegt (bei mir n0, n6, n12, n18, …), in welche ich die inzwischen von JSON in ein JavaScript Objekt konvertierten Daten der „next_6_hours“ als Objekt abspeichere.

Datenaufbereitung

Vor dem Speichern bereite ich die Daten noch ein wenig auf. Ich ordne den Symbolcodes zusätzlich die deutschen Bezeichnungen der Wetterlage zu, ergänze den Wochentag und bereite die Zeit auf, indem ich den Zeitraum errechne, für den diese Werte eine Gültigkeit haben (z.B. Dienstag, 06 – 12 Uhr). Zudem wird der Timestamp des Gültigkeitsstarts errechnet und mit abgelegt.

Nutzung der Wetterdaten

In definierten Regeln

Meine Vorhersage-Objekte (n0 – n42) sind immer aktuell, da ich die Wetterdaten einmal die Stunde abrufe. Somit weiß ich immer, im welchen Objekt welche Zeiträume stecken. Je nachdem wann ein Flow das Wetter abfragt, muss ich also nur den entsprechenden Betrachtungszeitraum festlegen.

Schaut die Rollo-Steuerung zum Beispiel um 9 Uhr, ob sie die südlichen Fenster abschatten sollte, dann schaut sie u.a. nach den Höchstwerten von „n0“, also wie warm es maximal zwischen 9 und 15 Uhr wird. Soll das Haus am Abend Bescheid sagen, dass die Fenster dank kühler Abendluft endlich geöffnet werden können, dann schaut es erst, wie warm es am nächsten und übernächsten Tag wird („n18“, „n42“).

Im Dashboard / UI

Natürlich schauen nicht nur automatische Steuerungen nach dem Wetter – ich gebe die Vorhersage auch im Dashboard aus. Neben der noch ausbaubaren tabellarischen Aufbereitung habe ich dafür inzwischen auch ein Temperatur-Vorhersage Chart.

Da ich die Daten direkt als JavaScript Objekt in das ioBroker Objekt abspeichere, kann ich auch direkt nach dem Auslesen des Objekts auf die Werte zugreifen.

msg.payload.details.air_temperature_max

Die tabellarische Ausgabe basiert somit auf einfachen Abfragen und Textausgaben, da alle Daten ja schon passend aufbereitet wurden.

Die entsprechenden ui_text – Nodes, welche durch CSS Klassen etwas gestaltet werden:

1. und 2. Spalte (n0)
3. Spalte (w0)

Für das Temperatur-Vorhersage-Diagramm müssen alle Objekte (iobroker list Node) geladen und daraus die passende Chart-JSON generiert werden. (siehe Flow Download)

Beispielhafte JSON zur Befüllung eines Node-RED Dashboard Charts

[{"series":["min °C","max °C"],"data":[[-0.6,-0.5,3.7,1.9,0.5,0.6,3.2,3.6],[1,2.5,5.8,3.8,1.3,2.1,5,4]],"labels":[1,7,13,19,1,7,13,19]}]

Da der abzubildende Zeitraum in der Zukunft liegt, können die Werte nicht anhand eines timestamps verortert werden, sondern brauchen feste Bezeichnungen. Hierbei habe ich mich für die Mitte des sechs Stunden Zeitraums entschieden.

Flows als Download

Wer es ausprobieren möchte, braucht im ioBroker unter 0_userdata.0.Vorhersage entsprechende Objekte und kann nach dem Import des folgenden Flows direkt loslegen.

httpStatic mit einer ioBroker Node-RED Installation konfigurieren

Inzwischen lässt sich httpStatic über die Konfiguration in ioBroker festlegen. (Adapterversion 3.2.0)

Um Bilder, welche man mit Node-RED verarbeitet hat, auch über Node-RED Dashboard z.B. in einem Template-Node ausgeben zu können, muss man diese irgendwo ablegen. Um nicht einen extra Webserver dafür betreiben zu müssen, bietet Node-RED in der settings.js die httpStatic Option an.

// When httpAdminRoot is used to move the UI to a different root path, the 
// following property can be used to identify a directory of static content 
// that should be served at http://localhost:1880/. 
//httpStatic: '/home/nol/node-red-static/',

Leider bietet der iobroker.node-red Adapter diese Einstell-Möglichkeit noch nicht an, sodass man selbst aktiv werden muss. Ein entsprechendes Issue liegt schon vor – wann es umgesetzt wird, ist aber noch nicht ersichtlich.

Achtung: diese Einstellung muss man nach jedem Update des iobroker.node-red Adapters erneut vornehmen.

Der Adapter iobroker.node-red generiert die settings.js des Node-RED über ein gleichnamiges Script, welches sich bei mir an diesem Ort befindet:
/opt/iobroker/node_modules/iobroker.node-red/settings.js

Die Option httpStatic ist hier aktuell hart auskommentiert – ändert man sie entsprechend, findet sie beim nächsten Speichern der Einstellungen im ioBroker auch Eingang in die generierte Node-RED settings.js.

(Bei mir läuft der Node-RED Prozess über den User iobroker – der entsprechende httpStatic-Ordner sollte also für diesen User les- und schreibbar sein, damit es funktioniert.)

Update 17.11.2021: Nachdem ich wieder selbst reingefallen bin, nochmal: Diese Einstellung muss man nach jedem Update des iobroker.node-red Adapters erneut vornehmen.

Update 18.01.2022: In einem der nächsten Releases des iobroker.node-red Adapters wird die Einstell-Möglichkeit vrstl. vorhanden sein.

Update 27.02.2022: Menno, ich möchte bitte einmal direkt dran denken, dass man diese Einstellung nach jedem Update des iobroker.node-red Adapters vornehmen muss.

Update 16.04.2022: Inzwischen lässt sich httpStatic über die Konfiguration in ioBroker festlegen. (Adapterversion 3.2.0)

Ein flexibler Benachrichtigungsbereich in Node-RED Dashboard

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)
Ein flexibler Benachrichtigungsbereich in Node-RED Dashboard weiterlesen

NodeRed UI Design

Das Design von NodeRed Dashboard braucht meiner Meinung nach noch ein wenig „Nachhilfe“. Hier die drei wichtigsten Styles meiner „Nachhilfe“, welche ich über ein ui_template Node in die <head> Section einfüge.

<style>
body {
        font-size:0.8em;
        background-color:#E6F7FF !important;
    }
md-card {
        border-bottom: 1px solid #F2F2F2;
    }
.nr-dashboard-cardpanel > p {
        margin-top: 3px !important;
        margin-bottom: 3px !important;
        min-height: 23px !important;
    }
</style>

Das Ergebnis:

UI-Performance – Diagramme von Flot

Auch wenn ich das Node-RED Dashboard sehr mag, eines ging mir gehörig auf den Keks: die Performance. Ursache dafür waren bei mir (diesmal) die Diagramme (chart), die den Ladevorgang eines Tabs teils um 10 Sekunden verlängerten. Daher bin ich auf die Diagramme von Flot umgestiegen, welche als iframe (template Node) eingebunden werden und damit asynchron laden. Das Ergebnis: Der Seitenaufbau ist schnell, das Diagramm braucht halt vielleicht noch kurz …

Um die Flot Diagramme an das Dashboard anzupassen nutze ich folgende Einstellungen:

Die Legende ist sicher je nach Anwendungsfall nötig oder nicht nötig. Auch die Höhe des Diagramms sollte je nach Art der Werte angepasst werden.

Das Ergebnis kann sich sehen lassen.

Ladevorgang läuft …
Geladen.

Die Nutzung von Flot hat noch weitere Vorteile:

  • auch nach einem Neustart von Node-RED habe ich noch „volle“ Diagramme
  • die Farben der Linien bleiben auch nach einem Neustart gleich
  • es lassen sich einfach zusätzliche Linien/Marken in die Diagramme einzeichnen
  • man kann die Kurven falls nötig leicht glätten

Wetterradar im Dashboard

Ich wollte gern ein Wetterradar im Dashboard haben. Doch an solche grafischen Darstellungen kommt man ja nicht immer ohne weiteres (kostenlos) ran – und der DWD bietet nur eine Drei-Länder-Darstellung.

Also musste etwas CSS her, um mittels eines Node-RED template Node nur einen Bildausschnitt darzustellen.

<div style="position:absolute;width: 296px;height: 250px; background-color: #BEBEBE;overflow: hidden;">
<img src="https://www.dwd.de/DWD/wetter/radar/radfilm_thu_akt.gif" style="position: absolute;top:-250px;	overflow: hidden;" />
</div>

Ich definiere einen Container mit overflow:hidden; sodass alles was nicht reinpasst auch nicht angezeigt wird. Mittels top und left kann ich das Bild entsprechend vertikal und horizontal verschieben.

Update 2021

Inzwischen bin ich auf das Radar eines anderen Wetterdienstes umgestiegen. Da dieser bei der Fremd-Verwendung nicht so freigiebig ist, wie der DWD, erwähne ich dessen Namen hier nicht und sagen nur so viel: mit einem simuliertem Website-Zugriff (http request-Node) und der Übermittlung des Cookies und des richtigen Referer-Headers beim Abruf der Radar-Grafik ist so manche unwirksame Sperre überwindbar. Die Grafik speichere ich dabei lokal zwischen (httpStatic).

Ein change-Node zwischen simuliertem Website-Zugriff und Grafik-Abruf

Shelly & Tasmota OTA Update anstoßen

Ab einer gewissen Zahl von WLAN Komponenten, wird es zur Herausforderung, die Firmware der Geräte auf dem aktuellen Stand zu halten. Zu diesem Zweck habe ich mir ein Tab in meinem Node-RED Dashboard eingerichtet.

Das grüne Herz zeigt, dass der Shelly online ist.

Die Abkürzung OTA steht für „Over The Air“, auf deutsch „über die Luft“. Es handelt sich dabei um eine drahtlose Methode, Updates auf Geräte zu übertragen.

Tasmota Firmware

Die aktuelle Tasmota Version, lässt sich durch einen automatisierten Abruf des GitHub-Feeds auslesen:

https://github.com/arendst/Tasmota/releases.atom

[{"id":"83b36f5b.0b8b","type":"xml","z":"c083a756.a72f18","name":"","property":"payload","attr":"","chr":"","x":370,"y":660,"wires":[["9fef7d11.d2a7c"]]},{"id":"390fe0f3.da48","type":"http request","z":"c083a756.a72f18","name":"","method":"GET","ret":"txt","paytoqs":false,"url":"https://github.com/arendst/Tasmota/releases.atom","tls":"","persist":false,"proxy":"","authType":"","x":710,"y":620,"wires":[["83b36f5b.0b8b"]]},{"id":"3a9bb870.4e0158","type":"inject","z":"c083a756.a72f18","name":"TASMOTA VERSION","topic":"","payload":"","payloadType":"date","repeat":"21600","crontab":"","once":true,"onceDelay":0.1,"x":400,"y":620,"wires":[["390fe0f3.da48"]]},{"id":"9fef7d11.d2a7c","type":"function","z":"c083a756.a72f18","name":"HIER Feed Version filtern","func":"msg.version = msg.payload[\"feed\"][\"entry\"][0][\"title\"][0];\nnode.status({text:msg.version});\nmsg.version = msg.version.replace(\"Tasmota v\",\"\");\nmsg.version = msg.version.replace(\"Tasmota\",\"\");\nmsg.version = msg.version.substring(0,6)\nreturn msg;","outputs":1,"noerr":0,"x":550,"y":660,"wires":[["e8c6fe5.7a082"]]},{"id":"e8c6fe5.7a082","type":"change","z":"c083a756.a72f18","name":"","rules":[{"t":"set","p":"tasmotaversion","pt":"global","to":"version","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":660,"wires":[["18e34f71.edaca1"]]}]

Die Tasmota Version steht immer aktuell in den MQTT Daten im ioBroker. Alternativ lässt sie sich über das Tasmota Command „Status“ auslesen.

Um bei einem Tasmota Geräte den Firmware-Update-Vorgang zu starten, reicht ein simpler HTTP-Aufruf von:

http://192.168.xx.xx/cm?cmnd=upgrade%201&user=BENUTZERNAME&password=PASSWORT

Auch die OtaUrl lässt sich per simplem HTTP-Aufruf anpassen. Leerzeichen oder Sonderzeichen in der OtaURL müssen durch ihre jeweiligen ASCII-Hex-Codes ersetzt werden. Dem Hex-Code muss ein Prozentzeichen % vorangestellt werden (z. B. %3B).

http://192.168.xx.xx/cm?cmnd=OtaUrl%20URL&user=BENUTZERNAME&password=PASSWORT

Mehr Infos zu Tasmota Commands

Shelly.cloud Firmware

Bei der Original Shelly Firmware, ist es ähnlich leicht, aber ein wenig anders. Die aktuell zur Verfügung stehende Firmware Version lese ich aus der JSON-Datei der offiziellen API:

https://api.shelly.cloud/files/firmware

Hier findet man auch die Download-Urls zu den Firmwares für die Shelly Geräte. Die aktuelle Firmware, hier für den Shelly 2.5, liegt zum Beispiel hier:

https://repo.shelly.cloud/firmware/SHSW-25_build.zip

Die Shellys verraten mit folgendem Kommando ihre aktuelle Version:

http://BENUTZERNAME:PASSWORT@192.168.xx.xx/status/

Den Update-Vorgang kann man mit folgendem Kommando starten:

http://BENUTZERNAME:PASSWORT@192.168.xx.xx/ota?update=1

Möchte man eine andere Shelly-Firmware-Version flashen, lässt sich hier auch eine Firmware-URL übergeben. Soweit ich weiß ist das Flashen auf eine alternative Firmware (Tasmota) so jedoch nicht möglich.

http://BENUTZERNAME:PASSWORT@192.168.xx.xx/ota?url=https://repo.shelly.cloud/firmware/SHSW-25_build.zip

Mehr Infos zur Common HTTP API der Shellys

Update: 25.10.2021

Seit Version 10.0.0 von Tasmota braucht man natürlich beim Filtern der Feed Version natürlich ein substring(0,6) – etwas unflexibel, sollte ich mal ändern.