Using Node-Red to fix stuff

Node-RED is a truly awesome tool that allows you to very quickly build an app that can talk to IoT hardware (eg devices like a Raspberry Pi), your local machine and online services. In the matter of a few minutes you can hook all these things together and getting them doing something useful.

At my place we have two Internet connections. One of them is hooked up via a router that is the best part of a decade old. It works pretty reliably, when it works, but every 36-48 hours it locks up and stops working and has to be rebooted.

I’ve put up with the inconvenience of this for years, but tonight I decided I’d finally had enough and it was time to solve the problem.

If you want to give this a try at home, the first step is to install Node.js. You can do that from here. Then, install Node-RED and some other npm modules we’ll want (these instructions work for MacOSX, you may need to vary them for your system):

sudo npm install -g node-red node-red-node-twilio pm2

Then, once everything is installed, run node-red using pm2:

pm2 start /usr/local/bin/node-red -- -v

If all has gone well, you should now be able to open a web browser, point it to localhost on your machine on port 1880 and see this:

If that’s what you see, you’re ready to build!

Building an app in Node-RED involves creating a “flow”. A flow is simply a set of nodes (those things in the list on the left), “wired” together and configured to do what you need.

A node can be an input node, and output node, or a function node through which messages can flow both in and out. Messages originate at input nodes, travel through from none to many function nodes and are emitted at an output node. Each node has the opportunity to modify the message payload before it is passed to the next node. Function nodes can have more than one output, which allows you to create branching logic.

Nodes are configured by double-clicking them, which opens a panel that allows you to set parameters for that node. “Wires” connect the output from one node to the input of the next. This is all done by pointing and clicking and dragging. Couldn’t get much simpler!

So, what I need is a flow that will attempt to access the Internet via the flaky router. If it succeeds, all is well and I don’t need to do anything more. If it fails, I want to call the web UI on the router and tell it to reset the router. Then I want an SMS notification to be sent to my phone, letting me know of the outage and router reset.

Here’s how to do this with Node-RED:

You can see I have three input nodes – two are used to trigger test scenarios, so I won’t describe them here. The one that matters is the “Check every 30 seconds” input node that I have configured to inject a message into my flow every 30 seconds. This message flows to the http request node which is configured (when triggered by a message arriving) to do a GET call on www.microsoft.com (initially I used Google, but they don’t like being used this way). The data returned from that request gets loaded into the message object’s payload slot and passed to the next node.

The “No ping?” node is a Javascript function that looks at the message payload data from the http request and checks to see if it looks like it comes from the pinged site.

If the data doesn’t contain the string “Microsoft”, and the device isn’t currently being rebooted, the function emits a message out of its second output that flows into and triggers the “Reset Modem” HTTP request node.

The HTTP request simply emulates the web call that my router’s web UI makes when I click the “Reset” button on it.

var rebooting = flow.get('rebooting') || false;
// if null, no message will be passed to the output
var msg2 = null;
var msg3 = null;

if (msg.payload.match(/Microsoft/)) {
    if (!rebooting) {
        msg.payload = 'OK';
    }
    else {
        node.warn("Reboot complete");
        var currentTime = new Date().toLocaleTimeString();
        msg.payload = 'Reboot complete at ' + currentTime;
        flow.set('rebooting', false);
        var smsMessage = 'Router down from ';
        smsMessage += flow.get('rebootStart');
        smsMessage += ' to ' + currentTime;
        msg3 = { payload: smsMessage };
    }
}
else {
    if (rebooting === false) {
        node.warn("Rebooting");
        var currentTime = new Date().toLocaleTimeString();
        msg2 = { payload: 'factory=E0' };
        flow.set('rebooting', true);
        flow.set('rebootStart', currentTime);
        msg.payload = 'Requesting Reboot at ' + currentTime;
    }
    else {
        msg.payload = 'Reboot in progress';
    }
}

return [msg, msg2, msg3];

If the data does contain the expected string, either the router is still working fine, in which case it emits a message out to the console to say “OK”, or it indicates that the router is working again after a reboot.

I’m storing state in the flow context so that I don’t trigger additional reboots when a reboot is already in progress. I also use the stored state to determine when a reboot is complete, and when it is I use a third output to send an SMS telling me the start and end time of the outage.

Now, whenever my router goes down, it’ll automatically get reset, and once it’s back up I’ll get an SMS to let me know what happened, and how long the outage lasted.

Much better!

Have a play with Node-RED and let me know what you think in the comments.

PS: Node-RED has other nodes for getting data and working with it in a myriad of ways, including support for different kinds of protocols, storage engines, cloud services, and home automation gear. All of it is Open Source and free to use. Check it out.