Node-RED Switchboard for IoT Devices Managed via iHost

Hi, all.

I installed a couple of Mini R4 relays, an NS Panel and a generic Zigbee water leak sensor in order to test the capabilities of Sonoff’s iHost. The NS Panel is wired with power, but its real purpose is to act as a 3-way (or 2-way, if you are in Europe :wink: ) switch with the Mini R4 relays, one of which is behind a normal switch and one of which is already connected to a couple of 3-way switches (I wanted to test a variety of wiring options, too :mag: ). Everything is stock because I wanted to see what the off-the-shelf capabilities permit.

I used iHost’s Smart Scenes to link one of the Mini R4 relays to one of the switches on the NS Panel, and I was able to control the light attached to the Mini R4 relay with the switch or the panel, and reflect the state of the light on the NS Panel (each switch on the NS Panel has an indicator to show the On / Off state of its own attached relay which, in my case, is not attached to anything). This worked, but it took four scenes to implement, had about a two-second delay when using the panel’s switch to control the Mini R4 relay and could get confused and stuck in a loop, turning the light on and off until the scene updates caught up with one another.

Especially the need to create four scenes seemed like a lot of overhead for each “virtual” 3-way switch that I might like to create. Plus, I would like to use one of the NS Panel buttons as a toggle for an “All On / All Off” feature instead of directly controlling one remote switch’s relay.

Because Sonoff is promoting Node-RED on iHost, I decided to try to create a “switchboard” that could duplicate the simple scenes and implement more complex logic. I am happy to report that I got it to work; it is marginally faster than the scenes, usually. Its main advantage is the extensibility of the control logic (adding more decision logic to “scenes” and having many more input and output options beyond buttons being pushed on devices attached to the iHost server).

For your benefit (I hope), here is what I did.

The “main switchboard” is a good summary of the basic flows needed. I created a model in memory of the state of the IoT devices, which the first flow initialises upon startup and synchronises at 03h30, every day. I used an object (variable) in the flow context during development, but, when I tidied up by putting some of the logic into subflows, the flow context did not work as expected (this is a possible iHost implementation issue, but I have not tested on another Node-RED implementation). I switched my object to the global context, and it worked, again. Since it is a collection of variables representing a global state model, I do not feel too terribly bad about that. :laughing:


The initialisation / synchronisation flow reads all the IoT devices, sets the global state model to match their current states and resets all the “ignore” flags. The “ignore” flags are used during updates to prevent “updating” the device that initiated a state change, keeping the switchboard from ending up in endless loops of “updates,” which is what kept happening when I was trying to base my flow decisions upon feedback from the devices, directly.

The switchboard uses an event listener to detect state changes on the IoT devices, a filter to keep duplicate messages out of the flow and a converter because the listener’s message is a string instead of an object.

Once it has a message, the switchboard can determine from which device it originates and send it along the correct path. The simple example is flipping one of the Mini R4 relays if a button on the NS Panel is pressed.


In this subflow, the current state of the switch is compared with the in-memory model. If they are different and if the “ignore” flag has not been set, the in-memory model is updated (and the “ignore” flag for the relay is set because we are about to make a change from our logic), the in-memory model is used to determine in what state the relay should be set, and, after a delay of a second to allow things in the real world to actually happen, the “ignore” flag is reset.

The more complex case turns out to be properly reflecting the state of the relays on the NS Panel’s switches. The NS Panel is only powered; I am not using its switches to control anything directly connected to it.


When the Mini R4 relay associated with Switch 1 on the NS Panel changes state, a subflow checks whether or not to take action. Because subflows can only have one input, I had to break the logic for controlling the NS Panel’s two switches into three subflows.

I also discovered that sending a signal to only one of the NS Panel’s switches caused inconsistent results. In a Smart Scene on the iHost, there are three options (On, Off and Keep), but in the Node-RED implementation for the NS Panel, there are only two options (On and Off), and both switches need to have a toggle instruction sent for the panel to react properly, I found. Node-RED’s “switch” function allows testing of only one variable so that I cascade logic to determine in which of four states the NS Panel’s two switches should be set and then execute the appropriate command, as in the simpler flow, above.

I have not decided what to do if my leak sensor alarms, but I have verified that I can see it and detect its updates.

If you are interested to import these flows, the code is below.

[
    {
        "id": "9815167b06393507",
        "type": "tab",
        "label": "Home Control - Main",
        "disabled": false,
        "info": "# Home Control\nMain flow to control home devices",
        "env": []
    },
    {
        "id": "14d6a2ab0c896db9",
        "type": "subflow",
        "name": "Update Mud Room Switch (3-way)",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 20,
                "y": 40,
                "wires": [
                    {
                        "id": "5ceab163d6cbc079"
                    }
                ]
            }
        ],
        "out": [],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "6d52e8ab71fb24a0",
        "type": "subflow",
        "name": "Update Dining / Entrance Switch (3-way)",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 20,
                "y": 40,
                "wires": [
                    {
                        "id": "bea8989a26d25ce0"
                    }
                ]
            }
        ],
        "out": [],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "655f19a10754f673",
        "type": "subflow",
        "name": "Update Mud Room Panel",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 20,
                "y": 60,
                "wires": [
                    {
                        "id": "3109757e5f0bdc55"
                    }
                ]
            }
        ],
        "out": [],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "d432de3e51d97082",
        "type": "subflow",
        "name": "Query All Status Update",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 20,
                "y": 40,
                "wires": [
                    {
                        "id": "2fd2b4de401ff72a"
                    }
                ]
            }
        ],
        "out": [],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "d6079c87fee897fb",
        "type": "subflow",
        "name": "Check Mud Room Switch for State Change",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 20,
                "y": 40,
                "wires": [
                    {
                        "id": "d9af7be5918463d6"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 500,
                "y": 240,
                "wires": [
                    {
                        "id": "c5e6c9bdf86497d5",
                        "port": 0
                    }
                ]
            }
        ],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "90399e13a3d78463",
        "type": "subflow",
        "name": "Check Dining / Entrance Switch for State Change",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 20,
                "y": 40,
                "wires": [
                    {
                        "id": "0844182e8a2e2d04"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 520,
                "y": 240,
                "wires": [
                    {
                        "id": "980a4f72e378fde4",
                        "port": 0
                    }
                ]
            }
        ],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "d40a5a854c9a5fab",
        "type": "api-server",
        "name": "Token",
        "ip": "192.168.0.202",
        "token": "cc8f0b9e-3ef9-40c7-9db1-d69ffbe6c41f"
    },
    {
        "id": "6685fad1b39abf7b",
        "type": "switch",
        "z": "14d6a2ab0c896db9",
        "name": "Process Mud Room Panel Switch 1?",
        "property": "globalState.mudRoomPanel.switch1.ignore",
        "propertyType": "global",
        "rules": [
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 190,
        "y": 160,
        "wires": [
            [
                "923210785c8f0694"
            ]
        ],
        "outputLabels": [
            "Process Switch 1"
        ]
    },
    {
        "id": "5ceab163d6cbc079",
        "type": "switch",
        "z": "14d6a2ab0c896db9",
        "name": "Mud Room Panel Switch 1 State Change?",
        "property": "payload.payload.toggle.1.toggleState",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "globalState.mudRoomPanel.switch1.state",
                "vt": "global"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 200,
        "y": 100,
        "wires": [
            [
                "6685fad1b39abf7b"
            ]
        ],
        "inputLabels": [
            "Mud Room Panel Message"
        ],
        "outputLabels": [
            "Switch 1 Changed"
        ]
    },
    {
        "id": "923210785c8f0694",
        "type": "change",
        "z": "14d6a2ab0c896db9",
        "name": "Synchronise Global State of Switch 1 and Mud Room Switch",
        "rules": [
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch1.state",
                "pt": "global",
                "to": "payload.payload.toggle.1.toggleState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomSwitch.ignore",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomSwitch.state",
                "pt": "global",
                "to": "payload.payload.toggle.1.toggleState",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 260,
        "y": 220,
        "wires": [
            [
                "a6ec182f7e9ea90a"
            ]
        ]
    },
    {
        "id": "ef56481acd77b183",
        "type": "control-device",
        "z": "14d6a2ab0c896db9",
        "name": "Mud Room Switch On",
        "server": "d40a5a854c9a5fab",
        "list": "{\"type\":\"single\",\"single\":\"on\",\"deviceId\":\"3660fa55-6ad7-4453-9760-b173d6a8b304\"}",
        "category": "switch",
        "device": "3660fa55-6ad7-4453-9760-b173d6a8b304",
        "action": "",
        "x": 400,
        "y": 280,
        "wires": [
            [
                "2b6c3ef87f2e40b2"
            ]
        ]
    },
    {
        "id": "7ef50b7740f8dfb4",
        "type": "control-device",
        "z": "14d6a2ab0c896db9",
        "name": "Mud Room Switch Off",
        "server": "d40a5a854c9a5fab",
        "list": "{\"type\":\"single\",\"single\":\"off\",\"deviceId\":\"3660fa55-6ad7-4453-9760-b173d6a8b304\"}",
        "category": "switch",
        "device": "3660fa55-6ad7-4453-9760-b173d6a8b304",
        "action": "",
        "x": 400,
        "y": 320,
        "wires": [
            [
                "2b6c3ef87f2e40b2"
            ]
        ]
    },
    {
        "id": "a6ec182f7e9ea90a",
        "type": "switch",
        "z": "14d6a2ab0c896db9",
        "name": "Mud Room Switch On or Off",
        "property": "globalState.mudRoomSwitch.state",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "on",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "off",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 160,
        "y": 300,
        "wires": [
            [
                "ef56481acd77b183"
            ],
            [
                "7ef50b7740f8dfb4"
            ]
        ]
    },
    {
        "id": "90a5da78069ab534",
        "type": "change",
        "z": "14d6a2ab0c896db9",
        "name": "Reset Ignore on Mud Room Switch",
        "rules": [
            {
                "t": "set",
                "p": "globalState.mudRoomSwitch.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 620,
        "y": 380,
        "wires": [
            []
        ]
    },
    {
        "id": "2b6c3ef87f2e40b2",
        "type": "delay",
        "z": "14d6a2ab0c896db9",
        "name": "",
        "pauseType": "delay",
        "timeout": "1",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 600,
        "y": 300,
        "wires": [
            [
                "90a5da78069ab534"
            ]
        ]
    },
    {
        "id": "bea8989a26d25ce0",
        "type": "switch",
        "z": "6d52e8ab71fb24a0",
        "name": "Mud Room Panel Switch 2 State Change?",
        "property": "payload.payload.toggle.2.toggleState",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "globalState.mudRoomPanel.switch2.state",
                "vt": "global"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 200,
        "y": 100,
        "wires": [
            [
                "f5395884edee63b2"
            ]
        ],
        "inputLabels": [
            "Mud Room Panel Message"
        ],
        "outputLabels": [
            "Switch 2 Changed"
        ]
    },
    {
        "id": "f5395884edee63b2",
        "type": "switch",
        "z": "6d52e8ab71fb24a0",
        "name": "Process Mud Room Panel Switch 2?",
        "property": "globalState.mudRoomPanel.switch2.ignore",
        "propertyType": "global",
        "rules": [
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 190,
        "y": 160,
        "wires": [
            [
                "0e95a8a2b95c496c"
            ]
        ],
        "outputLabels": [
            "Process Switch 1"
        ]
    },
    {
        "id": "0e95a8a2b95c496c",
        "type": "change",
        "z": "6d52e8ab71fb24a0",
        "name": "Synchronise Global State of Switch 2 and Dining / Entrance Switch",
        "rules": [
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch2.state",
                "pt": "global",
                "to": "payload.payload.toggle.2.toggleState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.diningEntranceSwitch.ignore",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.diningEntranceSwitch.state",
                "pt": "global",
                "to": "payload.payload.toggle.2.toggleState",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 280,
        "y": 220,
        "wires": [
            [
                "7aa8bfc86883cf65"
            ]
        ]
    },
    {
        "id": "7aa8bfc86883cf65",
        "type": "switch",
        "z": "6d52e8ab71fb24a0",
        "name": "Dining / Entrance Switch On or Off",
        "property": "globalState.diningEntranceSwitch.state",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "on",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "off",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 180,
        "y": 300,
        "wires": [
            [
                "a1b32fbe027e3e0a"
            ],
            [
                "b4569ceeb6789a84"
            ]
        ]
    },
    {
        "id": "a1b32fbe027e3e0a",
        "type": "control-device",
        "z": "6d52e8ab71fb24a0",
        "name": "Dining / Entrance Switch On",
        "server": "d40a5a854c9a5fab",
        "list": "{\"type\":\"single\",\"single\":\"on\",\"deviceId\":\"5da3c2d4-6b05-4e80-abfd-12fc23d12920\"}",
        "category": "switch",
        "device": "5da3c2d4-6b05-4e80-abfd-12fc23d12920",
        "action": "",
        "x": 460,
        "y": 280,
        "wires": [
            [
                "61fa526d4039da42"
            ]
        ]
    },
    {
        "id": "b4569ceeb6789a84",
        "type": "control-device",
        "z": "6d52e8ab71fb24a0",
        "name": "Dining / Entrance Switch Off",
        "server": "d40a5a854c9a5fab",
        "list": "{\"type\":\"single\",\"single\":\"off\",\"deviceId\":\"5da3c2d4-6b05-4e80-abfd-12fc23d12920\"}",
        "category": "switch",
        "device": "5da3c2d4-6b05-4e80-abfd-12fc23d12920",
        "action": "",
        "x": 460,
        "y": 320,
        "wires": [
            [
                "61fa526d4039da42"
            ]
        ]
    },
    {
        "id": "61fa526d4039da42",
        "type": "delay",
        "z": "6d52e8ab71fb24a0",
        "name": "",
        "pauseType": "delay",
        "timeout": "1",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 680,
        "y": 300,
        "wires": [
            [
                "807d63a4bf8b8867"
            ]
        ]
    },
    {
        "id": "807d63a4bf8b8867",
        "type": "change",
        "z": "6d52e8ab71fb24a0",
        "name": "Reset Ignore on Dining Entrance Switch",
        "rules": [
            {
                "t": "set",
                "p": "globalState.diningEntranceSwitch.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 600,
        "y": 400,
        "wires": [
            []
        ]
    },
    {
        "id": "286d3e50fa03e71a",
        "type": "control-device",
        "z": "655f19a10754f673",
        "name": "Mud Room Panel Switch 1 On Switch 2 Off",
        "server": "d40a5a854c9a5fab",
        "list": "{\"multi\":{\"1\":\"on\",\"2\":\"off\"},\"deviceId\":\"baa74d91-f21e-49c5-83c3-8babd473449d\",\"type\":\"multi\"}",
        "category": "switch",
        "device": "baa74d91-f21e-49c5-83c3-8babd473449d",
        "action": "",
        "x": 850,
        "y": 100,
        "wires": [
            [
                "44a117ae1c07c8d1"
            ]
        ]
    },
    {
        "id": "3109757e5f0bdc55",
        "type": "switch",
        "z": "655f19a10754f673",
        "name": "Mud Room Panel Switch 1 On or Off",
        "property": "globalState.mudRoomPanel.switch1.state",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "on",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "off",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 190,
        "y": 120,
        "wires": [
            [
                "b9fe5500f16f9e6f"
            ],
            [
                "19a2e23aa82783e6"
            ]
        ]
    },
    {
        "id": "42b12e7e1058d9c7",
        "type": "change",
        "z": "655f19a10754f673",
        "name": "Reset Ignore on Mud Room Panel",
        "rules": [
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch1.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch2.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1320,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "44a117ae1c07c8d1",
        "type": "delay",
        "z": "655f19a10754f673",
        "name": "",
        "pauseType": "delay",
        "timeout": "1",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 1100,
        "y": 120,
        "wires": [
            [
                "42b12e7e1058d9c7"
            ]
        ]
    },
    {
        "id": "a59df9fea128a453",
        "type": "control-device",
        "z": "655f19a10754f673",
        "name": "Mud Room Panel Switch 1 Off Switch 2 Off",
        "server": "d40a5a854c9a5fab",
        "list": "{\"multi\":{\"1\":\"off\",\"2\":\"off\"},\"deviceId\":\"baa74d91-f21e-49c5-83c3-8babd473449d\",\"type\":\"multi\"}",
        "category": "switch",
        "device": "baa74d91-f21e-49c5-83c3-8babd473449d",
        "action": "",
        "x": 850,
        "y": 180,
        "wires": [
            [
                "44a117ae1c07c8d1"
            ]
        ]
    },
    {
        "id": "b0c2bc56f0eccb95",
        "type": "control-device",
        "z": "655f19a10754f673",
        "name": "Mud Room Panel Switch 1 Off Switch 2 On",
        "server": "d40a5a854c9a5fab",
        "list": "{\"multi\":{\"1\":\"off\",\"2\":\"on\"},\"deviceId\":\"baa74d91-f21e-49c5-83c3-8babd473449d\",\"type\":\"multi\"}",
        "category": "switch",
        "device": "baa74d91-f21e-49c5-83c3-8babd473449d",
        "action": "",
        "x": 850,
        "y": 140,
        "wires": [
            [
                "44a117ae1c07c8d1"
            ]
        ]
    },
    {
        "id": "e821966a09458277",
        "type": "control-device",
        "z": "655f19a10754f673",
        "name": "Mud Room Panel Switch 1 On Switch 2 On",
        "server": "d40a5a854c9a5fab",
        "list": "{\"multi\":{\"1\":\"on\",\"2\":\"on\"},\"deviceId\":\"baa74d91-f21e-49c5-83c3-8babd473449d\",\"type\":\"multi\"}",
        "category": "switch",
        "device": "baa74d91-f21e-49c5-83c3-8babd473449d",
        "action": "",
        "x": 850,
        "y": 60,
        "wires": [
            [
                "44a117ae1c07c8d1"
            ]
        ]
    },
    {
        "id": "b9fe5500f16f9e6f",
        "type": "switch",
        "z": "655f19a10754f673",
        "name": "Mud Room Panel Switch 2 On or Off",
        "property": "globalState.mudRoomPanel.switch2.state",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "on",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "off",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 510,
        "y": 100,
        "wires": [
            [
                "e821966a09458277"
            ],
            [
                "286d3e50fa03e71a"
            ]
        ]
    },
    {
        "id": "19a2e23aa82783e6",
        "type": "switch",
        "z": "655f19a10754f673",
        "name": "Mud Room Panel Switch 2 On or Off",
        "property": "globalState.mudRoomPanel.switch2.state",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "on",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "off",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 510,
        "y": 140,
        "wires": [
            [
                "b0c2bc56f0eccb95"
            ],
            [
                "a59df9fea128a453"
            ]
        ]
    },
    {
        "id": "2fd2b4de401ff72a",
        "type": "get-devices",
        "z": "d432de3e51d97082",
        "name": "Get All Devices Info",
        "server": "d40a5a854c9a5fab",
        "category": "all",
        "device": "all",
        "x": 130,
        "y": 100,
        "wires": [
            [
                "c415210277882166",
                "47fce6209ab6442e"
            ]
        ]
    },
    {
        "id": "c415210277882166",
        "type": "debug",
        "z": "d432de3e51d97082",
        "name": "Report All Devices Info",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 380,
        "y": 100,
        "wires": []
    },
    {
        "id": "47fce6209ab6442e",
        "type": "split",
        "z": "d432de3e51d97082",
        "name": "Split Out Devices",
        "splt": "\\n",
        "spltType": "str",
        "arraySplt": 1,
        "arraySpltType": "len",
        "stream": false,
        "addname": "",
        "x": 130,
        "y": 160,
        "wires": [
            [
                "26c6ed001fdec4dd",
                "20eaac7d62663345"
            ]
        ]
    },
    {
        "id": "26c6ed001fdec4dd",
        "type": "debug",
        "z": "d432de3e51d97082",
        "name": "Individual Device Report",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 410,
        "y": 160,
        "wires": []
    },
    {
        "id": "20eaac7d62663345",
        "type": "switch",
        "z": "d432de3e51d97082",
        "name": "Parse Global Variables",
        "property": "payload.name",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "Mud Room Switch (3-way)",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Mud Room Panel",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Dining / Entrance Light",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Home Water Leak Sensor",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 4,
        "x": 140,
        "y": 280,
        "wires": [
            [
                "a6f9ddcc3c4c1805"
            ],
            [
                "2f930b63a4ac09d8"
            ],
            [
                "71c2ac5b5f88079e"
            ],
            [
                "33799a0ecf71f820"
            ]
        ],
        "inputLabels": [
            "Device Status Message"
        ],
        "outputLabels": [
            "Mud Room Switch (3-way)",
            "Mud Room Panel",
            "Dining / Entrance Light",
            "Home Water Leak Sensor"
        ]
    },
    {
        "id": "a6f9ddcc3c4c1805",
        "type": "change",
        "z": "d432de3e51d97082",
        "name": "Set Mud Room Switch State",
        "rules": [
            {
                "t": "set",
                "p": "globalState.mudRoomSwitch.state",
                "pt": "global",
                "to": "payload.state.power.powerState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomSwitch.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 440,
        "y": 220,
        "wires": [
            []
        ]
    },
    {
        "id": "2f930b63a4ac09d8",
        "type": "change",
        "z": "d432de3e51d97082",
        "name": "Set Mud Room Panel Switch States",
        "rules": [
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch1.state",
                "pt": "flow",
                "to": "payload.state.toggle.1.toggleState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch1.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch2.state",
                "pt": "global",
                "to": "payload.state.toggle.2.toggleState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch2.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 460,
        "y": 260,
        "wires": [
            []
        ]
    },
    {
        "id": "71c2ac5b5f88079e",
        "type": "change",
        "z": "d432de3e51d97082",
        "name": "Set Dining / Entrance Switch State",
        "rules": [
            {
                "t": "set",
                "p": "globalState.diningEntranceSwitch.state",
                "pt": "global",
                "to": "payload.state.power.powerState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.diningEntranceSwitch.ignore",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 460,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "33799a0ecf71f820",
        "type": "change",
        "z": "d432de3e51d97082",
        "name": "Set Water Leak Sensor State",
        "rules": [
            {
                "t": "set",
                "p": "globalState.waterLeakSensor",
                "pt": "global",
                "to": "payload.state.detect.detected",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 440,
        "y": 340,
        "wires": [
            []
        ]
    },
    {
        "id": "d9af7be5918463d6",
        "type": "switch",
        "z": "d6079c87fee897fb",
        "name": "Mud Room Switch State Change?",
        "property": "payload.payload.power.powerState",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "globalState.mudRoomSwitch.state",
                "vt": "global"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 180,
        "y": 100,
        "wires": [
            [
                "c68711ea57f2c045"
            ]
        ],
        "inputLabels": [
            "Mud Room Switch Message"
        ],
        "outputLabels": [
            "Mud Room Switch Changed"
        ]
    },
    {
        "id": "c68711ea57f2c045",
        "type": "switch",
        "z": "d6079c87fee897fb",
        "name": "Process Mud Room Switch?",
        "property": "globalState.mudRoomSwitch.ignore",
        "propertyType": "global",
        "rules": [
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 160,
        "y": 140,
        "wires": [
            [
                "c5e6c9bdf86497d5"
            ]
        ],
        "outputLabels": [
            "Process Mud Room Switch"
        ]
    },
    {
        "id": "c5e6c9bdf86497d5",
        "type": "change",
        "z": "d6079c87fee897fb",
        "name": "Synchronise Global State of Switch 1 and Mud Room Switch",
        "rules": [
            {
                "t": "set",
                "p": "globalState.mudRoomSwitch.state",
                "pt": "global",
                "to": "payload.payload.power.powerState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch1.ignore",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch2.ignore",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch1.state",
                "pt": "global",
                "to": "payload.payload.power.powerState",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 260,
        "y": 180,
        "wires": [
            []
        ]
    },
    {
        "id": "0844182e8a2e2d04",
        "type": "switch",
        "z": "90399e13a3d78463",
        "name": "Dining / Entrance Switch State Change?",
        "property": "payload.payload.power.powerState",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "globalState.diningEntranceSwitch.state",
                "vt": "global"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 200,
        "y": 100,
        "wires": [
            [
                "83ff49bb0b06a7a9"
            ]
        ],
        "inputLabels": [
            "Dining / Entrance Switch Message"
        ],
        "outputLabels": [
            "Dining / Entrance Switch Changed"
        ]
    },
    {
        "id": "83ff49bb0b06a7a9",
        "type": "switch",
        "z": "90399e13a3d78463",
        "name": "Process Dining / Entrance Switch?",
        "property": "globalState.diningEntranceSwitch.ignore",
        "propertyType": "global",
        "rules": [
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 180,
        "y": 140,
        "wires": [
            [
                "980a4f72e378fde4"
            ]
        ],
        "outputLabels": [
            "Process Dining / Entrance Switch"
        ]
    },
    {
        "id": "980a4f72e378fde4",
        "type": "change",
        "z": "90399e13a3d78463",
        "name": "Synchronise Global State of Switch 2 and Dining / Entrance Switch",
        "rules": [
            {
                "t": "set",
                "p": "globalState.diningEntranceSwitch.state",
                "pt": "global",
                "to": "payload.payload.power.powerState",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch1.ignore",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch2.ignore",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            },
            {
                "t": "set",
                "p": "globalState.mudRoomPanel.switch2.state",
                "pt": "global",
                "to": "payload.payload.power.powerState",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 280,
        "y": 180,
        "wires": [
            []
        ]
    },
    {
        "id": "dd4142ad56ed2739",
        "type": "inject",
        "z": "9815167b06393507",
        "name": "Initialise State Variables and Update State Daily",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "30 03 * * *",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 250,
        "y": 60,
        "wires": [
            [
                "74bbef5a3a60df63"
            ]
        ]
    },
    {
        "id": "64285ad806a0e0ac",
        "type": "event-state",
        "z": "9815167b06393507",
        "name": "Listen for Home Devices",
        "server": "d40a5a854c9a5fab",
        "list": "",
        "category": "",
        "device": "all",
        "state": "",
        "x": 170,
        "y": 140,
        "wires": [
            [
                "d78eac3def7d4d59"
            ]
        ]
    },
    {
        "id": "d78eac3def7d4d59",
        "type": "rbe",
        "z": "9815167b06393507",
        "name": "Home Device Message Filter",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": true,
        "property": "payload",
        "topi": "topic",
        "x": 180,
        "y": 180,
        "wires": [
            [
                "4fd98bb1d6bd748a"
            ]
        ],
        "info": "# Home Device Message Filter\nReduce the duplicate messages from iHost / eWeLink devices"
    },
    {
        "id": "4fd98bb1d6bd748a",
        "type": "json",
        "z": "9815167b06393507",
        "name": "iHost Payload Converter",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 170,
        "y": 220,
        "wires": [
            [
                "83c86fb4c4d21e2e"
            ]
        ]
    },
    {
        "id": "83c86fb4c4d21e2e",
        "type": "switch",
        "z": "9815167b06393507",
        "name": "Device Switchboard",
        "property": "payload.endpoint.serial_number",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "baa74d91-f21e-49c5-83c3-8babd473449d",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "3660fa55-6ad7-4453-9760-b173d6a8b304",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "5da3c2d4-6b05-4e80-abfd-12fc23d12920",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "847127fffe7bc29b",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 4,
        "x": 160,
        "y": 320,
        "wires": [
            [
                "abd10bfe29ae3665",
                "5583e0651766bd23"
            ],
            [
                "d013a1cae11c67bb"
            ],
            [
                "5f567bb29ac8f256"
            ],
            []
        ],
        "inputLabels": [
            "iHost Device Event"
        ],
        "outputLabels": [
            "Mud Room NS Panel",
            "Mud Room 3-way Switch",
            "Dining / Entrance 3-way Switch",
            "Home Water Leak Sensor"
        ],
        "info": "# Master Device Switchboard\nTake action on recognised messages from the iHost Home listener"
    },
    {
        "id": "abd10bfe29ae3665",
        "type": "subflow:14d6a2ab0c896db9",
        "z": "9815167b06393507",
        "name": "Update Mud Room Switch (3-way)",
        "x": 460,
        "y": 260,
        "wires": []
    },
    {
        "id": "5583e0651766bd23",
        "type": "subflow:6d52e8ab71fb24a0",
        "z": "9815167b06393507",
        "name": "Update Dining / Entrance Switch (3-way)",
        "x": 480,
        "y": 300,
        "wires": []
    },
    {
        "id": "97ea669432160c8f",
        "type": "subflow:655f19a10754f673",
        "z": "9815167b06393507",
        "name": "Update Mud Room Panel",
        "x": 830,
        "y": 340,
        "wires": []
    },
    {
        "id": "74bbef5a3a60df63",
        "type": "subflow:d432de3e51d97082",
        "z": "9815167b06393507",
        "name": "Query All Status Update",
        "x": 570,
        "y": 60,
        "wires": []
    },
    {
        "id": "8e54f8260f1a3d43",
        "type": "comment",
        "z": "9815167b06393507",
        "name": "Synchronise state object with actual state of devices",
        "info": "# Synchronise State\nA model of the smart home is kept in memory and updated at 03h30, every day.",
        "x": 290,
        "y": 20,
        "wires": []
    },
    {
        "id": "68f2bca820970ec8",
        "type": "comment",
        "z": "9815167b06393507",
        "name": "Event Listener and Switchboard",
        "info": "# Event Listener and Switchboard\nThe flow listens for events, updates the state model and synchronises devices with the updated state model.\n<p>Subflows keep the switchboard logic clear.",
        "x": 230,
        "y": 100,
        "wires": []
    },
    {
        "id": "d013a1cae11c67bb",
        "type": "subflow:d6079c87fee897fb",
        "z": "9815167b06393507",
        "name": "Check Mud Room Switch for State Change",
        "x": 490,
        "y": 340,
        "wires": [
            [
                "97ea669432160c8f"
            ]
        ]
    },
    {
        "id": "5f567bb29ac8f256",
        "type": "subflow:90399e13a3d78463",
        "z": "9815167b06393507",
        "name": "Check Dining / Entrance Switch for State Change",
        "x": 510,
        "y": 380,
        "wires": [
            [
                "97ea669432160c8f"
            ]
        ]
    },
    {
        "id": "10da0fb9eb9fa206",
        "type": "comment",
        "z": "9815167b06393507",
        "name": "Subflows can have only one input",
        "info": "# Subflow Rationale\nSubflows for devices with multiple components have to be broken into parts because subflows can have only one input.",
        "x": 810,
        "y": 300,
        "wires": []
    }
]

Updated 2023-07-20: The Main Switchboard picture (and exported code) had a typo. The names of two of the switchboard subflows attached to the “Mud Room NS Panel” output were the same. I forgot to change the name when I copied the subflow for the second switch. I corrected that.

2 Likes

:grinning: Thank you for sharing