Node-Red is a graphical programming environment based on node.js. (Node.js is basically server-side javascript). Since it's built on node.js, Node-Red can run anywhere node.js runs. It's all open source, and was started by IBM.
Installation
I mostly followed the instructions here to install Node-Red to an old laptop running Xubuntu.
First I installed the Node.js package manager and Node.js itself:
sudo apt-get install npm
sudo apt-get install node
Then I downloaded the latest release zip file from nodered.org and unzipped to ~/dev
cded into that dir and ran 'npm install --production'.
Next up I launched Node-Red it by doing 'nodejs red.js'. Running 'node red.js' doesn't work since the debian packages install it as nodejs to avoid a conflict. Eventually I ended up symlinking node to nodejs to make things easier:
ln -s /usr/bin/nodejs /usr/bin/node
And with that Node-Red seems to be up and running.
Node-Red ships with a pretty good set of basic nodes, but there is also a git repo that has even more nodes, so I thought I'd try that out. I installed the extra nodes by doing 'cd nodes; git clone https://github.com/node-red/node-red-nodes.git'.
It should be noted that many of these additional nodes rely on node.js libraries and until those libraries are installed, the new node will not appear in the Node-Red palette. As an example, if I want to use the suncalc node to generate an event at sunrise every day, I would need to install the suncalc library by doing 'npm install suncalc'.
Hello World
I ran through the initial tutorial and was successful. Programs in Node-Red are called "flows". The basic design pattern in Node-Red is: drop and config nodes, wire them together, hit deploy. Pretty simple, and even better, it actually works.
The debugging is a little simplistic in that you can print values to a debug log. But again, it's simple, and it works, so I can't complain too much.
Flows can be exported as a json string, and imported by the same mechanism. For the rest of this post I'll post the exported json for the flows I've used.
Controlling execution
The Node-Red documentation has a good "hello world" tutorial, and has a tutorial on creating new plugin nodes, but it's a little sparse beyond that. I did some experiments to figure out how the nodes in Node-Red actually execute. Here's what I've figured out.
First off, Node-Red is completely event based. Nothing executes unless an external event triggered it. The "delay" and "trigger" nodes also allow timing based events to be generated.
The wires between the nodes are there to symbolize a data connection to the previous node. More specifically the wires are really a graphical representation of how javascript Objects get passed to and from nodes. These are usually referred to as msg objects and their contents can be somewhat arbitrary but most often contain a data member called payload.
So how can you control whether a node executes, like you can with an "if" statement in most text based languages? If you return a "null" for any of the outputs then any of the subsequent nodes attached to that output don't execute. A way to demonstrate that is by creating a function node that has multiple outputs which means you return an array of msg objects. Any element in the array that has a value of null will prevent the connected node from executing. By the way, this is exactly how a "switch" node works. Check out the "demo switch" node in the example flow below.
Here's the example flow I'll be using for the rest of the post:
[{"id":"f64d219e.53a208","type":"inject","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":439,"y":454,"z":"d5a6cdcf.6203b","wires":[["5863bf74.888a3"]]},{"id":"5863bf74.888a3","type":"function","name":"demo switch","func":"msg.payload = \"hello\";\n//return [null, msg];\nreturn [msg, msg];","outputs":"2","x":628,"y":452,"z":"d5a6cdcf.6203b","wires":[["6d0fe6ba.2d2e88"],["d6a0f7c1.e949e8"]]},{"id":"6d0fe6ba.2d2e88","type":"debug","name":"demo out 1","active":true,"console":"false","complete":"false","x":821,"y":407,"z":"d5a6cdcf.6203b","wires":[]},{"id":"d6a0f7c1.e949e8","type":"debug","name":"demo out 2","active":true,"console":"false","complete":"false","x":820,"y":486,"z":"d5a6cdcf.6203b","wires":[]},{"id":"1ec662d2.3b7c25","type":"http in","name":"","url":"/test","method":"get","x":450,"y":277,"z":"d5a6cdcf.6203b","wires":[["dd8cd3d3.17b08"]]},{"id":"9c30b932.4da1e","type":"debug","name":"test URL","active":true,"console":"false","complete":"false","x":850,"y":189,"z":"d5a6cdcf.6203b","wires":[]},{"id":"6b3281a7.0a29c8","type":"http response","name":"","x":841,"y":276,"z":"d5a6cdcf.6203b","wires":[]},{"id":"dd8cd3d3.17b08","type":"function","name":"Gen response","func":"var resp = \"Hello \" + msg.payload.name;\nmsg.payload = resp;\nreturn msg;","outputs":1,"x":665,"y":276,"z":"d5a6cdcf.6203b","wires":[["6b3281a7.0a29c8","9c30b932.4da1e"]]},{"id":"7a4aeee7.85b51","type":"inject","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":424.5182189941406,"y":573.5229034423828,"z":"d5a6cdcf.6203b","wires":[["fbd25db9.042da"]]},{"id":"fbd25db9.042da","type":"function","name":"count","func":"if (context.hasOwnProperty(\"counter\"))\n\tcontext.counter += 1;\nelse\n\tcontext.counter = 0;\n\ncontext.global.counter = context.counter + 1;\n\nmsg.payload = \"local counter = \" + parseInt(context.counter, 10);\n\nreturn msg;","outputs":1,"x":602.5182647705078,"y":572.522876739502,"z":"d5a6cdcf.6203b","wires":[["caaa07a5.3555f8"]]},{"id":"caaa07a5.3555f8","type":"debug","name":"","active":true,"console":false,"complete":false,"x":782.5182189941406,"y":577.5229034423828,"z":"d5a6cdcf.6203b","wires":[]},{"id":"36a0278c.c95fd8","type":"inject","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":424.5182189941406,"y":638.5228900909424,"z":"d5a6cdcf.6203b","wires":[["4b22fcbc.b4dd04"]]},{"id":"4b22fcbc.b4dd04","type":"function","name":"get global count","func":"msg.payload = \"global counter = \" + parseInt(context.global.counter, 10);\n\nreturn msg;","outputs":1,"x":603.5182189941406,"y":634.5229034423828,"z":"d5a6cdcf.6203b","wires":[["f51d7f6a.0ae28"]]},{"id":"f51d7f6a.0ae28","type":"debug","name":"","active":true,"console":false,"complete":false,"x":782.5182762145996,"y":633.5228900909424,"z":"d5a6cdcf.6203b","wires":[]}]
Local and Global variables
Within a function node there is a way to persist data between calls to that node that is both local to that node and global to all nodes. It's documented pretty well here. I created a quick demonstration of globals using a couple of function nodes.
First web service
Just to try out what I'd learned I used some techniques from here, and got a working test URL using http in and http response nodes! Just pass a "name" parameter to the URL and it will print a Hello message. Something like this: "http://site/test?name=Ken"
Conclusions
I really liked working with Node-Red, and I think I'll probably continue using it.
Pros
- Open source and actively developed by IBM
- Web-based development
- Graphical programming/rule development
- https and basic http authentication is supported. See here for more info.
- Mobile-friendly interface
- No built-in UI
- No built-in database for sensor readings, though there are nodes to interface with various DB formats