Node-RED and MQTT Fortgeschritten

Erstellen einer funktionalen Benutzeroberfläche

Im letzten Abschnitt haben Wir ein Login-Formular in unserer Dashboard-Benutzeroberfläche erstellt, das die IP-Adresse, den Benutzernamen und das Passwort abfragt. Dieser Flow hat die Anmeldeinformationen direkt über MQTT veröffentlicht, so dass wir den aktuellen Status von der Kamera anfordern können.

Wir möchten sie nun zusätzlich in einer Form speichern, die anderen Flows zur Verfügung steht. Node-RED bietet uns die Kontextfunktion um Nachrichten in flows Variablen (sie für jeden Flow verfügbar zu machen, der sich auf derselben Registerkarte befindet wie der Flow, der die Payload erstellt hat) und in global Variablen zu speichern, damit sie überall verfügbar sind, indem sie wie folgt aufrufen:

var ip = global.get("ip");
var user = global.get("user");
var password = global.get("password");

Der einfachste Weg, eine Variable auf den global oder flow Kontext zu setzen, ist der Change Node (alle drei Teile des Quellcodes können aus dem folgenden JSON-Code importiert werden: Global Login, UI Buttons, SET Audio Detection State):

Node-RED

Wir können den Change Node zwischen unserem Input Form und dem MQTT Node im Flow Global Login hinzufügen, um alle drei Variablen auf einen globalen Kontext zu setzen. Und veröffentlichen sie zusätzlich mit einem MQTT-Ausgabeknoten, um eine CGI Abfrage direkt nach dem Login auslösen zu können.

Bei den UI-Buttons tragen die Payload von {" val ":" disabled "} und {" val ":" enabled "} ein. Das wird dann von einem anderen MQTT Ausgangsknoten veröffentlicht. Bitte stellen Sie sicher, dass die Topics aller MQTT-Ausgabeknoten auf eindeutige Werte eingestellt sind - unser Anmeldeknoten veröffentlicht seine Nutzdaten unter ipcam/login, während die UI-Schaltflächen ihre Nutzdaten an ipcam/alarm/setaudioalarmattr/activate senden.

Node-RED

Beachten Sie, dass der dritte Flow nun aus zwei Teilen besteht. Der untere Teil wird durch den Flow Global Login ausgelöst und sendet bereits beim Anmelden die Login Informationen der Kamera an die UI-Knoten - hier wird sofort nach dem Login der aktuelle Status von der Kamera abgefragt.

Node-RED

Der obere Teil hingegen sind die UI-Schaltflächen die auf eine Benutzerinteraktion warten. Immer wenn der Benutzer auf eine Schaltfläche klickt, wird der Status auf unserer Kamera festgelegt und die Textausgabe in unserem Dashboard wird aktualisiert, um die Statusänderung widerzuspiegeln. Gehen Sie zu http://localhost:1880/dashboard und geben Sie die IP-Adresse Ihrer Kamera und Ihre Admin-Anmeldung ein. Nachdem Sie diese Werte eingegeben haben, zeigt die Benutzeroberfläche den aktuellen Status Ihrer Kamera an:

Node-RED

Wenn Sie auf eine der beiden Schaltflächen klicken, wird der entsprechende Wert auf der Kamera eingestellt und der auf dem Dashboard angezeigte Text wird aktualisiert.

JSON Code export

Global Login

[{"id":"9f5d07da.b800b8","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":100,"wires":[["59d50e34.9ad15"]]},{"id":"d5b37a35.b7c358","type":"comment","z":"13bcbd83.ab1fe2","name":"Set Login Global","info":"","x":90,"y":40,"wires":[]},{"id":"59d50e34.9ad15","type":"change","z":"13bcbd83.ab1fe2","name":"Set Global Login","rules":[{"t":"set","p":"ip","pt":"global","to":"payload.ip","tot":"msg"},{"t":"set","p":"password","pt":"global","to":"payload.password","tot":"msg"},{"t":"set","p":"user","pt":"global","to":"payload.user","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":140,"y":160,"wires":[["abe4025e.efce5"]]},{"id":"abe4025e.efce5","type":"mqtt out","z":"13bcbd83.ab1fe2","name":"","topic":"ipcam/login","qos":"","retain":"","broker":"47feb3e4.56f11c","x":180,"y":220,"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"}]

UI Buttons

[{"id":"964c8404.63ed18","type":"ui_button","z":"13bcbd83.ab1fe2","name":"Deactivate Audio Detection","group":"eb6d72f9.38466","order":3,"width":0,"height":0,"passthru":false,"label":"Deactivate Audio Detection","color":"#000000","bgcolor":"#dddddd","icon":"fa-bell","payload":"{ \"val\" : \"disabled\"}","payloadType":"json","topic":"snap","x":121,"y":380,"wires":[["c9765093.00946"]]},{"id":"c9765093.00946","type":"mqtt out","z":"13bcbd83.ab1fe2","name":"Set Audio Alarm","topic":"ipcam/alarm/setaudioalarmattr/activate","qos":"1","retain":"","broker":"47feb3e4.56f11c","x":331,"y":360,"wires":[]},{"id":"9a80df92.8aab6","type":"ui_button","z":"13bcbd83.ab1fe2","name":"Activate Audio Detection","group":"eb6d72f9.38466","order":2,"width":0,"height":0,"passthru":false,"label":"Activate Audio Detection","color":"#ffffff","bgcolor":"#f17c35","icon":"fa-bell","payload":"{ \"val\" : \"enabled\"}","payloadType":"json","topic":"snap","x":121,"y":340,"wires":[["c9765093.00946"]]},{"id":"14d2d8ff.b3c887","type":"comment","z":"13bcbd83.ab1fe2","name":"Audio Detection Buttons","info":"","x":110,"y":288,"wires":[]},{"id":"eb6d72f9.38466","type":"ui_group","z":"","name":"Audio Detection","tab":"7a3391b6.03a81","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"}]

SET Audio Detection State

[{"id":"c5cf5c15.7cc0a","type":"mqtt in","z":"13bcbd83.ab1fe2","name":"Set Audio Alarm","topic":"ipcam/alarm/setaudioalarmattr/activate","qos":"1","broker":"47feb3e4.56f11c","x":90,"y":500,"wires":[["bebb4aa8.4fa1a8"]]},{"id":"282510af.cc1f9","type":"switch","z":"13bcbd83.ab1fe2","name":"Push","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"enabled","vt":"str"},{"t":"eq","v":"disabled","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":271,"y":500,"wires":[["2068dd82.b90c82"],["1c4f1b7e.b4bfb5"]]},{"id":"2068dd82.b90c82","type":"function","z":"13bcbd83.ab1fe2","name":"Enable Audio Detection","func":"var ip = global.get(\"ip\");\nvar user = global.get(\"user\");\nvar password = global.get(\"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 = \"type\";\nmsg.payload = ip+\"/param.cgi?cmd=setaudioalarmattr&-aa_enable=1&-usr=\"+user+\"&-pwd=\"+password;\nreturn msg;\n\n// msg.payload = 'http://192.168.1.116/param.cgi?cmd=setaudioalarmattr&-aa_enable=1&-usr=admin&-pwd=1029384756';\n// return msg;","outputs":1,"noerr":0,"x":436,"y":477,"wires":[["b51eb789.8f4058"]]},{"id":"370a40c.b9163c","type":"string","z":"13bcbd83.ab1fe2","name":"toJSON","methods":[{"name":"replaceAll","params":[{"type":"str","value":"[Succeed]set ok."},{"type":"str","value":"{ \"aa_enable\": \"enabled\" }"}]},{"name":"replaceAll","params":[{"type":"str","value":"[Error]Param error."},{"type":"str","value":"{ \"aa_enable\": \"Error\" }"}]}],"prop":"payload","propout":"payload","object":"msg","objectout":"msg","x":833,"y":477,"wires":[["102231e6.822f5e"]]},{"id":"102231e6.822f5e","type":"json","z":"13bcbd83.ab1fe2","name":"","property":"payload","action":"","pretty":false,"x":961,"y":516,"wires":[["91abfd33.6d186"]]},{"id":"91abfd33.6d186","type":"ui_text","z":"13bcbd83.ab1fe2","group":"eb6d72f9.38466","order":1,"width":0,"height":0,"name":"Audio Detection","label":"Audio Detection","format":"{{msg.payload.aa_enable}}","layout":"row-spread","x":1111,"y":516,"wires":[]},{"id":"b51eb789.8f4058","type":"change","z":"13bcbd83.ab1fe2","name":"Set URL","rules":[{"t":"set","p":"url","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":592,"y":477,"wires":[["25d9a3ff.508bac"]]},{"id":"25d9a3ff.508bac","type":"http request","z":"13bcbd83.ab1fe2","name":"snap","method":"GET","ret":"txt","url":"","tls":"","x":712,"y":477,"wires":[["370a40c.b9163c"]]},{"id":"bebb4aa8.4fa1a8","type":"function","z":"13bcbd83.ab1fe2","name":"process value","func":"msg.payload = JSON.parse(msg.payload).val;\nreturn msg;","outputs":1,"noerr":0,"x":111,"y":540,"wires":[["282510af.cc1f9"]]},{"id":"1c4f1b7e.b4bfb5","type":"function","z":"13bcbd83.ab1fe2","name":"Disable Audio Detection","func":"var ip = global.get(\"ip\");\nvar user = global.get(\"user\");\nvar password = global.get(\"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 = \"type\";\nmsg.payload = ip+\"/param.cgi?cmd=setaudioalarmattr&-aa_enable=0&-usr=\"+user+\"&-pwd=\"+password;\nreturn msg;\n\n// msg.payload = 'http://192.168.1.116/param.cgi?cmd=setaudioalarmattr&-aa_enable=0&-usr=admin&-pwd=1029384756';\n// return msg;","outputs":1,"noerr":0,"x":436,"y":517,"wires":[["86e3f600.a42a08"]]},{"id":"86e3f600.a42a08","type":"change","z":"13bcbd83.ab1fe2","name":"Set URL","rules":[{"t":"set","p":"url","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":592,"y":517,"wires":[["b150cc6.1cb693"]]},{"id":"b150cc6.1cb693","type":"http request","z":"13bcbd83.ab1fe2","name":"snap","method":"GET","ret":"txt","url":"","tls":"","x":712,"y":517,"wires":[["7cd1ddf0.8c4d54"]]},{"id":"7cd1ddf0.8c4d54","type":"string","z":"13bcbd83.ab1fe2","name":"toJSON","methods":[{"name":"replaceAll","params":[{"type":"str","value":"[Succeed]set ok."},{"type":"str","value":"{ \"aa_enable\": \"disabled\" }"}]},{"name":"replaceAll","params":[{"type":"str","value":"[Error]Param error."},{"type":"str","value":"{ \"aa_enable\": \"Error\" }"}]}],"prop":"payload","propout":"payload","object":"msg","objectout":"msg","x":832,"y":517,"wires":[["102231e6.822f5e"]]},{"id":"8e8eb1d2.917c2","type":"http request","z":"13bcbd83.ab1fe2","name":"aa_enable","method":"GET","ret":"txt","url":"","tls":"","x":720,"y":560,"wires":[["86f4a6aa.505e18"]]},{"id":"ad2301ba.7ecf2","type":"function","z":"13bcbd83.ab1fe2","name":"aa_enabled","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 = \"type\";\nmsg.payload = ip+\"/param.cgi?cmd=getaudioalarmattr&-usr=\"+user+\"&-pwd=\"+password;\nreturn msg;","outputs":1,"noerr":0,"x":564,"y":568,"wires":[["5ea07282.f2da8c"]]},{"id":"21531d43.05f9f2","type":"mqtt in","z":"13bcbd83.ab1fe2","name":"","topic":"ipcam/login","qos":"2","broker":"47feb3e4.56f11c","x":392,"y":567,"wires":[["bc315627.7ea318"]]},{"id":"e05fa8b0.8d2e88","type":"json","z":"13bcbd83.ab1fe2","name":"","property":"payload","action":"","pretty":false,"x":871,"y":566,"wires":[["5db7972.1d65668"]]},{"id":"5db7972.1d65668","type":"change","z":"13bcbd83.ab1fe2","name":"change 1/0","rules":[{"t":"change","p":"payload.aa_enable","pt":"msg","from":"0","fromt":"str","to":"disabled","tot":"str"},{"t":"change","p":"payload.aa_enable","pt":"msg","from":"1","fromt":"str","to":"enabled","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":994,"y":566,"wires":[["91abfd33.6d186"]]},{"id":"bc315627.7ea318","type":"json","z":"13bcbd83.ab1fe2","name":"","property":"payload","action":"","pretty":false,"x":450,"y":619,"wires":[["ad2301ba.7ecf2"]]},{"id":"5ea07282.f2da8c","type":"change","z":"13bcbd83.ab1fe2","name":"","rules":[{"t":"set","p":"url","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":620,"wires":[["8e8eb1d2.917c2"]]},{"id":"86f4a6aa.505e18","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":830,"y":620,"wires":[["e05fa8b0.8d2e88"]]},{"id":"5b58098b.bc2538","type":"comment","z":"13bcbd83.ab1fe2","name":"SET Audio Detection State","info":"","x":120,"y":460,"wires":[]},{"id":"3ef2b6da.cc575a","type":"comment","z":"13bcbd83.ab1fe2","name":"GET Audio Detection State","info":"","x":440,"y":660,"wires":[]},{"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":"eb6d72f9.38466","type":"ui_group","z":"","name":"Audio Detection","tab":"7a3391b6.03a81","disp":true,"width":"6","collapse":false},{"id":"7a3391b6.03a81","type":"ui_tab","z":"","name":"Wiki Tutorial","icon":"dashboard"}]