Node-RED ist ein visuelles Tool, mit dem sich IoT-Prototypen erstellen lassen, indem es Onlineservices, APIs und Hardwaregeräte miteinander grafisch verbindet. Dieser Artikel geht zunächst auf Node-RED ein, zeigt, wie sich ein einfacher „Flow“ anlegen lässt, Node-Module installiert und schließlich eigene Nodes geschrieben werden.
Das Open-Source-Tool Node-RED [1] steht unter der Apache-2.0-Lizenz und wird hauptsächlich von IBM Emerging Technologies entwickelt. Serverseitig basiert es auf Node.js, während der Browser die grafische Oberfläche darstellt. Mit Node-RED können in einem Flow verschiedene Eingabe-, Ausgabe- und Processing Nodes miteinander verbunden werden. Dadurch lassen sich Daten verarbeiten, verschiedene Dinge kontrollieren und steuern.
Die Node-RED-Oberfläche besteht aus drei Hauptbereichen, dem Node-Panel, dem Sheets-Panel und dem Info- und Debug-Panel. Das Node-Panel ist auf der linken Seite und enthält eine Liste der installierten und zur Verfügung stehenden Nodes. Diese werden in jeweilige Kategorien, wie zum Beispiel Eingabe- und Ausgabekategorien, gruppiert. Das Sheets-Panel in der Mittel definiert die so genannten Flows. Hier werden durch Drag and Drop die Nodes vom Node-Panel hineingezogen, miteinander verbunden und konfiguriert. Das Info- und Debug-Panel auf der rechten Seite ist in Tabs unterteilt. Der Infobereich zeigt für einen ausgewählten Node dessen Beschreibung an. Im Debug-Panel werden die Ausgaben der Debug Nodes oder interne Fehler aufgelistet.
Node-RED benötigt Node.js und den Package-Manager npm. Sie müssen auf dem System installiert sein. Eine ausführliche Installationsanleitung ist auf der Getting-Started-Seite von Node-RED [2] zu finden. Nach der Installation und dem Start ist Node-RED standardmäßig im Browser unter http://localhost:1880 erreichbar.
Auf der aktuellen Rasp‑bian Version, bei der es sich um ein Debian-Betriebssystem für den Raspberry Pi handelt, ist Node-RED bereits vorinstalliert. Node-RED bietet auch eigene Nodes für den Raspberry Pi [3] an, um zum Beispiel auf die GPIO-Pins des Raspberry Pi zuzugreifen.
Node-RED Nodes verschicken untereinander Message-Objekte, bei denen es sich um JSON-Daten handelt, die einen payload-Parameter enthalten (siehe Screenshot im Debug-Panel).
Input-Nodes, die am weißen Quadrat an der rechten Seite zu erkennen sind, erzeugen Daten, die als Eingabe für eine oder mehrere Output-Nodes dienen. Bei Output-Nodes hingegen liegt das Quadrat auf der linken Seite, und sie verarbeiten die eingehenden Daten. Processing Nodes sind an den weißen Quadraten auf beiden Seiten zu erkennen. Sie lesen die Eingabedaten der Input-Nodes, verarbeiten sie (intern) und geben das Ergebnis an einen Output-Node oder weitere Processing Nodes weiter. Dadurch lassen sich zum Beispiel Daten in andere Formate umwandeln.
Bestimmte Dienste haben sowohl einen Input-Node als auch einen Output-Node. Der Twitter-Node kann zum Beispiel auf öffentliche und persönliche Tweets, Follower oder Direct Messages reagieren, die aus bestimmten Wörtern, IDs oder Tags bestehen. Die Payload des Message-Objekts beinhaltet den Text des Tweets. Umgekehrt lassen sich bestimmte Ereignisse wiederum an Twitter als Tweet schicken. Hierzu schreibt ein Input-Node den Text für den Tweet in die Payload.
Neben den verschiedenen Services, wie zum Beispiel Twitter, WebSocket, MQTT, HTTP oder TCP, bietet Node-RED mit dem Inject Node einen einfachen Node zum Erzeugen von Daten an. Er funktioniert wie ein Button und kann verschiedene Datentypen erzeugen, die sich konfigurieren lassen. Dies kann ein String, ein Boolean-Wert, eine Zahl oder ein Timestamp sein. Mit dem Debug Node existiert auf der anderen Seite eine einfache Möglichkeit, die eingehenden Message-Objekte im Debug-Panel anzuzeigen.
Um einen ersten einfachen Flow anzulegen, reicht es aus, Inject Node und Debug Node in das Sheet-Panel zu ziehen und beide Enden miteinander zu verbinden. Oben rechts befindet sich der rote Deploy-Button, um den neuen Flow innerhalb von Node-RED zu deployen. Beim Drücken im vorderen Bereich des Inject Nodes, der die Bezeichnung timestamp trägt, wird im Debug-Panel-Reiter auf der rechten Seite der aktuelle Timestamp ausgegeben.
Ein Doppelklick auf einen Node öffnet den Konfigurationsdialog. Darin kann zum Beispiel beim Inject Node die Payload von einem Timestamp auf einen String geändert und der Output-Parameter des Debug Nodes auf complete msg object gesetzt werden. Nach einem erneuten Deployment zeigt das Debug-Panel daraufhin das komplette Message-Objekt mit dem konfigurierten String im payload-Parameter an (Abb. 1).
Die Function-Kategorie enthält verschiedene Processing Nodes. Um zum Beispiel einen eingehenden Tweet eines Twitter-Nodes, der den Text des Tweets in der Payload als String enthält, als leuchtende Leuchtdiode auf einem Arduino anzuzeigen, muss der String in einen Boolean-Wert umgewandelt werden. Mit dem Node-RED-eigenen Function Node ist es generell möglich, JavaScript-Funktionen innerhalb von Node-RED zu schreiben. Eine solche Funktion kann, wie im Twitter-Beispiel, auf eine Eingabe reagieren und einen Boolean-Wert ausgeben.
Ein weiterer Node ist der Change Node, der genau diese Aufgabe erfüllt und lediglich konfiguriert werden muss, indem bei der Konfiguration für den to-Parameter boolean mit true als Wert angeben wird. Daraufhin wandelt der Node beliebige Eingangswerte in true um.
Die im Beispiel erwähnte Arduino-Bibliothek ist nicht Bestandteil der Node-RED-Installation, lässt sich aber einfach mit dem Package-Manager npm installieren. Hierzu wechselt man in das Node-RED-Verzeichnis: Unter Linux und macOS befindet sich dieses im ~/.node-red-Verzeichnis. Mit dem Aufruf npm install node-red-node-arduino installiert npm die Arduino-Bibliothek in das Node-RED-Verzeichnis unterhalb des node_modules-Verzeichnisses. Zur Verwendung eines Arduinos siehe auch die entsprechende Node-RED-Dokumentation „Interacting with Arduino“ [4].
Die Node-RED-Library-Seite [5] listet über 750 Nodes, angefangen bei A wie Arduino über OpenWeatherMap bis hin zu ZeroMQ. Sie lassen sich meist, wie bereits erwähnt, über den npm-Package-Manager installieren und nutzen hierfür die npm-Infrastruktur. Dadurch steht auch der Quellcode zu den Bibliotheken zur Verfügung und in vielen Fällen auch das GitHub-Projekt.
Möchte man hingegen seinen eigenen Service einbinden, bietet Node-RED eine relativ einfache Möglichkeit, eine entsprechende Bibliothek zu schreiben. Wir haben bei Hybris Labs hierzu unsere eigene Node-RED-Bibliothek geschrieben, um unsere IoT-Prototypen an eine Microservices-Plattform [6] anzubinden. Zusätzlich können wir entsprechende Use Cases schneller konfigurieren und anpassen.
Die Node-RED-Dokumentation „Creating Nodes“ [7] bietet einen ersten Einstieg und Überblicke. Eine andere Möglichkeit ist es, eine bestehende Bibliothek etwas genauer zu betrachten: Ein Node Package ist in JavaScript- und HTML-Dateien unterteilt. Die JavaScript-Dateien sind für die Programmlogik und die Kommunikation zu anderen Systemen zuständig, die HTML-Dateien wiederum für die Darstellung. Hierzu gehören der Konfigurationsdialog, die Hilfe und das Aussehen der Nodes. Die JavaScript- und HTML-Dateien können in sich mehrere Node-Module in einer Datei definieren und dadurch gruppieren.
Installierte Node-Bibliotheken befinden sich unterhalb des node_modules-Verzeichnisses im .node-red-Verzeichnis. Eigene Nodes hingegen sind unterhalb des nodes-Verzeichnisses, das ebenfalls im .node-red-Verzeichnis liegt. Das nodes-Verzeichnis ist meistens nicht vorhanden und muss, wie das Verzeichnis für das Modul, angelegt werden:
cd ~/.node-red
mkdir -p nodes/beispiel-node
cd nodes/beispiel-node
Bei dem folgenden Beispiel handelt es sich um einen einfachen Trigger, der in einem konfigurierbarem Intervall die Payload der Ausgabe zwischen true und false wechselt. Verbindet man diesen Beispiel-Node mit einem Arduino-Node und konfiguriert den entsprechenden Pin, so kann eine Leuchtdiode konstant blinken, wie die Listings 1 und 2 zeigen.
Listing 1
module.exports = function (RED) {
function BeispielNode(config) {
// TODO
}
RED.nodes.registerType('beispiel', BeispielNode);
}
Listing 2
<script type="text/javascript">
RED.nodes.registerType('beispiel', {
category: 'beispiel',
color: '#a6bbcf',
defaults: {
name: { value: "" },
interval: { value: 5000 }
},
inputs: 0,
outputs: 1,
icon: "timer.png",
label: function () {
return this.name || "Trigger";
}
});
</script>
In den Listings 1 und 2 ist das Grundgerüst eines Node-RED-Nodes aufgeführt. Die JavaScript-Datei aus Listing 1 exportiert eine Funktion, die beim Starten von Node-RED als Parameter die Node-RED-Instanz mitbekommt. Die registerType-Methode registriert die BeispielNode-Funktion bei der Node-RED-Instanz mit einem eindeutigen Namen, in diesem Fall beispiel. Anhand dieses eindeutigen Namens findet Node-RED die anderen Gegenstücke in der HTML-Datei.
Die Node-Definition innerhalb der HTML-Datei besteht aus drei Bereichen. Hierzu gehören die Beschreibung des Nodes für die Darstellung auf der Oberfläche, der Konfigurationsdialog und die Hilfebeschreibung, die das Infopanel anzeigt. Für das Grundgerüst reicht es zunächst aus, nur den Darstellungsbereich des Nodes in der HTML-Datei zu definieren, so wie es in Listing 2 zu sehen ist.
Innerhalb des Script-Blocks registriert die registerType-Methode für den beispiel-Node ein JavaScript-Objekt. Dieses Objekt legt die Kategorie fest, in der der Node im Node-Panel zu sehen ist, die Farbe, Konfigurationswerte, Ein- und Ausgaben, das Icon sowie das Label. Die Konfigurationswerte bestehen aus einem Namen mit leerem String als Defaultwert und einem Intervall mit 5 000 als Defaultwert. Der Node hat lediglich eine Ausgabe. Das Label ist als Funktion definiert, die entweder den konfigurierten Namen zurückgibt, oder, wenn dieser nicht gesetzt ist, den String-Trigger.
Nach einem Neustart von Node-RED und einem Refresh des Browsers zeigt dieser in der beispiel-Kategorie den beispiel-Node an. Er lässt sich zwar in das Sheet-Panel ziehen, gibt aber noch nichts aus.
Listing 3 enthält den Code, um das Intervall in der BeispielNode-Funktion der beispiel.js-Datei zu implementieren. Die BeispielNode-Funktion erzeugt zunächst mit der createNode-Methode innerhalb der Node-RED-Instanz einen Node und übergibt die Node-Instanz und die übergebene Konfiguration. Die node-Variable ist ein Mapping der this-Variable, um in ihr den interval- und toggle-Wert und die timer-Instanz zu speichern, sodass es zu keiner Überschneidung mit anderen Node-Instanzen kommt.
Listing 3
module.exports = function (RED) {
function BeispielNode(config) {
RED.nodes.createNode(this, config);
var node = this;
node.interval = config.interval || 5000;
node.toggle = false;
node.timer = setInterval(function () {
node.toggle = !node.toggle;
node.status({ fill: "green", shape: "dot", text: node.toggle });
var msg = {};
msg.payload = node.toggle;
msg.interval = node.interval;
node.send(msg);
}, node.interval);
node.on("close", function () {
clearInterval(node.timer);
})
}
RED.nodes.registerType('beispiel', BeispielNode);
}
Das Intervall benutzt 5 000 Millisekunden als Defaultwert, falls dieser nicht konfiguriert wurde. Die Intervallmethode ändert mit jedem Durchlauf den toggle-Wert. Die status-Methode zeigt daraufhin den geänderten toggle-Wert als Status unterhalb des Nodes im Browser an und die send-Methode übergibt das msg-Objekt. Dieses beinhaltet in der Payload den toggle-Wert und als interval-Parameter den interval-Wert der Node-Instanz.
Beim Schließen des Nodes, zum Beispiel durch ein neues Deployment der Flows, löscht die clearInterval-Methode die bestehende timer-Instanz. Ansonsten würde die ursprüngliche Instanz weiterhin Intervallaufrufe produzieren. Da lediglich die beispiel.js-Datei angepasst wurde, reicht es aus, Node-RED neu zu starten. Daraufhin sollte im Browser innerhalb des Debug-Panels die Ausgaben zu sehen sein, wenn der Beispiel-Node mit einem Debug Node verbunden wurde.
Der Beispiel-Node wechselt nun alle fünf Sekunden die Ausgabe zwischen true und false. Durch die Erweiterung der Datei beispiel.html mit dem Codebeispiel aus Listing 4 wird der Node auch konfigurierbar. Zusätzlich zeigt das Infopanel die Beschreibung des Nodes an, wenn er ausgewählt wird.
Listing 4
<script type="text/x-red" data-template-name="beispiel">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-interval"><i class="fa fa-clock-o"></i> Interval</label>
<input type="number" id="node-input-interval" placeholder="ms">
</div>
</script>
<script type="text/x-red" data-help-name="beispiel">
<p>Abhängig vom <i>Interval</i>
wechselt der <code>msg.payload</code>
zwischen <i>true</i> und <i>false</i>.
</p>
</script>
Die script-Elemente definieren mit den Attributen data-template-name und data-help-name den Beispiel-Node und die Konfigurations- und Infobereiche. Innerhalb der script-Elemente liegen HTML-Stücke, die Node-RED entsprechend einfügt.
Die Konfiguration ist in Zeilen (form-row) aufgeteilt und beginnt mit einem Label, darauf folgt das Eingabefeld. Wichtig ist hierbei die Bezeichnung der id-Attribute der input-Elemente, da Node-RED einen Bezug zu den Defaultwerten in der beispiel.js-Datei herstellt. Anhand der type-Attribute führt Node-RED bei der Eingabe eine Validierung durch. Die CSS-Klassen-Attribute icon-tag und fa-clock-o zeigen vor dem Label die entsprechenden Symbole an.
Die Hilfebeschreibung besteht ebenfalls aus HTML-Stücken, die Node-RED einbindet. Das code-Element zeigt den Text, in diesem Fall msg.payload, anders formatiert an. Nach dem Refresh des Browsers und vorherigem Neustart von Node-RED lässt sich der Beispiel-Node konfigurieren, und im Infopanel ist die Beschreibung zu sehen.
Hiermit ist der Beispiel-Node fertig und lässt sich als Basis für andere Nodes verwenden oder erweitern – beispielsweise durch eine weitere Ausgabe, die den Boolean-Wert der ersten Ausgabe invertiert zurückgibt.
Die Vielzahl der bereits vorhandenen Nodes von Node-RED ist eine gute Basis für neue Ideen, um eigene Nodes zu schreiben oder zu erweitern. Zusätzlich steht der Quellcode zur Verfügung. Aber nicht nur, um eigene Nodes zu schreiben, ist diese Vielzahl hervorragend. Node-RED ist ideal, um schnell einen IoT-Prototyp mit einem Schalter und einer Leuchtdiode aufzubauen. Dabei ist es egal, ob der angeschlossene Service Twitter, ein Wetterdienst, Minecraft oder ein Bestellservice ist.
Lars Gregori arbeitet als Technology Strategist bei SAP Hybris in München und ist Mitglied im Hybris-Labs-Team. Er interessiert sich für neue Technologien rund um das Thema Internet of Things (IoT).