I took the opportunity to show how a custom UDP protocol subflow can be implemented. As an example I used the LightwaveRF UDP protocol.
@bobbles This may be complicated to setup, if you want to try it and need some help I’d be happy to setup a Teams meeting
With this flow no external service or anything except the subflow and other nodes in NR are needed, there may still be bugs, especially if the API docs are not correct or your firmware version is not new enough to use JSON output and unicast UDP packets. So far only “Lighting & Power” is implemented, but it would be easy to add additional parts of the API.
The following portmapping needs to be added to the NR config (sudo nano /etc/core/oll-node-red.conf):
PORT_MAP+=("CORE_IP_HERE:9761:9761/udp")
Some screenshots:
The flow itself is fairly large:
[{"id":"1dba3faa997ebb49","type":"subflow","name":"LightwaveRF Command","info":"For API documentation, see here:\nhttp://api.lightwaverf.com/\n\nTo override room, set msg.room to a number between 1 and 15.\n\nTo override device, set msg.device to a number between 0 and 16. 0 = all devices for those commands which support that.\n\nTo override on/off or open/close, send a msg.payload with a boolean.\n\nTo override dimlevel, set msg.dimlevel to a number between 0 and 100.\n\nTo override colour, set msg.colour to a number between 1 and 20.\n\nTo override mood, set msg.mood to a number between 1 and 5.\n\nTo override the command, set msg.command to an expected string (see the msg.payload to API call function for details).\n","category":"","in":[{"x":60,"y":40,"wires":[{"id":"413a2b6b423a69de"}]}],"out":[{"x":960,"y":100,"wires":[{"id":"4fe8355503263fe6","port":0}]}],"env":[{"name":"room","type":"num","value":"","ui":{"label":{"en-US":"Room"},"type":"spinner","opts":{"min":0,"max":15}}},{"name":"device","type":"num","value":"","ui":{"label":{"en-US":"Device"},"type":"spinner","opts":{"min":0,"max":16}}},{"name":"command","type":"str","value":"","ui":{"label":{"en-US":"Command"},"type":"select","opts":{"opts":[{"l":{"en-US":"Register"},"v":"register"},{"l":{"en-US":"De-Register"},"v":"de-register"},{"l":{"en-US":"On"},"v":"on"},{"l":{"en-US":"Off"},"v":"off"},{"l":{"en-US":"Dim"},"v":"dim"},{"l":{"en-US":"Open"},"v":"open"},{"l":{"en-US":"Close"},"v":"close"},{"l":{"en-US":"Stop"},"v":"stop"},{"l":{"en-US":"Colour"},"v":"colour"},{"l":{"en-US":"Cycle Colour"},"v":"colourcycle"},{"l":{"en-US":"Store Mood"},"v":"storemood"},{"l":{"en-US":"Recall Mood"},"v":"recallmood"}]}}},{"name":"dimlevel","type":"num","value":"","ui":{"label":{"en-US":"dimlevel"},"type":"spinner","opts":{"min":0,"max":100}}},{"name":"colour","type":"str","value":"","ui":{"label":{"en-US":"Colour"},"type":"select","opts":{"opts":[{"l":{"en-US":"Undefined"},"v":""},{"l":{"en-US":"White"},"v":"1"},{"l":{"en-US":"Green White"},"v":"2"},{"l":{"en-US":"Red White"},"v":"3"},{"l":{"en-US":"Yellow White"},"v":"4"},{"l":{"en-US":"Red"},"v":"5"},{"l":{"en-US":"Middle Red"},"v":"6"},{"l":{"en-US":"Pink"},"v":"7"},{"l":{"en-US":"Light Red"},"v":"8"},{"l":{"en-US":"Orange"},"v":"9"},{"l":{"en-US":"Middle Orange"},"v":"10"},{"l":{"en-US":"Green White"},"v":"11"},{"l":{"en-US":"Light Orange"},"v":"12"},{"l":{"en-US":"Green"},"v":"13"},{"l":{"en-US":"Middle Green"},"v":"14"},{"l":{"en-US":"Light Yellow Green"},"v":"15"},{"l":{"en-US":"Yellow Green"},"v":"16"},{"l":{"en-US":"Blue"},"v":"17"},{"l":{"en-US":"Water Blue"},"v":"18"},{"l":{"en-US":"Light Blue"},"v":"19"},{"l":{"en-US":"White Blue"},"v":"20"}]}}},{"name":"mood","type":"str","value":"","ui":{"label":{"en-US":"Mood"},"type":"select","opts":{"opts":[{"l":{"en-US":"1"},"v":"1"},{"l":{"en-US":"2"},"v":"2"},{"l":{"en-US":"3"},"v":"3"},{"l":{"en-US":"Entry"},"v":"4"},{"l":{"en-US":"Exit"},"v":"5"}]}}}],"meta":{},"color":"#DAC4B4","inputLabels":["trigger"],"outputLabels":["command"],"icon":"font-awesome/fa-lightbulb-o","status":{"x":920,"y":40,"wires":[{"id":"60aedbda44f185cc","port":0}]}},{"id":"413a2b6b423a69de","type":"change","z":"1dba3faa997ebb49","name":"Set Message from Properties","rules":[{"t":"set","p":"payload_input","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"payload.room","pt":"msg","to":"room","tot":"env"},{"t":"set","p":"payload.device","pt":"msg","to":"device","tot":"env"},{"t":"set","p":"payload.command","pt":"msg","to":"command","tot":"env"},{"t":"set","p":"payload.dimlevel","pt":"msg","to":"dimlevel","tot":"env"},{"t":"set","p":"payload.colour","pt":"msg","to":"colour","tot":"env"},{"t":"set","p":"payload.mood","pt":"msg","to":"mood","tot":"env"}],"action":"","property":"","from":"","to":"","reg":false,"x":240,"y":40,"wires":[["ee40b055f41c0181"]]},{"id":"ee40b055f41c0181","type":"function","z":"1dba3faa997ebb49","name":"msg.payload to API call","func":"api_call = {\n \"register\": \"F*p\"\n}\n\nmsg.property_payload = msg.payload;\nlet cmd = {\n roomNo: null,\n deviceNo: null,\n functionType: null,\n parameter: null,\n line1: msg.payload.line1 !== undefined ? msg.payload.line1 : null,\n line2: msg.payload.line2 !== undefined ? msg.payload.line2 : null\n};\nif(msg.room !== undefined) msg.payload.room = msg.room;\nif(msg.device !== undefined) msg.payload.device = msg.device;\nif(msg.command !== undefined) msg.payload.command = msg.command;\nif(msg.dimlevel !== undefined) msg.payload.dimlevel = msg.dimlevel;\nif(msg.colour !== undefined) msg.payload.colour = msg.colour;\nif(msg.mood !== undefined) msg.payload.mood = msg.mood;\nmsg.payload.command = msg.payload.command.toLowerCase();\nif(typeof msg.payload_input == 'boolean') {\n switch(msg.payload.command) {\n case \"on\":\n case \"off\":\n msg.payload.command = msg.payload_input ? \"on\" : \"off\";\n break;\n case \"open\":\n case \"close\":\n msg.payload.command = msg.payload_input ? \"open\" : \"close\";\n break;\n }\n}\nlet cmdPrint = msg.payload.command[0].toUpperCase() + msg.payload.command.slice(1).toLowerCase();\n// Colours are defined here: http://api.lightwaverf.com/lighting_power.html\nlet colourMap = {\n 1: \"White\",\n 2:\"Green White\",\n 3: \"Red White\",\n 4: \"Yellow White\",\n 5: \"Red\",\n 6: \"Middle Red\",\n 7: \"Pink\",\n 8: \"Light Red\",\n 9: \"Orange\",\n 10: \"Middle Orange\",\n 11: \"Green White\",\n 12: \"Light Orange\",\n 13: \"Green\",\n 14: \"Middle Green\",\n 15: \"Light Yellow Green\",\n 16: \"Yellow Green\",\n 17: \"Blue\",\n 18: \"Water Blue\",\n 19: \"Light Blue\",\n 20: \"White Blue\",\n}\nlet hasRoomAndDevice = true;\nlet hasRoom = null;\nswitch(msg.payload.command.toLowerCase()) {\n case \"register\":\n cmd.functionType = \"*\";\n cmd.parameter = \"\";\n hasRoomAndDevice = false;\n break;\n case \"deregister\":\n case \"de-register\":\n cmd.functionType = \"*x\";\n cmd.parameter = \"\";\n hasRoomAndDevice = false;\n break;\n case \"on\":\n cmd.functionType = \"1\";\n cmd.bool = true;\n break;\n case \"off\":\n if (msg.payload.device == 0) {\n hasRoom = true;\n hasRoomAndDevice = false;\n cmd.functionType = \"a\";\n } else {\n cmd.functionType = \"0\"; \n }\n cmd.bool = false;\n break;\n case \"alloff\":\n hasRoom = true;\n hasRoomAndDevice = false;\n cmd.functionType = \"a\";\n cmd.bool = false;\n break;\n case \"dim\":\n cmd.functionType = \"d\";\n let level = msg.payload.dimlevel < 0 ? 0 : msg.payload.dimlevel > 100 ? 100 : msg.payload.dimlevel;\n cmd.parameter = Math.round((32 / 100) * level ).toString();\n cmd.line1 = cmdPrint + ' ' + level.toString() + '%';\n cmd.bool = level > 0;\n break;\n case \"open\":\n cmd.functionType = \"(\";\n cmd.bool = true;\n break;\n case \"close\":\n cmd.functionType = \")\";\n cmd.bool = false;\n break;\n case \"stop\":\n cmd.functionType = \"^\";\n break;\n case \"colour\":\n cmd.functionType = \"*c\";\n let colour = msg.payload.colour < 1 ? 1 : msg.payload.colour > 20 ? 20 : msg.payload.colour;\n cmd.parameter = colour.toString();\n cmd.line1 = 'C:' + colourMap[colour];\n break;\n case \"colourcycle\":\n cmd.functionType = \"*y\";\n break;\n case \"storemood\":\n hasRoom = true;\n hasRoomAndDevice = false;\n cmd.functionType = \"s\";\n let mood = msg.payload.mood < 1 ? 1 : msg.payload.mood > 5 ? 5 : msg.payload.mood;\n cmd.parameter = mood.toString();\n cmd.line1 = 'StrMood:' + (mood < 4 ? mood.toString() : mood == 4 ? 'Entry' : mood == 5 ? 'Exit' : 'Unknown');\n break;\n case \"recallmood\":\n hasRoom = true;\n hasRoomAndDevice = false;\n cmd.functionType = \"m\";\n let moodRec = msg.payload.mood < 1 ? 1 : msg.payload.mood > 5 ? 5 : msg.payload.mood;\n cmd.parameter = moodRec.toString();\n cmd.line1 = 'RecMood:' + (moodRec < 4 ? moodRec.toString() : moodRec == 4 ? 'Entry' : moodRec == 5 ? 'Exit' : 'Unknown');\n break;\n}\nhasRoom = hasRoom == null ? hasRoomAndDevice : hasRoom;\nif (cmd.line1 == null) {\n cmd.line1 = cmdPrint;\n}\nif (hasRoom) {\n cmd.roomNo = msg.payload.room > 0 ? msg.payload.room.toString() : null;\n}\nif (hasRoomAndDevice) {\n cmd.deviceNo = (msg.payload.device !== undefined && msg.payload.device > 0) ? msg.payload.device.toString() : null;\n}\nif (cmd.line2 == null && (hasRoomAndDevice || hasRoom)) {\n cmd.line2 = `${cmd.roomNo !== null ? 'R:' + cmd.roomNo : ''}` + \n `${cmd.deviceNo !== null ? ', D:' + cmd.deviceNo : ''}`;\n}\ncmdStr = `${cmd.roomNo !== null ? 'R' + cmd.roomNo : ''}` + \n `${cmd.deviceNo !== null ? 'D' + cmd.deviceNo : ''}` + \n `F${cmd.functionType}` + \n `${cmd.parameter !== null ? 'P' + cmd.parameter : ''}` +\n `${cmd.line1 !== null ? '|' + cmd.line1 : ''}` + \n `${cmd.line2 !== null ? '|' + cmd.line2 : ''}`;\n\nif (msg.topic === undefined || msg.topic == '') {\n msg.topic = 'lightwaverf/' + `${cmd.roomNo !== null ? cmd.roomNo + '/' : ''}` + `${cmd.deviceNo !== null ? cmd.deviceNo + '/' : ''}` + msg.payload.command.toLowerCase()\n}\nmsg.payload = \"!\" + cmdStr;\nmsg.cmd = cmd;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":40,"wires":[["4fe8355503263fe6","60aedbda44f185cc"]]},{"id":"4fe8355503263fe6","type":"function","z":"1dba3faa997ebb49","name":"Add Transaction #","func":"let trans = global.get(\"lw_messageCounter\");\ntrans = trans === undefined ? 1 : trans + 1;\nif (trans > 4294967295) trans = 1;\nglobal.set(\"lw_messageCounter\", trans);\n// node.status({fill: 'green', shape: \"dot\", text: trans });\ntransStr = trans.toString();\nwhile(transStr.length < 3) {\n transStr = '0' + transStr;\n}\n\nmsg.payload = transStr + \",\" + msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":100,"wires":[[]]},{"id":"60aedbda44f185cc","type":"function","z":"1dba3faa997ebb49","name":"Set Status","func":"let colour = msg.cmd.bool == undefined ? 'black' : msg.cmd.bool == true ? 'green' : 'red';\nmsg.payload = {fill: colour, shape: \"dot\", text: msg.cmd.line1 + (msg.cmd.line2 != '' && msg.cmd.line2 != null ? ' | ' + msg.cmd.line2 : '') }\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":40,"wires":[[]]},{"id":"0c8427c5c139a88e","type":"tab","label":"LightwaveRF","disabled":false,"info":"http://api.lightwaverf.com/\n[https://github.com/ollieparsley/node-lightwaverf/blob/master/index.js](Sample CODE)\n","env":[]},{"id":"eccf14cc5fb112e8","type":"group","z":"0c8427c5c139a88e","name":"LightwaveRF Simulator","style":{"stroke":"#ff0000","fill":"#ffdf7f","label":true,"color":"#000000"},"nodes":["d3aa0a8a2001c234","990b07dfced64a10","d7d61be428baad50","3fb2a5348ab42928","6122b0ee8964028e","d94fb4f7aa5335b4","3745b2bcc95ec6bc","1e2abf2a779976f2","33b2dd0b445feecb","6ec1e7d3c5302bab","b782e1461b191e7a","1b687031da818283","a22218b62d155884"],"x":54,"y":79,"w":1732,"h":222},{"id":"959c014f5f9b2e90","type":"udp in","z":"0c8427c5c139a88e","name":"LightwaveRF (9761) IN","iface":"","port":"9761","ipv":"udp4","multicast":"false","group":"","datatype":"utf8","x":260,"y":1700,"wires":[["d04cc6ae17ff6399","f7546de1653cd67f"]]},{"id":"e5d6bd505620676d","type":"udp out","z":"0c8427c5c139a88e","name":"LightwaveRF (9760) OUT","addr":"192.168.9.1","iface":"","port":"9760","ipv":"udp4","outport":"","base64":false,"multicast":"false","x":1390,"y":1080,"wires":[]},{"id":"545694b3e7ca4548","type":"inject","z":"0c8427c5c139a88e","name":"Register","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":540,"wires":[["24677677b6710afb"]]},{"id":"c391539786c435e7","type":"debug","z":"0c8427c5c139a88e","name":"RAW Cmd","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1370,"y":980,"wires":[]},{"id":"be37a978526b716b","type":"link out","z":"0c8427c5c139a88e","name":"Simulated LighwaveRF Command - OUT","mode":"link","links":["d3aa0a8a2001c234","11cb3bd7cfa214d0"],"x":1440,"y":1040,"wires":[],"l":true},{"id":"d3aa0a8a2001c234","type":"link in","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Simulated LighwaveRF Command - IN","links":["be37a978526b716b"],"x":230,"y":160,"wires":[["990b07dfced64a10"]],"l":true},{"id":"990b07dfced64a10","type":"function","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"LighwaveRF Simulator","func":"// Split the command\nlet cmd_regex = /^(?<trans>[0-9]+),\\!(?<roomNo>([Rr][0-9]*)?)?(?<deviceNo>[Dd][0-9]*)?(?<functionType>([Ff]\\*?[^Pp]?))(?<parameter>([Pp][a-zA-Z0-9]*)?)?(?<line1>\\|[^\\|]*)?(?<line2>\\|[^\\|]*)?$/;\n// cmd_regex = /^\\!(?<functionType>[Ff][\\*a-zA-Z01])(?<parameter>[Pp][a-zA-Z0-9]*)?$/;\nlet cmd_groups = msg.payload.match(cmd_regex).groups;\ncmd_groups.trans = parseInt(cmd_groups.trans, 10);\ncmd_groups.roomNo = cmd_groups.roomNo === undefined ? null : parseInt(cmd_groups.roomNo.slice(1), 10);\ncmd_groups.deviceNo = cmd_groups.deviceNo === undefined ? null : parseInt(cmd_groups.deviceNo.slice(1), 10);\ncmd_groups.functionType = cmd_groups.functionType.slice(1);\ncmd_groups.parameter = cmd_groups.parameter === undefined ? '' : cmd_groups.parameter.slice(1);\ncmd_groups.line1 = cmd_groups.line1 === undefined ? \"\" : cmd_groups.line1.slice(1, 16);\ncmd_groups.line2 = cmd_groups.line2 === undefined ? \"\" : cmd_groups.line2.slice(1, 16);\n// for (const [key, value] of Object.entries(cmd_groups)) {\n// if(key != 'trans') {\n// cmd_groups[key] = value.slice(1);\n// }\n \n// }\n\nmsg.cmd_groups = cmd_groups;\n\nlet simulatedRegistered = flow.get(\"lw_simulatedRegistered\");\nnode.status({fill: 'green', shape: \"dot\", text: cmd_groups.line1 + (cmd_groups.line2 != '' ? ' | ' : '') + cmd_groups.line2 });\nlet response = {\n trans: null,\n mac: null,\n time: null\n}\n\nif(!simulatedRegistered) {\n response.pkt = \"error\";\n response.fn = \"nonRegistered\";\n if(cmd_groups.functionType == '*' && cmd_groups.parameter == '') {\n response.payload = \"Not yet registered. See LightwaveLink\";\n // flow.set(\"lw_simulatedRegistered\", true);\n } else {\n response.payload = \"Not yet registered. Send !F*p to register\";\n }\n} else {\n response.pkt = \"433T\";\n switch(cmd_groups.functionType) {\n case \"*\":\n response.pkt = 'OK';\n break;\n case \"*x\":\n response.pkt = 'OK';\n flow.set(\"lw_simulatedRegistered\", false)\n // Update the registration status\n node.send([null, {}]);\n break;\n case \"1\":\n response.fn = 'on';\n break;\n case \"0\":\n response.fn = 'off';\n break;\n case \"a\":\n response.fn = 'allOff';\n break;\n case \"(\":\n response.fn = 'open';\n break;\n case \")\":\n response.fn = 'close';\n break;\n case \"^\":\n response.fn = 'stop';\n break;\n case \"d\":\n response.fn = 'dim';\n break;\n case \"*c\":\n response.fn = 'ledColour';\n break;\n case \"*y\":\n response.fn = 'ledColourCycle';\n break;\n case \"s\":\n response.fn = 'moodStore';\n break;\n case \"m\":\n response.fn = 'moodRecall';\n break;\n default:\n response.fn = 'unknown';\n break;\n }\n if(cmd_groups.deviceNo != null) {\n response.dev = cmd_groups.deviceNo;\n }\n if(cmd_groups.roomNo != null) {\n response.room = cmd_groups.roomNo;\n // In the real world multiple devices would respond, in this simulator device 1 always responds if no device was specified\n if(response.dev == null) response.dev = 1;\n }\n if(cmd_groups.parameter != '') {\n response.param = parseInt(cmd_groups.parameter);\n }\n}\nmsg.payload = response;\nif(response.fn == \"nonRegistered\") {\n return [msg, msg]; \n} else {\n return [msg, null];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":160,"wires":[["b782e1461b191e7a"],["6ec1e7d3c5302bab","3745b2bcc95ec6bc"]]},{"id":"d7d61be428baad50","type":"debug","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Response","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1680,"y":220,"wires":[]},{"id":"e5567cd782ee0adb","type":"change","z":"0c8427c5c139a88e","name":"Reset global.lw_messageCounter","rules":[{"t":"set","p":"lw_messageCounter","pt":"global","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":400,"wires":[[]]},{"id":"6836833a9b80766f","type":"inject","z":"0c8427c5c139a88e","name":"Reset Message Counter","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":190,"y":400,"wires":[["e5567cd782ee0adb"]]},{"id":"3fb2a5348ab42928","type":"inject","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Reset Simulated Transaction Counter","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":120,"wires":[["6122b0ee8964028e"]]},{"id":"6122b0ee8964028e","type":"change","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Reset flow.lw_simulatedTransactionCounter","rules":[{"t":"set","p":"lw_simulatedTransactionCounter","pt":"flow","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":120,"wires":[[]]},{"id":"d94fb4f7aa5335b4","type":"link out","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Simulated LighwaveRF Response - OUT","mode":"link","links":["3e4f674c22295fd8"],"x":1380,"y":260,"wires":[],"l":true},{"id":"3e4f674c22295fd8","type":"link in","z":"0c8427c5c139a88e","name":"Simulated LighwaveRF Response - IN","links":["d94fb4f7aa5335b4","4cf32f7005d73049"],"x":210,"y":1660,"wires":[["d04cc6ae17ff6399"]],"l":true},{"id":"d04cc6ae17ff6399","type":"function","z":"0c8427c5c139a88e","name":"Parse response JSON","func":"if (msg.payload.startsWith(\"*!\")) {\n msg.payload = JSON.parse(msg.payload.slice(2));\n let type = msg.payload.type !== undefined ? msg.payload.type : msg.payload.pkt !== undefined ? msg.payload.pkt : 'link';\n let room = msg.payload.room !== undefined ? '/' + msg.payload.room.toString() : '';\n let device = msg.payload.dev !== undefined ? '/' + msg.payload.dev.toString() : '';\n msg.topic = 'lightwaverf/' + type + room + device\n return msg;\n}\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":1660,"wires":[["c2640e76b05f1e9e","9db7cba00b4d9dc6"]]},{"id":"c2640e76b05f1e9e","type":"debug","z":"0c8427c5c139a88e","name":"Parsed Response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1130,"y":1660,"wires":[]},{"id":"3745b2bcc95ec6bc","type":"function","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"LighwaveRF Simulator Registration Status","func":"let simulatedRegistered = flow.get(\"lw_simulatedRegistered\");\nnode.status({fill: simulatedRegistered ? 'green' : 'red', shape: \"dot\", text: simulatedRegistered ? 'registered' : 'unregistered' });\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":180,"wires":[[]]},{"id":"1e2abf2a779976f2","type":"function","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Send Registration packet","func":"if (msg.payload !== undefined && msg.payload.payload == 'Not yet registered. See LightwaveLink') {\n let response = {\n trans: null,\n mac: null,\n time: null,\n type: \"link\",\n prod: \"lwl\",\n pairType: \"local\",\n msg: \"success\",\n class: \"\",\n serial: \"\"\n }\n flow.set(\"lw_simulatedRegistered\", true);\n msg.payload = response;\n return msg;\n}\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":260,"wires":[["3745b2bcc95ec6bc","b782e1461b191e7a","1b687031da818283"]]},{"id":"33b2dd0b445feecb","type":"function","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"LighwaveRF Simulator - Stringify payload","func":"msg.payload = \"*!\" + JSON.stringify(msg.payload);\n// Deleting parts of the message to be more like the \"real\" replies\ndelete msg.topic;\ndelete msg.property_payload;\ndelete msg.cmd;\ndelete msg.cmd_groups;\ndelete msg.payload_input;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1400,"y":220,"wires":[["d94fb4f7aa5335b4","d7d61be428baad50"]]},{"id":"6ec1e7d3c5302bab","type":"delay","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":540,"y":220,"wires":[["1e2abf2a779976f2"]]},{"id":"14c8b49b8e652a16","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":740,"wires":[["95d6469b66f310fb"]]},{"id":"b782e1461b191e7a","type":"function","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Set transaction #, mac and timestamp","func":"let trans = flow.get(\"lw_simulatedTransactionCounter\");\ntrans = trans == undefined ? 1 : trans + 1;\nnode.status({fill: 'green', shape: \"dot\", text: trans.toString() });\nflow.set(\"lw_simulatedTransactionCounter\", trans);\nvar d = new Date();\nmsg.payload.trans = trans;\nmsg.payload.mac = \"21:34:56\";\nmsg.payload.time = Math.round(d.getTime() / 1000);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1390,"y":160,"wires":[["33b2dd0b445feecb"]]},{"id":"9db7cba00b4d9dc6","type":"switch","z":"0c8427c5c139a88e","name":"Room & Device","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"lightwaverf/433T/1/5","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":980,"y":1720,"wires":[["5725ce5842405aee"]]},{"id":"5725ce5842405aee","type":"debug","z":"0c8427c5c139a88e","name":"Room 1 Dev 5","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1200,"y":1720,"wires":[]},{"id":"cdd4091851e8797b","type":"inject","z":"0c8427c5c139a88e","name":"De-register","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":600,"wires":[["beed1f45d132d01f"]]},{"id":"1b687031da818283","type":"debug","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"Register","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1020,"y":260,"wires":[]},{"id":"f7546de1653cd67f","type":"debug","z":"0c8427c5c139a88e","name":"RAW Response","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":680,"y":1700,"wires":[]},{"id":"24677677b6710afb","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Register","env":[{"name":"command","value":"register","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":480,"y":540,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"beed1f45d132d01f","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"De-Register","env":[{"name":"command","value":"de-register","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":470,"y":600,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"a65771f757f48c81","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":130,"y":660,"wires":[["126975b7cc41b796"]]},{"id":"126975b7cc41b796","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - Device 5 - On","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"5","type":"num"},{"name":"command","value":"on","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":440,"y":660,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"556f7495d8fe02b9","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":130,"y":700,"wires":[["126975b7cc41b796"]]},{"id":"b16a97aa9fc5f2c9","type":"function","z":"0c8427c5c139a88e","name":"NOOP","func":"// This function just forward the message = NOOP\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1110,"y":1040,"wires":[["c391539786c435e7","be37a978526b716b","e5d6bd505620676d"]]},{"id":"95d6469b66f310fb","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - Device 5 - Off","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"5","type":"num"},{"name":"command","value":"off","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":440,"y":740,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"c0e503b645eee94d","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":800,"wires":[["446cd6df3ae541a8"]]},{"id":"446cd6df3ae541a8","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - Device 5 - On","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"5","type":"num"},{"name":"command","value":"on","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":440,"y":800,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"4948055d3ae4f7c2","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - All Devices - Off","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"0","type":"num"},{"name":"command","value":"off","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":430,"y":860,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"ef6f2c1ad48a4e1b","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":860,"wires":[["4948055d3ae4f7c2"]]},{"id":"5e30af4fdfae4f07","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 2 - Device 1 - Dim 50%","env":[{"name":"room","value":"2","type":"num"},{"name":"device","value":"1","type":"num"},{"name":"command","value":"dim","type":"str"},{"name":"dimlevel","value":"50","type":"num"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":420,"y":920,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"69c9280451def820","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":920,"wires":[["5e30af4fdfae4f07"]]},{"id":"fd19bd66e0f9b7d2","type":"inject","z":"0c8427c5c139a88e","name":"Dim 30%","props":[{"p":"dimlevel","v":"30","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":140,"y":960,"wires":[["5e30af4fdfae4f07"]]},{"id":"717515309b8db8da","type":"inject","z":"0c8427c5c139a88e","name":"Dim 0%","props":[{"p":"dimlevel","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":130,"y":1000,"wires":[["5e30af4fdfae4f07"]]},{"id":"c6d9e3441fc0447c","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":1160,"wires":[["1252fc710ba6678b"]]},{"id":"1d59ce6405dede51","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":1220,"wires":[["a5e2ef53045b578b"]]},{"id":"db75821d7a6f22dc","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":130,"y":1080,"wires":[["54bc540b07d6cde2"]]},{"id":"34e44b6bdb8159a0","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":130,"y":1040,"wires":[["54bc540b07d6cde2"]]},{"id":"54bc540b07d6cde2","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 2 - Device 3 - Open","env":[{"name":"room","value":"2","type":"num"},{"name":"device","value":"3","type":"num"},{"name":"command","value":"open","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":430,"y":1040,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"1252fc710ba6678b","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 2 - Device 3 - Open","env":[{"name":"room","value":"2","type":"num"},{"name":"device","value":"3","type":"num"},{"name":"command","value":"open","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":430,"y":1160,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"a5e2ef53045b578b","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 2 - Device 3 - Close","env":[{"name":"room","value":"2","type":"num"},{"name":"device","value":"3","type":"num"},{"name":"command","value":"close","type":"str"},{"name":"colour","value":null,"type":"str"},{"name":"mood","value":null,"type":"str"}],"x":430,"y":1220,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"44193e1f5294eb72","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 2 - Device 3 - Stop","env":[{"name":"room","value":"2","type":"num"},{"name":"device","value":"3","type":"num"},{"name":"command","value":"stop","type":"str"},{"name":"mood","value":null,"type":"str"}],"x":430,"y":1280,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"b5d5e212886b37b0","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":1280,"wires":[["44193e1f5294eb72"]]},{"id":"61943bed88ae34bc","type":"inject","z":"0c8427c5c139a88e","name":"stop","props":[{"p":"command","v":"stop","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":130,"y":1120,"wires":[["54bc540b07d6cde2"]]},{"id":"6b6b27d298964d47","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - Device 3 - Colour Orange","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"3","type":"num"},{"name":"command","value":"colour","type":"str"},{"name":"colour","value":"9","type":"str"},{"name":"mood","value":null,"type":"str"}],"x":400,"y":1340,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"4e18f83ff00e7a57","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":1340,"wires":[["6b6b27d298964d47"]]},{"id":"8dc121012645c8c2","type":"inject","z":"0c8427c5c139a88e","name":"White Blue","props":[{"p":"colour","v":"20","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":140,"y":1380,"wires":[["6b6b27d298964d47"]]},{"id":"018df32c762cc5e0","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - Device 3 - Cycle Colour","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"3","type":"num"},{"name":"command","value":"colourcycle","type":"str"},{"name":"mood","value":null,"type":"str"}],"x":410,"y":1420,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"cf40307b18d24878","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":1420,"wires":[["018df32c762cc5e0"]]},{"id":"e68115868c782c62","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - Store Mood 3","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"0","type":"num"},{"name":"command","value":"storemood","type":"str"},{"name":"mood","value":"3","type":"str"}],"x":440,"y":1480,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"20f0ab3029c15869","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":1480,"wires":[["e68115868c782c62"]]},{"id":"d0c6ef305fc00afd","type":"subflow:1dba3faa997ebb49","z":"0c8427c5c139a88e","name":"Room 1 - Recall Mood 3","env":[{"name":"room","value":"1","type":"num"},{"name":"device","value":"0","type":"num"},{"name":"command","value":"recallmood","type":"str"},{"name":"mood","value":"3","type":"str"}],"x":430,"y":1540,"wires":[["b16a97aa9fc5f2c9"]]},{"id":"2c2f408e02e09136","type":"inject","z":"0c8427c5c139a88e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":1540,"wires":[["d0c6ef305fc00afd"]]},{"id":"a22218b62d155884","type":"comment","z":"0c8427c5c139a88e","g":"eccf14cc5fb112e8","name":"READ THIS!!!","info":"The simulator is NOT needed to use an actual LightwaveRF device! It is only included to facilitate development.\nDisconnect the input to the simulator when running a REAL LightwaveRF device!","x":270,"y":220,"wires":[]},{"id":"80efdbed5cb728ae","type":"comment","z":"0c8427c5c139a88e","name":"Message Counter","info":"The emssage counter doesn't need to be reset, it is handled automatically, it's just here for debugging and convenience.","x":170,"y":360,"wires":[]},{"id":"a058bbfd39904354","type":"comment","z":"0c8427c5c139a88e","name":"--- Sample Commands ---","info":"","x":190,"y":500,"wires":[]},{"id":"98d4206e1828c1bd","type":"comment","z":"0c8427c5c139a88e","name":"--- Sample Input parsing ---","info":"Use topic to determine which message is for which device, see the switch sample \"Room & Device\".\n","x":170,"y":1620,"wires":[]}]
EDIT: Changed ‘p’ to ‘P’ in flow