Node-RED flow -- EVOK to MQTT to EVOK
-
Hey all,
The following Node-RED flow might be useful to people who like to tinker with home automation and use different systems or multiple Unipi Neuron devices in unison.
What it does, is take specific data from an MQTT stream of topics and translates it to the EVOK JSON API. This way you can control all the relays of your Unipi Neuron with the MQTT protocol.
It also announces all changes to the specified MQTT topics for all relay states (when changed) and announces press / long press for all digital inputs on the Unipi Neuron.These topics are used:
- /home/--name-of-neuron--/relay/--number-of-relay--/set [0/1]
- /home/--name-of-neuron--/relay/--number-of-relay--/state [0/1]
- /home/--name-of-neuron--/input/--number-of-input--/short/set [0/1]
- /home/--name-of-neuron--/input/--number-of-input--/short/state [0/1]
- /home/--name-of-neuron--/input/--number-of-input--/long/set [0/1]
- /home/--name-of-neuron--/input/--number-of-input--/long/state [0/1]
I'm currently using 2 Unipi Neuron L203 devices in the same network (this setup could even be used over the internet!) and needed a way for them to talk and listen to commands from my home automation system, which is currently Pimatic.
In a few weeks I want to expand even more, but with some homemade arduino based setup for outside use, which also communicates over MQTT ;-)Anyway, here is a look at my current Node-RED flow, running on the same RaspberryPi as my MQTT broker and Pimatic system.
Here is the code for the flow, just change the settings that are commented.
[ { "id": "49e50089.02aeb", "type": "tab", "label": "Flow 1", "disabled": false, "info": "" }, { "id": "21946c18.8d5ba4", "type": "websocket in", "z": "49e50089.02aeb", "name": "EVOK - Neuron 2", "server": "", "client": "c9e778d4.219068", "x": 160, "y": 400, "wires": [ [ "d67a87db.929288" ] ] }, { "id": "d67a87db.929288", "type": "json", "z": "49e50089.02aeb", "name": "", "x": 310, "y": 400, "wires": [ [ "58898526.d520cc" ] ] }, { "id": "58898526.d520cc", "type": "split", "z": "49e50089.02aeb", "name": "", "splt": "\\n", "x": 430, "y": 400, "wires": [ [ "c78bc1eb.6df81" ] ] }, { "id": "2b2f8289.2fdc6e", "type": "switch", "z": "49e50089.02aeb", "name": "", "property": "payload.dev", "propertyType": "msg", "rules": [ { "t": "eq", "v": "input", "vt": "str" }, { "t": "eq", "v": "relay", "vt": "str" }, { "t": "neq", "v": "ai", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 3, "x": 750, "y": 360, "wires": [ [ "e173eb6.0b37118", "46c95350.2e22fc" ], [ "d54243ad.10aad", "43e533dc.c7c6bc" ], [] ] }, { "id": "d54243ad.10aad", "type": "debug", "z": "49e50089.02aeb", "name": "", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "x": 950, "y": 400, "wires": [] }, { "id": "43e533dc.c7c6bc", "type": "function", "z": "49e50089.02aeb", "name": "Relay Status to MQTT", "func": "newMsg = {};\n\nrelayName = msg.payload.device+\"_relay_\"+msg.payload.circuit;\npreviousRelayState = flow.get(relayName)||0;\nrelayState = msg.payload.value;\nflow.set(relayName,msg.payload.value);\n\nif (relayState != previousRelayState) {\n newMsg.topic = \"/home/\"+msg.payload.device+\"/relay/\"+msg.payload.circuit+\"/state\";\n newMsg.payload = msg.payload.value;\n return [newMsg];\n} else {\n}", "outputs": 1, "noerr": 0, "x": 980, "y": 360, "wires": [ [ "bf555b66.8c8698", "547a9c49.66a784" ] ] }, { "id": "bf555b66.8c8698", "type": "debug", "z": "49e50089.02aeb", "name": "", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "x": 1230, "y": 420, "wires": [] }, { "id": "547a9c49.66a784", "type": "mqtt out", "z": "49e50089.02aeb", "name": "MQTT", "topic": "", "qos": "", "retain": "", "broker": "2090ed8f.9d8142", "x": 1210, "y": 360, "wires": [] }, { "id": "46c95350.2e22fc", "type": "debug", "z": "49e50089.02aeb", "name": "", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "x": 950, "y": 280, "wires": [] }, { "id": "e173eb6.0b37118", "type": "function", "z": "49e50089.02aeb", "name": "Input Status to MQTT", "func": "newMsg = {};\n\ninputState = msg.payload.value;\ninputPort = msg.payload.device+\"_input_\"+msg.payload.circuit;\n\ninputPressed = inputPort+\"_pressed\";\ntimerLast = inputPort+\"_time\";\ninputTimer = inputPort+\"_timer\"\ninputLongName = inputPort+\"_long\";\ninputShortName = inputPort+\"_short\";\n\nnewMsg.topic = \"/home/\"+msg.payload.device+\"/input/\"+msg.payload.circuit;\n\nprevLongValue = flow.get(inputLongName)||0;\nprevShortValue = flow.get(inputShortName)||0;\n\nvar interval = (750*1); // minimum interval between messages (ms)\nflow.inputTimer = flow.inputTimer || 0;\n\nif (inputState === 1) {\n var now = Date.now();\n flow.set(inputTimer,now);\n flow.set(inputPressed,1);\n} \n\nif (inputState === 0) {\n\n if (flow.get(inputPressed) === 1) {\n\n var now = Date.now();\n \n if (now-flow.get(inputTimer) > interval) { // do something on long press\n flow.set(inputTimer,0); // reset timer\n \n if (prevLongValue === 0) {\n newLongValue = 1;\n newMsg.topic += \"/long/state\";\n newMsg.payload = 1;\n } else {\n newLongValue = 0;\n newMsg.topic += \"/long/state\";\n newMsg.payload = 0;\n }\n flow.set(inputLongName, newLongValue);\n flow.set(inputPressed,0);\n return [newMsg];\n } else {\n flow.set(inputTimer,0);\n // do something on short press\n if (prevShortValue === 0) {\n newShortValue = 1;\n newMsg.topic += \"/short/state\";\n newMsg.payload = 1;\n } else {\n newShortValue = 0;\n newMsg.topic += \"/short/state\";\n newMsg.payload = 0;\n }\n flow.set(inputShortName, newShortValue);\n flow.set(inputPressed,0);\n return [newMsg];\n }\n\n }\n\n flow.set(inputPressed,0);\n \n}\n\n\n", "outputs": 1, "noerr": 0, "x": 980, "y": 320, "wires": [ [ "b1b92233.807b7", "547a9c49.66a784" ] ] }, { "id": "b1b92233.807b7", "type": "debug", "z": "49e50089.02aeb", "name": "", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "x": 1230, "y": 280, "wires": [] }, { "id": "9965b21f.00a7f", "type": "websocket in", "z": "49e50089.02aeb", "name": "EVOK - Neuron 1", "server": "", "client": "2e0f8aa6.859726", "x": 160, "y": 320, "wires": [ [ "5dbca402.a7e41c" ] ] }, { "id": "57b3c796.390058", "type": "mqtt in", "z": "49e50089.02aeb", "name": "MQTT", "topic": "/home/#", "qos": "2", "broker": "2090ed8f.9d8142", "x": 130, "y": 160, "wires": [ [ "a8619cff.4b222" ] ] }, { "id": "a8619cff.4b222", "type": "function", "z": "49e50089.02aeb", "name": "Populate Variables", "func": "var output = msg.topic.split(\"/\");\n \nvar device = output[2];\nvar type = output[3];\n\nif (output[3] == \"relay\") {\n\n var relay = output[4];\n var mode = output[5];\n var value = msg.payload;\n\n node.debug(\"Relay Detected\");\n return { device, type, relay, mode, value };\n\n} else if (output[3] == \"input\") {\n\n var input = output[4];\n var timing = output[5];\n var mode = output[6];\n var value = msg.payload;\n\n node.debug(\"Input Detected\");\n return { device, type, input, timing, mode, value };\n\n} else {\n \n node.debug(\"Nothing Detected\");\n\n}\n\n", "outputs": 1, "noerr": 0, "x": 310, "y": 160, "wires": [ [ "688b4e69.f85ea" ] ] }, { "id": "688b4e69.f85ea", "type": "switch", "z": "49e50089.02aeb", "name": "input / relay", "property": "type", "propertyType": "msg", "rules": [ { "t": "eq", "v": "input", "vt": "str" }, { "t": "eq", "v": "relay", "vt": "str" } ], "checkall": "false", "repair": false, "outputs": 2, "x": 510, "y": 160, "wires": [ [ "8e08aecc.268c5" ], [ "c5b81807.5f0178" ] ] }, { "id": "c5b81807.5f0178", "type": "function", "z": "49e50089.02aeb", "name": "MQTT to EVOK", "func": "newMsg = {};\n\nif (msg.mode == \"set\") {\n\n relayName = msg.device+\"_relay_\"+msg.relay;\n previousRelayState = flow.get(relayName)||0;\n relayState = msg.value;\n flow.set(relayName,relayState);\n \n if (relayState != previousRelayState) {\n newMsg.relay = msg.relay;\n newMsg.ip = flow.get(msg.device+\"_IP\");\n newMsg.value = relayState;\n newMsg.payload = '{\"value\":\"'+relayState+'\"}';\n return [newMsg];\n } else {\n\n }\n \n} else if (msg.mode == \"state\") {\n \n}\n", "outputs": 1, "noerr": 0, "x": 700, "y": 180, "wires": [ [ "98ebefac.a9b81" ] ] }, { "id": "98ebefac.a9b81", "type": "http request", "z": "49e50089.02aeb", "name": "Neuron JSON", "method": "POST", "ret": "txt", "url": "http://{{ip}}:8080/json/relay/{{relay}}", "tls": "", "x": 900, "y": 180, "wires": [ [] ], "icon": "node-red/bridge.png" }, { "id": "7a340aca.03f074", "type": "function", "z": "49e50089.02aeb", "name": "Flow Settings", "func": "// -------------------------------------------------------\n//\n// Every Device needs to be setup with an IP and TYPE\n// \n// flow.set(\"[DEVICENAME]_IP\",\"[IP]\");\n// sets the type of the device (EVOK,arduino,...)\n//\n// flow.set(\"[DEVICENAME]_TYPE\",\"[TYPE]\");\n// sets the IP number of the device\n//\n// -------------------------------------------------------\n\nflow.set(\"neuron1_IP\",\"10.10.1.6\");\nflow.set(\"neuron1_TYPE\",\"EVOK\");\n\nflow.set(\"neuron2_IP\",\"10.10.1.7\");\nflow.set(\"neuron2_TYPE\",\"EVOK\");\n\nflow.set(\"neuron3_IP\",\"10.10.1.8\");\nflow.set(\"neuron3_TYPE\",\"EVOK\");\n", "outputs": 1, "noerr": 0, "x": 320, "y": 120, "wires": [ [] ], "icon": "node-red/file.png" }, { "id": "4ea4bedc.9766d", "type": "inject", "z": "49e50089.02aeb", "name": "Startup", "topic": "", "payload": "", "payloadType": "str", "repeat": "", "crontab": "", "once": true, "onceDelay": "0", "x": 140, "y": 120, "wires": [ [ "7a340aca.03f074" ] ] }, { "id": "8e08aecc.268c5", "type": "function", "z": "49e50089.02aeb", "name": "INPUT update", "func": "if (msg.mode == \"set\") {\n \n // generate name for storage\n inputName = msg.device+\"_input_\"+msg.input+\"_\"+msg.timing;\n \n // get new value\n inputState = msg.value;\n \n // store new value\n flow.set(inputName,inputState);\n \n}", "outputs": 1, "noerr": 0, "x": 700, "y": 140, "wires": [ [] ] }, { "id": "c78bc1eb.6df81", "type": "change", "z": "49e50089.02aeb", "name": "Set DEVICE", "rules": [ { "t": "set", "p": "payload.device", "pt": "msg", "to": "neuron2", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 570, "y": 400, "wires": [ [ "2b2f8289.2fdc6e" ] ] }, { "id": "5dbca402.a7e41c", "type": "json", "z": "49e50089.02aeb", "name": "", "x": 310, "y": 320, "wires": [ [ "d05e8eb9.6822f" ] ] }, { "id": "d05e8eb9.6822f", "type": "split", "z": "49e50089.02aeb", "name": "", "splt": "\\n", "x": 430, "y": 320, "wires": [ [ "6f0eebff.be9c74" ] ] }, { "id": "6f0eebff.be9c74", "type": "change", "z": "49e50089.02aeb", "name": "Set DEVICE", "rules": [ { "t": "set", "p": "payload.device", "pt": "msg", "to": "neuron1", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 570, "y": 320, "wires": [ [ "2b2f8289.2fdc6e" ] ] }, { "id": "ab98e297.393d", "type": "comment", "z": "49e50089.02aeb", "name": "List all devices", "info": "", "x": 320, "y": 80, "wires": [] }, { "id": "c24e5aa8.cbe8d8", "type": "comment", "z": "49e50089.02aeb", "name": "Change IP", "info": "", "x": 140, "y": 280, "wires": [] }, { "id": "1ffa1591.b7a49a", "type": "comment", "z": "49e50089.02aeb", "name": "Change DEVICE name", "info": "", "x": 600, "y": 280, "wires": [] }, { "id": "c9e778d4.219068", "type": "websocket-client", "z": "", "path": "ws://--IP-NEURON2--::80/ws", "tls": "", "wholemsg": "false" }, { "id": "2090ed8f.9d8142", "type": "mqtt-broker", "z": "", "name": "", "broker": "127.0.0.1", "port": "1883", "clientid": "node-red", "usetls": false, "compatmode": true, "keepalive": "60", "cleansession": true, "birthTopic": "home/neuron2", "birthQos": "0", "birthRetain": "false", "birthPayload": "online", "closeTopic": "", "closePayload": "", "willTopic": "", "willQos": "0", "willPayload": "" }, { "id": "2e0f8aa6.859726", "type": "websocket-client", "z": "", "path": "ws://--IP-NEURON1--:80/ws", "tls": "", "wholemsg": "false" } ]
Hope someone can do something with this code... or give some hints on how to improve on this?