In der Welt der Automatisierung und des Internet der Dinge (IoT) hat sich Node-RED als ein unschätzbares Werkzeug etabliert. Seine visuelle Programmierumgebung ermöglicht es Entwicklern und Nicht-Entwicklern gleichermaßen, komplexe Datenflüsse mit minimalem Aufwand zu erstellen. Doch inmitten der Vielzahl leistungsfähiger Knotenpunkte gibt es einen, der oft unterschätzt wird, dessen wahre Macht aber weit über seine scheinbar einfachen Funktionen hinausgeht: die Change-Funktion (oder Change-Node).
Auf den ersten Blick mag die Change-Funktion wie ein simples Werkzeug erscheinen, das lediglich dazu dient, Werte in Nachrichten zu setzen, zu verschieben oder zu löschen – eine Art „Suchen und Ersetzen” für Ihre Datenströme. Doch wer tiefer gräbt, entdeckt eine Welt der Flexibilität, insbesondere durch die Integration von JSONata. Dieser Artikel führt Sie durch die Grundlagen und fortgeschrittenen Techniken der Node-RED Change-Funktion und zeigt Ihnen, wie Sie sie von einem einfachen Hilfsmittel zu einem zentralen Bestandteil Ihrer intelligenten Datenverarbeitung machen können.
Die Grundlagen der Change-Funktion verstehen
Die Change-Funktion ist primär dafür konzipiert, Eigenschaften des eingehenden Nachrichtenobjekts (msg
) zu manipulieren. Jede Nachricht in Node-RED ist ein JavaScript-Objekt, und die Change-Funktion bietet eine deklarative Möglichkeit, dessen Struktur und Inhalt zu verändern, bevor die Nachricht an den nächsten Knoten weitergeleitet wird.
Im Kern bietet die Change-Funktion vier grundlegende Operationen:
- Setzen (Set): Dies ist die am häufigsten verwendete Operation. Sie ermöglicht es Ihnen, eine Eigenschaft des
msg
-Objekts (z.B.msg.payload
,msg.topic
) auf einen bestimmten Wert zu setzen. Dieser Wert kann ein fester Wert (String, Zahl, Boolean), eine andere Eigenschaft aus demmsg
-Objekt, eine Flow-Variable (flow
), eine Global-Variable (global
) oder – und hier wird es spannend – ein JSONata-Ausdruck sein. - Ändern (Change): Diese Operation ist der klassische „Suchen und Ersetzen”-Mechanismus. Sie erlaubt es Ihnen, einen bestimmten String-Wert innerhalb einer Eigenschaft zu finden und durch einen anderen String zu ersetzen. Dies kann auch mit regulären Ausdrücken geschehen, was eine hohe Flexibilität bei der String-Manipulation bietet.
- Löschen (Delete): Mit dieser Option können Sie unerwünschte Eigenschaften aus dem
msg
-Objekt entfernen. Das ist nützlich, um Nachrichten schlanker zu halten oder sensible Daten zu entfernen. - Verschieben (Move): Die „Verschieben”-Operation dient dazu, eine Eigenschaft umzubenennen oder an eine andere Position innerhalb des
msg
-Objekts zu verschieben. Sie können beispielsweisemsg.oldValue
zumsg.newValue
umbenennen odermsg.data.sensorA
nachmsg.payload
verschieben.
Der Schlüssel zur wahren Meisterschaft liegt jedoch nicht in diesen einfachen Operationen allein, sondern in der intelligenten Nutzung von JSONata-Ausdrücken als Wertquellen. Diese ermöglichen es Ihnen, komplexe Transformationen und bedingte Logik direkt im Change-Knoten zu implementieren.
Tiefer eintauchen: JSONata in der Change-Funktion
JSONata ist eine leistungsstarke Abfrage- und Transformationssprache für JSON-Daten. Sie ist speziell dafür konzipiert, JSON-Dokumente einfach zu filtern, zu transformieren und zu aggregieren. Die Integration von JSONata in die Node-RED Change-Funktion hebt deren Fähigkeiten auf ein völlig neues Niveau und macht sie zu einem unverzichtbaren Werkzeug für fast jede Datenverarbeitungsaufgabe.
Wenn Sie im Change-Knoten den Typ „JSONata” für den Wert auswählen, öffnet sich ein Editor, in dem Sie komplexe Ausdrücke definieren können. Das eingehende msg
-Objekt ist hierbei der Kontext, auf den Sie zugreifen können.
Praktische Anwendungsfälle und Beispiele mit JSONata:
1. Bedingte Logik und Entscheidungsfindung
Mit JSONata können Sie if-else
-Anweisungen implementieren, um Werte basierend auf bestimmten Bedingungen zu setzen. Dies ist weit mächtiger als ein einfaches Suchen und Ersetzen.
- Beispiel: Status-Zuweisung basierend auf einem Schwellenwert
Angenommen, Sie erhalten einen Temperaturwert inmsg.payload.temperature
und möchten einen Status setzen:
msg.payload.status
auf den Wert:$$.payload.temperature > 25 ? "HOT" : "NORMAL"
(Hierbei verweist$$
auf das gesamtemsg
-Objekt.$$.payload.temperature
ist eine gängige Art, auf Eigenschaften des eingehendenmsg
-Objekts zuzugreifen, während$
oft den aktuellen Kontext innerhalb einer Schleife oder eines Sub-Ausdrucks bezeichnet.)
2. Datenaggregation und Transformation
JSONata ist hervorragend geeignet, um aus komplexen, verschachtelten JSON-Strukturen genau die benötigten Daten zu extrahieren und in ein neues Format zu bringen.
- Beispiel: Reformatierung von Sensor-Daten
Sie erhalten Daten wiemsg.payload = { "device_id": "abc", "sensors": [{ "type": "temp", "value": 22 }, { "type": "hum", "value": 60 }] }
. Sie möchten daraus ein flacheres Objekt machen, das nur die Temperatur enthält:
msg.payload
auf den Wert:{"id": $$.payload.device_id, "temperature": $$.payload.sensors[type='temp'].value[0]}
Dieser Ausdruck filtert das Sensor-Array nach dem Typ „temp” und extrahiert dessen Wert. - Beispiel: Aggregation von Werten
Wenn Sie mehrere numerische Werte haben und deren Summe benötigen:
msg.payload.total
auf den Wert:$sum([$$.payload.value1, $$.payload.value2, $$.payload.value3])
3. Arbeiten mit Arrays
JSONata bietet eine Fülle von Funktionen zur Bearbeitung von Arrays, wie Filtern, Mappen, Reduzieren oder das Extrahieren bestimmter Elemente.
- Beispiel: Filtern einer Liste von Objekten
Sie haben eine Liste von Geräten inmsg.payload.devices
und möchten nur die „online” Geräte:
msg.payload.onlineDevices
auf den Wert:$filter($$.payload.devices, function($v){$v.status = "online"})
- Beispiel: Transformation jedes Elements in einem Array (Map)
Sie möchten jedem Objekt in einem Array eine neue Eigenschaft hinzufügen:
msg.payload.transformedList
auf den Wert:$map($$.payload.items, function($v){$v ~> | $ | {'processedAt': $now()} |})
(Hier wird jedem Element ein Zeitstempel hinzugefügt.)
4. Erweiterte String-Manipulationen
Über das einfache Suchen und Ersetzen hinaus ermöglicht JSONata komplexe String-Operationen wie das Verketten, Extrahieren von Substrings oder das Formatieren.
- Beispiel: Generieren einer formatierten Nachricht
msg.payload.message
auf den Wert:"Alarm! Device " & $$.payload.deviceID & " exceeded threshold with " & $string($$.payload.value) & " at " & $fromMillis($now(), "[H01]:[m01]:[s01]")
- Beispiel: Teile eines Strings extrahieren
Wennmsg.payload.data = "sensor_room1_temp"
und Sie nur „room1” extrahieren möchten:
msg.payload.room
auf den Wert:$$.payload.data ~> $match(/sensor_(.*)_temp/).[1]
(Hier wird ein regulärer Ausdruck verwendet, um den mittleren Teil zu erfassen.)
5. Mathematische Operationen und Typkonvertierungen
Direkte Berechnungen und das Umwandeln von Datentypen sind ebenfalls mühelos möglich.
- Beispiel: Umrechnung von Temperatur
msg.payload.tempFahrenheit
auf den Wert:($$.payload.tempCelsius * 9/5) + 32
- Beispiel: Sicherstellen eines numerischen Wertes
msg.payload.numericValue
auf den Wert:$number($$.payload.stringValue)
6. Zugriff auf Flow- und Global-Kontexte
JSONata kann direkt auf den Flow-Kontext und den Global-Kontext zugreifen, was die Nutzung von Konfigurationsvariablen oder persistenten Daten extrem vereinfacht.
- Beispiel: Konfigurationswert aus dem Flow-Kontext verwenden
msg.payload.threshold
auf den Wert:$flowContext('min_threshold')
- Beispiel: Globalen Zähler inkrementieren und setzen
Dies ist etwas komplexer, da der Change-Knoten keine direkten side effects auf den Kontext hat, aber Sie können einen Wert aus dem Kontext lesen, manipulieren und dann *in einen anderen Change-Knoten* schreiben oder im Function-Knoten setzen. Für das Lesen:
msg.payload.currentCounter
auf den Wert:$globalContext('processing_counter')
7. Fehlerbehandlung und Standardwerte
Sie können Standardwerte definieren, falls eine Eigenschaft nicht vorhanden oder null
ist, um robuste Flows zu gewährleisten.
- Beispiel: Standardwert für eine fehlende Eigenschaft
msg.payload.userDisplayName
auf den Wert:$$.payload.user.name or "Gast"
(Wennuser.name
nicht existiert, wird „Gast” zugewiesen.)
Best Practices und Tipps für die Change-Funktion
Um das volle Potenzial der Change-Funktion auszuschöpfen und wartbare, effiziente Flows zu erstellen, sollten Sie einige bewährte Methoden beachten:
- Fokus bewahren: Versuchen Sie, jeden Change-Knoten eine spezifische, logische Aufgabe erledigen zu lassen. Vermeiden Sie es, eine übermäßig lange Kette von JSONata-Ausdrücken in einem einzigen Knoten zu erstellen, die mehrere unabhängige Transformationen durchführt. Es ist oft besser, mehrere Change-Knoten hintereinanderzuschalten, von denen jeder eine klare, atomare Aufgabe erfüllt.
- Lesbarkeit und Kommentare: Obwohl der JSONata-Editor in Node-RED keine direkten Kommentare unterstützt, können Sie bei komplexeren Ausdrücken die Logik in einem begleitenden Textfeld des Knotens oder mit einem separaten Kommentar-Knoten dokumentieren. Halten Sie die Ausdrücke so prägnant wie möglich.
- Ausgiebiges Testen mit dem Debug-Knoten: Nutzen Sie den Debug-Knoten intensiv, um die Nachrichten nach jeder Change-Funktion zu überprüfen. Dies hilft Ihnen, Fehler in Ihren JSONata-Ausdrücken schnell zu identifizieren.
- Kontext-Nutzung: Verstehen Sie den Unterschied zwischen Flow-Kontext und Global-Kontext. Flow-Kontext ist ideal für Daten, die nur innerhalb eines bestimmten Flows relevant sind, während Global-Kontext für applikationsweite Konfigurationen oder Zähler verwendet wird.
- Performance: JSONata ist sehr effizient. In den meisten Fällen müssen Sie sich keine Sorgen um die Performance machen. Erst bei extrem hohen Datenmengen oder sehr komplexen, rekursiven Transformationen sollten Sie über Alternativen nachdenken.
- Sicherheit: Achten Sie darauf, welche Daten Sie transformieren oder manipulieren. Stellen Sie sicher, dass keine sensiblen Informationen unbeabsichtigt exponiert werden.
Change-Funktion vs. Function-Node: Wann nutze ich was?
Eine häufig gestellte Frage ist, wann man die Change-Funktion und wann die flexiblere Function-Node (die JavaScript-Code ausführt) verwenden sollte. Hier ist eine einfache Faustregel:
- Nutzen Sie die Change-Funktion, wenn:
- Sie deklarative Operationen durchführen möchten (Werte setzen, ändern, löschen, verschieben).
- Ihre Logik hauptsächlich auf Abfragen und Transformationen von JSON-Daten basiert.
- Sie keine komplexen Schleifen, externe Bibliotheken oder fortgeschrittene Side-Effects benötigen.
- Sie eine visuell klarere und oft weniger fehleranfällige Lösung bevorzugen.
- Sie Bedinungen, Aggregationen und Formatierungen mit JSONata umsetzen können.
- Nutzen Sie die Function-Node, wenn:
- Sie imperative Programmierung benötigen (z.B. komplexe Schleifen über Daten, dynamische Logik, die nicht gut in JSONata abgebildet werden kann).
- Sie auf externe JavaScript-Bibliotheken zugreifen müssen.
- Sie fortgeschrittene Fehlerbehandlung oder Logging implementieren möchten.
- Sie sehr spezifische Side-Effects auf das Flow- oder Global-Kontext außerhalb einfacher
set
Operationen benötigen. - Die Logik in JSONata zu kompliziert oder unübersichtlich werden würde.
Beginnen Sie immer mit der Change-Funktion. Wenn Sie feststellen, dass Ihre Anforderungen die Fähigkeiten von JSONata übersteigen oder die Implementierung zu umständlich wird, dann wechseln Sie zur Function-Node.
Häufige Fallstricke und deren Vermeidung
Selbst erfahrene Node-RED-Nutzer können auf einige Stolpersteine stoßen:
- Tippfehler in Pfaden: Ein kleiner Fehler wie
msg.payload.Value
stattmsg.payload.value
kann dazu führen, dass Ihr Ausdruck nicht funktioniert. Überprüfen Sie immer die Pfade sehr genau. - Falsche JSONata-Syntax: Klammern, Anführungszeichen, Kommas – ein kleiner Fehler kann den gesamten Ausdruck ungültig machen. Nutzen Sie einen externen JSONata-Tester (z.B. jsonata.org/expression) für komplexe Ausdrücke.
- Annahme von Datentypen: JSONata ist typensensitiv. Wenn Sie eine Zahl erwarten, aber einen String erhalten, können Operationen fehlschlagen. Nutzen Sie Funktionen wie
$number()
oder$string()
zur expliziten Typkonvertierung. - Modifikation von Objekten per Referenz: Wenn Sie ein Objekt aus
msg.payload
lesen und in einem neuenmsg.payload
setzen, aber nicht explizit klonen (z.B. mit$$.payload ~> $merge({})
, um ein neues Objekt zu erstellen), könnten Sie das ursprüngliche Objekt an anderer Stelle im Flow unbeabsichtigt ändern. Seien Sie sich dessen bewusst.
Fazit
Die Node-RED Change-Funktion ist weit mehr als ein einfaches „Suchen und Ersetzen”-Werkzeug. Durch die geschickte Integration von JSONata wird sie zu einem mächtigen Motor für die Datenverarbeitung und -transformation, der Ihnen ermöglicht, komplexe Logiken deklarativ und effizient in Ihren Flows umzusetzen. Von einfacher Bedingungsprüfung über anspruchsvolle Datenaggregation bis hin zu flexiblen Array-Manipulationen – die Möglichkeiten sind nahezu unbegrenzt.
Indem Sie die Grundlagen verstehen und sich mit der Syntax und den Funktionen von JSONata vertraut machen, können Sie Ihre Node-RED-Fähigkeiten erheblich erweitern und Flows erstellen, die nicht nur leistungsfähig, sondern auch elegant und leicht wartbar sind. Experimentieren Sie, testen Sie und entdecken Sie die volle Kraft dieser unterschätzten Funktion. Sie werden feststellen, dass das Meistern der Change-Funktion ein entscheidender Schritt ist, um ein wahrer Node-RED-Experte zu werden.