Custom implementation of Open-Zwave-Control-Panel (https://github.com/OpenZWave/open-zwave-control-panel) using node-contrib-openzwave (https://github.com/OpenZWave/node-red-contrib-openzwave) and node-red-dashboard.
-
-
Save robertsLando/41ae75dbfbe6465d5cf40248e2500e0a to your computer and use it in GitHub Desktop.
[{"id":"c8fe91b8.daa3","type":"tab","label":"Flow 2"},{"id":"5b1e3f38.50856","type":"zwave-in","z":"c8fe91b8.daa3","name":"read_zwave","controller":"93bf10ea.1c4c3","x":194,"y":180,"wires":[["ee5ce307.9c417","c46bb234.40dcc"]]},{"id":"c46bb234.40dcc","type":"debug","z":"c8fe91b8.daa3","name":"","active":false,"console":"false","complete":"false","x":416,"y":74,"wires":[]},{"id":"ee5ce307.9c417","type":"switch","z":"c8fe91b8.daa3","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"zwave: driver ready","vt":"str"},{"t":"eq","v":"zwave: node added","vt":"str"},{"t":"eq","v":"zwave: value added","vt":"str"},{"t":"eq","v":"zwave: notification","vt":"str"},{"t":"eq","v":"zwave: value changed","vt":"str"},{"t":"eq","v":"zwave: node ready","vt":"str"},{"t":"eq","v":"zwave: scan complete","vt":"str"}],"checkall":"true","outputs":7,"x":455,"y":180,"wires":[["2bbed007.b1598"],["f439bf74.64e93"],["5dd0df27.0fddb"],["c1c7ad30.1fe2"],["1000fdaf.d177e2"],["592813f3.15708c"],["b9cbd4d9.694eb8"]]},{"id":"2bbed007.b1598","type":"function","z":"c8fe91b8.daa3","name":"init nodes","func":"flow.set(\"nodes\", []);\nflow.set(\"scanComplete\", false);\n\n","outputs":"0","noerr":0,"x":698,"y":77,"wires":[]},{"id":"f439bf74.64e93","type":"function","z":"c8fe91b8.daa3","name":"add node","func":"var nodes = flow.get(\"nodes\");\nmsg.payload.values = [];\nmsg.payload.ready = false;\n\nnodes.push(msg.payload);\n\n","outputs":"0","noerr":0,"x":697,"y":111,"wires":[]},{"id":"5dd0df27.0fddb","type":"function","z":"c8fe91b8.daa3","name":"add value to node","func":"var nodes = flow.get(\"nodes\");\n\nvar n = nodes.find(n => n.nodeid == msg.payload.nodeid);\n\nvar value = msg.payload.value;\n\nn.values.push(value);\n\n//optionally send enablepool \nif(value.label == \"Temperature\"){\n node.send({topic: \"enablePool\", payload: {\"args\": [value.nodeid, value.cmdclass]}});\n}\n\n\n","outputs":"1","noerr":0,"x":727,"y":147,"wires":[[]]},{"id":"c1c7ad30.1fe2","type":"function","z":"c8fe91b8.daa3","name":"notification","func":"var nodes = flow.get(\"nodes\");\nvar nodeid = msg.payload.nodeid;\nvar n = nodes.find(n => n.nodeid == nodeid);\n\nswitch (msg.payload.notification) {\n case 0:\n console.log('node%d: message complete', nodeid);\n break;\n case 1:\n console.log('node%d: timeout', nodeid);\n break;\n case 2:\n console.log('node%d: nop', nodeid);\n break;\n case 3:\n console.log('node%d: node awake', nodeid);\n break;\n case 4:\n console.log('node%d: node sleep', nodeid);\n break;\n case 5:\n console.log('node%d: node dead', nodeid);\n n.ready = false;\n node.send({topic: \"node_status\", nodeid: nodeid, payload: false});\n break;\n case 6:\n console.log('node%d: node alive', nodeid);\n n.ready = true;\n node.send({topic: \"node_status\", nodeid: nodeid, payload: true});\n break;\n }\n","outputs":1,"noerr":0,"x":705,"y":186,"wires":[[]]},{"id":"1000fdaf.d177e2","type":"function","z":"c8fe91b8.daa3","name":"value changed","func":"var nodes = flow.get(\"nodes\");\n\nvar node = nodes.find(n => n.nodeid == msg.payload.nodeid);\n\nvar value = node.values.find(v => v.value_id == msg.payload.value.value_id);\n\nvalue.value = msg.payload.value.value;\n\n\nmsg.payload = value;\nmsg.topic = \"value_changed\";\n\nreturn msg;","outputs":"1","noerr":0,"x":713,"y":227,"wires":[["3c7afb39.71dd74"]]},{"id":"592813f3.15708c","type":"function","z":"c8fe91b8.daa3","name":"node ready","func":"var nodes = flow.get(\"nodes\");\n\nvar n = nodes.find(n => n.nodeid == msg.payload.nodeid);\n\nn.ready = true;\nn.type = msg.payload.nodeinfo.type;\n\n/* NODEINFO\n{\"manufacturer\":\"Qubino\",\"manufacturerid\":\"0x0159\",\"product\":\"ZMNHKDx Flush Heat and Cool thermostat\",\"producttype\":\"0x0005\",\"productid\":\"0x0052\",\"type\":\"Thermostat HVAC\",\"name\":\"\",\"loc\":\"\"}\n*/","outputs":"0","noerr":0,"x":705,"y":266,"wires":[]},{"id":"b9cbd4d9.694eb8","type":"function","z":"c8fe91b8.daa3","name":"scan complete","func":"var nodes = flow.get(\"nodes\");\n\nflow.set(\"scanComplete\", true);\n\nreturn [{payload:{}, topic: \"writeConfig\"}, {payload: nodes, topic: \"init\"}];","outputs":"2","noerr":0,"x":714,"y":304,"wires":[[],[]]},{"id":"cd817b4e.3f49b8","type":"link in","z":"c8fe91b8.daa3","name":"zwaveWrite","links":["59251ba3.b3c2b4","bea6132.04aacf","177b2fe1.737a1"],"x":975,"y":480,"wires":[["47d342b5.7563cc"]]},{"id":"47d342b5.7563cc","type":"zwave-out","z":"c8fe91b8.daa3","name":"write","controller":"93bf10ea.1c4c3","x":1090,"y":480,"wires":[[]]},{"id":"d2183a6d.1b43e8","type":"ui_dropdown","z":"c8fe91b8.daa3","name":"","label":"Nodes","place":"Select a node","group":"91eff83b.4842a8","order":0,"width":0,"height":0,"passthru":false,"options":[{"label":"","value":"","type":"str"}],"payload":"","topic":"","x":340,"y":480,"wires":[["84c3d05.479453"]]},{"id":"59fc0fa2.398dc","type":"function","z":"c8fe91b8.daa3","name":"parse options","func":"var nodes = msg.payload;\nvar options = [];\n\nfor(var i=0;i<nodes.length; i++){\n var n = nodes[i];\n var tmp = {};\n var title = n.nodeid +\": \"+n.type;\n tmp[title] = n.nodeid;\n options.push(tmp);\n}\n\nmsg.options = options;\n\nreturn msg;","outputs":1,"noerr":0,"x":160,"y":480,"wires":[["d2183a6d.1b43e8"]]},{"id":"84c3d05.479453","type":"function","z":"c8fe91b8.daa3","name":"","func":"var n = flow.get(\"nodes\").find(n => n.nodeid == msg.payload);\n\nmsg.payload = n.values;\n\nmsg.topic = \"init\";\n\nreturn msg;","outputs":1,"noerr":0,"x":485,"y":480,"wires":[["a9ba8c06.0593c"]]},{"id":"a9ba8c06.0593c","type":"ui_template","z":"c8fe91b8.daa3","group":"d8a503ba.8c0ad","name":"config","order":0,"width":"20","height":"20","format":"<style>\nmd-tooltip .md-content {\n height: auto !important;\n max-width: 200px !important;\n font-size: 13px !important;\n}\n\nmd-tooltip {\n height: auto !important;\n max-width: 200px !important;\n font-size: 13px !important;\n overflow: visible !important;\n white-space: normal !important;\n}\n\nmd-tooltip ._md-content {\n height: auto !important;\n max-width: 200px !important;\n font-size: 13px !important;\n}\n</style>\n\n<div ng-repeat=\"v in values\">\n <label>\n {{v.label}}\n <md-tooltip ng-if=\"v.help\" md-direction=\"right\">{{v.help}}</md-tooltip>\n <select ng-if=\"v.values\" id=\"{{v.value_id}}\" ng-model=\"v.value\" ng-change=\"updateValue(v)\" ng-options=\"value as value for (key , value) in v.values\">\n </select>\n <div ng-if=\"!v.values\">\n <input type=\"text\" id=\"{{v.value_id}}\" ng-model=\"v.value\" ng-disabled=\"v.read_only\">\n <button ng-if=\"!v.read_only\" ng-click=\"updateValue(v)\">Submit</button>\n </div>\n </label>\n</div>\n\n<script>\n(function(scope) {\n \n scope.values = [];\n scope.send({topic:\"load\"});\n \n scope.updateValue = function(v){\n var newValue = $('#'+v.value_id).val();\n if(v.values) newValue = newValue.split(':')[1];\n var cmd = {};\n cmd.nodeid = v.node_id;\n cmd.cmdclass = v.class_id;\n cmd.instance = v.instance;\n cmd.cmdidx = v.index;\n cmd.value = newValue;\n v.value = \"\"; //to check updating\n scope.send({payload: cmd, topic: \"setValue\"});\n }\n \n scope.$watch('msg', function (msg) { \n if(msg){\n switch(msg.topic){\n case \"init\":\n if(scope.values.length == 0)\n scope.values = msg.payload;\n break;\n case \"value_changed\":\n var value = scope.values.find(n => n.value_id == msg.payload.value_id);\n if(value)\n value.value = msg.payload.value;\n break;\n }\n }\n }); \n\n})(scope);\n\n\n</script>\n","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":625,"y":480,"wires":[["30212724.e08828"]]},{"id":"30212724.e08828","type":"switch","z":"c8fe91b8.daa3","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"setValue","vt":"str"},{"t":"eq","v":"load","vt":"str"}],"checkall":"true","outputs":2,"x":767,"y":480,"wires":[["59251ba3.b3c2b4"],["85f1bb2a.ed8278"]]},{"id":"59251ba3.b3c2b4","type":"link out","z":"c8fe91b8.daa3","name":"writeConfig","links":["cd817b4e.3f49b8"],"x":876,"y":480,"wires":[]},{"id":"a7c360ff.c8e3","type":"link in","z":"c8fe91b8.daa3","name":"configs","links":["3c7afb39.71dd74"],"x":565,"y":420,"wires":[["a9ba8c06.0593c"]]},{"id":"85f1bb2a.ed8278","type":"change","z":"c8fe91b8.daa3","name":"load","rules":[{"t":"set","p":"payload","pt":"msg","to":"nodes","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":560,"wires":[["59fc0fa2.398dc"]]},{"id":"3c7afb39.71dd74","type":"link out","z":"c8fe91b8.daa3","name":"value_changed","links":["a7c360ff.c8e3"],"x":857,"y":227,"wires":[]},{"id":"185c5e39.6f0d12","type":"inject","z":"c8fe91b8.daa3","name":"","topic":"","payload":"nodes","payloadType":"flow","repeat":"","crontab":"","once":false,"x":120,"y":280,"wires":[["589071d.12e7d9"]]},{"id":"589071d.12e7d9","type":"debug","z":"c8fe91b8.daa3","name":"","active":true,"console":"false","complete":"false","x":305,"y":280,"wires":[]},{"id":"8afcb2c1.c6946","type":"inject","z":"c8fe91b8.daa3","name":"healNetwork","topic":"healNetwork","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"x":132,"y":327,"wires":[["bea6132.04aacf"]]},{"id":"bea6132.04aacf","type":"link out","z":"c8fe91b8.daa3","name":"healNetwork","links":["cd817b4e.3f49b8"],"x":282,"y":327,"wires":[]},{"id":"171a9cd4.bb6a53","type":"inject","z":"c8fe91b8.daa3","name":"softReset","topic":"softReset","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"x":120,"y":371,"wires":[["177b2fe1.737a1"]]},{"id":"177b2fe1.737a1","type":"link out","z":"c8fe91b8.daa3","name":"softReset","links":["cd817b4e.3f49b8"],"x":281,"y":371,"wires":[]},{"id":"93bf10ea.1c4c3","type":"zwave-controller","z":"","port":"/dev/ttyACM0","driverattempts":"3","pollinterval":"10000","allowunreadyupdates":false,"logging":"full"},{"id":"91eff83b.4842a8","type":"ui_group","z":"","name":"Seleziona nodo","tab":"f990693c.63b0e8","disp":true,"width":"6"},{"id":"d8a503ba.8c0ad","type":"ui_group","z":"","name":"Config","tab":"f990693c.63b0e8","disp":true,"width":"20"},{"id":"f990693c.63b0e8","type":"ui_tab","z":"","name":"Configurazione","icon":"dashboard"}] |
Nice flow - it really jump started my understanding of node-red and this zwave module. A few notes though to help others in the future:
- node-red-dashboard component is needed for this flow. UI is then accessible at the node-red-url/ui
- Some devices (ie: Everspring Leak Detector) apparently emit "node event" for sensor changes, which is not a part of this flow.
- *The UI did not work reliably for me; namely I couldn't change node selection more than once. To resolve, in the JS code in the 'config' node for the "init" case, I removed the conditional such that scope.values is always updated.
- Be sure to restart node-red after importing this module, otherwise the UI will not show any nodes.
- FYI, instead of configuring your device for /dev/ttyACM0, using UDEV rules will be more reliable if you have a lot of USB devices connected.
-- Use "lsusb -v | less" to find your device. Look specifically for the vendor and product ID.
-- For the Aeotec Gen5 Stick, your udev rule (to be placed in /etc/udev/rules.d/99-my.rules) would be: SUBSYSTEM=="tty", ATTRS{idVendor}=="0658", ATTRS{idProduct}=="0200",
SYMLINK+="zwave"
very good flow! Helped me a lot to understand z-wave.
I have the description of this command below in order to get a report from node 3 (devolo meter switch)
[(https://www.devolo.de/fileadmin/Web-Content/DE/Products/HC_Schalt-_und_Messsteckdose_2.0/Documents/DE/Full_Manual_Home_Control_Schalt_Messsteckdose_V2_0317_DE.pdf)]
Meter Get Command: [Command Class Meter, Meter Get, Scale =0x02(W)]
What do I have to put into your report field to get the report?
If someone is interested in a zwave control panel nodeJS based that also works as zwave to MQTT gateway check my proejct: Zwave2MQTT
very nice flow! is it posible to show the parameter ID in the node's config? So we can found the right parameter from the tec. spec.
Thank you.
@jccoral can you give me some more information about your error? Try to identify the node that cause this error, just check the debug on node-red: at the top of the log line with the error you should see the
node <nodeID>
where<nodeID>
is the reference ID of the node, If you press there the node is highlighted so you can check where I have used.find
and just add a check that the array is not undefined before using.find()
.