Node-RED and MQTT Intermediate

Using a UI Form Element to set a Global Login

In the last course we used a button to send a message over MQTT to our Node-RED Installation. This message then triggered sending a CGI command over HTTP to our cameras REST API. Let's expand on that.

The code we had was nice, but was not reusable due to the hardcoded IP address and camera login. We now want to create a Login Form in our dashboard UI that receives the IP address, user name and password. This flow should then publish them directly via MQTT so we can subscribe to the current state from other flows. This way those functions automatically update once the form element is submitted.

Node-RED

The source code for this can be found at the end of this article. Please copy both the Login Flow and afterwards the getserverinfo Flow and import them to Node-RED using the import dialogue (see previous course for how to do that). The result should look something like this:

Node-RED

Login Form

When you double-click the first node of the Global Login flow, you will see that it is a Form Node that adds three input fields to our dashboard:

Node-RED

We can test the output of the Form Node by adding a Debug Node to it. Attach this node directly to the form node and deploy your flow. Open http://localhost:1880/dashboard/ in your browser and type in the IP address of your camera, as well as the administrator login:

Node-RED

MQTT Node

You should be seeing your cameras system information displayed after submitting the form. Going back to our flow, you will also see the debug information that was send to the connected MQTT Node to be published:

{
    "ip": "192.168.1.52",
    "user": "admin",
    "password": "instar"
}

Node-RED

The MQTT publication follows under the topic ipcam/login (double-click the MQTT Output Node to verify). That means that we are now able to connect a MQTT Input Node to any flow, subscribe it to ipcam/login and receive the login information via MQTT. Which is what we are doing in the getserverinfo Flow.

getserverinfo Flow

Node-RED

The first node receives the login information, runs it through a JSON Node, to turn it into a JavaScript object, and then imports it to a Function Node that we use to build the CGI command we want to send to our camera's REST API:

var ip = msg.payload.ip;
var user = msg.payload.user;
var password = msg.payload.password;

msg.topic = "getserverinfo";
msg.payload = ip+"/param.cgi?cmd=getserverinfo&-usr="+user+"&-pwd="+password;
return msg;

To make the flow prettier we can additionally add a timestamp to the function node, showing when the function was last triggered (optional):

// Update the status with current timestamp
var now = new Date();
var yyyy = now.getFullYear();
var mm = now.getMonth() < 9 ? "0" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based
var dd  = now.getDate() < 10 ? "0" + now.getDate() : now.getDate();
var hh = now.getHours() < 10 ? "0" + now.getHours() : now.getHours();
var mmm  = now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes();
var ss  = now.getSeconds() < 10 ? "0" + now.getSeconds() : now.getSeconds();
var currenttime= hh + ":" + mmm + ":" + ss;
var currentdate= dd + "." + mm + "." + yyyy;

node.status({fill:"blue",shape:"ring",text:"Last updateChanged: " + currentdate + " - " + currenttime});

The rest of the code is identical to the code we wrote in the last course. We set the created string to be the URL of the HTTP GET Node, which sends the CGI command to our camera. The camera response is then turned into a JavaScript object and send to Text fields in our Node-RED dashboard:

Node-RED

JSON Code export

Global Login

[{"id":"2cc6b79f.c6bbe8","type":"ui_form","z":"13bcbd83.ab1fe2","name":"Camera Address","label":"Change Address","group":"c0689660.4e98c8","order":0,"width":0,"height":0,"options":[{"label":"IP Address","value":"ip","type":"text","required":true},{"label":"Username","value":"user","type":"text","required":true},{"label":"Password","value":"password","type":"password","required":true}],"formValue":{"ip":"","user":"","password":""},"payload":"","submit":"Submit","cancel":"Cancel","topic":"user_set","x":90,"y":180,"wires":[["88c16822.071318"]]},{"id":"cc9d17da.1cb338","type":"comment","z":"13bcbd83.ab1fe2","name":"Set Login Global","info":"","x":90,"y":120,"wires":[]},{"id":"88c16822.071318","type":"mqtt out","z":"13bcbd83.ab1fe2","name":"","topic":"ipcam/login","qos":"","retain":"","broker":"47feb3e4.56f11c","x":140,"y":240,"wires":[]},{"id":"c0689660.4e98c8","type":"ui_group","z":"","name":"Camera Login","tab":"7a3391b6.03a81","order":1,"disp":true,"width":"6","collapse":false},{"id":"47feb3e4.56f11c","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"7a3391b6.03a81","type":"ui_tab","z":"","name":"Wiki Tutorial","icon":"dashboard"}]

CGI Response for getserverinfo

[{"id":"ac242510.97db68","type":"http request","z":"13bcbd83.ab1fe2","name":"getserverinfo","method":"GET","ret":"txt","url":"","tls":"","x":410,"y":220,"wires":[["a12e8db8.35994","666a1b4a.34a9a4"]]},{"id":"6c7c8dd0.47fbc4","type":"debug","z":"13bcbd83.ab1fe2","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":490,"y":280,"wires":[]},{"id":"a12e8db8.35994","type":"string","z":"13bcbd83.ab1fe2","name":"toJSON","methods":[{"name":"delRightMost","params":[{"type":"str","value":";"}]},{"name":"append","params":[{"type":"str","value":" }"}]},{"name":"prepend","params":[{"type":"str","value":"{ "}]},{"name":"replaceAll","params":[{"type":"str","value":"var "},{"type":"str","value":"\""}]},{"name":"replaceAll","params":[{"type":"str","value":"="},{"type":"str","value":"\":"}]},{"name":"replaceAll","params":[{"type":"str","value":";"},{"type":"str","value":","}]}],"prop":"payload","propout":"payload","object":"msg","objectout":"msg","x":310,"y":280,"wires":[["6c7c8dd0.47fbc4","7fcdef72.9d0ad"]]},{"id":"7fcdef72.9d0ad","type":"json","z":"13bcbd83.ab1fe2","name":"","property":"payload","action":"","pretty":false,"x":350,"y":380,"wires":[["85ab24c9.c72438","db9fc761.e39c78","1662357a.43fcdb","97b8e776.bc2d28","3156cc1d.d59374","20181942.7f2086","731237ad.db1298","558f4beb.7a05d4","74026d1d.aacb14","c6dc73f5.f9d63","267e6114.855b8e","51831234.f13bbc","9cb9be2b.0adfe","b57c38a3.206f78"]]},{"id":"85ab24c9.c72438","type":"debug","z":"13bcbd83.ab1fe2","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":510,"y":340,"wires":[]},{"id":"666a1b4a.34a9a4","type":"debug","z":"13bcbd83.ab1fe2","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":590,"y":220,"wires":[]},{"id":"97b8e776.bc2d28","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Firmware","label":"Firmware","format":"{{msg.payload.softVersion}}","layout":"row-spread","x":540,"y":460,"wires":[]},{"id":"db9fc761.e39c78","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Interface","label":"Interface","format":"{{msg.payload.webVersion}}","layout":"row-spread","x":540,"y":500,"wires":[]},{"id":"3156cc1d.d59374","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Designation","label":"Designation","format":"{{msg.payload.name}}","layout":"row-spread","x":540,"y":540,"wires":[]},{"id":"731237ad.db1298","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Device ID","label":"Device ID","format":"{{msg.payload.model}}","layout":"row-spread","x":540,"y":380,"wires":[]},{"id":"558f4beb.7a05d4","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Revision","label":"Revision","format":"{{msg.payload.hardVersion}}","layout":"row-spread","x":530,"y":420,"wires":[]},{"id":"c885359a.ad7d08","type":"function","z":"13bcbd83.ab1fe2","name":"getserverinfo","func":"var ip = msg.payload.ip;\nvar user = msg.payload.user;\nvar password = msg.payload.password;\n\n// Update the status with current timestamp\nvar now = new Date();\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd  = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm  = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss  = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\nvar currenttime= hh + \":\" + mmm + \":\" + ss;\nvar currentdate= dd + \".\" + mm + \".\" + yyyy;\n\nnode.status({fill:\"blue\",shape:\"ring\",text:\"Last updateChanged: \" + currentdate + \" - \" + currenttime});\n\nmsg.topic = \"getserverinfo\";\nmsg.payload = ip+\"/param.cgi?cmd=getserverinfo&-usr=\"+user+\"&-pwd=\"+password;\nreturn msg;","outputs":1,"noerr":0,"x":410,"y":160,"wires":[["6ff16ff3.d7eb4","93a6f3a4.dea83"]]},{"id":"3b9d124a.b870ae","type":"mqtt in","z":"13bcbd83.ab1fe2","name":"","topic":"ipcam/login","qos":"2","broker":"47feb3e4.56f11c","x":260,"y":40,"wires":[["84ac7b9c.d41048","684ca76b.506f88"]]},{"id":"6ff16ff3.d7eb4","type":"debug","z":"13bcbd83.ab1fe2","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":510,"y":100,"wires":[]},{"id":"84ac7b9c.d41048","type":"debug","z":"13bcbd83.ab1fe2","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":450,"y":40,"wires":[]},{"id":"684ca76b.506f88","type":"json","z":"13bcbd83.ab1fe2","name":"","property":"payload","action":"","pretty":false,"x":330,"y":100,"wires":[["c885359a.ad7d08"]]},{"id":"93a6f3a4.dea83","type":"change","z":"13bcbd83.ab1fe2","name":"","rules":[{"t":"set","p":"url","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":160,"wires":[["ac242510.97db68"]]},{"id":"ceb785e3.f052c8","type":"comment","z":"13bcbd83.ab1fe2","name":"getserverinfo CGI","info":"","x":90,"y":40,"wires":[]},{"id":"74026d1d.aacb14","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Active since","label":"Active since","format":"{{msg.payload.startdate}}","layout":"row-spread","x":540,"y":580,"wires":[]},{"id":"1662357a.43fcdb","type":"change","z":"13bcbd83.ab1fe2","name":"change 1/0","rules":[{"t":"change","p":"payload.upnpstatus","pt":"msg","from":"on","fromt":"str","to":"enabled","tot":"str"},{"t":"change","p":"payload.upnpstatus","pt":"msg","from":"off","fromt":"str","to":"disabled","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":620,"wires":[["9266e35e.05e9"]]},{"id":"20181942.7f2086","type":"change","z":"13bcbd83.ab1fe2","name":"change 1/0","rules":[{"t":"change","p":"payload.facddnsstatus","pt":"msg","from":"ok","fromt":"str","to":"active","tot":"str"},{"t":"change","p":"payload.facddnsstatus","pt":"msg","from":"off","fromt":"str","to":"deactivated","tot":"str"},{"t":"change","p":"payload.facddnsstatus","pt":"msg","from":"failed","fromt":"str","to":"connection error","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":660,"wires":[["4f8b632c.0c791c"]]},{"id":"c6dc73f5.f9d63","type":"change","z":"13bcbd83.ab1fe2","name":"change 1/0","rules":[{"t":"change","p":"payload.th3ddnsstatus","pt":"msg","from":"ok","fromt":"str","to":"active","tot":"str"},{"t":"change","p":"payload.th3ddnsstatus","pt":"msg","from":"off","fromt":"str","to":"deactivated","tot":"str"},{"t":"change","p":"payload.th3ddnsstatus","pt":"msg","from":"failed","fromt":"str","to":"connection error","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":700,"wires":[["252d7e10.889722"]]},{"id":"267e6114.855b8e","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Platform Status","label":"Platform Status","format":"{{msg.payload.platformstatus}}","layout":"row-spread","x":550,"y":740,"wires":[]},{"id":"51831234.f13bbc","type":"change","z":"13bcbd83.ab1fe2","name":"change 1/0","rules":[{"t":"change","p":"payload.sdstatus","pt":"msg","from":"out","fromt":"str","to":"no card found","tot":"str"},{"t":"change","p":"payload.sdstatus","pt":"msg","from":"read only","fromt":"str","to":"write protection","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":780,"wires":[["83152650.386d68"]]},{"id":"9cb9be2b.0adfe","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Card Free Space","label":"Card Free Space","format":"{{msg.payload.sdfreespace}}","layout":"row-spread","x":560,"y":820,"wires":[]},{"id":"b57c38a3.206f78","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"Card Total Space","label":"Card Total Space","format":"{{msg.payload.sdtotalspace}}","layout":"row-spread","x":560,"y":860,"wires":[]},{"id":"83152650.386d68","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"SD Card Status","label":"SD Card Status","format":"{{msg.payload.sdstatus}}","layout":"row-spread","x":690,"y":780,"wires":[]},{"id":"252d7e10.889722","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"3rd Party DDNS","label":"3rd Party DDNS","format":"{{msg.payload.th3ddnsstatus}}","layout":"row-spread","x":690,"y":700,"wires":[]},{"id":"4f8b632c.0c791c","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"INSTAR DDNS","label":"INSTAR DDNS","format":"{{msg.payload.facddnsstatus}}","layout":"row-spread","x":690,"y":660,"wires":[]},{"id":"9266e35e.05e9","type":"ui_text","z":"13bcbd83.ab1fe2","group":"6652a2b5.79fc9c","order":2,"width":0,"height":0,"name":"UPnP Status","label":"UPnP Status","format":"{{msg.payload.upnpstatus}}","layout":"row-spread","x":680,"y":620,"wires":[]},{"id":"6652a2b5.79fc9c","type":"ui_group","z":"","name":"/param.cgi?cmd=getserverinfo","tab":"7a3391b6.03a81","order":2,"disp":true,"width":"6","collapse":false},{"id":"47feb3e4.56f11c","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"7a3391b6.03a81","type":"ui_tab","z":"","name":"Wiki Tutorial","icon":"dashboard"}]