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.

    0_1536491730161_a3e9c68a-413e-4a81-a369-76a7eb781b6a-image.png

    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?