Node-RED

Point 'n' Click workflow automation
Project started on November 01, 2021.
Last updated on July 07, 2022.

...back to Smart Home overview

Node-RED is a great tool for visually creating workflows based on events. I use it in conjunction with my ESP-Env sensor and actor hardware and MQTT to automate my smart home.

I plan to extend these workflows as required when I add new hardware to my network.

Here is a simple setup that automatically switches lights in my bathroom according to the sun position, using node-red-contrib-sunevents. It allows forcing a temporary override via another MQTT topic, for a lightswitch. In the automatic, non-overridden mode it also includes presence detection using node-red-contrib-fritz. This is using the API of my Fritz!Box Router to check if specific MAC Addresses are connected to the WiFi network.

The fan in my window-less bathroom is kept on all the time for now. In the future I want to add movement sensors to the bathroom and integrate them into this logic, as well as the temperature / humidity sensors I already have in there. All these devices run my ESP-Env firmware.

Switching the lights
WiFi presence detection
Keeping fan always on
Helper function for MQTT

And here are the flows for importing in Node-RED.

[
    {
        "id": "490116e54af5ff9a",
        "type": "tab",
        "label": "Bathroom Lights",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "74341bea93cc320d",
        "type": "tab",
        "label": "Bathroom Fan",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "8a6a748b145123cb",
        "type": "tab",
        "label": "Presence Detection",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "b9932c4c66b18537",
        "type": "tab",
        "label": "Functions",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "5be146a97e98891c",
        "type": "mqtt-broker",
        "name": "iot.fritz.box",
        "broker": "iot.fritz.box",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "5",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    },
    {
        "id": "bec40e042190e5b4",
        "type": "fritzbox-config",
        "name": "Fritz Box",
        "host": "fritz.box",
        "port": "49000",
        "ssl": false,
        "user": "YOUR_USERNAME_HERE"
    },
    {
        "id": "4039bc1ca3cd6e11",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Check for new Sun Events",
        "info": "",
        "x": 150,
        "y": 40,
        "wires": []
    },
    {
        "id": "ce5504b6991f8bf4",
        "type": "inject",
        "z": "490116e54af5ff9a",
        "name": "Every 12h",
        "props": [
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "43200",
        "crontab": "",
        "once": true,
        "onceDelay": "1",
        "topic": "",
        "x": 110,
        "y": 80,
        "wires": [
            [
                "5151002c9539d2d6"
            ]
        ]
    },
    {
        "id": "5151002c9539d2d6",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Set Location",
        "rules": [
            {
                "t": "set",
                "p": "payload.latitude",
                "pt": "msg",
                "to": "47.0",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.longitude",
                "pt": "msg",
                "to": "9.0",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 270,
        "y": 80,
        "wires": [
            [
                "bc6e8bf098875279"
            ]
        ]
    },
    {
        "id": "bc6e8bf098875279",
        "type": "sun events",
        "z": "490116e54af5ff9a",
        "testmode": false,
        "verbose": "N",
        "topic": "",
        "name": "Sun Events",
        "x": 430,
        "y": 80,
        "wires": [
            [
                "4384fcd3b3567c6c",
                "9c21e093193d54e4"
            ]
        ]
    },
    {
        "id": "d8f1896d6ed5ca0d",
        "type": "switch",
        "z": "490116e54af5ff9a",
        "name": "On Sunrise",
        "property": "sunevent",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "sunrise",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 1,
        "x": 770,
        "y": 220,
        "wires": [
            [
                "98d848700eaa119d",
                "76965df29f0c2651"
            ]
        ]
    },
    {
        "id": "d17e0c1b295d531a",
        "type": "switch",
        "z": "490116e54af5ff9a",
        "name": "On Sunset",
        "property": "sunevent",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "sunset",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 1,
        "x": 770,
        "y": 260,
        "wires": [
            [
                "3e4a16697db5b83d",
                "6df166fb14211461"
            ]
        ]
    },
    {
        "id": "895f005abb0c7a30",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Big Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_big",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1150,
        "y": 200,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "c8246e3ed2f6ac51",
        "type": "link call",
        "z": "490116e54af5ff9a",
        "name": "",
        "links": [
            "fd3c7a7a7a3b2e99"
        ],
        "timeout": "30",
        "x": 1340,
        "y": 560,
        "wires": [
            []
        ]
    },
    {
        "id": "bbd4b1ede10ea09e",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Small Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_small",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1160,
        "y": 320,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "0516f39aac309884",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Store Sun State",
        "rules": [
            {
                "t": "set",
                "p": "sunevent",
                "pt": "global",
                "to": "payload.sunevent",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 800,
        "y": 80,
        "wires": [
            [
                "23bd87871e2f0ecb"
            ]
        ]
    },
    {
        "id": "86248602292b2932",
        "type": "inject",
        "z": "490116e54af5ff9a",
        "name": "Every 15sec",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "15",
        "crontab": "",
        "once": true,
        "onceDelay": "5",
        "topic": "",
        "payloadType": "date",
        "x": 120,
        "y": 540,
        "wires": [
            [
                "ce6a5620f1e6af76"
            ]
        ]
    },
    {
        "id": "98d848700eaa119d",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn On",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "on",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 980,
        "y": 200,
        "wires": [
            [
                "895f005abb0c7a30"
            ]
        ]
    },
    {
        "id": "76965df29f0c2651",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn Off",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "off",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 980,
        "y": 240,
        "wires": [
            [
                "b7ea8636c374cd25"
            ]
        ]
    },
    {
        "id": "b7ea8636c374cd25",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Small Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_small",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1160,
        "y": 240,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "3e4a16697db5b83d",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn On",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "on",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 980,
        "y": 320,
        "wires": [
            [
                "bbd4b1ede10ea09e"
            ]
        ]
    },
    {
        "id": "6df166fb14211461",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn Off",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "off",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 980,
        "y": 360,
        "wires": [
            [
                "8c16ac287cbf877c"
            ]
        ]
    },
    {
        "id": "8c16ac287cbf877c",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Big Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_big",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1150,
        "y": 360,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "6af17a7135211b49",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Set Lights",
        "info": "",
        "x": 100,
        "y": 500,
        "wires": []
    },
    {
        "id": "23bd87871e2f0ecb",
        "type": "debug",
        "z": "490116e54af5ff9a",
        "name": "Sun Event Data",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "sunevent",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 980,
        "y": 80,
        "wires": []
    },
    {
        "id": "9c21e093193d54e4",
        "type": "switch",
        "z": "490116e54af5ff9a",
        "name": "Is Sunrise?",
        "property": "payload.sunevent",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "sunrise",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 610,
        "y": 120,
        "wires": [
            [
                "0516f39aac309884"
            ]
        ]
    },
    {
        "id": "4384fcd3b3567c6c",
        "type": "switch",
        "z": "490116e54af5ff9a",
        "name": "Is Sunset?",
        "property": "payload.sunevent",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "sunset",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 610,
        "y": 80,
        "wires": [
            [
                "0516f39aac309884"
            ]
        ]
    },
    {
        "id": "669b4c826982a274",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Only store relevant states",
        "info": "",
        "x": 630,
        "y": 40,
        "wires": []
    },
    {
        "id": "125e5ada06d3bb84",
        "type": "mqtt in",
        "z": "490116e54af5ff9a",
        "name": "Forced Light",
        "topic": "bathroom/force_light",
        "qos": "2",
        "datatype": "auto",
        "broker": "5be146a97e98891c",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 110,
        "y": 260,
        "wires": [
            [
                "0e7e6ebfd46450f7",
                "12082b0fb4909c57",
                "42ee21fcc64e2f6b"
            ]
        ]
    },
    {
        "id": "0e7e6ebfd46450f7",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Store Forced Light State",
        "rules": [
            {
                "t": "set",
                "p": "forced_light_bathroom",
                "pt": "global",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 370,
        "y": 260,
        "wires": [
            [
                "ce6a5620f1e6af76"
            ]
        ]
    },
    {
        "id": "3bcc8e43c0acccd5",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Allow overriding light schedule",
        "info": "",
        "x": 160,
        "y": 220,
        "wires": []
    },
    {
        "id": "12082b0fb4909c57",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Store Time of forcing",
        "rules": [
            {
                "t": "set",
                "p": "forced_light_bathroom_time",
                "pt": "global",
                "to": "",
                "tot": "date"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 360,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "ce6a5620f1e6af76",
        "type": "switch",
        "z": "490116e54af5ff9a",
        "name": "Lights Forced?",
        "property": "forced_light_bathroom",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "none",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "off",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "big",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "small",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 4,
        "x": 400,
        "y": 540,
        "wires": [
            [
                "d0b79744f8f9eacb",
                "3c92f7e580f2d926"
            ],
            [
                "9241d0a69fa0e0ba"
            ],
            [
                "0106c313423b119b",
                "c5b95b0a9268e6d6"
            ],
            [
                "72b9d0ad81123696",
                "511135c0e9357ffe"
            ]
        ]
    },
    {
        "id": "6ed0af4d2180e6c1",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Set Lights according to Sun Schedule",
        "info": "",
        "x": 850,
        "y": 160,
        "wires": []
    },
    {
        "id": "9241d0a69fa0e0ba",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn Off",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "off",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 660,
        "y": 580,
        "wires": [
            [
                "5118b6346eb963da",
                "169b79020e5cc427"
            ]
        ]
    },
    {
        "id": "169b79020e5cc427",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Small Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_small",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 860,
        "y": 580,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "5118b6346eb963da",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Big Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_big",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 850,
        "y": 620,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "a4d3930695a8574e",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Forced Off",
        "info": "",
        "x": 660,
        "y": 540,
        "wires": []
    },
    {
        "id": "617d2b7904f23127",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Forced Big Light",
        "info": "",
        "x": 680,
        "y": 700,
        "wires": []
    },
    {
        "id": "0106c313423b119b",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn Off",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "off",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 660,
        "y": 740,
        "wires": [
            [
                "857e6aea1df251ba"
            ]
        ]
    },
    {
        "id": "857e6aea1df251ba",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Small Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_small",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 840,
        "y": 740,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "c5b95b0a9268e6d6",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn On",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "on",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 660,
        "y": 780,
        "wires": [
            [
                "10e0fc6e5014f1a8"
            ]
        ]
    },
    {
        "id": "10e0fc6e5014f1a8",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Big Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_big",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 830,
        "y": 780,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "be6439f19a674c49",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Forced Small Light",
        "info": "",
        "x": 690,
        "y": 840,
        "wires": []
    },
    {
        "id": "511135c0e9357ffe",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn Off",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "off",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 660,
        "y": 920,
        "wires": [
            [
                "d6c93118a00de8be"
            ]
        ]
    },
    {
        "id": "0e14dde521822fd9",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Small Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_small",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 840,
        "y": 880,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "72b9d0ad81123696",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Turn On",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "on",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 660,
        "y": 880,
        "wires": [
            [
                "0e14dde521822fd9"
            ]
        ]
    },
    {
        "id": "d6c93118a00de8be",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Bathroom Big Light",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/light_big",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 830,
        "y": 920,
        "wires": [
            [
                "c8246e3ed2f6ac51"
            ]
        ]
    },
    {
        "id": "14e28adbe9d5ab9b",
        "type": "inject",
        "z": "490116e54af5ff9a",
        "name": "Every 60sec",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "60",
        "crontab": "",
        "once": true,
        "onceDelay": "5",
        "topic": "",
        "payloadType": "date",
        "x": 120,
        "y": 740,
        "wires": [
            [
                "ca79c73ef3fd979b"
            ]
        ]
    },
    {
        "id": "cfca4ed8e126b999",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Stop Override after time",
        "info": "",
        "x": 140,
        "y": 700,
        "wires": []
    },
    {
        "id": "ca79c73ef3fd979b",
        "type": "switch",
        "z": "490116e54af5ff9a",
        "name": "Lights Forced?",
        "property": "forced_light_bathroom",
        "propertyType": "global",
        "rules": [
            {
                "t": "neq",
                "v": "none",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 300,
        "y": 740,
        "wires": [
            [
                "3ec81549844232cc"
            ]
        ]
    },
    {
        "id": "3ec81549844232cc",
        "type": "function",
        "z": "490116e54af5ff9a",
        "name": "New Override State",
        "func": "var trig_t = global.get(\"forced_light_bathroom_time\");\n//node.warn(\"trig_t = \" + trig_t);\n\ntrig_t += (1000 * 60 * 60 * 2); // 2 hours\n\n//trig_t += (1000 * 60); // 1 minute\n//node.warn(\"trig_t+1 = \" + trig_t);\n\nvar trig = new Date(trig_t);\n//node.warn(\"trig = \" + trig);\n\nvar now = new Date();\n//node.warn(\"now = \" + now);\n\nif (trig <= now) {\n    msg.payload = \"none\";\n    return msg;\n} else {\n    return null;\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 150,
        "y": 840,
        "wires": [
            [
                "d9c6f73e0340a93e"
            ]
        ]
    },
    {
        "id": "88d475840651d470",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Disable after 2 hours",
        "info": "",
        "x": 150,
        "y": 800,
        "wires": []
    },
    {
        "id": "d9c6f73e0340a93e",
        "type": "change",
        "z": "490116e54af5ff9a",
        "name": "Store Forced Light State",
        "rules": [
            {
                "t": "set",
                "p": "forced_light_bathroom",
                "pt": "global",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 370,
        "y": 840,
        "wires": [
            [
                "100da1b7bffe4ab2",
                "ce6a5620f1e6af76"
            ]
        ]
    },
    {
        "id": "42ee21fcc64e2f6b",
        "type": "debug",
        "z": "490116e54af5ff9a",
        "name": "Override",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 320,
        "y": 340,
        "wires": []
    },
    {
        "id": "100da1b7bffe4ab2",
        "type": "debug",
        "z": "490116e54af5ff9a",
        "name": "Override Refresh",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 390,
        "y": 900,
        "wires": []
    },
    {
        "id": "d0b79744f8f9eacb",
        "type": "function",
        "z": "490116e54af5ff9a",
        "name": "User or Spouse Present?",
        "func": "var user = global.get(\"user_present\");\nvar work = global.get(\"user_present_work\");\nvar spouse = global.get(\"spouse_present\");\n\nif ((user == \"1\") || (work == \"1\") || (spouse == \"1\")) {\n    msg.payload = \"none\";\n    return msg;\n} else {\n    return null;\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 610,
        "y": 400,
        "wires": [
            [
                "d8f1896d6ed5ca0d",
                "d17e0c1b295d531a"
            ]
        ]
    },
    {
        "id": "3c92f7e580f2d926",
        "type": "function",
        "z": "490116e54af5ff9a",
        "name": "Nobody Present?",
        "func": "var user = global.get(\"user_present\");\nvar work = global.get(\"user_present_work\");\nvar spouse = global.get(\"spouse_present\");\n\nif ((user == \"0\") && (work == \"0\") && (spouse == \"0\")) {\n    msg.payload = \"none\";\n    return msg;\n} else {\n    return null;\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 590,
        "y": 440,
        "wires": [
            [
                "9241d0a69fa0e0ba"
            ]
        ]
    },
    {
        "id": "23201910ed265e74",
        "type": "comment",
        "z": "490116e54af5ff9a",
        "name": "Presence Detection",
        "info": "",
        "x": 590,
        "y": 360,
        "wires": []
    },
    {
        "id": "e70de37a1b248a25",
        "type": "inject",
        "z": "74341bea93cc320d",
        "name": "Every 1min",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "60",
        "crontab": "",
        "once": true,
        "onceDelay": "10",
        "topic": "",
        "payloadType": "date",
        "x": 130,
        "y": 100,
        "wires": [
            [
                "4375482d5ce5df1c"
            ]
        ]
    },
    {
        "id": "4375482d5ce5df1c",
        "type": "change",
        "z": "74341bea93cc320d",
        "name": "Turn On",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "on",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 300,
        "y": 100,
        "wires": [
            [
                "9f7db20cf89cff6d"
            ]
        ]
    },
    {
        "id": "86f29e0ed5d23b51",
        "type": "comment",
        "z": "74341bea93cc320d",
        "name": "Keep bathroom fan always on",
        "info": "Make sure fan is always turned on.\nNo sensors available yet to do this better.",
        "x": 160,
        "y": 60,
        "wires": []
    },
    {
        "id": "9f7db20cf89cff6d",
        "type": "change",
        "z": "74341bea93cc320d",
        "name": "Bathroom Fan",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "bathroom/fan",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 480,
        "y": 100,
        "wires": [
            [
                "2e5626a57c7fdbe1"
            ]
        ]
    },
    {
        "id": "2e5626a57c7fdbe1",
        "type": "link call",
        "z": "74341bea93cc320d",
        "name": "",
        "links": [
            "fd3c7a7a7a3b2e99"
        ],
        "timeout": "30",
        "x": 660,
        "y": 100,
        "wires": [
            []
        ]
    },
    {
        "id": "f80158f1.d27ab8",
        "type": "inject",
        "z": "8a6a748b145123cb",
        "name": "MAC iPhone SE",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "30",
        "crontab": "",
        "once": true,
        "onceDelay": "5",
        "topic": "",
        "payload": "{\"NewMACAddress\": \"XX:XX:XX:XX:XX:XX\" }",
        "payloadType": "json",
        "x": 130,
        "y": 80,
        "wires": [
            [
                "7b27936b.08bc8c"
            ]
        ]
    },
    {
        "id": "7b27936b.08bc8c",
        "type": "fritzbox-in",
        "z": "8a6a748b145123cb",
        "device": "bec40e042190e5b4",
        "name": "",
        "service": "urn:dslforum-org:service:Hosts:1",
        "action": "GetSpecificHostEntry",
        "arguments": "{\"NewMACAddress\":\"value\"}",
        "x": 330,
        "y": 80,
        "wires": [
            [
                "12274598.0e46da",
                "9c0d010be5886f15",
                "b6d4657cbec9268d"
            ]
        ]
    },
    {
        "id": "12274598.0e46da",
        "type": "debug",
        "z": "8a6a748b145123cb",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 570,
        "y": 80,
        "wires": []
    },
    {
        "id": "9c0d010be5886f15",
        "type": "switch",
        "z": "8a6a748b145123cb",
        "name": "Is Present?",
        "property": "payload.NewActive",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "1",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 570,
        "y": 120,
        "wires": [
            [
                "8e9714f633561c05"
            ]
        ]
    },
    {
        "id": "b6d4657cbec9268d",
        "type": "switch",
        "z": "8a6a748b145123cb",
        "name": "Is Not Present?",
        "property": "payload.NewActive",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "1",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 580,
        "y": 160,
        "wires": [
            [
                "3b561094f8825a29"
            ]
        ]
    },
    {
        "id": "8e9714f633561c05",
        "type": "change",
        "z": "8a6a748b145123cb",
        "name": "User present",
        "rules": [
            {
                "t": "set",
                "p": "user_present",
                "pt": "global",
                "to": "1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 770,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "3b561094f8825a29",
        "type": "change",
        "z": "8a6a748b145123cb",
        "name": "User not present",
        "rules": [
            {
                "t": "set",
                "p": "user_present",
                "pt": "global",
                "to": "0",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 780,
        "y": 160,
        "wires": [
            []
        ]
    },
    {
        "id": "f4ace639729b079b",
        "type": "comment",
        "z": "8a6a748b145123cb",
        "name": "Main User Presence Detection",
        "info": "",
        "x": 160,
        "y": 40,
        "wires": []
    },
    {
        "id": "2f609a2d2eb6c36a",
        "type": "inject",
        "z": "8a6a748b145123cb",
        "name": "Samsung S20 FE",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "30",
        "crontab": "",
        "once": true,
        "onceDelay": "10",
        "topic": "",
        "payload": "{\"NewMACAddress\": \"XX:XX:XX:XX:XX:XX\" }",
        "payloadType": "json",
        "x": 130,
        "y": 460,
        "wires": [
            [
                "1e09a8dddf0a36c2"
            ]
        ]
    },
    {
        "id": "1e09a8dddf0a36c2",
        "type": "fritzbox-in",
        "z": "8a6a748b145123cb",
        "device": "bec40e042190e5b4",
        "name": "",
        "service": "urn:dslforum-org:service:Hosts:1",
        "action": "GetSpecificHostEntry",
        "arguments": "{\"NewMACAddress\":\"value\"}",
        "x": 330,
        "y": 460,
        "wires": [
            [
                "9b618d89591e4c32",
                "6bae9ec64ae22aa4",
                "ae2aacab4e21becd"
            ]
        ]
    },
    {
        "id": "9b618d89591e4c32",
        "type": "debug",
        "z": "8a6a748b145123cb",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 570,
        "y": 460,
        "wires": []
    },
    {
        "id": "6bae9ec64ae22aa4",
        "type": "switch",
        "z": "8a6a748b145123cb",
        "name": "Is Present?",
        "property": "payload.NewActive",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "1",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 570,
        "y": 500,
        "wires": [
            [
                "9d204199083405ac"
            ]
        ]
    },
    {
        "id": "ae2aacab4e21becd",
        "type": "switch",
        "z": "8a6a748b145123cb",
        "name": "Is Not Present?",
        "property": "payload.NewActive",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "1",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 580,
        "y": 540,
        "wires": [
            [
                "7790892a62cafef9"
            ]
        ]
    },
    {
        "id": "9d204199083405ac",
        "type": "change",
        "z": "8a6a748b145123cb",
        "name": "Spouse present",
        "rules": [
            {
                "t": "set",
                "p": "spouse_present",
                "pt": "global",
                "to": "1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 780,
        "y": 500,
        "wires": [
            []
        ]
    },
    {
        "id": "7790892a62cafef9",
        "type": "change",
        "z": "8a6a748b145123cb",
        "name": "Spouse not present",
        "rules": [
            {
                "t": "set",
                "p": "spouse_present",
                "pt": "global",
                "to": "0",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 790,
        "y": 540,
        "wires": [
            []
        ]
    },
    {
        "id": "14272cd4a2102720",
        "type": "comment",
        "z": "8a6a748b145123cb",
        "name": "Spouse Presence Detection",
        "info": "",
        "x": 160,
        "y": 420,
        "wires": []
    },
    {
        "id": "f2c68c21a68054df",
        "type": "inject",
        "z": "8a6a748b145123cb",
        "name": "MAC Samsung S22",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "30",
        "crontab": "",
        "once": true,
        "onceDelay": "6",
        "topic": "",
        "payload": "{\"NewMACAddress\": \"XX:XX:XX:XX:XX:XX\" }",
        "payloadType": "json",
        "x": 140,
        "y": 240,
        "wires": [
            [
                "1b5d2574cd2e541f"
            ]
        ]
    },
    {
        "id": "1b5d2574cd2e541f",
        "type": "fritzbox-in",
        "z": "8a6a748b145123cb",
        "device": "bec40e042190e5b4",
        "name": "",
        "service": "urn:dslforum-org:service:Hosts:1",
        "action": "GetSpecificHostEntry",
        "arguments": "{\"NewMACAddress\":\"value\"}",
        "x": 330,
        "y": 240,
        "wires": [
            [
                "2cbb2b7a8ebf4ac8",
                "08dca32f4d3512b3",
                "b2d30bc31b36a369"
            ]
        ]
    },
    {
        "id": "2cbb2b7a8ebf4ac8",
        "type": "debug",
        "z": "8a6a748b145123cb",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 570,
        "y": 240,
        "wires": []
    },
    {
        "id": "08dca32f4d3512b3",
        "type": "switch",
        "z": "8a6a748b145123cb",
        "name": "Is Present?",
        "property": "payload.NewActive",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "1",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 570,
        "y": 280,
        "wires": [
            [
                "09abca3d9ff5e44c"
            ]
        ]
    },
    {
        "id": "b2d30bc31b36a369",
        "type": "switch",
        "z": "8a6a748b145123cb",
        "name": "Is Not Present?",
        "property": "payload.NewActive",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "1",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 580,
        "y": 320,
        "wires": [
            [
                "10582e300715e0b0"
            ]
        ]
    },
    {
        "id": "09abca3d9ff5e44c",
        "type": "change",
        "z": "8a6a748b145123cb",
        "name": "User (Work) present",
        "rules": [
            {
                "t": "set",
                "p": "user_present_work",
                "pt": "global",
                "to": "1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 800,
        "y": 280,
        "wires": [
            []
        ]
    },
    {
        "id": "10582e300715e0b0",
        "type": "change",
        "z": "8a6a748b145123cb",
        "name": "User (Work) not present",
        "rules": [
            {
                "t": "set",
                "p": "user_present_work",
                "pt": "global",
                "to": "0",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 810,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "e6010f1672bac815",
        "type": "mqtt out",
        "z": "b9932c4c66b18537",
        "name": "IoT MQTT",
        "topic": "",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "text/plain",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "5be146a97e98891c",
        "x": 210,
        "y": 160,
        "wires": []
    },
    {
        "id": "8a4031a3be97bf0a",
        "type": "debug",
        "z": "b9932c4c66b18537",
        "name": "Debug Print",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 210,
        "y": 120,
        "wires": []
    },
    {
        "id": "fd3c7a7a7a3b2e99",
        "type": "link in",
        "z": "b9932c4c66b18537",
        "name": "set_mqtt",
        "links": [],
        "x": 55,
        "y": 80,
        "wires": [
            [
                "8a4031a3be97bf0a",
                "e6010f1672bac815",
                "d185ca7a7211b37e"
            ]
        ]
    },
    {
        "id": "db9d2ccfce698ced",
        "type": "comment",
        "z": "b9932c4c66b18537",
        "name": "set_mqtt",
        "info": "",
        "x": 100,
        "y": 40,
        "wires": []
    },
    {
        "id": "d185ca7a7211b37e",
        "type": "link out",
        "z": "b9932c4c66b18537",
        "name": "",
        "mode": "return",
        "links": [],
        "x": 155,
        "y": 80,
        "wires": []
    },
    {
        "id": "2f4447ab9df7cdc9",
        "type": "comment",
        "z": "b9932c4c66b18537",
        "name": "Initialize States",
        "info": "",
        "x": 120,
        "y": 280,
        "wires": []
    },
    {
        "id": "b8d60dba22486026",
        "type": "inject",
        "z": "b9932c4c66b18537",
        "name": "Once",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payloadType": "date",
        "x": 90,
        "y": 320,
        "wires": [
            [
                "0c523fcd13255b79",
                "cdede78de7e379d1",
                "31d1f2ec3504d186",
                "7d3895abe57fe91b"
            ]
        ]
    },
    {
        "id": "dd5d9dc12a9e14f0",
        "type": "change",
        "z": "b9932c4c66b18537",
        "name": "Set Sun State",
        "rules": [
            {
                "t": "set",
                "p": "sunevent",
                "pt": "global",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 580,
        "y": 360,
        "wires": [
            []
        ]
    },
    {
        "id": "0c523fcd13255b79",
        "type": "change",
        "z": "b9932c4c66b18537",
        "name": "Bathroom Lights not forced",
        "rules": [
            {
                "t": "set",
                "p": "forced_light_bathroom",
                "pt": "global",
                "to": "none",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 340,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "cdede78de7e379d1",
        "type": "function",
        "z": "b9932c4c66b18537",
        "name": "Return current approx. sun state",
        "func": "var now = new Date();\nvar h = now.getHours();\n\nif ((h >= 8) && (h <= 19)) {\n    msg.payload = \"sunrise\";\n} else {\n    msg.payload = \"sunset\";\n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 350,
        "y": 360,
        "wires": [
            [
                "dd5d9dc12a9e14f0"
            ]
        ]
    },
    {
        "id": "31d1f2ec3504d186",
        "type": "change",
        "z": "b9932c4c66b18537",
        "name": "User present",
        "rules": [
            {
                "t": "set",
                "p": "user_present",
                "pt": "global",
                "to": "1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 290,
        "y": 400,
        "wires": [
            []
        ]
    },
    {
        "id": "7d3895abe57fe91b",
        "type": "change",
        "z": "b9932c4c66b18537",
        "name": "Spouse present",
        "rules": [
            {
                "t": "set",
                "p": "spouse_present",
                "pt": "global",
                "to": "1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 300,
        "y": 480,
        "wires": [
            []
        ]
    },
    {
        "id": "0cd19755c3e94219",
        "type": "change",
        "z": "b9932c4c66b18537",
        "name": "User (Work) present",
        "rules": [
            {
                "t": "set",
                "p": "user_present_work",
                "pt": "global",
                "to": "1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 320,
        "y": 440,
        "wires": [
            []
        ]
    }
]

The Node-RED installation is handled by an ansible script.