Geräten das Internet sperren

Manchmal möchte man Geräten im lokalen Netzwerk den Internet sperren. Als Besitzer einer Fritz!Box geht das denkbar einfach auch übers SmartHome. Klar lässt sich auch direkt im WLAN-Router einiges einstellen, doch braucht man es flexibler, ist das Node „node-red-contrib-fritz“ dein Helfer.

Zuvor muss ggf. die Schnittstellen in der Fritz!Box aktiviert werden. Diese Möglichkeit findet Ihr unter Heimnetz – Netzwerk / weitere Einstellungen (FRITZ!OS: 07.29). Wie man an die nötigen Benutzerdaten kommt, wird hier auch gleich erklärt.

In der Fritz!Box muss der Zugriff für Anwendungen zugelassen werden. Dafür werden jedoch auch Benutzerdaten benötigt.

Der nötige Service über die TR-069 Schnittstelle der Fritz!Box ist urn:dslforum-org:service:X_AVM-DE_HostFilter:1 und die passende Action ist DisallowWANAccessByIP:

Über ein simples JavaScript Objekt übergibt man die benötigten Werte, dabei ist NewDisallow: 1 = Sperren, und 0 = Entsperren.

msg.payload = {
    "NewIPv4Address": "192.168.2.10",
    "NewDisallow": 1
};

return msg;

Über die Action GetWanAccessByIP kann auch der Status abgerufen werden. In diesem Fall enthält das Objekt nur die IP. Beliebige Automationen sind so denkbar. Und Kinder und Jugendliche sind von dieser Funktion sicher auch „begeistert“.

Eine Statusanzeige/Ampel fürs SmartHome

Manchmal möchte man Informationen aus dem SmartHome in der realen Welt darstellen. Gerade für Kinder oder weniger technisch versierte Familienmitglieder ist dies praktisch bis notwendig. In meinem Fall ist dies beim Warmwasserbestand der Fall. Im Winter, wenn das Warmwasser der Solarthermie nicht mehr reicht, wird das Warmwasser nur auf Bestellung erwärmt, um unnötiges Aufheizen und Abkühlen im Warmwasserspeicher zu beschränken.

Im Winter und auch in den Übergangszeiten kommt es dann aber immer wieder zu der Frage: ist Warmwasser da? Diese Frage beantwortet inzwischen eine kleine Ampel im Bad, welche sich die Information aus dem SmartHome – besser gesagt direkt aus Node-RED über eine HTTP Schnittstelle – bezieht.

Hardware

Technische Basis bildet ein Wemos D1 mini (ESP8266) mit einem RGB LED SHIELD. Dazu noch eine kleine Powerbank (ein Werbegeschenk) und ein Schalter, damit er keine Steckdose blockiert.

Der Wemos D1 mini ist extrem einsteigerfreundlich, da er direkt über MicroUSB geflasht werden kann.

Software

Für die Software nutze ich einen Arduino IDE Sketch, den ich für meine Zwecke mit der Zeit immer weiter verfeinert habe.

Und so funktioniert der Sketch grob

  • sobald der D1 mini Strom hat, verbindet er sich mit dem definierten WLAN, während des Verbindungsversuch blinkt die LED lila
  • nach der erfolgreichen Verbindung mit dem WLAN fragt er die eingestellte URL ab und bekommt als Antwort eine Zahl zwischen 1 und 3, die für eine der drei Farben der Ampel steht
  • die LED des Shields zeigt die entsprechende Farbe
  • bei Fehlern leuchtet die LED weiß
  • alle 30 Sekunden wird die Schnittstelle neu abgefragt und die Farbe aktualisiert

Die komplette Logik liegt beim SmartHome – der Wemos D1 mini ist bei mir ein reines Ausgabegerät. In meinem Fall bedeutet 3, dass genug Warmwasser da ist – die Ampel/LED zeigt grün. Bei 2 wird das Warmwasser langsam knapp – gelb. Und die 1 bedeutet – wer hätte es anders erwartet – eine kalte Dusche (die rote Ampel hat dich gewarnt).

Natürlich könnte man auch einen Sensorwert abfragen und die Zuordnungen von Farben zu Messwert den Wemos D1 mini machen lassen. Für mich erschien die Möglichkeit, die entsprechenden Schwellen später ohne neues Flashen der Ampel ändern zu können, komfortabler.

Schnittstelle zu Node-RED

Der Flow in Node-RED ist bei mir richtig öde, aber sei hier doch aufgrund der Vollständigkeit erwähnt. An anderer Stelle wird der Warmwasserbestand schon für Dashboard/UI ausgewertet. Daher greife ich auf die vorgefertigten Daten zurück und gebe sie nach dem Request auf das HTTP-in Node einfach aus.

Und hier mein Sketch

(Immer wenn im Code ein xx steht, musst du etwas „persönliches“ eintragen. An anderen Stellen sicher auch.)

#include <Adafruit_NeoPixel.h> 
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

#define PIN D2 //RGB LED Chip auf dem Digitalen PIN D2

const char* ssid = "xx";
const char* password = "xx";

IPAddress staticIP(192, 168, xx, xx); //ESP static ip
IPAddress gateway(192, 168, xx, xx);   //IP Address of your WiFi Router (Gateway)
IPAddress subnet(255, 255, 255, 0);  //Subnet mask
IPAddress dns(192, 168, xx, xx);  //DNS

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(1, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
    Serial.begin(115200);
    WiFi.hostname("xx");
    pixels.begin(); //initialisieren
    WiFi.config(staticIP, subnet, gateway, dns);
    WiFi.begin(ssid, password);
    WiFi.mode(WIFI_STA); 
    while(WiFi.status() != WL_CONNECTED){
        Serial.println(".");
        pixels.setBrightness(15); 
        pixels.setPixelColor(0, pixels.Color(255, 0, 255)); 
        pixels.show(); //Aktualisieren des NeoPixels
        delay(100);
        pixels.fill(0x000000);
        pixels.show();
        delay(100);
    }
  
    Serial.println(millis()); 
}

void loop() {
  if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status
     WiFiClient client;
     HTTPClient http;
     http.begin(client, "http://192.168.xx.xx:1880/arduino/ww");
     int httpCode = http.GET();
  
     if(httpCode == 200) {
        String payload = http.getString();
        if(payload == "1") {
           pixels.setPixelColor(0, pixels.Color(255, 0, 0)); 
           Serial.println("Kein Warmwasser da");
        }
        else {
           if(payload == "2") {
              pixels.setPixelColor(0, pixels.Color(255, 255, 0));         
              Serial.println("Wenig Warmwasser da");
           }
           else {
              pixels.setPixelColor(0, pixels.Color(0, 255, 0));          
              Serial.println("Genug Warmwasser da");
           }
        }
     }
     else {
        Serial.println("Error on HTTP request");
        pixels.setPixelColor(0, pixels.Color(255, 255, 255));          
     }
     
     http.end();
     pixels.show(); //Aktualisieren des NeoPixels
  }
  delay(30000);
      pixels.setPixelColor(0, pixels.Color(255, 0, 255)); 
      pixels.show(); //Aktualisieren des NeoPixels
      delay(300);
}

Hinweise

  • Die Vergabe einer festen IP beschleunigt den Verbindungsversuch um ein paar Millisekunden, kann man machen, muss man aber nicht.
  • Die LED des Shields ist sehr hell, ich habe die Helligkeit daher deutlich reduziert.
  • Über die serielle Schnittstelle (Baud 115200) ist der Sketch sehr gesprächig.

Bei einer Vielzahl von Sensoren den Akkustand prüfen

Inzwischen haben sich jede Menge Zigbee-Sensoren angesammelt. Diese halten mit einer Batterie echt lange durch. Viele zwei Jahre und mehr. Trotzdem geht die Batterie irgendwann mal zur Neige und davon möchte ich nicht überrascht werden. Also habe ich einen kleinen Flow in Node-RED, welcher die Akkustände jeden Tag einmal prüft und mich bei Bedarf benachrichtigt.

Um nicht für jeden Sensor neue Nodes ergänzen zu müssen, nutze ich die Möglichkeit, Nachrichten mit node.send(msg); asynchron zu versenden.

Sensoren zentral hinterlegen und einzeln durchgehen

var zigbeesensoren = [2,4,5,6,9,10,11,14,15,16,17,21,23,26,29,30,31,32,33,34,36,38,41,44,45,46,49,50,51,52,53,56,57,58];

zigbeesensoren.forEach(function(val) {
    msg.topic = "deconz.0.Sensors."+val+".battery";
    node.send(msg);
});

return;

Im Array zigbeesensoren sind einfach die abzufragenden Sensornummern (der ioBroker-Objekte) hinterlegt. Die forEach-Schleife sorgt dafür, dass das ganze Array durchgegangen wird und das richtige msg.topic für jeden Sensor gebaut und als Nachricht abgeschickt wird. So kann über „IoB read value“ jeder einzelne Batteriestand ausgelesen und in der Funktion „Batterie leer?“ geprüft werden. Wie gewohnt erstellt „UI Notification“ im Bedarfsfall die Benachrichtigung und leitet sie an das zentrale Benachrichtigungssystem.

Einen ähnlichen Aufbau kann man natürlich auch für beliebige andere Objekte nutzen, die vielfach ausgewertet werden müssen.

Dumme Haustechnik einfach smart machen

Alle Angaben ohne Gewähr. Alle Ideen, die aus diesem Artikel entstehen, auf eigene Gefahr.
Die beschriebenen Möglichkeiten sollte man als Mieter gar nicht erst lesen, oder direkt wieder vergessen.

Die Steuergerät meiner Gastherme und der Solarpumpe sind dumm. Natürlich kann man einiges einstellen, aber Schnittstellen, um sie smart zu machen, gibt es nicht. Zwar könnte ich für absurde Beträge zumindest der Gastherme mit einem Zusatzgerät ein wenig Vernetzung einhauchen, aber beim Blick auf die Kosten verzichtete ich darauf. Stattdessen trickse ich die Steuergeräte über ihre Sensoren einfach aus.

Was will ich steuern?

Am Anfang stand die Sorge um die Solarthermie-Anlage. Im Hochsommer bringt sie enorme Erträge und heizt den Warmwasserspeicher (leider unterdimensioniert) so weit und so schnell auf, dass die Gefahr besteht, dass sie in praller Mittagssonne aufgrund der nötigen Temperaturbegrenzung aufhört zu arbeiten, die Solarflüssigkeit sich nicht mehr bewegt und anfängt zu kochen. Das Steuergerät hat keine Funktion, dem zu begegnen. Also wollte ich die Möglichkeit, die Solaranlage bei Bedarf in der Nacht laufen zu lassen, um den Speicher abzukühlen, damit es am nächsten Tag nicht zu Problemen kommt.

Weiter ging es, als ich eigene Sensoren am Warmwasserspeicher hatte, und so in schönen Charts mit ansehen konnte, wie schnell das warme Wasser auch ohne Nutzung wieder kalt wird. Dies galt es effektiver zu gestalten und so wollte ich ein Bestellsystem für Warmwasser aufbauen, welches Komfort und Energieeffizienz unter einen Hut bringt. Das Bestellsystem benötigen wir sowieso eigentlich nur im Winter, weil die überwiegende andere Zeit im Jahr genug Warmwasser durch die Solarthermie-Anlage zusammenkommt.

Wie lässt sich die Steuerung umsetzen? (Theorie)

Die Steuerungen von Therme und Pumpe basiert auf Sensorwerten. Anhand dieser Sensorwerte werden bestimmte konfigurierbare Automatismen ausgelöst. Durch die Manipulation der Sensorwerte kann ich also bestimmen, wann ein Automatismus ausgelöst werden soll, und wann nicht. Voraussetzung dafür ist, dass ich die wahren Sensorwerte kenne, damit ich selbst den Überblick habe.

Da es mir nicht möglich war, die Sensorwerte aus den Hausgeräten auszulesen, habe ich eigene Sensoren (DS18B20) am Speicher und der Solarstation ergänzt und mit den Sensoren der Hausgeräte entsprechend abgeglichen.

Beispiel Solarthermie Steuerung: Die Solarthermie-Steuerung vergleicht die Temperatur im Speicher mit der Temperatur auf dem Dach. Ist die Temperatur auf dem Dach um ein konfiguierbares Offset höher als die im Speicher, startet die Steuerung die Pumpe. Wenn ich also mitten in der Nacht sage: „Hallo Steuerung, im Speicher sind es nur 5°C“, dann ist im Hochsommer die Temperatur auf dem Dach definitiv höher und die Solarpumpe bringt fleißig überschüssige Wärme zurück auf das Dach.

Beispiel Gastherme / Warmwasser: Die Steuerung der Gastherme hält den Speicher in einem konfigurierbaren Zeitraum auf einer gewissen Temperatur. Wenn ich also dem Steuergerät vorspiegele, dass die Speichertemperatur über der Zieltemperatur liegt, sieht sie keinen Handlungsbedarf. Schalte ich auf den realen Sensor um, greift wieder die eingebaute Steuerung und das Aufheizen des Speichers kann beginnen.

Wie lässt sich die Steuerung in der Praxis umsetzen?

Für die technischen Umsetzung muss man sich erst einmal vergegenwärtigen, wie ein Temperatursensor technisch funktioniert. Je nach Temperatur liefert der Sensor einen gewissen elektrischen Widerstand. Dieser unterscheidet sich von Modell zu Modell. Weiter hilft hier das Handbuch des Hausgeräts, in dessen Anhang meist eine Tabelle der Widerstände und zugehörigen Temperaturen zu finden ist. Teils muss man dafür das Installateurs-Handbuch bemühen, aber das findet man auch meist online.

Widerstände des Speichertemperaturfühlers einer Buderus GB172-14

Der erste Schritt ist also den passenden Widerstand für die beabsichtigte Temperatur zu besorgen – natürlich müssen es dabei nicht die exakten Werte aus der Tabelle sein, da es egal ist, ob es bspws. 57,0 oder 57,2°C sind. Der zweite Schritt bringt ein wenig Nervenkitzel ins Spiel, wenn man das Sensorkabel des Hausgeräts möglichst mit gut Luft in beide Richtungen einfach durchschneidet (Gerät vorher abschalten!) – im Ernstfall beeinflusst eine Lüsterklemme den Widerstand/Sensorwert aber auch nicht.

Mit einem herkömmlichen Relaisboard an den GPIO des Raspberry PI richtig verkabelt, hat man dann die Möglichkeit, zwischen echtem Sensorwert und vorbestimmten Sensorwert hin und her zu schalten. Meine Geräte stören sich daran nicht. Nach ausgiebigen Tests landete das ganze dann noch auf einer Platine.

Links das Relaisboard, rechts die Platine mit den Anschlüssen für die Sensorkabel und den Widerständen.

Dashboard / UI

Im Winter bemühen wir also jetzt das Dashboard, wenn es um Warmwasser geht.

Im Dashboard habe ich nach anfänglich komplizierten Überlegungen die Steuerelemente sehr simplifiziert. Für Heute, Morgen und Übermorgen gibt es je die Möglichkeit, Warmwasser für den Morgen, Mittag oder Abend im Voraus zu bestellen. Um Mittnacht rutscht natürlich Morgen ➡ Heute und Übermorgen ➡ Morgen. Weiterhin gibt es neben der aktuellen Speichertemperatur auch eine Bewertung der Lage, inkl. Prognose, wie lange der aktuelle Warmwasserbestand noch für eine ausgiebige Dusche reicht. Für die Berechnung der Prognose habe ich Langzeitdaten über die Abkühlung des Speichers erhoben und ausgewertet.

„Nur halb aufheizen“ kommt zum Einsatz, wenn vrstl. nur wenig Warmwasser benötigt wird. Hier schaltet das Relais schon vor erreichen der Zieltemperatur auf den Fake-Wert um und die Therme sieht ihre Arbeit als erledigt an und hört auf zu heizen.

Auch kann man „Warmwasser jetzt!“ bestellen, dann dauert es durch die Leistung der Gastherme nur gut fünf Minuten, bis man duschen kann. Hat man eine trägere Wärmeerzeugung, ist das natürlich bei allen Überlegungen zu berücksichtigen.

Tipps / Warnhinweise

  1. Die Verkabelung am Relais sollte so erfolgen, dass im Fehlerfall (Raspberry Pi kaputt, Relaisboard ohne Strom, etc.) der reale Wert an das Hausgerät gemeldet wird. So kann man zur Not auch mit „Stecker ziehen“ beim Raspberry Pi den „Normalzustand“ wiederherstellen.
  2. Man sollte versuchen, alle Eventualitäten zu durchdenken. Hat man zum Beispiel eine thermische Desinfektion in seiner Warmwasserbereitung aktiviert, hat man neben dem beeinflussten Sensor-Trigger auch einen zeitlichen Trigger, der das Heizen startet. Hat die Therme in diesem Fall keine realen Speichertemperaturen, heizt sie im Ernstfall ohne Ende.
  3. Wiederum ist es ratsam, einmal in der Woche gut durchzuheizen, um Kontaminationen zu verhindern.
  4. Man sollte die Speichertemperaturen überwachen und mit entsprechenden Regeln reagieren. Steigt die Temperatur im Speicher zu stark an, wird bei mir z.B. automatisch der korrekte Sensorwert der Gastherme bereitgestellt.
  5. Es kann günstig sein, einen zweiten Indikator zur Überwachung mit einzubeziehen, zum Beispiel einen Zwischenstecker mit Power Measurement. So kann man am Stromverbrauch des Gerätes ablesen, wie der Status des Hausgerätes ist und ob Steuerbefehle ausgeführt werden.

ioBroker out / Type / ACK

Was hat es mit dem Type des „ioBroker out“ – Nodes in Node-RED auf sich?

value? command? Was wie wo?

Nachdem ich in den Anfängen meiner SmartHome-Experimente festgestellt hatte, dass Adapter wie ioBroker.deconz oder ioBroker.sonoff die Befehle aus Node-RED nur ausführen, wenn man den Type auf „command“ stellt, machte ich das bis jetzt eigentlich immer so. Dass die Objekte im ioBroker dann teils Rot waren, ignorierte ich, funktionierte ja. Nach einigen Recherchen – ausgelöst durch einen Kommentar zu einem anderen Blogbeitrag – weiß ich jetzt, dass dies nicht unbedingt richtig war.

Was macht der Type im Node „ioBroker out“?

Der Type setzt das ioBroker Attribut ACK (ACKnowledgement = Bestätigung). Dabei steht „value“ für ACK = true, und „command“ für ACK = false.

Warum sollte man ack = false / command setzen?

Viele Adapter verarbeiten Änderungen von Adapter-States, die von anderen Adaptern geändert wurden, nur dann, wenn diese ACK=false haben (auf der Admin-Seite rot dargestellt).

Also werden Werte beispielsweise nur an ein Gerät geschickt, wenn ACK = false ist. Dieses quittiert den Empfang des Befehls auf ihre protokollspezifische Weise, was dann durch ein ACK = true im ioBroker vermerkt wird.

Wann sollte man ack = true / value setzen?

Nutzt man ein State/Objekt nur zum Speichern von Werten (zum Beispiel in 0_userdata), so nutzt man Type = value. Das mache ich jetzt auch häufiger.

Übrigens: In Node-RED hat man im „ioBroker in“ – Node die Wahl, ob nur ACK = true verarbeitet werden soll.

Eine eigene PHP-Web-API bauen

Um Daten aus dem SmartHome auf eine eigene Website zu bringen, habe ich mir eine Authentifizierung gebaut, die es mir erlaubt, per POST Request Daten bereitzustellen – und zwar nur mir. Einfach nur ein festes Passwort war mir da zu unsicher. Daher habe ich eine zeitbasierte Komponente mit eingebaut.

Zum Einsatz kommt seitens Node-RED node-red-contrib-cryptography.

Ich generiere einen Auth Token, dieser basiert auf der aktuellen Zeit und einem geheimzuhaltenden Passwort, quasi einem Salt.

msg.payload = Math.round(Date.now()/10000) + "dasGeheimePasswort1234!";
msg.headers = {};
msg.headers["Content-Type"] = "application/x-www-form-urlencoded";
return msg;

Beim Token habe ich mich für eine Gültigkeit von 10 Sekunden (daher geteilt durch 10.000) entschieden, dann sind minimale Ungenauigkeiten bei den Uhrzeiten zwischen SmartHome und Webserver nicht so dramatisch. Der Payload wird dann durch die hash-Funktion sha256 gehasht. Anschließend wird der POST Request mit Daten bepackt und der http request abgeschickt.

msg.payload = {
"auth": msg.payload,
"temperatur": msg.temperatur
};
return msg;

Um dem POST Request zu empfangen und zu authentifizieren muss die PHP Gegenstelle den gleichen Token generieren und mit dem mitgeschickten Token abgleichen. Dazu braucht die Gegenstelle natürlich auch den auf 10 Sekunden gerundeten Timestamp und das gleiche geheime Passwort, wie es im Node-RED hinterlegt ist.

<?php
   if($_POST["auth"] == hash('sha256',(round(time()/10))."dasGeheimePasswort1234!")) {
echo "true";
}
?>

Achtung! PHP arbeitet bei den Timestamps in Sekunden, während JavaScript in Millisekunden arbeitet. Daher ist hier nur eine Division durch 10 nötig.

Statt dem echo „true“; kann man natürlich beliebiges mit den mitgeschickten Werten anfangen.

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.

Diese Node-RED Nodes nutze ich

Die Auswahl an Node-RED Nodes ist riesig und dadurch ziemlich unübersichtlich. Daher finde ich es immer spannend, welche Nodes tatsächlich von anderen Nutzern genutzt werden. Um anderen genau diesen Einblick zu ermöglichen – und den ein oder anderen vielleicht auf Ideen zu bringen, hier meine Liste, die ich sicher in unregelmäßigen Abständen aktualisieren werde.

Stand: 22.01.2022

  • node-red
  • node-red-contrib-astrodata – Nodes for calculation of sun and moon position, sunset, sunrise etc.
  • node-red-contrib-combine – Node-RED Nodes that output combinations of consecutive incoming messages
  • node-red-contrib-cryptography – Simple cryptography, hash with SHA-256 and RIPEMD-160
  • node-red-contrib-ical-events – NodeRed calender event adapter
  • node-red-contrib-influxdb – Node-RED nodes to save and query data from an influxdb time series database
  • node-red-contrib-os – Node-Red nodes for obtaining cpu system information.
  • node-red-contrib-presenchecker – Checks fritzbox, whether a given device (by mac address) is online or offline
  • node-red-contrib-schedex – Scheduler for node-red which allows you to enter suncalc events (e.g. goldenHour).
  • node-red-dashboard – A set of dashboard nodes for Node-RED
  • node-red-node-email – Node-RED nodes to send and receive simple emails.
  • node-red-node-pi-gpio – The basic Node-RED node for Pi GPIO
  • node-red-node-ping – A Node-RED node to ping a remote server, for use as a keep-alive check.
  • node-red-node-rbe – A Node-RED node that provides report-by-exception (RBE) and deadband capabilities.
  • node-red-node-twitter – A Node-RED node to talk to Twitter
  • node-red-node-xmpp – A Node-RED node to talk to an XMPP server

json auf Gültigkeit prüfen

Es ist mal wieder soweit – die Schnittstelle von opensensemap.org streikt. Da dies immer mal wieder (nicht nur dort) vorkommt, habe ich vor einiger Zeit eine Prüfung des JSON-Response eingebaut, sodass ich diese Fehler abfangen kann.

Damit ich dieses Code-Snippet nicht immer suchen muss, sei es hier archiviert. Vielleicht hilft es jemandem anders ja auch weiter.

var json_valid = true;

try
{
   var json = JSON.parse(msg.payload);
}
catch(e)
{
  json_valid=false;
}
finally {
    if(json_valid === true) {
        node.status({fill:"green",shape:"dot",text:"Okay"});
        return [msg,null];
       
    }
    else {
        node.status({fill:"red",shape:"dot",text:"Error"});
        return [null,msg];

    }
}

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)