Computer werden nicht grundlos auch Rechner genannt. Sie können rechnen. Eigentlich ist das sogar ihre grundlegendste Hauptaufgabe. Wer jetzt aber einen Node mit zwei Eingängen sucht, wo wir zwei verschiedene Werte eingeben können und nur den Operanten auswählen müssen damit am Ausgang das Ergebnis raus kommt, der sucht hier vergebens.
Wie wir im Kapitel Erste Flows und die grundsätzliche Funktionsweise von Node-RED bereits festgestellt haben, arbeitet Node-RED etwas anders. Alleine der Gedanken, das wir die Werte von zwei Eingängen miteinander verrechnen könnten, widerspricht der Tatsache das auf den Eingängen die Werte nicht dauerhaft anliegen, sondern zwischengespeichert werden. Hier wird nur eine Nachricht (msg) mit verschiedenen Attributen (.payload, .topic, etc.) übermittelt. Übermitteln heißt, ein Node sendet, der andere nimmt entgegen. Nach abgeschlossener Übertragung ist der Kanal wieder frei. Die Dauer einer solchen Übertragung liegt im Mikrosekundenbereich. Es ist somit nahezu ausgeschlossen das die Nachrichten aus zwei verschiedenen Quellen, auf zwei parallelen Eingängen, zeitgleich eintreffen.
Node-RED ist eine Entwicklungsumgebung und bewegt sich daher auch nahe an der tatsächlichen Funktionsweise eines digitalen Rechners. Digitale Rechner arbeiten nicht so, das sie zwei Eingänge, wo Werte anliegen, miteinander verrechnen. Das wäre analoges Rechnen: Zwei Kabel, auf jedem liegen 5 Volt an, werden zum addieren in Serie geschaltet und es kommen 10 Volt raus.
Digitale Rechner arbeiten anders, nämlich binär mit 0 und 1. Und das macht es erforderlich, das die zu verrechnenden Werte erstmal in den Arbeitsspeicher geschrieben werden müssen. Von da lädt die CPU die Werte in die internen Register, wo sie verarbeitet werden. Werte liegen also niemals auf den Datenleitungen dauerhaft an, sondern werden immer nur zwischen Speicherstellen verschoben.
Und genau so funktioniert das auch in Node-RED: Der Rechnen-Node bekommt eine Nachricht übermittelt, nennen wir ihn einfach mal Wert1, wobei eine Funktion im Empfänger-Node gestartet wird. Dieser Wert1 wird zuerst im temporären Speicher des Nodes empfangen und zusätzlich im Langzeitspeicher des Flows zwischengespeichert. Dann prüft die Funktion ob weitere Werte zum Arbeiten vorhanden sind. Wenn nicht, ist die Funktion schon wieder beendet, ohne ein Ergebnis auszugeben. Im Fall der Addition wird nur Wert1 selbst weiter geschickt. Anderenfalls werden die Werte miteinander verrechnet und das Ergebnis mit return.msg an den nächsten Node weitergesendet.
Funktion zur Addition
Nach der ganzen theoretischen Einleitung, kommen wir nun zur Funktion, die erforderlich ist um mehrere Werte zu addieren. Hier erstmal der Code:
let key = "Summe_Verbrauch"; // << Nur diesen Key ändern für jede Instanz
let topic = msg.topic;
let value = Number(msg.payload);
// Bestehende Werte für diesen Key holen oder neues Objekt erstellen
let sensorValues = flow.get(key) || {};
// Neuen Wert für das aktuelle Topic setzen
sensorValues[topic] = value;
// Sensorwerte im Flow-Context aktualisieren
flow.set(key, sensorValues);
// Summe aller gespeicherten Werte berechnen
let sum = Object.values(sensorValues).reduce((acc, val) => acc + val, 0);
// Ergebnis zurückgeben
msg.payload = sum;
return msg;Grundsätzlich passiert hier erstmal nichts anderes als in unserer Subtraktionsfunktion im vorletzten Kapitel. Nachricht empfangen, Nachricht speichern – sowohl kurz- wie auch langfristig über das Ende der Funktion hinweg, und dann weiter verarbeiten, wenn möglich. Wem das nicht geläufig ist, im Kapitel Erste Flows und die grundsätzliche Funktionsweise von Node-RED habe ich das alles bereits beschrieben.
Neu ist hier der vorletzte Abschnitt let sum = Object.values(sensorValues).reduce((acc, val) => acc + val, 0);, wo wir nochmal drauf eingehen müssen. Hier steckt nämlich einiges drin.
let kennen wir ja schon. Hier wird die Variable sum deklariert, der das Ergebnis der nachfolgenden Funktion direkt zugewiesen wird.
Object.values(sensorValues) bereitet ein Array aus den Werten im Objekt sensorValues vor, dem Objekt in welchem alle zuvor empfangenen Werte liegen, was anschließend von der .reduce() Funktion verarbeitet werden kann.
.reduce() ist im Grunde eine Schleife. Man könnte also auch schreiben:
let sum = 0; // << Festlegen des Startwertes
for (let key in sensorValues) {
sum += sensorValues[key]; // << Summe um gerade gewählten Wert erhöhen
}.reduce((acc, val) => acc + val, 0) macht nichts anderes. Es wird eine Schleife gestartet, die für jeden Wert in sensorValues einmal durchlaufen wird. Es wird festgelegt, dass das Zwischenergebnis bei jedem Durchlauf in der Variable acc gehalten wird und der damit zu verrechnende Werte in der Variable val. Dann wird bei jedem Durchlauf acc + val gerechnet; gestartet wird bei 0. Das ist alles was als Anweisung da steht.
Wer sich damit sicherer fühlt kann natürlich auch weiterhin die Schleife for() verwenden. Immerhin ist die Funktion hier einfacher zu lesen und nachzuvollziehen. .reduce() ist jedoch die modernere und kompaktere Version davon und nur so kann alles zusammen direkt hinter der Variablendeklaration let sum verpackt werden. Die Funktion ist jedoch grundlegend die gleiche.
Anwendung in Node-RED
Bauen wir die Funktion nun in Node-RED ein:


Wir ziehen uns also einen neuen function-Node in unseren Flow hinein. Diesen bearbeiten wir wieder mit einem Doppelklick auf ihn.
In den Einstellungen des Nodes müssen wir nun unseren Funktionscode eingeben:

Unserer Summenfunktion ist es egal wie viele Werte zum Summieren kommen. Das Object sensorValues wird immer nur erweitert, wenn eine Nachricht mit einem neuen Topic eintrifft. Ist das Topic bereits vorhanden, wird es überschrieben, der Werte somit aktualisiert. Bei jedem eintreffenden Wert wird die Funktion neu gestartet und damit alle vorhandenen Werte addiert.
Wahrscheinlich werden wir die Funktion mehrfach in einem Flow benötigen. Dann ist es nur wichtig das wir in der ersten Zeile den Werte der Variable Key ändern. Dieser muss einmalig im gesamten Flow sein, da er bestimmt wo die Werte im Langzeitspeicher des Flows hinterlegt werden. Verwenden zwei Funktionen den selben Key, überschreiben sich die Werte gegenseitig und wir bekommen in beiden Funktionen falsche Ergebnisse. Der Rest der Funktion kann beliebig oft, und für beliebig viele Eingangswerte, wiederverwendet werden.
Möchten wir komplexe Berechnungen aus einer Vielzahl von Werten durchführen, könnten wir so aber auch ganz gezielt von einer Funktion aus auf die Werte einer anderen Funktion zugreifen. Wir würden damit quasi die grafische Verkettung der Nodes umgehen. Das widerspricht zwar dem Grundgedanken von Node-RED, das der Weg der Nachricht grafisch nachvollziehbar ist, macht aber komplexe Berechnungen wesentlich einfacher, da eingehende Werte nicht an verschiedenen Stellen gleichzeitig ausgewertet, kategorisiert und gespeichert werden müssen.
Wir werden sicher noch auf die eine oder andere komplexe Berechnung zurück kommen, doch als nächstes stellen wir erstmal sicher, das unsere MQTT-Verbindung zum ioBroker wirklich zuverlässig und ausfallsicher arbeitet. Lest hierzu weiter im nächsten Kapitel:
MQTT Watchdog in Node-RED: Die Verbindung zum ioBroker absichern