From d755d3c1ed217ada649989b1e9336eaa181edb85 Mon Sep 17 00:00:00 2001 From: nearxos Date: Wed, 1 Apr 2026 14:43:30 +0300 Subject: [PATCH] Update Node-RED configuration and documentation for room management - Added node-red/config_files/flows_cred.json to .gitignore to prevent committing sensitive credentials. - Updated documentation references to the new location of room-config.js in node-red/config_files/ for clarity. - Removed the obsolete room-config.js file, centralizing configuration management. This update enhances security and improves the organization of Node-RED configuration files, ensuring better management of room settings. --- .gitignore | 3 + docs/integration/all-lights-and-rooms.md | 2 +- docs/integration/nodered-flow-check-report.md | 2 +- node-red/config_files/flows.json | 15047 ++++++++++++++++ ...-red-contrib-home-assistant-websocket.json | 12 + node-red/config_files/package-lock.json | 4101 +++++ node-red/config_files/package.json | 17 + node-red/{ => config_files}/room-config.js | 2 +- node-red/config_files/settings.js | 594 + node-red/room-config-loader.js | 2 +- 10 files changed, 19778 insertions(+), 4 deletions(-) create mode 100644 node-red/config_files/flows.json create mode 100644 node-red/config_files/node-red-contrib-home-assistant-websocket.json create mode 100644 node-red/config_files/package-lock.json create mode 100644 node-red/config_files/package.json rename node-red/{ => config_files}/room-config.js (99%) create mode 100644 node-red/config_files/settings.js diff --git a/.gitignore b/.gitignore index 0e23726..f26fb09 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,9 @@ temp/ # Node modules (if any) node_modules/ +# Node-RED: encrypted credentials (pull from server for backup; do not commit) +node-red/config_files/flows_cred.json + # Python __pycache__/ *.pyc diff --git a/docs/integration/all-lights-and-rooms.md b/docs/integration/all-lights-and-rooms.md index 67f90c6..6e5489b 100644 --- a/docs/integration/all-lights-and-rooms.md +++ b/docs/integration/all-lights-and-rooms.md @@ -8,7 +8,7 @@ This doc explains how to scale the working “test light” setup to **all your To avoid adding a new room or light in several places, use **one config file** that lives on the Node-RED server and is loaded into **global context** at startup. All function nodes read from `global.get('roomConfig')`. -**Config file:** [`node-red/room-config.js`](../../node-red/room-config.js) — exports `ROOM_CONFIG` with: **roomNames** (NVL SEND), **lightEntityMap** (NVL to HA Sync), **entityToRoom** (HA to NVL), **deviceToRoom** (Zigbee to NVL). +**Config file:** [`node-red/config_files/room-config.js`](../../node-red/config_files/room-config.js) — exports `ROOM_CONFIG` with: **roomNames** (NVL SEND), **lightEntityMap** (NVL to HA Sync), **entityToRoom** (HA to NVL), **deviceToRoom** (Zigbee to NVL). **Server location:** `/root/.node-red/room-config.js` (uploaded via `scp`). diff --git a/docs/integration/nodered-flow-check-report.md b/docs/integration/nodered-flow-check-report.md index 898de27..ebf8dec 100644 --- a/docs/integration/nodered-flow-check-report.md +++ b/docs/integration/nodered-flow-check-report.md @@ -68,7 +68,7 @@ Full file: [node-red/room-config-loader.js](../../node-red/room-config-loader.js ## room-config.js on server `/root/.node-red/room-config.js` is present and contains `roomNames`, `switchBindings`, etc. Upload from repo when you change config: -`scp node-red/room-config.js root@10.20.30.12:/root/.node-red/room-config.js` +`scp node-red/config_files/room-config.js root@nodered.paraskeva.net:/root/.node-red/room-config.js` --- diff --git a/node-red/config_files/flows.json b/node-red/config_files/flows.json new file mode 100644 index 0000000..9f72d9c --- /dev/null +++ b/node-red/config_files/flows.json @@ -0,0 +1,15047 @@ +[ + { + "id": "46892ced77481340", + "type": "tab", + "label": "Flow 1", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "7de41d810b04d992", + "type": "tab", + "label": "Living Room", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "480553bb6f5b2b51", + "type": "tab", + "label": "Notifications", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "f9a2fb576f0d360d", + "type": "tab", + "label": "Zigbee ", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "6722d185624fd57a", + "type": "tab", + "label": "Scheduler", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "7eef035843cf87db", + "type": "tab", + "label": "Flow 2", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "31693504fcbbd954", + "type": "tab", + "label": "Flow 3", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "50971c50479dfd7a", + "type": "tab", + "label": "Flow 4", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "a6a28d6b6c0b4f4a", + "type": "tab", + "label": "Flow 5", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "b673e10beab091ff", + "type": "tab", + "label": "Flow 6", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "24e9a56732cfa6f1", + "type": "tab", + "label": "Flow 7", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "57bd1b149526fcee", + "type": "subflow", + "name": "Room Lights", + "info": "", + "category": "", + "in": [ + { + "x": 260, + "y": 220, + "wires": [ + { + "id": "ee7c2a47d6441c26" + } + ] + } + ], + "out": [ + { + "x": 560, + "y": 120, + "wires": [ + { + "id": "ee7c2a47d6441c26", + "port": 0 + } + ] + }, + { + "x": 560, + "y": 160, + "wires": [ + { + "id": "ee7c2a47d6441c26", + "port": 1 + } + ] + }, + { + "x": 560, + "y": 200, + "wires": [ + { + "id": "ee7c2a47d6441c26", + "port": 2 + } + ] + }, + { + "x": 560, + "y": 240, + "wires": [ + { + "id": "ee7c2a47d6441c26", + "port": 3 + } + ] + }, + { + "x": 560, + "y": 280, + "wires": [ + { + "id": "ee7c2a47d6441c26", + "port": 4 + } + ] + }, + { + "x": 560, + "y": 320, + "wires": [ + { + "id": "ee7c2a47d6441c26", + "port": 5 + } + ] + } + ], + "env": [ + { + "name": "room_name", + "type": "str", + "value": "" + } + ], + "meta": {}, + "color": "#DDAA99" + }, + { + "id": "d0de479f6b80ab6c", + "type": "subflow", + "name": "Subflow 1", + "info": "", + "in": [ + { + "x": 60, + "y": 80, + "wires": [ + { + "id": "f9557fd9a8b2eee2" + } + ] + } + ], + "out": [ + { + "x": 340, + "y": 80, + "wires": [ + { + "id": "f9557fd9a8b2eee2", + "port": 0 + } + ] + } + ] + }, + { + "id": "4dc13919bcc9d676", + "type": "subflow", + "name": "Room filter", + "info": "", + "category": "", + "in": [ + { + "x": 60, + "y": 80, + "wires": [ + { + "id": "d2a3760f36aa6f41" + } + ] + } + ], + "out": [ + { + "x": 640, + "y": 100, + "wires": [ + { + "id": "0ce841560c8c4087", + "port": 0 + }, + { + "id": "64b045db5a4541fa", + "port": 0 + }, + { + "id": "4a891a7505b5fd00", + "port": 0 + }, + { + "id": "258a84ec1d54a43a", + "port": 0 + }, + { + "id": "5532273136423869", + "port": 0 + } + ] + } + ], + "env": [ + { + "name": "switch_1", + "type": "str", + "value": ".sw_1" + }, + { + "name": "switch_2", + "type": "str", + "value": ".sw_2" + }, + { + "name": "switch_3", + "type": "str", + "value": ".sw_3" + }, + { + "name": "switch_4", + "type": "str", + "value": ".sw_4" + }, + { + "name": "single", + "type": "str", + "value": ".sw_1" + } + ], + "meta": {}, + "color": "#DDAA99" + }, + { + "id": "664de832a54bc4e0", + "type": "group", + "z": "46892ced77481340", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "039e1be0cfe8e630", + "740b766e9bdc0d2a" + ], + "x": 1934, + "y": 179, + "w": 292, + "h": 82 + }, + { + "id": "f27a48975d61e376", + "type": "group", + "z": "46892ced77481340", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "e37aa132859c1ea8", + "d3f0c80e0188aabf", + "1a534f2c5be60889" + ], + "x": 2244, + "y": 179, + "w": 502, + "h": 152 + }, + { + "id": "9113c5ebf5c089e3", + "type": "group", + "z": "480553bb6f5b2b51", + "name": "Door Sensor Notifications", + "style": { + "fill": "#addb7b", + "fill-opacity": "0.89", + "label": true, + "stroke": "#ffffff", + "stroke-opacity": "0" + }, + "nodes": [ + "0385d24efa18d698", + "9d611c0ff2a0b1ca", + "10448f46514c1406", + "a9d786b1ba52704b", + "e250cc2f901bea9f", + "5e94eb39aec6e82d", + "8b8cce25637907b1", + "3f9797c13c3a11a1", + "7301d71523d87e94", + "4055425d6c42dd13", + "ada6fe8339eab29b", + "8e0d5ec04f029b9a", + "eb25ece689ea1045", + "63cb264d89898c6a", + "bd4b7dd437c1aaaf", + "f6adcaa35a793e58" + ], + "x": 34, + "y": -1, + "w": 1562, + "h": 302 + }, + { + "id": "91b1f4cb0e1ffa84", + "type": "group", + "z": "480553bb6f5b2b51", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "d592161c8e50638a", + "2a218aee475ce267", + "f5ef8cb0a1bc7def" + ], + "x": 2234, + "y": 39, + "w": 412, + "h": 122 + }, + { + "id": "d24bcb9ea3d9711c", + "type": "group", + "z": "46892ced77481340", + "name": "", + "style": { + "fill": "#ffdf7f", + "label": true + }, + "nodes": [ + "925b79f58f75e449", + "b88648a578b60cb8", + "4a9361ba088ba046", + "0271d58953459100", + "4b360c7d54e39e0a", + "62cc8431c77a4797", + "2d6eb3ac6de382be", + "af99b05ba44c6741", + "3c7f8838aa2349b9", + "0b0b27deacee2bfc" + ], + "x": 38, + "y": 653, + "w": 1634, + "h": 684 + }, + { + "id": "7c29230c98070fd5", + "type": "group", + "z": "46892ced77481340", + "name": "", + "style": { + "fill": "#bfdbef", + "label": true, + "color": "#000000" + }, + "nodes": [ + "ff517137dc8eb4e5", + "2b9f0ac7a58d6e99", + "007a0818e00b6e8a", + "42040eb51b6909d2", + "888d856dae38ce27", + "2ba7fb1e189ec017", + "3cc3b21e7b1f7b13", + "f16b851b413f0d32", + "1a348789bcd14214", + "8044f4e5402ca464", + "f83a4ef2662f4f11", + "48b019b444db2a5a", + "639e1c2545c2de96" + ], + "x": 48, + "y": 133, + "w": 1764, + "h": 494 + }, + { + "id": "41609eab5db77f0a", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "36d1d2c1e10100e7", + "4c584ffba6a17ccb", + "c53543186fd45d8b", + "33eb6e0725214c0b", + "5df52e7f8217a6d9", + "6dd69a9285fc9583", + "3e5fabb6aa93449c", + "aadcf8d1921d77e6" + ], + "x": 94, + "y": 2019, + "w": 1272, + "h": 162 + }, + { + "id": "b6271f4a3284f8d9", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "46517eb4342b3d18", + "a3d5be29d6effa34", + "d23c32a704af639d", + "68f7d3015de2dbab", + "701c1cd70874e615" + ], + "x": 94, + "y": 1879, + "w": 1272, + "h": 122 + }, + { + "id": "3db6f7ccc63a5f8b", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "1633e8e6861be2e0", + "9c36d1335b0f8ebb", + "fe5612a41399f1f5", + "7f48ed25c8ec1fce", + "ab17cf6a7d65fcb9" + ], + "x": 94, + "y": 1699, + "w": 1272, + "h": 162 + }, + { + "id": "5270af8a6312b29d", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "25cf5d71108cbbb4", + "cbcd3d741247b12b", + "07e63813fb04b906", + "4d3d4a82a512ca10", + "0bf96ec77f6d1809" + ], + "x": 94, + "y": 1459, + "w": 1272, + "h": 122 + }, + { + "id": "6809b1c79783ab0c", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "a330bf2f4d26c536", + "398c06a48e4daa13" + ], + "x": 94, + "y": 1599, + "w": 1272, + "h": 82 + }, + { + "id": "1166ae03751d953e", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "ef6df11582a22110", + "774c15def7e6cf96" + ], + "x": 94, + "y": 1359, + "w": 1272, + "h": 82 + }, + { + "id": "fb2d97888a83fc98", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "6f028b285108ff17", + "82c1d5f7ae54b348" + ], + "x": 94, + "y": 1259, + "w": 1272, + "h": 82 + }, + { + "id": "5b22d521e762f0da", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "6e30880884a2cdb2", + "da7ab159be01b35c" + ], + "x": 94, + "y": 1159, + "w": 1272, + "h": 82 + }, + { + "id": "74596bdef53b62cf", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "0bd6051323b66072", + "6aac621e2a9acf79" + ], + "x": 94, + "y": 1059, + "w": 1272, + "h": 82 + }, + { + "id": "5d202845936f0275", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "becbc51cb810ff3d", + "4673e2e3f2c0626f" + ], + "x": 94, + "y": 959, + "w": 1272, + "h": 82 + }, + { + "id": "68db2262f7c7313e", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "675ba75711ef84e7", + "84272551b322cde2" + ], + "x": 94, + "y": 859, + "w": 1272, + "h": 82 + }, + { + "id": "39f557d8eec2fbae", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "a6cf1bd4ae01ee56", + "16eaca15570ff092", + "109c390c131b09a8", + "d02a9d4e0468bc67", + "2d6bcc49a2785fd6" + ], + "x": 94, + "y": 379, + "w": 1272, + "h": 122 + }, + { + "id": "795d604329d51272", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "1aa9e0ff37d1afee", + "b9c07d22cbaae6a2" + ], + "x": 94, + "y": 279, + "w": 1272, + "h": 82 + }, + { + "id": "4b06cbc568091f6f", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "6654c150377ed2bb", + "83bbabef65831016" + ], + "x": 94, + "y": 179, + "w": 1272, + "h": 82 + }, + { + "id": "b5ffb4a4212da258", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "ca1a1f7f8b90cfc9", + "b8f3d3242fb39bdd" + ], + "x": 94, + "y": 79, + "w": 1272, + "h": 82 + }, + { + "id": "5c95d99a679a5e00", + "type": "group", + "z": "480553bb6f5b2b51", + "name": "Water Leak Notifications", + "style": { + "label": true, + "fill": "#0070c0", + "fill-opacity": "0.54", + "stroke": "#000000", + "color": "#000000" + }, + "nodes": [ + "8f42e8fdfef40579", + "58ee1ac91f379cb9", + "a1f1e1ea2cf90f61", + "3242605bce7f6f92", + "22740e52f6cc3399", + "53799a37bb6cf7c6", + "8b9761ea2f86b4f1", + "70f797398b0e6783", + "2157cdd444dfec91", + "8b2f06afcf0d773e", + "e31ec1ce332c4c99", + "1164cfc4fd2e5382", + "748c39a404648158", + "a73395db2426b4fc" + ], + "x": 54, + "y": 379, + "w": 1562, + "h": 288 + }, + { + "id": "1c73b8aaec035f06", + "type": "group", + "z": "480553bb6f5b2b51", + "name": "Battery notifications", + "style": { + "fill": "#ffbfbf", + "label": true, + "color": "#000000" + }, + "nodes": [ + "eb721beae24b271b", + "41f1e9feaf1ea6aa", + "c60307d3b68ebe34", + "81d16e7a5307e9cc", + "209d79611a5414cc", + "e6a93b0cbc48af98", + "a9f755806e4e8a75", + "5c2272c244ffc8b0", + "6d60df5c0c6920d2" + ], + "x": 54, + "y": 699, + "w": 1562, + "h": 162 + }, + { + "id": "bd16cebc90c528e9", + "type": "group", + "z": "46892ced77481340", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "b4e49387d80fa951", + "860779317fad91b5", + "2ae3fedce613ecfe", + "1a8d95afd537e151", + "d1c2e1ee1fba88c6" + ], + "x": 1944, + "y": 319, + "w": 482, + "h": 232 + }, + { + "id": "507ca4f1b82ad9c2", + "type": "group", + "z": "46892ced77481340", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "66255b06a0d4a4c9", + "9c99ecea6e4aefd8" + ], + "x": 1954, + "y": 559, + "w": 292, + "h": 82 + }, + { + "id": "cc230a0894b116d3", + "type": "group", + "z": "6722d185624fd57a", + "name": "", + "style": { + "fill": "#7fb7df", + "label": true + }, + "nodes": [ + "05ecf6f492d4a8cf", + "ad81a0918c31a862", + "7049d2e629127b0e", + "e44fa1b081b4aba7", + "532476a808456423", + "9e01966beafd582b", + "301e955ac79a9326", + "54161a78e09f85c3", + "0fdf497d70900d11", + "434ef7e821c63223" + ], + "x": 134, + "y": 939, + "w": 992, + "h": 277 + }, + { + "id": "b73455843d7a5a9f", + "type": "group", + "z": "6722d185624fd57a", + "name": "Watering Schedule", + "style": { + "label": true, + "fill": "#b797cf" + }, + "nodes": [ + "905629455f4dd222", + "33e972f98673a6b6", + "ab7182ad5c37be48", + "d51ae77e82bb86f0", + "b43dc5183a67d128", + "2896d687417bbc1d", + "1199fd928d0abd32", + "86153b2dba01756f" + ], + "x": 54, + "y": 379, + "w": 1432, + "h": 242 + }, + { + "id": "ef555db28dba4c46", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "0b112f9f314fa0bb", + "a1f5a94baf1fbcd7", + "ae314d3d3457b679", + "0c35b08a3c7d347f", + "f0de9c882920d07c" + ], + "x": 94, + "y": 519, + "w": 1272, + "h": 122 + }, + { + "id": "1756bd5d4ff2c23b", + "type": "group", + "z": "f9a2fb576f0d360d", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "310f749a07eded9d", + "d0febe343ef897ac", + "e1965a78e430acad", + "8cce4a0a7061ac87", + "d5cad6d8ca5fe08c" + ], + "x": 94, + "y": 2199, + "w": 1272, + "h": 162 + }, + { + "id": "860e99939cf4fb5c", + "type": "group", + "z": "6722d185624fd57a", + "name": "Christmas Light", + "style": { + "label": true + }, + "nodes": [ + "d97b1b86e0e0040e", + "3737a8c897de2221", + "ca74d224370a0b5a", + "3a8262144b91d661", + "d20a28e1dea2fcde", + "01df5278eead8a98", + "1460ed0d062f8b73", + "6fdd0a95dd76a2e8", + "bb1639973b4a6a05", + "800cb2f6a21c882f", + "893d9228d166c0d2", + "be86177040153c9f" + ], + "x": 654, + "y": 1679, + "w": 1272, + "h": 382 + }, + { + "id": "ce79bea4667a77e2", + "type": "group", + "z": "480553bb6f5b2b51", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "8656a450e0ae020d", + "7ff8359abb38dca4", + "doorbell_state_check", + "bd7fed2198a7b950", + "78a1c2a40934ef20", + "05f0d77a9b605f22", + "19d0e8d8c5addb1f", + "eff49303c6d84242", + "c74ddf6b84ef7a95", + "d496d62222d1a073", + "48bfe0ad8a973a84", + "88d855e525ab78f0", + "a4752681740af47e", + "7e88e31d094e343d", + "365adc2679063bc8", + "842a49fa21a0587a", + "bedee5f9397c27f1", + "3f9c2db4dfea3095", + "0446448ed8c8da8a", + "d241b1f4991cfc05" + ], + "x": 14, + "y": 1129, + "w": 2362, + "h": 272 + }, + { + "id": "9da6393897881dca", + "type": "group", + "z": "46892ced77481340", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "5351a670ea3bcf15", + "8dd17125d6bb49f7", + "9b4f318fa5f438aa" + ], + "x": 1764, + "y": 839, + "w": 522, + "h": 152 + }, + { + "id": "ff517137dc8eb4e5", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Office ", + "style": { + "label": true + }, + "nodes": [ + "a91dcb332bae2807", + "11f594c5b9f0fce8" + ], + "x": 354, + "y": 379, + "w": 312, + "h": 102 + }, + { + "id": "2b9f0ac7a58d6e99", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Shower", + "style": { + "label": true + }, + "nodes": [ + "a2eceb1ef60667cb", + "102d7d54a45e3f18", + "a024d8a903074aa4", + "d680ff6c7ac109cf", + "9dc26f31e8b58f14" + ], + "x": 1034, + "y": 259, + "w": 402, + "h": 222 + }, + { + "id": "007a0818e00b6e8a", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Master Bedroom", + "style": { + "label": true + }, + "nodes": [ + "67149a39f56c5694", + "d5dacec70d47b2e5", + "28d2293b97a7b72b", + "a6493ad8864f2b68" + ], + "x": 354, + "y": 159, + "w": 312, + "h": 202 + }, + { + "id": "42040eb51b6909d2", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Kitchen", + "style": { + "label": true, + "fill": "#d1d1d1", + "color": "#000000" + }, + "nodes": [ + "e9aa8b33edf056f6", + "eda94d52e6cf650a", + "188a3b16710467e7", + "2dc64fabc1602ec8", + "a1f44cc63b77a2c3" + ], + "x": 74, + "y": 159, + "w": 262, + "h": 422 + }, + { + "id": "888d856dae38ce27", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Veranda", + "style": { + "label": true + }, + "nodes": [ + "f714485f125865af", + "2565695beaa14d76", + "9594045be414b84c" + ], + "x": 1434, + "y": 159, + "w": 352, + "h": 142 + }, + { + "id": "2ba7fb1e189ec017", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Living Room ", + "style": { + "label": true + }, + "nodes": [ + "58525896ff637a27", + "beeb512081d25291" + ], + "x": 694, + "y": 379, + "w": 312, + "h": 82 + }, + { + "id": "3cc3b21e7b1f7b13", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Dining Room", + "style": { + "label": true + }, + "nodes": [ + "66cafcf3b261e8b5", + "04e183fcfe9e9f6d", + "1f3ba1a73e155da9", + "6190a90971a43aba" + ], + "x": 694, + "y": 159, + "w": 312, + "h": 202 + }, + { + "id": "f16b851b413f0d32", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Guest WC", + "style": { + "label": true + }, + "nodes": [ + "f1cf19264e9957f3", + "0d7f6d11034f98ee" + ], + "x": 1034, + "y": 499, + "w": 342, + "h": 82 + }, + { + "id": "1a348789bcd14214", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Back yard", + "style": { + "label": true + }, + "nodes": [ + "518a9fd6f52bfc23", + "839bb9d987729d16" + ], + "x": 1434, + "y": 419, + "w": 352, + "h": 82 + }, + { + "id": "8044f4e5402ca464", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Master Bathroom", + "style": { + "label": true + }, + "nodes": [ + "0f93e95f3e92150a", + "ed3b84f4d63a8bd5" + ], + "x": 1034, + "y": 159, + "w": 342, + "h": 82 + }, + { + "id": "f83a4ef2662f4f11", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Front Yard", + "style": { + "label": true + }, + "nodes": [ + "194c35655de1a128", + "45e37c698b0b2e5a" + ], + "x": 1434, + "y": 319, + "w": 352, + "h": 82 + }, + { + "id": "48b019b444db2a5a", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "name": "Drive ways", + "style": { + "label": true + }, + "nodes": [ + "bbda8fd42d8aa120", + "01da9e8ebba7f592" + ], + "x": 1434, + "y": 519, + "w": 352, + "h": 82 + }, + { + "id": "925b79f58f75e449", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "40fd804bb4acf519", + "b9ab51a28b433f94", + "0d1a60ec03adac5c" + ], + "x": 64, + "y": 999, + "w": 522, + "h": 152 + }, + { + "id": "b88648a578b60cb8", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "1feb11ab8be3ef2e", + "01b4eb4b91f9c9e2", + "1ee2b22951639d6b" + ], + "x": 64, + "y": 839, + "w": 522, + "h": 152 + }, + { + "id": "4a9361ba088ba046", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "3c894953f414add5", + "7ec85095958da9f5", + "566e5b2ec261934f", + "89762968e1556a42" + ], + "x": 64, + "y": 679, + "w": 522, + "h": 152 + }, + { + "id": "0271d58953459100", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "af7220d8f5bb9bf2", + "b51586d393da96cd", + "e8db33fc81400531", + "e0739e7f8c82e487", + "1530556181903c3a" + ], + "x": 624, + "y": 1099, + "w": 482, + "h": 202 + }, + { + "id": "4b360c7d54e39e0a", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "53178ccb3f691dad", + "b2641f71595c1d2d", + "ba2859e1aa882eee" + ], + "x": 624, + "y": 919, + "w": 482, + "h": 152 + }, + { + "id": "62cc8431c77a4797", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "b664207011dd548a", + "39d435c6a32d4f09", + "5f7ffd82e5200d9a", + "f0bc3094d4af717e", + "0a4414fc667405b4", + "22c73018f4564256" + ], + "x": 624, + "y": 679, + "w": 482, + "h": 262 + }, + { + "id": "2d6eb3ac6de382be", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "e6e67dc471f2ecb3", + "fc5020edbd894baa", + "29af11e532f234d4" + ], + "x": 1164, + "y": 839, + "w": 482, + "h": 152 + }, + { + "id": "af99b05ba44c6741", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "b7e6812909ef1051", + "37cdfd56758051ef", + "280e6cc7a9de62b5" + ], + "x": 1164, + "y": 679, + "w": 482, + "h": 152 + }, + { + "id": "3c7f8838aa2349b9", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "806720bc7ab2d4e8", + "3b5e1f4f9ccd9c74", + "07bae699fea75512" + ], + "x": 1164, + "y": 999, + "w": 482, + "h": 152 + }, + { + "id": "0b0b27deacee2bfc", + "type": "group", + "z": "46892ced77481340", + "g": "d24bcb9ea3d9711c", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "3ec8968914e324f5", + "2b29c71c180a50df", + "197fc35230069190" + ], + "x": 64, + "y": 1159, + "w": 522, + "h": 152 + }, + { + "id": "639e1c2545c2de96", + "type": "group", + "z": "46892ced77481340", + "g": "7c29230c98070fd5", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "a8ac75d5944a28b9", + "f11fa2bedfc41e41" + ], + "x": 354, + "y": 499, + "w": 292, + "h": 82 + }, + { + "id": "a73395db2426b4fc", + "type": "group", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Watering Schedule Notification", + "style": { + "label": true, + "fill": "#c8e7a7", + "color": "#000000" + }, + "nodes": [ + "c57ea029ee569428", + "fe7372c7f33d96f3", + "c1d2a4dc34b63730", + "141f635c6c9d380f", + "18454d6cad2042dc" + ], + "x": 474, + "y": 499, + "w": 622, + "h": 142 + }, + { + "id": "4e20fc347c658518", + "type": "zigbee2mqtt-server", + "name": "", + "host": "mqtt.paraskeva.net", + "mqtt_port": 1883, + "mqtt_username": "nodered", + "mqtt_password": "sh1pb0x1", + "mqtt_qos": "0", + "tls": "", + "usetls": false, + "base_topic": "zigbee2mqtt" + }, + { + "id": "29eb84a2708d96e0", + "type": "server", + "name": "Home Assistant", + "version": 6, + "addon": false, + "rejectUnauthorizedCerts": true, + "ha_boolean": [ + "y", + "yes", + "true", + "on", + "home", + "open" + ], + "connectionDelay": true, + "cacheJson": true, + "heartbeat": false, + "heartbeatInterval": 30, + "areaSelector": "friendlyName", + "deviceSelector": "friendlyName", + "entitySelector": "friendlyName", + "statusSeparator": ": ", + "statusYear": "hidden", + "statusMonth": "short", + "statusDay": "numeric", + "statusHourCycle": "default", + "statusTimeFormat": "h:m", + "enableGlobalContextStore": false + }, + { + "id": "70fca1d2a3467c93", + "type": "ha-device-config", + "name": "Office", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "7fe55db85a29f18c", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "70fca1d2a3467c93", + "name": "Office Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Office Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "4f23c8e7043f542b", + "type": "ha-device-config", + "name": "Master Bedroom", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "5e7f6287035ecc94", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "4f23c8e7043f542b", + "name": "Master Bedroom Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Master Bedroom Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "b2c7564cfe776a2d", + "type": "nvl-datatypes", + "name": "Example types", + "datatypes": "//Structure definition\nTYPE ST_MyStruct:\nSTRUCT\n xBool: BOOL;\n bByte: BYTE;\nEND_STRUCT\nEND_TYPE\n\n//Enumeration definition\nTYPE ENUM_Light: (\n Red,\n Orange,\n Green := 10\n);\nEND_TYPE" + }, + { + "id": "17b3adc633ee3394", + "type": "ha-device-config", + "name": "Shower", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "88f51f9c233f4cc3", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Shower Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Shower Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "3cec5437caacb905", + "type": "ha-device-config", + "name": "Kitchen", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "29592d5555367344", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Kitchen Island", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Kitchen Island Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "cc2640502094371a", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Kitchen Main Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Kitchen Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "8e22cfb83ec013d1", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Fridge Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Fridge Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "085d42240f91eec2", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Pantry Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Pantry Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "451e5988101b1aac", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "4f23c8e7043f542b", + "name": "Master Bedroom Door Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Master Bedroom Door Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "c21001f594a359c9", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "4f23c8e7043f542b", + "name": "Master Bedroom Dresser Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Master Bedroom Dresser Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "29f5afd91874c0b8", + "type": "ha-device-config", + "name": "Veranda", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "2a94bae266d9423d", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "29f5afd91874c0b8", + "name": "Veranda Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Veranda Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "d721225e966ecf1b", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "29f5afd91874c0b8", + "name": "Veranda BBQ Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Veranda BBQ Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "2380526f974b097d", + "type": "ha-device-config", + "name": "Living Room", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "eca0cca02aabd143", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "2380526f974b097d", + "name": "Living Room Main Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Living Room Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "5d56cc73c39843cc", + "type": "ha-device-config", + "name": "Dining Room", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "8c493a05a6971f90", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "5d56cc73c39843cc", + "name": "Dining Room Main Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Dining Room Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "9f0f51223282478b", + "type": "ha-device-config", + "name": "Guest WC", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "85fe098bb4010c10", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "9f0f51223282478b", + "name": "Guest WC Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Guest WC Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "87957c9f7179fe85", + "type": "ha-device-config", + "name": "Master Bathroom", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "ac7e5f5dfeb9ce54", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "87957c9f7179fe85", + "name": "Master Bathroom Main Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Master Bathroom Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "d46352482122dfe6", + "type": "ha-device-config", + "name": "Front Yard", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "aa9cf2e1dc39616f", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "d46352482122dfe6", + "name": "Front Entrance Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Front Entrance Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "be89dbc3425f9cc0", + "type": "ha-device-config", + "name": "Back Yard", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "a3fb2a27c9896974", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "be89dbc3425f9cc0", + "name": "Flood Light 1", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Flood Light 1" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "a1743637a1619654", + "type": "ha-device-config", + "name": "Driveways", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "a97f2813611faac8", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "a1743637a1619654", + "name": "Driveway Flood Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Driveway Flood Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "f744459771e70676", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "70fca1d2a3467c93", + "name": "Office Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Office Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "2b453fcb286e51f5", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "4f23c8e7043f542b", + "name": "Master Bedroom Main Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Master Bedroom Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "edf39a33a28182ca", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "87957c9f7179fe85", + "name": "Master Bathroom Main Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Master Bathroom Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "16560fc93e9523d4", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Shower Main Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Shower Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "9b2dc1266b7513da", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Shower Fan", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Shower Fan" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "12e315808eced5db", + "type": "ha-device-config", + "name": "Bedroom", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "ac2dba905819562f", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Shower Fan Switch", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Shower Fan Switch" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "c494e187e88c3208", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Pantry Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Pantry Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "293fc06a888710c5", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "12e315808eced5db", + "name": "Bedroom Main Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Bedroom Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "1a22f89094ea7b38", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "29f5afd91874c0b8", + "name": "Veranda BBQ Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Veranda BBQ Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "8b4d5408fdcd8c0d", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "12e315808eced5db", + "name": "Bedroom Main Light", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Bedroom Main Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "f190a69cf72af140", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "5d56cc73c39843cc", + "name": "Dining Room Main", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Dining Room Main" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "b43c6846b0baeeec", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "2380526f974b097d", + "name": "Living Room Main", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Living Room Main" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "be947f3b20336437", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Kitchen Main", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Kitchen Main" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "9b99cd4af3bdb9a9", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Kitchen Island", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Kitchen Island" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "d75c31dd7a1edcbf", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3cec5437caacb905", + "name": "Fridge Light", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Fridge Light" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "3dbb887330025103", + "type": "telegram bot", + "botname": "spito_robot", + "usernames": "nearxos", + "chatids": "4747176681", + "baseapiurl": "", + "testenvironment": false, + "updatemode": "polling", + "addressfamily": "0", + "pollinterval": 300, + "usesocks": false, + "sockshost": "", + "socksprotocol": "socks5", + "socksport": 6667, + "socksusername": "anonymous", + "sockspassword": "", + "bothost": "", + "botpath": "", + "localbothost": "0.0.0.0", + "localbotport": 8443, + "publicbotport": 8443, + "privatekey": "", + "certificate": "", + "useselfsignedcertificate": false, + "sslterminated": false, + "verboselogging": true + }, + { + "id": "fe79cfd2fda112db", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "d46352482122dfe6", + "name": "Front Lights", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Front Lights" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "311d801ad1f1bc0f", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "d46352482122dfe6", + "name": "Front Lights", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Front Lights" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "a1235a09a8d34dea", + "type": "mqtt-broker", + "name": "", + "broker": "10.20.30.6", + "port": 1883, + "clientid": "", + "autoConnect": true, + "usetls": false, + "protocolVersion": 4, + "keepalive": 60, + "cleansession": true, + "autoUnsubscribe": true, + "birthTopic": "", + "birthQos": "0", + "birthRetain": "false", + "birthPayload": "", + "birthMsg": {}, + "closeTopic": "", + "closeQos": "0", + "closeRetain": "false", + "closePayload": "", + "closeMsg": {}, + "willTopic": "", + "willQos": "0", + "willRetain": "false", + "willPayload": "", + "willMsg": {}, + "userProps": "", + "sessionExpiry": "" + }, + { + "id": "telegram_bot_config", + "type": "telegram bot", + "botname": "Your Bot Name", + "usernames": "", + "chatids": "", + "baseapiurl": "", + "updatemode": "polling", + "pollinterval": "300", + "usesocks": false, + "sockshost": "", + "socksprotocol": "socks5", + "socksport": "6667", + "socksusername": "", + "sockspassword": "", + "bothost": "", + "botpath": "", + "localbotport": "8443", + "publicbotport": "8443", + "privatekey": "", + "certificate": "", + "useselfsignedcertificate": false, + "sslterminated": false, + "verboselogging": false + }, + { + "id": "0d71a45029c6da27", + "type": "ui_tab", + "name": "Lights", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "7df2b0bd19b6ef42", + "type": "ui_base", + "theme": { + "name": "theme-light", + "lightTheme": { + "default": "#0094CE", + "baseColor": "#0094CE", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": true, + "reset": false + }, + "darkTheme": { + "default": "#097479", + "baseColor": "#097479", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": false + }, + "customTheme": { + "name": "Untitled Theme 1", + "default": "#4B7930", + "baseColor": "#4B7930", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" + }, + "themeState": { + "base-color": { + "default": "#0094CE", + "value": "#0094CE", + "edited": false + }, + "page-titlebar-backgroundColor": { + "value": "#0094CE", + "edited": false + }, + "page-backgroundColor": { + "value": "#fafafa", + "edited": false + }, + "page-sidebar-backgroundColor": { + "value": "#ffffff", + "edited": false + }, + "group-textColor": { + "value": "#1bbfff", + "edited": false + }, + "group-borderColor": { + "value": "#ffffff", + "edited": false + }, + "group-backgroundColor": { + "value": "#ffffff", + "edited": false + }, + "widget-textColor": { + "value": "#111111", + "edited": false + }, + "widget-backgroundColor": { + "value": "#0094ce", + "edited": false + }, + "widget-borderColor": { + "value": "#ffffff", + "edited": false + }, + "base-font": { + "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" + } + }, + "angularTheme": { + "primary": "indigo", + "accents": "blue", + "warn": "red", + "background": "grey", + "palette": "light" + } + }, + "site": { + "name": "Node-RED Dashboard", + "hideToolbar": "false", + "allowSwipe": "false", + "lockMenu": "false", + "allowTempTheme": "true", + "dateFormat": "DD/MM/YYYY", + "sizes": { + "sx": 48, + "sy": 48, + "gx": 6, + "gy": 6, + "cx": 6, + "cy": 6, + "px": 0, + "py": 0 + } + } + }, + { + "id": "f0dee03e196ee8bc", + "type": "ui_group", + "name": "Default", + "tab": "0d71a45029c6da27", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_masterBedroom", + "type": "ui_tab", + "name": "masterBedroom", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_masterBedroom", + "type": "ui_group", + "name": "masterBedroom", + "tab": "tab_masterBedroom", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_masterBathroom", + "type": "ui_tab", + "name": "masterBathroom", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_masterBathroom", + "type": "ui_group", + "name": "masterBathroom", + "tab": "tab_masterBathroom", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_bedroom_1", + "type": "ui_tab", + "name": "bedroom_1", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_bedroom_1", + "type": "ui_group", + "name": "bedroom_1", + "tab": "tab_bedroom_1", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_bedroom_2", + "type": "ui_tab", + "name": "bedroom_2", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_bedroom_2", + "type": "ui_group", + "name": "bedroom_2", + "tab": "tab_bedroom_2", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_bathroom", + "type": "ui_tab", + "name": "bathroom", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_bathroom", + "type": "ui_group", + "name": "bathroom", + "tab": "tab_bathroom", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_hallway", + "type": "ui_tab", + "name": "hallway", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_hallway", + "type": "ui_group", + "name": "hallway", + "tab": "tab_hallway", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_pantry", + "type": "ui_tab", + "name": "pantry", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_pantry", + "type": "ui_group", + "name": "pantry", + "tab": "tab_pantry", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_kitchen", + "type": "ui_tab", + "name": "kitchen", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_kitchen", + "type": "ui_group", + "name": "kitchen", + "tab": "tab_kitchen", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_livingRoom", + "type": "ui_tab", + "name": "livingRoom", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_livingRoom", + "type": "ui_group", + "name": "livingRoom", + "tab": "tab_livingRoom", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_dinningRoom", + "type": "ui_tab", + "name": "dinningRoom", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_dinningRoom", + "type": "ui_group", + "name": "dinningRoom", + "tab": "tab_dinningRoom", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_entrance", + "type": "ui_tab", + "name": "entrance", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_entrance", + "type": "ui_group", + "name": "entrance", + "tab": "tab_entrance", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_guestWc", + "type": "ui_tab", + "name": "guestWc", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_guestWc", + "type": "ui_group", + "name": "guestWc", + "tab": "tab_guestWc", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_outVeranda", + "type": "ui_tab", + "name": "outVeranda", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_outVeranda", + "type": "ui_group", + "name": "outVeranda", + "tab": "tab_outVeranda", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_outFront", + "type": "ui_tab", + "name": "outFront", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_outFront", + "type": "ui_group", + "name": "outFront", + "tab": "tab_outFront", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_outSide", + "type": "ui_tab", + "name": "outSide", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_outSide", + "type": "ui_group", + "name": "outSide", + "tab": "tab_outSide", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "tab_outBack", + "type": "ui_tab", + "name": "outBack", + "icon": "dashboard", + "disabled": false, + "hidden": false + }, + { + "id": "group_outBack", + "type": "ui_group", + "name": "outBack", + "tab": "tab_outBack", + "order": 1, + "disp": true, + "width": 6, + "collapse": false, + "className": "" + }, + { + "id": "0c9a4c8d912d0a38", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Water_boiler", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Water_boiler" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "8166d41aaab8a32a", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Water_boiler", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Water_boiler" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "timer30min_config", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Timer_30_Min", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Timer 30 Min" + }, + { + "property": "icon", + "value": "mdi:timer" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "timer1hour_config", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Timer_1_Hour", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Timer 1 Hour" + }, + { + "property": "icon", + "value": "mdi:timer" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "timer2hours_config", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Timer_2_Hours", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Timer 2 Hours" + }, + { + "property": "icon", + "value": "mdi:timer" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "timerstop_config", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Stop_Timer", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Stop Timer" + }, + { + "property": "icon", + "value": "mdi:timer-off" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "timer_remaining_config", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "17b3adc633ee3394", + "name": "Timer_Remaining", + "version": 6, + "entityType": "sensor", + "haConfig": [ + { + "property": "name", + "value": "Water Boiler Timer Remaining" + }, + { + "property": "icon", + "value": "mdi:timer-sand" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + }, + { + "property": "unit_of_measurement", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "c5b6375b9e419298", + "type": "ha-device-config", + "name": "Entrance", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "c1b8d65524acbab3", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "c5b6375b9e419298", + "name": "Entrance Mirror", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Entrance Mirror" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "72e9fc1709b9891c", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "c5b6375b9e419298", + "name": "Entrance Mirror", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Entrance Mirror" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "121b1dead369ae82", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "c5b6375b9e419298", + "name": "Entrance Light In", + "version": 6, + "entityType": "button", + "haConfig": [ + { + "property": "name", + "value": "Entrance Light In" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "395ea43be3b0e20c", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "c5b6375b9e419298", + "name": "Entrance Light In", + "version": 6, + "entityType": "binary_sensor", + "haConfig": [ + { + "property": "name", + "value": "Entrance Light In" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "3e00db06a8f6a759", + "type": "ha-device-config", + "name": "Living_Room_New", + "hwVersion": "", + "manufacturer": "Node-RED", + "model": "", + "swVersion": "" + }, + { + "id": "6d1793815598b149", + "type": "ha-entity-config", + "server": "29eb84a2708d96e0", + "deviceConfig": "3e00db06a8f6a759", + "name": "Living_room_Lights", + "version": 6, + "entityType": "switch", + "haConfig": [ + { + "property": "name", + "value": "" + }, + { + "property": "icon", + "value": "" + }, + { + "property": "entity_picture", + "value": "" + }, + { + "property": "entity_category", + "value": "" + }, + { + "property": "device_class", + "value": "" + } + ], + "resend": false, + "debugEnabled": false + }, + { + "id": "ee7c2a47d6441c26", + "type": "function", + "z": "57bd1b149526fcee", + "name": "function 1", + "func": "// Get the room name from the environment variable\nconst roomName = env.get(\"room_name\");\n\n// Access Node-RED context for state storage\nlet contextState = context.get(roomName) || {};\n\n// Check if the payload contains the specified room\nif (msg.payload.hasOwnProperty(roomName)) {\n // Get the lights status for the specified room\n let lights = msg.payload[roomName];\n\n // Check if this is the first run (initial state)\n // Context is empty or missing expected light keys\n const expectedLights = ['l_1', 'l_2', 'l_3', 'l_4', 'l_5', 'l_6'];\n const isInitialState = Object.keys(contextState).length === 0 || \n expectedLights.some(light => !(light in contextState));\n\n // Create an output array for the 6 ports\n let outputs = [null, null, null, null, null, null];\n\n // Check each light status and detect changes\n for (let light in lights) {\n let lightStatus = lights[light];\n\n // On initial state, send ALL states to initialize binary sensors\n // Otherwise, only send changed states\n if (isInitialState || contextState[light] !== lightStatus) {\n // Update the context with the new status\n contextState[light] = lightStatus;\n\n // Create a new message preserving original metadata\n let newMsg = {\n payload: lightStatus,\n topic: msg.topic,\n _msgid: msg._msgid\n };\n\n // Assign the message to the correct output port\n switch (light) {\n case 'l_1':\n outputs[0] = newMsg;\n break;\n case 'l_2':\n outputs[1] = newMsg;\n break;\n case 'l_3':\n outputs[2] = newMsg;\n break;\n case 'l_4':\n outputs[3] = newMsg;\n break;\n case 'l_5':\n outputs[4] = newMsg;\n break;\n case 'l_6':\n outputs[5] = newMsg;\n break;\n default:\n // Ignore any unexpected light keys\n break;\n }\n }\n }\n\n // Store the updated state in context\n context.set(roomName, contextState);\n\n // Send states to the respective ports (all on initial, only changes otherwise)\n return outputs;\n\n} else {\n // If the specified room does not exist in the payload, clean up stored context\n context.set(roomName, null); // Remove stored state for the room\n return [null, null, null, null, null, null]; // Return array instead of null\n}\n\n", + "outputs": 6, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 400, + "y": 220, + "wires": [ + [], + [], + [], + [], + [], + [] + ] + }, + { + "id": "f9557fd9a8b2eee2", + "type": "function", + "z": "d0de479f6b80ab6c", + "name": "function 2", + "func": "// Define the masterBedroom and sw_1 variables\nvar masterBedroom = \"bedroom_1\"; // Example variable for masterBedroom\nvar sw_1 = \"sw_1\"; // Example variable for sw_1\n\n// Create a message object to set the value to true\nvar msgTrue = {\n payload: {}\n};\nmsgTrue.payload[masterBedroom] = {};\nmsgTrue.payload[masterBedroom][sw_1] = true;\n\n// Create a message object to set the value to false\nvar msgFalse = {\n payload: {}\n};\nmsgFalse.payload[masterBedroom] = {};\nmsgFalse.payload[masterBedroom][sw_1] = false;\n\n// Send the message to set the value to true\nnode.send(msgTrue);\n\n// Delay for 1 second\nsetTimeout(function() {\n // Send the message to set the value to false after 1 second\n node.send(msgFalse);\n}, 1000); // 1000 milliseconds = 1 second\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 200, + "y": 80, + "wires": [ + [] + ] + }, + { + "id": "d2a3760f36aa6f41", + "type": "switch", + "z": "4dc13919bcc9d676", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "1_single", + "vt": "str" + }, + { + "t": "eq", + "v": "2_single", + "vt": "str" + }, + { + "t": "eq", + "v": "3_single", + "vt": "str" + }, + { + "t": "eq", + "v": "4_single", + "vt": "str" + }, + { + "t": "eq", + "v": "single", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 5, + "x": 190, + "y": 80, + "wires": [ + [ + "0ce841560c8c4087" + ], + [ + "64b045db5a4541fa" + ], + [ + "4a891a7505b5fd00" + ], + [ + "258a84ec1d54a43a" + ], + [ + "5532273136423869" + ] + ] + }, + { + "id": "0ce841560c8c4087", + "type": "function", + "z": "4dc13919bcc9d676", + "name": "Switch 1", + "func": "// Retrieve the combined device variable (e.g., \"bedroom_1.sw1\")\nvar switch_1 = env.get(\"switch_1\");\n\n// Split the string into room and switch name parts using the dot as a delimiter\nvar parts = switch_1.split(\".\");\nvar room = parts[0];\nvar switchName = parts[1];\n\n// Create a message object to set the switch to true\nvar msgTrue = {\n payload: {}\n};\nmsgTrue.payload[room] = {};\nmsgTrue.payload[room][switchName] = true;\n\n// Create a message object to set the switch to false\nvar msgFalse = {\n payload: {}\n};\nmsgFalse.payload[room] = {};\nmsgFalse.payload[room][switchName] = false;\n\n// Send the message to set the switch to true\nnode.send(msgTrue);\n\n// Delay for 1 second before setting the switch to false\nsetTimeout(function () {\n node.send(msgFalse);\n}, 1000);\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 380, + "y": 60, + "wires": [ + [] + ] + }, + { + "id": "64b045db5a4541fa", + "type": "function", + "z": "4dc13919bcc9d676", + "name": "Switch 2", + "func": "// Retrieve the combined device variable (e.g., \"bedroom_1.sw1\")\nvar switch_2 = env.get(\"switch_2\");\n\n// Split the string into room and switch name parts using the dot as a delimiter\nvar parts = switch_2.split(\".\");\nvar room = parts[0];\nvar switchName = parts[1];\n\n// Create a message object to set the switch to true\nvar msgTrue = {\n payload: {}\n};\nmsgTrue.payload[room] = {};\nmsgTrue.payload[room][switchName] = true;\n\n// Create a message object to set the switch to false\nvar msgFalse = {\n payload: {}\n};\nmsgFalse.payload[room] = {};\nmsgFalse.payload[room][switchName] = false;\n\n// Send the message to set the switch to true\nnode.send(msgTrue);\n\n// Delay for 1 second before setting the switch to false\nsetTimeout(function () {\n node.send(msgFalse);\n}, 1000);\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 380, + "y": 100, + "wires": [ + [] + ] + }, + { + "id": "4a891a7505b5fd00", + "type": "function", + "z": "4dc13919bcc9d676", + "name": "Switch 3", + "func": "// Retrieve the combined device variable (e.g., \"bedroom_1.sw1\")\nvar switch_3 = env.get(\"switch_3\");\n\n// Split the string into room and switch name parts using the dot as a delimiter\nvar parts = switch_3.split(\".\");\nvar room = parts[0];\nvar switchName = parts[1];\n\n// Create a message object to set the switch to true\nvar msgTrue = {\n payload: {}\n};\nmsgTrue.payload[room] = {};\nmsgTrue.payload[room][switchName] = true;\n\n// Create a message object to set the switch to false\nvar msgFalse = {\n payload: {}\n};\nmsgFalse.payload[room] = {};\nmsgFalse.payload[room][switchName] = false;\n\n// Send the message to set the switch to true\nnode.send(msgTrue);\n\n// Delay for 1 second before setting the switch to false\nsetTimeout(function () {\n node.send(msgFalse);\n}, 1000);\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 380, + "y": 140, + "wires": [ + [] + ] + }, + { + "id": "258a84ec1d54a43a", + "type": "function", + "z": "4dc13919bcc9d676", + "name": "Switch 4", + "func": "// Retrieve the combined device variable (e.g., \"bedroom_1.sw1\")\nvar switch_4 = env.get(\"switch_4\");\n\n// Split the string into room and switch name parts using the dot as a delimiter\nvar parts = switch_4.split(\".\");\nvar room = parts[0];\nvar switchName = parts[1];\n\n// Create a message object to set the switch to true\nvar msgTrue = {\n payload: {}\n};\nmsgTrue.payload[room] = {};\nmsgTrue.payload[room][switchName] = true;\n\n// Create a message object to set the switch to false\nvar msgFalse = {\n payload: {}\n};\nmsgFalse.payload[room] = {};\nmsgFalse.payload[room][switchName] = false;\n\n// Send the message to set the switch to true\nnode.send(msgTrue);\n\n// Delay for 1 second before setting the switch to false\nsetTimeout(function () {\n node.send(msgFalse);\n}, 1000);\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 380, + "y": 180, + "wires": [ + [] + ] + }, + { + "id": "5532273136423869", + "type": "function", + "z": "4dc13919bcc9d676", + "name": "Switch 4", + "func": "// Retrieve the combined device variable (e.g., \"bedroom_1.sw1\")\nvar single = env.get(\"single\");\n\n// Split the string into room and switch name parts using the dot as a delimiter\nvar parts = single.split(\".\");\nvar room = parts[0];\nvar switchName = parts[1];\n\n// Create a message object to set the switch to true\nvar msgTrue = {\n payload: {}\n};\nmsgTrue.payload[room] = {};\nmsgTrue.payload[room][switchName] = true;\n\n// Create a message object to set the switch to false\nvar msgFalse = {\n payload: {}\n};\nmsgFalse.payload[room] = {};\nmsgFalse.payload[room][switchName] = false;\n\n// Send the message to set the switch to true\nnode.send(msgTrue);\n\n// Delay for 1 second before setting the switch to false\nsetTimeout(function () {\n node.send(msgFalse);\n}, 1000);\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 380, + "y": 220, + "wires": [ + [] + ] + }, + { + "id": "030bba61776bd099", + "type": "nvl-send", + "z": "46892ced77481340", + "name": "", + "definition": "VAR_GLOBAL\n\tmasterBedroom \t:\tstruct_switches;\n\tmasterBathroom\t:\tstruct_switches;\n\tbedroom_1\t\t:\tstruct_switches;\n\tbedroom_2\t\t:\tstruct_switches;\n\tbathroom\t\t:\tstruct_switches;\n\thallway\t\t\t:\tstruct_switches;\n\tpantry\t\t\t:\tstruct_switches;\n\tkitchen\t\t\t:\tstruct_switches;\n\tlivingRoom\t\t:\tstruct_switches;\n\tdinningRoom\t\t:\tstruct_switches;\n\tentrance\t\t:\tstruct_switches;\n\tguestWc\t\t\t:\tstruct_switches;\n\toutVeranda\t\t:\tstruct_switches;\n\toutFront\t\t:\tstruct_switches;\n\toutSide\t\t\t:\tstruct_switches;\n\toutBack\t\t\t:\tstruct_switches;\n\tcmd_livingroom\t:\tstruct_room_cmds;\nEND_VAR\n\nTYPE struct_switches :\nSTRUCT\n\tall_off:BOOL;\n\tall_on:BOOL;\t\n\tsw_1:BOOL;\n\tsw_2:BOOL;\n\tsw_3:BOOL;\n\tsw_4:BOOL;\n\tsw_5:BOOL;\n\tsw_6:BOOL;\t\nEND_STRUCT\nEND_TYPE\n\nTYPE struct_room_cmds :\nSTRUCT\n // Home Assistant Commands (set/reset)\n ha_l1_on: BOOL;\n ha_l1_off: BOOL;\n ha_l2_on: BOOL;\n ha_l2_off: BOOL;\n ha_l3_on: BOOL;\n ha_l3_off: BOOL;\n ha_l4_on: BOOL;\n ha_l4_off: BOOL;\n ha_l5_on: BOOL;\n ha_l5_off: BOOL;\n ha_l6_on: BOOL;\n ha_l6_off: BOOL;\n \n // Zigbee Switch Inputs (edge detection for toggle)\n zigbee_sw1: BOOL; \n zigbee_sw2: BOOL;\n zigbee_sw3: BOOL;\n zigbee_sw4: BOOL;\n zigbee_sw5: BOOL;\n zigbee_sw6: BOOL;\n \n // Global Commands\n ha_all_on: BOOL;\n ha_all_off: BOOL;\nEND_STRUCT\nEND_TYPE", + "listId": "3", + "globaltypes": "", + "pack": "true", + "port": "", + "x": 400, + "y": 40, + "wires": [ + [ + "040e9ff14df4a326", + "f48d2c10e183fb95" + ] + ] + }, + { + "id": "a18b0ead6d0e88cb", + "type": "nvl-receive", + "z": "46892ced77481340", + "name": "", + "definition": "\nVAR_GLOBAL\n\n\tl_masterBedroom \t:\tstruct_lights;\n\tl_masterBathroom\t:\tstruct_lights;\n\tl_bedroom_1\t\t\t:\tstruct_lights;\n\tl_bedroom_2\t\t\t:\tstruct_lights;\n\tl_bathroom\t\t\t:\tstruct_lights;\n\tl_hallway\t\t\t:\tstruct_lights;\n\tl_pantry\t\t\t:\tstruct_lights;\n\tl_kitchen\t\t\t:\tstruct_lights;\n\tl_livingRoom\t\t:\tstruct_lights;\n\tl_dinningRoom\t\t:\tstruct_lights;\n\tl_entrance\t\t\t:\tstruct_lights;\n\tl_guestWc\t\t\t:\tstruct_lights;\n\tl_outVeranda\t\t:\tstruct_lights;\n\tl_outFront\t\t\t:\tstruct_lights;\n\tl_outSide\t\t\t:\tstruct_lights;\n\tl_outBack\t\t\t:\tstruct_lights;\n\tlight_livingRoom\t\t:\tstruct_room_outs;\nEND_VAR\n\n\nTYPE struct_lights:\nSTRUCT\n l_1 \t\t: BOOL;\n\tl_2 \t\t: BOOL;\n\tl_3 \t\t: BOOL;\n\tl_4 \t\t: BOOL;\n\tl_5 \t\t: BOOL;\n\tl_6 \t\t: BOOL;\t\nEND_STRUCT\nEND_TYPE\n\nTYPE struct_room_outs :\nSTRUCT\n l_1: BOOL; // Light 1 control output\n l_2: BOOL; // Light 2 control output\n l_3: BOOL; // Light 3 control output\n l_4: BOOL; // Light 4 control output\n l_5: BOOL; // Light 5 control output\n l_6: BOOL; // Light 6 control output\n \n // Status feedback (read from actual relay outputs)\n l_1_status: BOOL; // Actual relay 1 state\n l_2_status: BOOL; // Actual relay 2 state\n l_3_status: BOOL; // Actual relay 3 state\n l_4_status: BOOL; // Actual relay 4 state\n l_5_status: BOOL; // Actual relay 5 state\n l_6_status: BOOL; // Actual relay 6 state\nEND_STRUCT\nEND_TYPE", + "listId": "1", + "globaltypes": "", + "pack": "true", + "x": 990, + "y": 40, + "wires": [ + [ + "e059a9fd1da3c3fd", + "9672723b593ef966" + ] + ] + }, + { + "id": "040e9ff14df4a326", + "type": "udp out", + "z": "46892ced77481340", + "name": "", + "addr": "plc.paraskeva.net", + "iface": "", + "port": "1202", + "ipv": "udp4", + "outport": "", + "base64": false, + "multicast": "false", + "x": 640, + "y": 40, + "wires": [] + }, + { + "id": "67f7ec38bbf87e95", + "type": "udp in", + "z": "46892ced77481340", + "name": "", + "iface": "", + "port": "1202", + "ipv": "udp4", + "multicast": "false", + "group": "", + "datatype": "buffer", + "x": 820, + "y": 40, + "wires": [ + [ + "a18b0ead6d0e88cb" + ] + ] + }, + { + "id": "a91dcb332bae2807", + "type": "ha-button", + "z": "46892ced77481340", + "g": "ff517137dc8eb4e5", + "name": "Office", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "7fe55db85a29f18c", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "bedroom_1", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 430, + "y": 420, + "wires": [ + [ + "11f594c5b9f0fce8" + ] + ] + }, + { + "id": "67149a39f56c5694", + "type": "ha-button", + "z": "46892ced77481340", + "g": "007a0818e00b6e8a", + "name": "Main Light", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "5e7f6287035ecc94", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "masterBedroom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 440, + "y": 200, + "wires": [ + [ + "a6493ad8864f2b68" + ] + ] + }, + { + "id": "40fd804bb4acf519", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "925b79f58f75e449", + "name": "Office", + "env": [ + { + "name": "room_name", + "value": "l_bedroom_1", + "type": "str" + } + ], + "x": 230, + "y": 1080, + "wires": [ + [ + "b9ab51a28b433f94" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "123bb560d193b3aa", + "type": "function", + "z": "46892ced77481340", + "name": "function 3", + "func": "// Retrieve room and switch from the input message\nvar room = msg.topic; // e.g., \"masterBedroom\"\nvar switchName = msg.payload; // e.g., \"sw_1\"\n\n// Create a message object to set the value to true\nvar msgTrue = {\n payload: {}\n};\nmsgTrue.payload[room] = {};\nmsgTrue.payload[room][switchName] = true;\n\n// Create a message object to set the value to false\nvar msgFalse = {\n payload: {}\n};\nmsgFalse.payload[room] = {};\nmsgFalse.payload[room][switchName] = false;\n\n// Send the message to set the value to true\nnode.send(msgTrue);\n\n// Delay for 1 second\nsetTimeout(function() {\n // Send the message to set the value to false after 1 second\n node.send(msgFalse);\n}, 1000); // 1000 milliseconds = 1 second\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 200, + "y": 40, + "wires": [ + [ + "030bba61776bd099", + "438b13c5cdbb5f1c" + ] + ] + }, + { + "id": "c6162cb59e21491f", + "type": "link in", + "z": "46892ced77481340", + "name": "from zigbee", + "links": [ + "629cbd7ce15bb9e3", + "6fb9cc41c7b14fac", + "74c2640db1c8bd3e", + "bcf5055c45c97cc3" + ], + "x": 245, + "y": 100, + "wires": [ + [ + "030bba61776bd099" + ] + ] + }, + { + "id": "a2eceb1ef60667cb", + "type": "ha-button", + "z": "46892ced77481340", + "g": "2b9f0ac7a58d6e99", + "name": "Shower Light", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "88f51f9c233f4cc3", + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + }, + { + "property": "topic", + "propertyType": "msg", + "value": "bathroom", + "valueType": "str" + } + ], + "x": 1130, + "y": 300, + "wires": [ + [ + "102d7d54a45e3f18" + ] + ] + }, + { + "id": "e9aa8b33edf056f6", + "type": "ha-button", + "z": "46892ced77481340", + "g": "42040eb51b6909d2", + "name": "Kitchen Island", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "29592d5555367344", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "kitchen", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_2", + "valueType": "str" + } + ], + "x": 170, + "y": 220, + "wires": [ + [ + "a1f44cc63b77a2c3" + ] + ] + }, + { + "id": "eda94d52e6cf650a", + "type": "ha-button", + "z": "46892ced77481340", + "g": "42040eb51b6909d2", + "name": "Main", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "cc2640502094371a", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "kitchen", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 150, + "y": 280, + "wires": [ + [ + "a1f44cc63b77a2c3" + ] + ] + }, + { + "id": "188a3b16710467e7", + "type": "ha-button", + "z": "46892ced77481340", + "g": "42040eb51b6909d2", + "name": "Fridge", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "8e22cfb83ec013d1", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "kitchen", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_3", + "valueType": "str" + } + ], + "x": 150, + "y": 340, + "wires": [ + [ + "a1f44cc63b77a2c3" + ] + ] + }, + { + "id": "f714485f125865af", + "type": "ha-button", + "z": "46892ced77481340", + "g": "888d856dae38ce27", + "name": "outVeranda", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "2a94bae266d9423d", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "outVeranda", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_2", + "valueType": "str" + } + ], + "x": 1530, + "y": 200, + "wires": [ + [ + "9594045be414b84c" + ] + ] + }, + { + "id": "2dc64fabc1602ec8", + "type": "ha-button", + "z": "46892ced77481340", + "g": "42040eb51b6909d2", + "name": "Pantry", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "085d42240f91eec2", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "pantry", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 210, + "y": 540, + "wires": [ + [ + "a1f44cc63b77a2c3" + ] + ] + }, + { + "id": "58525896ff637a27", + "type": "ha-button", + "z": "46892ced77481340", + "g": "2ba7fb1e189ec017", + "name": "Main", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "eca0cca02aabd143", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "livingRoom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 770, + "y": 420, + "wires": [ + [ + "beeb512081d25291" + ] + ] + }, + { + "id": "d5dacec70d47b2e5", + "type": "ha-button", + "z": "46892ced77481340", + "g": "007a0818e00b6e8a", + "name": "Door", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "451e5988101b1aac", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "masterBedroom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_2", + "valueType": "str" + } + ], + "x": 430, + "y": 260, + "wires": [ + [ + "a6493ad8864f2b68" + ] + ] + }, + { + "id": "28d2293b97a7b72b", + "type": "ha-button", + "z": "46892ced77481340", + "g": "007a0818e00b6e8a", + "name": "Dresser", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "c21001f594a359c9", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "str" + } + ], + "x": 430, + "y": 320, + "wires": [ + [ + "a6493ad8864f2b68" + ] + ] + }, + { + "id": "2565695beaa14d76", + "type": "ha-button", + "z": "46892ced77481340", + "g": "888d856dae38ce27", + "name": "BBQ", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "d721225e966ecf1b", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "outSide", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 1510, + "y": 260, + "wires": [ + [ + "9594045be414b84c" + ] + ] + }, + { + "id": "66cafcf3b261e8b5", + "type": "ha-button", + "z": "46892ced77481340", + "g": "3cc3b21e7b1f7b13", + "name": "Main", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "8c493a05a6971f90", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "dinningRoom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 770, + "y": 200, + "wires": [ + [ + "04e183fcfe9e9f6d" + ] + ] + }, + { + "id": "0bbcb269da9b75c7", + "type": "link in", + "z": "46892ced77481340", + "name": "From HA Entities", + "links": [ + "11f594c5b9f0fce8", + "102d7d54a45e3f18", + "a6493ad8864f2b68", + "9594045be414b84c", + "beeb512081d25291", + "04e183fcfe9e9f6d", + "0d7f6d11034f98ee", + "839bb9d987729d16", + "ed3b84f4d63a8bd5", + "45e37c698b0b2e5a", + "01da9e8ebba7f592", + "740b766e9bdc0d2a", + "a1f44cc63b77a2c3", + "f11fa2bedfc41e41", + "33eb6e0725214c0b", + "9c99ecea6e4aefd8", + "e44fa1b081b4aba7", + "1d0a46b9d5255560" + ], + "x": 75, + "y": 40, + "wires": [ + [ + "123bb560d193b3aa", + "e4d5c7b8295b4251" + ] + ] + }, + { + "id": "11f594c5b9f0fce8", + "type": "link out", + "z": "46892ced77481340", + "g": "ff517137dc8eb4e5", + "name": "to PLC Function", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 625, + "y": 440, + "wires": [] + }, + { + "id": "102d7d54a45e3f18", + "type": "link out", + "z": "46892ced77481340", + "g": "2b9f0ac7a58d6e99", + "name": "link out 2", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1335, + "y": 300, + "wires": [] + }, + { + "id": "a6493ad8864f2b68", + "type": "link out", + "z": "46892ced77481340", + "g": "007a0818e00b6e8a", + "name": "link out 3", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 625, + "y": 200, + "wires": [] + }, + { + "id": "a1f44cc63b77a2c3", + "type": "link out", + "z": "46892ced77481340", + "g": "42040eb51b6909d2", + "name": "link out 4", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 295, + "y": 200, + "wires": [] + }, + { + "id": "9594045be414b84c", + "type": "link out", + "z": "46892ced77481340", + "g": "888d856dae38ce27", + "name": "link out 5", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1745, + "y": 200, + "wires": [] + }, + { + "id": "beeb512081d25291", + "type": "link out", + "z": "46892ced77481340", + "g": "2ba7fb1e189ec017", + "name": "link out 6", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 965, + "y": 420, + "wires": [] + }, + { + "id": "04e183fcfe9e9f6d", + "type": "link out", + "z": "46892ced77481340", + "g": "3cc3b21e7b1f7b13", + "name": "link out 7", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 965, + "y": 200, + "wires": [] + }, + { + "id": "f1cf19264e9957f3", + "type": "ha-button", + "z": "46892ced77481340", + "g": "f16b851b413f0d32", + "name": "Guest WC Light", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "85fe098bb4010c10", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "guestWc", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 1140, + "y": 540, + "wires": [ + [ + "0d7f6d11034f98ee" + ] + ] + }, + { + "id": "0d7f6d11034f98ee", + "type": "link out", + "z": "46892ced77481340", + "g": "f16b851b413f0d32", + "name": "link out 8", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1335, + "y": 540, + "wires": [] + }, + { + "id": "518a9fd6f52bfc23", + "type": "ha-button", + "z": "46892ced77481340", + "g": "1a348789bcd14214", + "name": "Flood Light 1", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "a3fb2a27c9896974", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "outBack", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "str" + } + ], + "x": 1530, + "y": 460, + "wires": [ + [ + "839bb9d987729d16" + ] + ] + }, + { + "id": "839bb9d987729d16", + "type": "link out", + "z": "46892ced77481340", + "g": "1a348789bcd14214", + "name": "link out 9", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1745, + "y": 460, + "wires": [] + }, + { + "id": "0f93e95f3e92150a", + "type": "ha-button", + "z": "46892ced77481340", + "g": "8044f4e5402ca464", + "name": "Main", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "ac7e5f5dfeb9ce54", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "masterBathroom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 1110, + "y": 200, + "wires": [ + [ + "ed3b84f4d63a8bd5" + ] + ] + }, + { + "id": "ed3b84f4d63a8bd5", + "type": "link out", + "z": "46892ced77481340", + "g": "8044f4e5402ca464", + "name": "link out 10", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1335, + "y": 200, + "wires": [] + }, + { + "id": "194c35655de1a128", + "type": "ha-button", + "z": "46892ced77481340", + "g": "f83a4ef2662f4f11", + "name": "Entrance Light out", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "aa9cf2e1dc39616f", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "outFront", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "str" + } + ], + "x": 1550, + "y": 360, + "wires": [ + [ + "45e37c698b0b2e5a" + ] + ] + }, + { + "id": "45e37c698b0b2e5a", + "type": "link out", + "z": "46892ced77481340", + "g": "f83a4ef2662f4f11", + "name": "link out 11", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1745, + "y": 360, + "wires": [] + }, + { + "id": "bbda8fd42d8aa120", + "type": "ha-button", + "z": "46892ced77481340", + "g": "48b019b444db2a5a", + "name": "Driveway Flood Light", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "a97f2813611faac8", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "str" + } + ], + "x": 1560, + "y": 560, + "wires": [ + [ + "01da9e8ebba7f592" + ] + ] + }, + { + "id": "01da9e8ebba7f592", + "type": "link out", + "z": "46892ced77481340", + "g": "48b019b444db2a5a", + "name": "link out 12", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1745, + "y": 560, + "wires": [] + }, + { + "id": "039e1be0cfe8e630", + "type": "ha-button", + "z": "46892ced77481340", + "d": true, + "g": "664de832a54bc4e0", + "name": "", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "str" + } + ], + "x": 2010, + "y": 220, + "wires": [ + [ + "740b766e9bdc0d2a" + ] + ] + }, + { + "id": "740b766e9bdc0d2a", + "type": "link out", + "z": "46892ced77481340", + "g": "664de832a54bc4e0", + "name": "link out 13", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 2185, + "y": 220, + "wires": [] + }, + { + "id": "b9ab51a28b433f94", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "925b79f58f75e449", + "name": "Office Light", + "entityConfig": "f744459771e70676", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 490, + "y": 1040, + "wires": [ + [] + ] + }, + { + "id": "e059a9fd1da3c3fd", + "type": "link out", + "z": "46892ced77481340", + "name": "from PLC states", + "mode": "link", + "links": [ + "0d1a60ec03adac5c", + "01b4eb4b91f9c9e2", + "7ec85095958da9f5", + "b51586d393da96cd", + "b2641f71595c1d2d", + "39d435c6a32d4f09", + "fc5020edbd894baa", + "37cdfd56758051ef", + "d3f0c80e0188aabf", + "3b5e1f4f9ccd9c74", + "2b29c71c180a50df", + "860779317fad91b5", + "8dd17125d6bb49f7", + "7c4268195123e319", + "032f98ab9d5085b8" + ], + "x": 1205, + "y": 100, + "wires": [] + }, + { + "id": "0d1a60ec03adac5c", + "type": "link in", + "z": "46892ced77481340", + "g": "925b79f58f75e449", + "name": "link in 1", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 105, + "y": 1080, + "wires": [ + [ + "40fd804bb4acf519" + ] + ] + }, + { + "id": "1feb11ab8be3ef2e", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "b88648a578b60cb8", + "name": "Living Room", + "env": [ + { + "name": "room_name", + "value": "l_livingRoom", + "type": "str" + } + ], + "x": 250, + "y": 920, + "wires": [ + [ + "1ee2b22951639d6b" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "01b4eb4b91f9c9e2", + "type": "link in", + "z": "46892ced77481340", + "g": "b88648a578b60cb8", + "name": "link in 2", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 105, + "y": 920, + "wires": [ + [ + "1feb11ab8be3ef2e" + ] + ] + }, + { + "id": "3c894953f414add5", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "4a9361ba088ba046", + "name": "Dining Room", + "env": [ + { + "name": "room_name", + "value": "l_dinningRoom", + "type": "str" + } + ], + "x": 250, + "y": 760, + "wires": [ + [ + "566e5b2ec261934f" + ], + [], + [ + "89762968e1556a42" + ], + [], + [], + [] + ] + }, + { + "id": "7ec85095958da9f5", + "type": "link in", + "z": "46892ced77481340", + "g": "4a9361ba088ba046", + "name": "link in 3", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 105, + "y": 760, + "wires": [ + [ + "3c894953f414add5" + ] + ] + }, + { + "id": "af7220d8f5bb9bf2", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "0271d58953459100", + "name": "Kitchen", + "env": [ + { + "name": "room_name", + "value": "l_kitchen", + "type": "str" + } + ], + "x": 800, + "y": 1180, + "wires": [ + [ + "e8db33fc81400531" + ], + [ + "e0739e7f8c82e487" + ], + [ + "1530556181903c3a" + ], + [], + [], + [] + ] + }, + { + "id": "b51586d393da96cd", + "type": "link in", + "z": "46892ced77481340", + "g": "0271d58953459100", + "name": "link in 4", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 665, + "y": 1180, + "wires": [ + [ + "af7220d8f5bb9bf2" + ] + ] + }, + { + "id": "53178ccb3f691dad", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "4b360c7d54e39e0a", + "name": "Bedroom", + "env": [ + { + "name": "room_name", + "value": "l_bedroom_2", + "type": "str" + } + ], + "x": 800, + "y": 1000, + "wires": [ + [ + "ba2859e1aa882eee" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "b2641f71595c1d2d", + "type": "link in", + "z": "46892ced77481340", + "g": "4b360c7d54e39e0a", + "name": "link in 5", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 665, + "y": 1000, + "wires": [ + [ + "53178ccb3f691dad" + ] + ] + }, + { + "id": "b664207011dd548a", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "62cc8431c77a4797", + "name": "Shower", + "env": [ + { + "name": "room_name", + "value": "l_bathroom", + "type": "str" + } + ], + "x": 800, + "y": 760, + "wires": [ + [ + "5f7ffd82e5200d9a" + ], + [ + "f0bc3094d4af717e" + ], + [], + [], + [], + [ + "0a4414fc667405b4", + "22c73018f4564256" + ] + ] + }, + { + "id": "39d435c6a32d4f09", + "type": "link in", + "z": "46892ced77481340", + "g": "62cc8431c77a4797", + "name": "link in 6", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 665, + "y": 760, + "wires": [ + [ + "b664207011dd548a" + ] + ] + }, + { + "id": "1ee2b22951639d6b", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "b88648a578b60cb8", + "name": "Living Room Main", + "entityConfig": "b43c6846b0baeeec", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 470, + "y": 880, + "wires": [ + [] + ] + }, + { + "id": "566e5b2ec261934f", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "4a9361ba088ba046", + "name": "Dining Room Main", + "entityConfig": "f190a69cf72af140", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 470, + "y": 720, + "wires": [ + [] + ] + }, + { + "id": "5f7ffd82e5200d9a", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "62cc8431c77a4797", + "name": "Main", + "entityConfig": "16560fc93e9523d4", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1030, + "y": 720, + "wires": [ + [] + ] + }, + { + "id": "ba2859e1aa882eee", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "4b360c7d54e39e0a", + "name": "Main", + "entityConfig": "293fc06a888710c5", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1030, + "y": 960, + "wires": [ + [] + ] + }, + { + "id": "e8db33fc81400531", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "0271d58953459100", + "name": "Kitchen Main", + "entityConfig": "be947f3b20336437", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 990, + "y": 1140, + "wires": [ + [] + ] + }, + { + "id": "e6e67dc471f2ecb3", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "2d6eb3ac6de382be", + "name": "Master Bathroom", + "env": [ + { + "name": "room_name", + "value": "l_masterBathroom", + "type": "str" + } + ], + "x": 1370, + "y": 920, + "wires": [ + [ + "29af11e532f234d4" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "fc5020edbd894baa", + "type": "link in", + "z": "46892ced77481340", + "g": "2d6eb3ac6de382be", + "name": "link in 7", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 1205, + "y": 920, + "wires": [ + [ + "e6e67dc471f2ecb3" + ] + ] + }, + { + "id": "29af11e532f234d4", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "2d6eb3ac6de382be", + "name": "Main", + "entityConfig": "edf39a33a28182ca", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1570, + "y": 880, + "wires": [ + [] + ] + }, + { + "id": "b7e6812909ef1051", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "af99b05ba44c6741", + "name": "Master Bedroom", + "env": [ + { + "name": "room_name", + "value": "l_masterBedroom", + "type": "str" + } + ], + "x": 1360, + "y": 760, + "wires": [ + [ + "280e6cc7a9de62b5" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "37cdfd56758051ef", + "type": "link in", + "z": "46892ced77481340", + "g": "af99b05ba44c6741", + "name": "link in 8", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 1205, + "y": 760, + "wires": [ + [ + "b7e6812909ef1051" + ] + ] + }, + { + "id": "280e6cc7a9de62b5", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "af99b05ba44c6741", + "name": "Main", + "entityConfig": "2b453fcb286e51f5", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1570, + "y": 720, + "wires": [ + [] + ] + }, + { + "id": "e0739e7f8c82e487", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "0271d58953459100", + "name": "Kitchen Island", + "entityConfig": "9b99cd4af3bdb9a9", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1000, + "y": 1200, + "wires": [ + [] + ] + }, + { + "id": "1530556181903c3a", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "0271d58953459100", + "name": "Fridge", + "entityConfig": "d75c31dd7a1edcbf", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 970, + "y": 1260, + "wires": [ + [] + ] + }, + { + "id": "e37aa132859c1ea8", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "d": true, + "g": "f27a48975d61e376", + "name": "Bedroom", + "env": [ + { + "name": "room_name", + "value": "l_bedroom_2", + "type": "str" + } + ], + "x": 2420, + "y": 260, + "wires": [ + [ + "1a534f2c5be60889" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "d3f0c80e0188aabf", + "type": "link in", + "z": "46892ced77481340", + "g": "f27a48975d61e376", + "name": "link in 9", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 2285, + "y": 260, + "wires": [ + [ + "e37aa132859c1ea8" + ] + ] + }, + { + "id": "1a534f2c5be60889", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "d": true, + "g": "f27a48975d61e376", + "name": "", + "entityConfig": "", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 2640, + "y": 220, + "wires": [ + [] + ] + }, + { + "id": "f0bc3094d4af717e", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "62cc8431c77a4797", + "name": "Fan", + "entityConfig": "9b2dc1266b7513da", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1030, + "y": 780, + "wires": [ + [] + ] + }, + { + "id": "a024d8a903074aa4", + "type": "ha-button", + "z": "46892ced77481340", + "g": "2b9f0ac7a58d6e99", + "name": "Fan", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "ac2dba905819562f", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "bathroom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_2", + "valueType": "str" + } + ], + "x": 1110, + "y": 360, + "wires": [ + [ + "102d7d54a45e3f18" + ] + ] + }, + { + "id": "07bae699fea75512", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "3c7f8838aa2349b9", + "name": "Pantry", + "entityConfig": "c494e187e88c3208", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1570, + "y": 1040, + "wires": [ + [] + ] + }, + { + "id": "806720bc7ab2d4e8", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "3c7f8838aa2349b9", + "name": "Pantry", + "env": [ + { + "name": "room_name", + "value": "l_pantry", + "type": "str" + } + ], + "x": 1330, + "y": 1080, + "wires": [ + [ + "07bae699fea75512" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "3b5e1f4f9ccd9c74", + "type": "link in", + "z": "46892ced77481340", + "g": "3c7f8838aa2349b9", + "name": "link in 10", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 1205, + "y": 1080, + "wires": [ + [ + "806720bc7ab2d4e8" + ] + ] + }, + { + "id": "9672723b593ef966", + "type": "debug", + "z": "46892ced77481340", + "name": "debug 1", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1340, + "y": 40, + "wires": [] + }, + { + "id": "3ec8968914e324f5", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "0b0b27deacee2bfc", + "name": "Bedroom", + "env": [ + { + "name": "room_name", + "value": "l_outSide", + "type": "str" + } + ], + "x": 240, + "y": 1240, + "wires": [ + [ + "197fc35230069190" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "2b29c71c180a50df", + "type": "link in", + "z": "46892ced77481340", + "g": "0b0b27deacee2bfc", + "name": "link in 11", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 105, + "y": 1240, + "wires": [ + [ + "3ec8968914e324f5" + ] + ] + }, + { + "id": "197fc35230069190", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "0b0b27deacee2bfc", + "name": "Veranda BBQ Light", + "entityConfig": "1a22f89094ea7b38", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 470, + "y": 1200, + "wires": [ + [] + ] + }, + { + "id": "a8ac75d5944a28b9", + "type": "ha-button", + "z": "46892ced77481340", + "g": "639e1c2545c2de96", + "name": "Baby Bedroom", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "8b4d5408fdcd8c0d", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "bedroom_2", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 460, + "y": 540, + "wires": [ + [ + "f11fa2bedfc41e41" + ] + ] + }, + { + "id": "f11fa2bedfc41e41", + "type": "link out", + "z": "46892ced77481340", + "g": "639e1c2545c2de96", + "name": "link out 14", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 605, + "y": 540, + "wires": [] + }, + { + "id": "f785dab1a0f37204", + "type": "comment", + "z": "46892ced77481340", + "name": "", + "info": "VAR_GLOBAL\n\tmasterBedroom \t:\tstruct_switches;\n\tmasterBathroom\t:\tstruct_switches;\n\tbedroom_1\t\t:\tstruct_switches;\n\tbedroom_2\t\t:\tstruct_switches;\n\tbathroom\t\t:\tstruct_switches;\n\thallway\t\t\t:\tstruct_switches;\n\tpantry\t\t\t:\tstruct_switches;\n\tkitchen\t\t\t:\tstruct_switches;\n\tlivingRoom\t\t:\tstruct_switches;\n\tdinningRoom\t\t:\tstruct_switches;\n\tentrance\t\t:\tstruct_switches;\n\tguestWc\t\t\t:\tstruct_switches;\n\toutVeranda\t\t:\tstruct_switches;\n\toutFront\t\t:\tstruct_switches;\n\toutSide\t\t\t:\tstruct_switches;\n\toutBack\t\t\t:\tstruct_switches;\nEND_VAR\n\nTYPE struct_switches :\nSTRUCT\n\tall_off:BOOL;\n\tall_on:BOOL;\t\n\tsw_1:BOOL;\n\tsw_2:BOOL;\n\tsw_3:BOOL;\n\tsw_4:BOOL;\n\tsw_5:BOOL;\n\tsw_6:BOOL;\t\nEND_STRUCT\nEND_TYPE", + "x": 1560, + "y": 40, + "wires": [] + }, + { + "id": "b4e49387d80fa951", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "bd16cebc90c528e9", + "name": "Out", + "env": [ + { + "name": "room_name", + "value": "l_outVeranda", + "type": "str" + } + ], + "x": 2110, + "y": 480, + "wires": [ + [ + "2ae3fedce613ecfe", + "d1c2e1ee1fba88c6" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "860779317fad91b5", + "type": "link in", + "z": "46892ced77481340", + "g": "bd16cebc90c528e9", + "name": "link in 12", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 1985, + "y": 480, + "wires": [ + [ + "b4e49387d80fa951", + "1a8d95afd537e151" + ] + ] + }, + { + "id": "2ae3fedce613ecfe", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "bd16cebc90c528e9", + "name": "Front Lights", + "entityConfig": "fe79cfd2fda112db", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 2330, + "y": 440, + "wires": [ + [] + ] + }, + { + "id": "66255b06a0d4a4c9", + "type": "ha-button", + "z": "46892ced77481340", + "g": "507ca4f1b82ad9c2", + "name": "Front Lights", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "311d801ad1f1bc0f", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "outVeranda", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 2050, + "y": 600, + "wires": [ + [ + "9c99ecea6e4aefd8" + ] + ] + }, + { + "id": "9c99ecea6e4aefd8", + "type": "link out", + "z": "46892ced77481340", + "g": "507ca4f1b82ad9c2", + "name": "link out 20", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 2205, + "y": 600, + "wires": [] + }, + { + "id": "f48d2c10e183fb95", + "type": "debug", + "z": "46892ced77481340", + "name": "debug 15", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 600, + "y": 80, + "wires": [] + }, + { + "id": "438b13c5cdbb5f1c", + "type": "debug", + "z": "46892ced77481340", + "name": "debug 16", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 380, + "y": 100, + "wires": [] + }, + { + "id": "d680ff6c7ac109cf", + "type": "ha-button", + "z": "46892ced77481340", + "g": "2b9f0ac7a58d6e99", + "name": "Water Boiler", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "0c9a4c8d912d0a38", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "bathroom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_6", + "valueType": "str" + } + ], + "x": 1130, + "y": 420, + "wires": [ + [ + "102d7d54a45e3f18", + "9dc26f31e8b58f14" + ] + ] + }, + { + "id": "0a4414fc667405b4", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "62cc8431c77a4797", + "name": "Water_boiler", + "entityConfig": "8166d41aaab8a32a", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 1010, + "y": 840, + "wires": [ + [] + ] + }, + { + "id": "9dc26f31e8b58f14", + "type": "debug", + "z": "46892ced77481340", + "g": "2b9f0ac7a58d6e99", + "name": "debug 29", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1330, + "y": 440, + "wires": [] + }, + { + "id": "1a8d95afd537e151", + "type": "debug", + "z": "46892ced77481340", + "g": "bd16cebc90c528e9", + "name": "debug 31", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 2090, + "y": 360, + "wires": [] + }, + { + "id": "d1c2e1ee1fba88c6", + "type": "debug", + "z": "46892ced77481340", + "g": "bd16cebc90c528e9", + "name": "debug 32", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 2250, + "y": 400, + "wires": [] + }, + { + "id": "e4d5c7b8295b4251", + "type": "debug", + "z": "46892ced77481340", + "name": "debug 27", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 120, + "y": 100, + "wires": [] + }, + { + "id": "22c73018f4564256", + "type": "debug", + "z": "46892ced77481340", + "g": "62cc8431c77a4797", + "name": "debug 34", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 980, + "y": 900, + "wires": [] + }, + { + "id": "1f3ba1a73e155da9", + "type": "ha-button", + "z": "46892ced77481340", + "g": "3cc3b21e7b1f7b13", + "name": "Entrance Mirror", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "c1b8d65524acbab3", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "dinningRoom", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_3", + "valueType": "str" + } + ], + "x": 800, + "y": 260, + "wires": [ + [ + "04e183fcfe9e9f6d" + ] + ] + }, + { + "id": "89762968e1556a42", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "4a9361ba088ba046", + "name": "Entrance Miror", + "entityConfig": "72e9fc1709b9891c", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 460, + "y": 780, + "wires": [ + [] + ] + }, + { + "id": "6190a90971a43aba", + "type": "ha-button", + "z": "46892ced77481340", + "g": "3cc3b21e7b1f7b13", + "name": "Entrance Light in", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "121b1dead369ae82", + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "entrance", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "x": 800, + "y": 320, + "wires": [ + [ + "04e183fcfe9e9f6d" + ] + ] + }, + { + "id": "5351a670ea3bcf15", + "type": "subflow:57bd1b149526fcee", + "z": "46892ced77481340", + "g": "9da6393897881dca", + "name": "Entrance", + "env": [ + { + "name": "room_name", + "value": "l_entrance", + "type": "str" + } + ], + "x": 1940, + "y": 920, + "wires": [ + [ + "9b4f318fa5f438aa" + ], + [], + [], + [], + [], + [] + ] + }, + { + "id": "8dd17125d6bb49f7", + "type": "link in", + "z": "46892ced77481340", + "g": "9da6393897881dca", + "name": "link in 13", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 1805, + "y": 920, + "wires": [ + [ + "5351a670ea3bcf15" + ] + ] + }, + { + "id": "9b4f318fa5f438aa", + "type": "ha-binary-sensor", + "z": "46892ced77481340", + "g": "9da6393897881dca", + "name": "Entrance Light In", + "entityConfig": "395ea43be3b0e20c", + "version": 0, + "state": "payload", + "stateType": "msg", + "attributes": [], + "inputOverride": "allow", + "outputProperties": [], + "x": 2170, + "y": 880, + "wires": [ + [] + ] + }, + { + "id": "61d0351b6a2d43cc", + "type": "link in", + "z": "46892ced77481340", + "name": "to UDP", + "links": [], + "x": 285, + "y": 20, + "wires": [ + [ + "040e9ff14df4a326" + ] + ] + }, + { + "id": "4045239f675b77d2", + "type": "function", + "z": "7de41d810b04d992", + "name": "HA to NVL", + "func": "// Writes to state.rooms.. Room from entity_id via global roomConfig.entityToRoom. See /root/.node-red/room-config.js.\nconst entityId = (msg.topic || '').toString();\nconst config = global.get('roomConfig');\nconst entityToRoom = (config && config.entityToRoom) ? config.entityToRoom : { living_room_new: 'cmd_livingroom' };\n// Parse entity_id e.g. input_boolean.living_room_1 → base \"living_room_1\", then room + light\nconst base = (entityId.split('.')[1] || '').replace(/\\s/g, '');\nconst numMatch = base.match(/_(\\d+)$/);\nconst lightNum = (numMatch && parseInt(numMatch[1], 10) >= 1) ? Math.min(6, parseInt(numMatch[1], 10)) : 1;\nconst baseWithoutNum = base.replace(/_?\\d+$/, '') || base;\nconst ROOM_NAME = entityToRoom[baseWithoutNum] || entityToRoom[base] || 'cmd_livingroom';\n\n// Normalize state: HA / trigger-state can send payload as string, object with .state, or data.new_state.state\nlet rawState = msg.payload;\nif (rawState !== null && typeof rawState === 'object' && rawState.state !== undefined) rawState = rawState.state;\nif (msg.data && msg.data.new_state && msg.data.new_state.state !== undefined) rawState = msg.data.new_state.state;\nconst isOn = (rawState === 'on' || rawState === true);\n\nnode.warn('[HA to NVL] topic=' + entityId + ' rawState=' + JSON.stringify(rawState) + ' → lightNum=' + lightNum + ' isOn=' + isOn);\n\nif (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });\nconst state = flow.get('nvlInState');\nif (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};\nconst r = state.rooms[ROOM_NAME];\nr['ha_l' + lightNum + '_on'] = false;\nr['ha_l' + lightNum + '_off'] = false;\nif (isOn) r['ha_l' + lightNum + '_on'] = true;\nelse r['ha_l' + lightNum + '_off'] = true;\nflow.set('nvlInState', state);\n\nnode.warn('[HA to NVL] set ' + ROOM_NAME + ' ha_l' + lightNum + '_' + (isOn ? 'on' : 'off') + '=true, buildAndSend');\n\n// PLC uses R_TRIG (rising edge). If \"off\" still doesn’t work, add a flow: after this node, 80ms delay then clear ha_l*_off and buildAndSend again (pulse).\nmsg.payload = { buildAndSend: true };\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1170, + "y": 320, + "wires": [ + [ + "75212a4bc3d085db" + ] + ] + }, + { + "id": "718ba68ae647874f", + "type": "function", + "z": "7de41d810b04d992", + "name": "Zigbee to NVL", + "func": "// Each button maps to (room, light) via global roomConfig.switchBindings. See /root/.node-red/room-config.js.\n// Supports single-device payload (action, friendly_name at top level) and multi-device payload (keyed by IEEE address).\nconst actionToSwitch = { single: 1, double: 2, hold: 3, release: 4, triple: 5, quad: 6 };\nconst config = global.get('roomConfig');\nconst switchBindings = (config && config.switchBindings) ? config.switchBindings : {};\nconst deviceToRoom = (config && config.deviceToRoom) ? config.deviceToRoom : { 'Office Switch': 'cmd_livingroom' };\n// Optional: map IEEE address to friendly name when multi-device node doesn't provide it. e.g. deviceIdToName: { '0xa4c138a5b9771b05': 'Office Switch' }\nconst deviceIdToName = (config && config.deviceIdToName) ? config.deviceIdToName : {};\n\nconst payload = msg.payload != null ? msg.payload : {};\n// Payload can be: single-device { action, friendly_name }, multi-device { \"0x...\": { ... } }, or plain action string \"1_single\"\nlet friendlyName = (typeof payload === 'object' && payload.friendly_name != null) ? payload.friendly_name.toString() : '';\nlet actionRaw = (typeof payload === 'string' ? payload : (payload.action || payload.click || msg.action || (payload && payload.action)) || '').toString().toLowerCase();\n\n// Multi-device payload: prefer the device that has an action AND a configured binding (so we don't pick another device's stale action)\nif (!actionRaw && typeof payload === 'object' && !Array.isArray(payload)) {\n const keys = Object.keys(payload);\n let fallback = null;\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const v = payload[key];\n if (key && key.toLowerCase().indexOf('0x') === 0 && v && typeof v === 'object') {\n const inner = v.payload || v.message || v.state || v;\n const a = (inner.action || inner.click || v.action || v.click || '').toString().toLowerCase();\n if (!a) continue;\n const name = (v.friendly_name || v.name || (v.item && (v.item.friendly_name || v.item.name)) || deviceIdToName[key] || key).toString();\n if (switchBindings[name] || deviceToRoom[name]) {\n actionRaw = a;\n friendlyName = name;\n break;\n }\n if (!fallback) fallback = { actionRaw: a, friendlyName: name };\n }\n }\n if (!actionRaw && fallback) {\n actionRaw = fallback.actionRaw;\n friendlyName = fallback.friendlyName;\n }\n}\n// If still no action, try msg.data or msg.topic (some nodes put action in topic, e.g. \"1_single\")\nif (!actionRaw && msg.data && msg.data.action) actionRaw = msg.data.action.toString().toLowerCase();\nif (!actionRaw && typeof msg.topic === 'string' && /^\\d_[a-z_]+$/.test(msg.topic.trim())) actionRaw = msg.topic.trim().toLowerCase();\n// If payload was the action string, get friendly_name from topic e.g. zigbee2mqtt/Office Switch/action\nif (actionRaw && !friendlyName && typeof msg.topic === 'string') {\n const parts = msg.topic.split('/');\n if (parts.length >= 2) friendlyName = parts[1].trim(); // zigbee2mqtt/Office Switch/action → Office Switch\n}\n\nif (!actionRaw) {\n node.warn('[Zigbee to NVL] no action in payload. Keys: ' + (typeof payload === 'object' ? Object.keys(payload).join(', ') : 'n/a'));\n return null;\n}\n\nconst parts = actionRaw.split('_');\nlet buttonIndex = null;\nif (parts.length >= 2) {\n const n = parseInt(parts[0], 10);\n if (n >= 1 && n <= 6) buttonIndex = n;\n}\nif (buttonIndex == null) buttonIndex = actionToSwitch[actionRaw];\nif (buttonIndex == null && parts.length >= 2) buttonIndex = actionToSwitch[parts.slice(1).join('_')];\nif (buttonIndex == null) buttonIndex = actionToSwitch[parts[parts.length - 1]];\n\nif (buttonIndex == null) {\n node.warn('[Zigbee to NVL] unknown action: ' + JSON.stringify(actionRaw));\n return null;\n}\n\nnode.warn('[Zigbee to NVL] action=' + actionRaw + ' buttonIndex=' + buttonIndex + ' device=' + friendlyName);\n\n// Prefer switchBindings by friendly name; fallback by device ID (IEEE) if we have switchBindingsByDeviceId\nconst bindingByDeviceId = (config && config.switchBindingsByDeviceId) ? config.switchBindingsByDeviceId : null;\nlet targets = [];\nlet binding = switchBindings[friendlyName] && switchBindings[friendlyName][buttonIndex];\nif ((binding === undefined || binding === null) && bindingByDeviceId) {\n const deviceId = typeof payload === 'object' && Object.keys(payload).some(function (k) { return k.indexOf('0x') === 0; })\n ? Object.keys(payload).find(function (k) { return k.indexOf('0x') === 0; })\n : null;\n if (deviceId) binding = bindingByDeviceId[deviceId] && bindingByDeviceId[deviceId][buttonIndex];\n}\nif (binding !== undefined && binding !== null) {\n targets = Array.isArray(binding) ? binding : [binding];\n targets = targets.filter(function (t) { return t && t.room && t.light >= 1 && t.light <= 6; });\n}\nif (targets.length === 0) {\n const room = deviceToRoom[friendlyName] || 'cmd_livingroom';\n targets = [{ room: room, light: buttonIndex }];\n}\n\nif (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });\nconst state = flow.get('nvlInState');\nconst clearList = [];\n\nfor (const t of targets) {\n const key = 'zigbee_sw' + t.light;\n if (!state.rooms[t.room]) state.rooms[t.room] = {};\n state.rooms[t.room][key] = true;\n clearList.push({ room: t.room, key: key });\n}\n\nflow.set('nvlInState', state);\n\nnode.warn('[Zigbee to NVL] ' + (friendlyName || 'device') + ' btn' + buttonIndex + ' → ' + targets.map(function (t) { return t.room + ' L' + t.light; }).join(', '));\n\nmsg.payload = { buildAndSend: true };\nmsg.zigbeeClear = clearList;\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1160, + "y": 380, + "wires": [ + [ + "886c1334e909e2cd", + "75212a4bc3d085db" + ] + ] + }, + { + "id": "886c1334e909e2cd", + "type": "delay", + "z": "7de41d810b04d992", + "name": "80ms", + "pauseType": "delay", + "timeout": "80", + "timeoutUnits": "milliseconds", + "rate": "1", + "nbRateUnits": "1", + "rateUnits": "seconds", + "randomFirst": "1", + "randomLast": "5", + "randomUnits": "seconds", + "drop": true, + "outputs": 1, + "x": 1350, + "y": 380, + "wires": [ + [ + "44822e048dbaee31" + ] + ] + }, + { + "id": "44822e048dbaee31", + "type": "function", + "z": "7de41d810b04d992", + "name": "Clear zigbee edge", + "func": "// Paste into the \"Clear zigbee edge\" Function node (after 80 ms delay).\n// Clears zigbee_swN in nvlInState so the PLC sees one pulse. Supports one room or many (switchBindings).\n// msg.zigbeeClear can be: { room, key } or [ { room, key }, ... ]\n\nif (msg.zigbeeClear) {\n const state = flow.get('nvlInState') || { rooms: {}, boiler: {} };\n const list = Array.isArray(msg.zigbeeClear) ? msg.zigbeeClear : [msg.zigbeeClear];\n for (const item of list) {\n const r = state.rooms[item.room];\n if (r && item.key) r[item.key] = false;\n }\n flow.set('nvlInState', state);\n}\nmsg.payload = { buildAndSend: true };\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1550, + "y": 380, + "wires": [ + [ + "75212a4bc3d085db" + ] + ] + }, + { + "id": "17814d6d1beda546", + "type": "zigbee2mqtt-in", + "z": "7de41d810b04d992", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "2 selected", + "device_id": [ + "0xa4c138a5b9771b05", + "0xa4c138112a12d55f" + ], + "state": null, + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": true, + "x": 680, + "y": 380, + "wires": [ + [ + "718ba68ae647874f", + "2edb29caf874a91e" + ] + ] + }, + { + "id": "74c2640db1c8bd3e", + "type": "link out", + "z": "7de41d810b04d992", + "name": "link out 26", + "mode": "link", + "links": [ + "c6162cb59e21491f" + ], + "x": 1570, + "y": 320, + "wires": [], + "l": true + }, + { + "id": "95b626ea6980446e", + "type": "trigger-state", + "z": "7de41d810b04d992", + "name": "", + "server": "29eb84a2708d96e0", + "version": 5, + "inputs": 1, + "outputs": 2, + "exposeAsEntityConfig": "", + "entities": { + "entity": [ + "input_boolean.living_room_new" + ], + "substring": [], + "regex": [] + }, + "debugEnabled": true, + "constraints": [], + "customOutputs": [], + "outputInitially": false, + "stateType": "str", + "enableInput": true, + "x": 830, + "y": 320, + "wires": [ + [ + "4045239f675b77d2" + ], + [] + ] + }, + { + "id": "75212a4bc3d085db", + "type": "function", + "z": "7de41d810b04d992", + "name": "STATE TO NVL", + "func": "// Build the payload for nvl-send from flow.nvlInState.\n// Room list comes from global roomConfig (single source of truth). See /root/.node-red/room-config.js.\n// Wire: HA to NVL / Zigbee to NVL → this function → nvl-send → udp out\n\nconst struct_room_cmds_default = {\n ha_l1_on: false, ha_l1_off: false, ha_l2_on: false, ha_l2_off: false,\n ha_l3_on: false, ha_l3_off: false, ha_l4_on: false, ha_l4_off: false,\n ha_l5_on: false, ha_l5_off: false, ha_l6_on: false, ha_l6_off: false,\n zigbee_sw1: false, zigbee_sw2: false, zigbee_sw3: false,\n zigbee_sw4: false, zigbee_sw5: false, zigbee_sw6: false,\n ha_all_on: false, ha_all_off: false\n};\n\nconst config = global.get('roomConfig');\nconst roomNames = (config && config.roomNames && config.roomNames.length) ? config.roomNames : ['cmd_livingroom'];\n\nconst state = flow.get('nvlInState') || { rooms: {}, boiler: {} };\nconst rooms = state.rooms || {};\nconst payload = {};\n\nfor (const name of roomNames) {\n const cmd = rooms[name] || {};\n payload[name] = { ...struct_room_cmds_default };\n for (const k of Object.keys(struct_room_cmds_default)) {\n if (cmd[k] !== undefined) payload[name][k] = !!cmd[k];\n }\n}\n\nmsg.payload = payload;\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1380, + "y": 320, + "wires": [ + [ + "74c2640db1c8bd3e", + "561398f1884392c1" + ] + ] + }, + { + "id": "d5cdc38171230744", + "type": "function", + "z": "7de41d810b04d992", + "name": "NVL to HA Sync", + "func": "// Run this AFTER nvl-receive. Syncs all mapped lights from PLC state to HA entities.\n// Map comes from global roomConfig (single source of truth). See /root/.node-red/room-config.js.\n// Connect output 1 → Action node (turn_on), output 2 → Action node (turn_off).\n\nconst config = global.get('roomConfig');\nconst LIGHT_ENTITY_MAP = (config && config.lightEntityMap && config.lightEntityMap.length) ? config.lightEntityMap : [\n { room: 'light_livingRoom', light: 1, entityId: 'input_boolean.living_room_new' },\n];\n\nconst payload = msg.payload || {};\nconst onMsgs = [];\nconst offMsgs = [];\n\nfor (const entry of LIGHT_ENTITY_MAP) {\n const room = payload[entry.room] || {};\n const isOn = !!(room['l_' + entry.light] || room['l' + entry.light]);\n\n const flowKey = 'nvlToHa_' + entry.entityId.replace(/\\./g, '_');\n const last = flow.get(flowKey);\n if (last === isOn) continue;\n flow.set(flowKey, isOn);\n\n const domain = entry.entityId.split('.')[0] || 'input_boolean';\n const action = domain + '.' + (isOn ? 'turn_on' : 'turn_off');\n const out = { payload: { action: action, target: { entity_id: [entry.entityId] } } };\n if (isOn) onMsgs.push(out); else offMsgs.push(out);\n}\n\nif (onMsgs.length === 0 && offMsgs.length === 0) return null;\nreturn [onMsgs, offMsgs];\n", + "outputs": 2, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1240, + "y": 220, + "wires": [ + [ + "566f142b2e6e841f" + ], + [ + "5789f9ce23be8782" + ] + ] + }, + { + "id": "7c4268195123e319", + "type": "link in", + "z": "7de41d810b04d992", + "name": "link in 14", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 1015, + "y": 220, + "wires": [ + [ + "d5cdc38171230744" + ] + ] + }, + { + "id": "566f142b2e6e841f", + "type": "api-call-service", + "z": "7de41d810b04d992", + "name": "", + "server": "29eb84a2708d96e0", + "version": 7, + "debugenabled": false, + "action": "input_boolean.turn_on", + "floorId": [], + "areaId": [], + "deviceId": [], + "entityId": [ + "input_boolean.living_room_new" + ], + "labelId": [], + "data": "", + "dataType": "jsonata", + "mergeContext": "", + "mustacheAltTags": false, + "outputProperties": [], + "queue": "none", + "blockInputOverrides": false, + "domain": "input_boolean", + "service": "turn_on", + "x": 1540, + "y": 200, + "wires": [ + [] + ] + }, + { + "id": "5789f9ce23be8782", + "type": "api-call-service", + "z": "7de41d810b04d992", + "name": "", + "server": "29eb84a2708d96e0", + "version": 7, + "debugenabled": false, + "action": "input_boolean.turn_off", + "floorId": [], + "areaId": [], + "deviceId": [], + "entityId": [ + "input_boolean.living_room_new" + ], + "labelId": [], + "data": "", + "dataType": "jsonata", + "mergeContext": "", + "mustacheAltTags": false, + "outputProperties": [], + "queue": "none", + "blockInputOverrides": false, + "domain": "input_boolean", + "service": "turn_off", + "x": 1540, + "y": 260, + "wires": [ + [] + ] + }, + { + "id": "3b1ea5253e0fbf22", + "type": "function", + "z": "7de41d810b04d992", + "d": true, + "name": "HA to NVL", + "func": "// Writes to state.rooms.cmd_livingroom (NVL variable cmd_livingroom).\nconst ROOM_NAME = 'cmd_livingroom';\nconst entityId = (msg.topic || '').toString();\nconst parts = entityId.split('_');\nconst last = parts[parts.length - 1] || '';\nconst num = parseInt(last, 10);\nconst lightNum = (!isNaN(num) && num >= 1) ? Math.min(6, num) : 1;\n\n// Normalize state: HA / trigger-state can send payload as string, object with .state, or data.new_state.state\nlet rawState = msg.payload;\nif (rawState !== null && typeof rawState === 'object' && rawState.state !== undefined) rawState = rawState.state;\nif (msg.data && msg.data.new_state && msg.data.new_state.state !== undefined) rawState = msg.data.new_state.state;\nconst isOn = (rawState === 'on' || rawState === true);\n\nnode.warn('[HA to NVL] topic=' + entityId + ' rawState=' + JSON.stringify(rawState) + ' → lightNum=' + lightNum + ' isOn=' + isOn);\n\nif (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });\nconst state = flow.get('nvlInState');\nif (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};\nconst r = state.rooms[ROOM_NAME];\nr['ha_l' + lightNum + '_on'] = false;\nr['ha_l' + lightNum + '_off'] = false;\nif (isOn) r['ha_l' + lightNum + '_on'] = true;\nelse r['ha_l' + lightNum + '_off'] = true;\nflow.set('nvlInState', state);\n\nnode.warn('[HA to NVL] set cmd_livingroom ha_l' + lightNum + '_' + (isOn ? 'on' : 'off') + '=true, buildAndSend');\n\n// PLC uses R_TRIG (rising edge). If \"off\" still doesn’t work, add a flow: after this node, 80ms delay then clear ha_l*_off and buildAndSend again (pulse).\nmsg.payload = { buildAndSend: true };\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1130, + "y": 740, + "wires": [ + [ + "3e962e16c44a4b8a" + ] + ] + }, + { + "id": "1957eb3666681641", + "type": "function", + "z": "7de41d810b04d992", + "d": true, + "name": "Zigbee to NVL", + "func": "// Writes to state.rooms.cmd_livingroom (same as HA to NVL). Zigbee action → zigbee_sw1..6.\nconst ROOM_NAME = 'cmd_livingroom';\nconst actionToSwitch = { single: 1, double: 2, hold: 3, release: 4, triple: 5, quad: 6 };\nconst payload = msg.payload || {};\nconst actionRaw = (payload.action || payload.click || '').toLowerCase();\n// Support \"1_single\", \"2_double\" etc. (button_number + action) or plain \"single\", \"double\"\nconst parts = actionRaw.split('_');\nlet swIndex = null;\nif (parts.length >= 2) {\n const buttonNum = parseInt(parts[0], 10);\n if (buttonNum >= 1 && buttonNum <= 6) swIndex = buttonNum;\n}\nif (swIndex == null) swIndex = actionToSwitch[actionRaw];\nif (swIndex == null && parts.length >= 2) swIndex = actionToSwitch[parts.slice(1).join('_')];\nif (swIndex == null) swIndex = actionToSwitch[parts[parts.length - 1]];\n\nif (swIndex == null) {\n node.warn('[Zigbee to NVL] unknown action: ' + JSON.stringify(actionRaw) + ' payload=' + JSON.stringify(payload));\n return null;\n}\n\nif (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });\nconst state = flow.get('nvlInState');\nif (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};\nstate.rooms[ROOM_NAME]['zigbee_sw' + swIndex] = true;\nflow.set('nvlInState', state);\n\nnode.warn('[Zigbee to NVL] cmd_livingroom zigbee_sw' + swIndex + '=true (action=' + actionRaw + '), buildAndSend + zigbeeClear');\n\nmsg.payload = { buildAndSend: true };\nmsg.zigbeeClear = { room: ROOM_NAME, key: 'zigbee_sw' + swIndex };\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1120, + "y": 800, + "wires": [ + [ + "30fab57c9aad15eb", + "3e962e16c44a4b8a" + ] + ] + }, + { + "id": "30fab57c9aad15eb", + "type": "delay", + "z": "7de41d810b04d992", + "d": true, + "name": "80ms", + "pauseType": "delay", + "timeout": "80", + "timeoutUnits": "milliseconds", + "rate": "1", + "nbRateUnits": "1", + "rateUnits": "seconds", + "randomFirst": "1", + "randomLast": "5", + "randomUnits": "seconds", + "drop": true, + "outputs": 1, + "x": 1310, + "y": 800, + "wires": [ + [ + "ef18cce9c468177c" + ] + ] + }, + { + "id": "ef18cce9c468177c", + "type": "function", + "z": "7de41d810b04d992", + "d": true, + "name": "Clear zigbee edge", + "func": "if (msg.zigbeeClear) {\n const state = flow.get('nvlInState') || { rooms: {}, boiler: {} };\n const r = state.rooms[msg.zigbeeClear.room];\n if (r) r[msg.zigbeeClear.key] = false;\n flow.set('nvlInState', state);\n}\nmsg.payload = { buildAndSend: true };\nreturn msg;", + "outputs": 1, + "x": 1510, + "y": 800, + "wires": [ + [ + "3e962e16c44a4b8a" + ] + ] + }, + { + "id": "79c6501879706e27", + "type": "zigbee2mqtt-in", + "z": "7de41d810b04d992", + "d": true, + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Office Switch (TS0042)", + "device_id": "0xa4c138a5b9771b05", + "state": "0", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 900, + "y": 800, + "wires": [ + [ + "1957eb3666681641" + ] + ] + }, + { + "id": "bcf5055c45c97cc3", + "type": "link out", + "z": "7de41d810b04d992", + "d": true, + "name": "link out 27", + "mode": "link", + "links": [ + "c6162cb59e21491f" + ], + "x": 1530, + "y": 740, + "wires": [], + "l": true + }, + { + "id": "96ed40d44eeab413", + "type": "trigger-state", + "z": "7de41d810b04d992", + "d": true, + "name": "", + "server": "29eb84a2708d96e0", + "version": 5, + "inputs": 1, + "outputs": 2, + "exposeAsEntityConfig": "", + "entities": { + "entity": [ + "input_boolean.living_room_new" + ], + "substring": [], + "regex": [] + }, + "debugEnabled": true, + "constraints": [], + "customOutputs": [], + "outputInitially": false, + "stateType": "str", + "enableInput": true, + "x": 850, + "y": 740, + "wires": [ + [ + "3b1ea5253e0fbf22" + ], + [] + ] + }, + { + "id": "3e962e16c44a4b8a", + "type": "function", + "z": "7de41d810b04d992", + "d": true, + "name": "NVL SEND", + "func": "// Build the payload for nvl-send from flow.nvlInState.\n// Redesign: all rooms use struct_room_cmds. Add more names to roomNames when you add rooms.\n// Wire: HA to NVL / Zigbee to NVL → this function → nvl-send → udp out\n\nconst struct_room_cmds_default = {\n ha_l1_on: false, ha_l1_off: false, ha_l2_on: false, ha_l2_off: false,\n ha_l3_on: false, ha_l3_off: false, ha_l4_on: false, ha_l4_off: false,\n ha_l5_on: false, ha_l5_off: false, ha_l6_on: false, ha_l6_off: false,\n zigbee_sw1: false, zigbee_sw2: false, zigbee_sw3: false,\n zigbee_sw4: false, zigbee_sw5: false, zigbee_sw6: false,\n ha_all_on: false, ha_all_off: false\n};\n\n// NVL variable names per room (struct_room_cmds). Add more when you add rooms.\nconst roomNames = ['cmd_livingroom'];\n\nconst state = flow.get('nvlInState') || { rooms: {}, boiler: {} };\nconst rooms = state.rooms || {};\nconst payload = {};\n\nfor (const name of roomNames) {\n const cmd = rooms[name] || {};\n payload[name] = { ...struct_room_cmds_default };\n for (const k of Object.keys(struct_room_cmds_default)) {\n if (cmd[k] !== undefined) payload[name][k] = !!cmd[k];\n }\n}\n\nmsg.payload = payload;\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1330, + "y": 740, + "wires": [ + [ + "bcf5055c45c97cc3" + ] + ] + }, + { + "id": "747c5e9bf3763dac", + "type": "function", + "z": "7de41d810b04d992", + "d": true, + "name": "NVL to HA Sync", + "func": "// Run this AFTER nvl-receive. Syncs all mapped lights from PLC state to HA entities.\n// Connect output 1 → api-call-service (input_boolean.turn_on / light.turn_on), output 2 → api-call-service (turn_off).\n// Only outputs when state changed (per entity). Add more entries to LIGHT_ENTITY_MAP as you add rooms/lights.\n\n// Map: NVL room key (in payload) + light index 1..6 → HA entity_id\nconst LIGHT_ENTITY_MAP = [\n { room: 'light_livingRoom', light: 1, entityId: 'input_boolean.living_room_new' },\n // { room: 'light_livingRoom', light: 2, entityId: 'input_boolean.living_room_2' },\n // { room: 'l_kitchen', light: 1, entityId: 'light.kitchen_ceiling' },\n];\n\nconst payload = msg.payload || {};\nconst onMsgs = [];\nconst offMsgs = [];\n\nfor (const entry of LIGHT_ENTITY_MAP) {\n const room = payload[entry.room] || {};\n const isOn = !!(room['l_' + entry.light] || room['l' + entry.light]);\n\n const flowKey = 'nvlToHa_' + entry.entityId.replace(/\\./g, '_');\n const last = flow.get(flowKey);\n if (last === isOn) continue;\n flow.set(flowKey, isOn);\n\n const out = { payload: { entity_id: entry.entityId } };\n if (isOn) onMsgs.push(out); else offMsgs.push(out);\n}\n\nif (onMsgs.length === 0 && offMsgs.length === 0) return null;\nreturn [onMsgs, offMsgs];\n", + "outputs": 2, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1200, + "y": 640, + "wires": [ + [ + "320ececfad817f94" + ], + [ + "577d462afbb43835" + ] + ] + }, + { + "id": "032f98ab9d5085b8", + "type": "link in", + "z": "7de41d810b04d992", + "d": true, + "name": "link in 15", + "links": [ + "e059a9fd1da3c3fd" + ], + "x": 975, + "y": 640, + "wires": [ + [ + "747c5e9bf3763dac" + ] + ] + }, + { + "id": "320ececfad817f94", + "type": "api-call-service", + "z": "7de41d810b04d992", + "d": true, + "name": "", + "server": "29eb84a2708d96e0", + "version": 7, + "debugenabled": false, + "action": "input_boolean.turn_on", + "floorId": [], + "areaId": [], + "deviceId": [], + "entityId": [ + "input_boolean.living_room_new" + ], + "labelId": [], + "data": "", + "dataType": "jsonata", + "mergeContext": "", + "mustacheAltTags": false, + "outputProperties": [], + "queue": "none", + "blockInputOverrides": false, + "domain": "input_boolean", + "service": "turn_on", + "x": 1500, + "y": 620, + "wires": [ + [] + ] + }, + { + "id": "577d462afbb43835", + "type": "api-call-service", + "z": "7de41d810b04d992", + "d": true, + "name": "", + "server": "29eb84a2708d96e0", + "version": 7, + "debugenabled": false, + "action": "input_boolean.turn_off", + "floorId": [], + "areaId": [], + "deviceId": [], + "entityId": [ + "input_boolean.living_room_new" + ], + "labelId": [], + "data": "", + "dataType": "jsonata", + "mergeContext": "", + "mustacheAltTags": false, + "outputProperties": [], + "queue": "none", + "blockInputOverrides": false, + "domain": "input_boolean", + "service": "turn_off", + "x": 1500, + "y": 680, + "wires": [ + [] + ] + }, + { + "id": "9f25a0709e29f737", + "type": "function", + "z": "7de41d810b04d992", + "d": true, + "name": "Zigbee to NVL", + "func": "// Writes to state.rooms.. Room from Zigbee device via global roomConfig.deviceToRoom. See /root/.node-red/room-config.js.\nconst actionToSwitch = { single: 1, double: 2, hold: 3, release: 4, triple: 5, quad: 6 };\nconst config = global.get('roomConfig');\nconst deviceToRoom = (config && config.deviceToRoom) ? config.deviceToRoom : { 'Office Switch': 'cmd_livingroom' };\nconst friendlyName = (msg.payload && msg.payload.friendly_name) ? msg.payload.friendly_name : '';\nconst ROOM_NAME = deviceToRoom[friendlyName] || 'cmd_livingroom';\nconst payload = msg.payload || {};\nconst actionRaw = (payload.action || payload.click || '').toLowerCase();\n// Support \"1_single\", \"2_double\" etc. (button_number + action) or plain \"single\", \"double\"\nconst parts = actionRaw.split('_');\nlet swIndex = null;\nif (parts.length >= 2) {\n const buttonNum = parseInt(parts[0], 10);\n if (buttonNum >= 1 && buttonNum <= 6) swIndex = buttonNum;\n}\nif (swIndex == null) swIndex = actionToSwitch[actionRaw];\nif (swIndex == null && parts.length >= 2) swIndex = actionToSwitch[parts.slice(1).join('_')];\nif (swIndex == null) swIndex = actionToSwitch[parts[parts.length - 1]];\n\nif (swIndex == null) {\n node.warn('[Zigbee to NVL] unknown action: ' + JSON.stringify(actionRaw) + ' payload=' + JSON.stringify(payload));\n return null;\n}\n\nif (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });\nconst state = flow.get('nvlInState');\nif (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};\nstate.rooms[ROOM_NAME]['zigbee_sw' + swIndex] = true;\nflow.set('nvlInState', state);\n\nnode.warn('[Zigbee to NVL] ' + ROOM_NAME + ' zigbee_sw' + swIndex + '=true (action=' + actionRaw + '), buildAndSend + zigbeeClear');\n\nmsg.payload = { buildAndSend: true };\nmsg.zigbeeClear = { room: ROOM_NAME, key: 'zigbee_sw' + swIndex };\nreturn msg;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1200, + "y": 540, + "wires": [ + [] + ] + }, + { + "id": "a0b2279b89ef6f15", + "type": "function", + "z": "7de41d810b04d992", + "d": true, + "name": "Load room config", + "func": "// Paste this into a Function node named \"Load room config\".\n// Connect an Inject node (once after 0.5 s) to it so it runs at startup.\n//\n// Since functionExternalModules is true in settings.js, you can require files.\n// room-config.js must be on the Node-RED server at /root/.node-red/room-config.js\n\n// Delete cached version so changes to the file are picked up on redeploy\ndelete require.cache[require.resolve('/root/.node-red/room-config.js')];\nconst ROOM_CONFIG = require('/root/.node-red/room-config.js');\n\nglobal.set('roomConfig', ROOM_CONFIG);\nnode.warn('[Load room config] loaded: ' + ROOM_CONFIG.roomNames.length + ' rooms, ' + ROOM_CONFIG.lightEntityMap.length + ' light mappings');\nreturn null;\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 850, + "y": 140, + "wires": [ + [ + "8150e2ee9131f8b9" + ] + ] + }, + { + "id": "7667d77649229573", + "type": "inject", + "z": "7de41d810b04d992", + "d": true, + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "0.5", + "topic": "", + "payload": "", + "payloadType": "date", + "x": 650, + "y": 140, + "wires": [ + [ + "a0b2279b89ef6f15" + ] + ] + }, + { + "id": "8150e2ee9131f8b9", + "type": "debug", + "z": "7de41d810b04d992", + "d": true, + "name": "debug 36", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1040, + "y": 140, + "wires": [] + }, + { + "id": "a4e26ffa4890dced", + "type": "watch", + "z": "7de41d810b04d992", + "name": "Watch room-config.js", + "files": "/root/.node-red/room-config.js", + "recursive": "", + "x": 780, + "y": 80, + "wires": [ + [ + "9b42b7c654e069ab", + "5f1f1b1d56c0c6cc" + ] + ] + }, + { + "id": "9b42b7c654e069ab", + "type": "exec", + "z": "7de41d810b04d992", + "command": "node -e \"console.log(JSON.stringify(require('/root/.node-red/room-config.js')))\"", + "addpay": "payload", + "append": "", + "useSpawn": "false", + "timer": "", + "winHide": false, + "oldrc": false, + "name": "Read room-config as JSON", + "x": 1120, + "y": 80, + "wires": [ + [ + "8fcbcdbb7ce4c8e5", + "2744d5e479e716b2" + ], + [ + "c59934f4f1db0826" + ], + [ + "1b367a15a867effc" + ] + ] + }, + { + "id": "8fcbcdbb7ce4c8e5", + "type": "function", + "z": "7de41d810b04d992", + "name": "Reload room config", + "func": "// Paste into a Function node named \"Reload room config from payload\".\n// Input: msg.payload = JSON string (from Exec node that runs the script below).\n// Sets global roomConfig so all flows see the new config without restarting Node-RED.\n\nconst raw = msg.payload;\nif (typeof raw !== 'string') {\n node.warn('[Reload config] expected string payload, got ' + typeof raw);\n return null;\n}\ntry {\n const config = JSON.parse(raw.trim());\n if (!config.roomNames || !Array.isArray(config.roomNames)) {\n node.error('[Reload config] invalid config: missing roomNames');\n return null;\n }\n global.set('roomConfig', config);\n node.warn('[Reload config] OK: ' + config.roomNames.length + ' rooms, ' + (config.lightEntityMap ? config.lightEntityMap.length : 0) + ' light mappings');\n return msg;\n} catch (e) {\n node.error('[Reload config] parse error: ' + e.message);\n return null;\n}\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1430, + "y": 80, + "wires": [ + [] + ] + }, + { + "id": "5f1f1b1d56c0c6cc", + "type": "debug", + "z": "7de41d810b04d992", + "name": "debug 37", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 950, + "y": 40, + "wires": [] + }, + { + "id": "2744d5e479e716b2", + "type": "debug", + "z": "7de41d810b04d992", + "name": "debug 39", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1420, + "y": 40, + "wires": [] + }, + { + "id": "c59934f4f1db0826", + "type": "debug", + "z": "7de41d810b04d992", + "name": "debug 40", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1420, + "y": 120, + "wires": [] + }, + { + "id": "1b367a15a867effc", + "type": "debug", + "z": "7de41d810b04d992", + "name": "debug 41", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1350, + "y": 160, + "wires": [] + }, + { + "id": "561398f1884392c1", + "type": "debug", + "z": "7de41d810b04d992", + "name": "debug 42", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1710, + "y": 540, + "wires": [] + }, + { + "id": "2edb29caf874a91e", + "type": "debug", + "z": "7de41d810b04d992", + "name": "debug 43", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1070, + "y": 460, + "wires": [] + }, + { + "id": "d592161c8e50638a", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "91b1f4cb0e1ffa84", + "name": "debug 7", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 2540, + "y": 80, + "wires": [] + }, + { + "id": "2a218aee475ce267", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "91b1f4cb0e1ffa84", + "name": "debug 10", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 2540, + "y": 120, + "wires": [] + }, + { + "id": "f5ef8cb0a1bc7def", + "type": "telegram receiver", + "z": "480553bb6f5b2b51", + "g": "91b1f4cb0e1ffa84", + "name": "", + "bot": "3dbb887330025103", + "saveDataDir": "", + "filterCommands": false, + "inputs": 0, + "x": 2350, + "y": 80, + "wires": [ + [ + "d592161c8e50638a" + ], + [ + "2a218aee475ce267" + ] + ] + }, + { + "id": "0385d24efa18d698", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Hallway Door Sensor (TS0203)", + "device_id": "0xa4c138eb9239e0de", + "state": "contact", + "outputAtStartup": true, + "filterChanges": true, + "enableMultiple": false, + "x": 190, + "y": 120, + "wires": [ + [ + "4055425d6c42dd13" + ] + ] + }, + { + "id": "9d611c0ff2a0b1ca", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Basement Door Sensor (E2013)", + "device_id": "0x048727fffea28deb", + "state": "contact", + "outputAtStartup": true, + "filterChanges": true, + "enableMultiple": false, + "x": 190, + "y": 160, + "wires": [ + [ + "4055425d6c42dd13" + ] + ] + }, + { + "id": "10448f46514c1406", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Front Door Sensor (TS0203)", + "device_id": "0xa4c138f4b1035c1d", + "state": "contact", + "outputAtStartup": true, + "filterChanges": true, + "enableMultiple": false, + "x": 180, + "y": 200, + "wires": [ + [ + "4055425d6c42dd13" + ] + ] + }, + { + "id": "a9d786b1ba52704b", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "Generate Message with snapshot", + "func": "let contact = msg.payload;\nlet topic = msg.topic || '';\nlet rawName = topic.split('/')[1] || 'Unknown';\nlet cleanName = rawName.replace(/sensor/i, '').trim();\n\n// Mapping between sensor names (or topic parts) and their camera IPs\nconst cameraMap = {\n \"Hallway\": \"10.20.30.33\",\n \"Basement\": \"10.20.30.30\",\n \"Front\": \"10.20.30.30\",\n \"Veranda\": \"10.20.30.32\"\n};\n\n// Find matching camera IP based on topic\nlet cameraIP = Object.keys(cameraMap).find(key => topic.includes(key));\nlet ip = cameraMap[cameraIP] || \"10.20.30.30\"; // fallback to Hallway if not matched\n\n// Construct URL\nmsg.url = `http://${ip}/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=${Math.random().toString(36).substring(7)}&user=admin&password=sh1pb0x1`;\n\n// Final message structure\nmsg.payload = null; // clear previous payload\nmsg.friendly_name = cleanName;\nmsg.door_status = contact === true ? \"CLOSED\" : \"OPENED\";\n\nreturn msg;\n\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 920, + "y": 120, + "wires": [ + [ + "5e94eb39aec6e82d", + "8b8cce25637907b1" + ] + ] + }, + { + "id": "e250cc2f901bea9f", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "Send Message to group", + "func": "// Check that the snapshot came in as a binary buffer\nif (!Buffer.isBuffer(msg.payload)) {\n node.warn(\"❌ Snapshot fetch failed — not a binary image buffer\");\n return null;\n}\n\nmsg.payload = {\n chatId: -4747176681,\n type: 'photo',\n content: msg.payload, // this is the buffer\n caption: `🚪 ${msg.friendly_name} just ${msg.door_status}`\n};\n\nreturn msg;\n\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1410, + "y": 120, + "wires": [ + [ + "7301d71523d87e94" + ] + ] + }, + { + "id": "5e94eb39aec6e82d", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "debug 6", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 1160, + "y": 60, + "wires": [] + }, + { + "id": "8b8cce25637907b1", + "type": "http request", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "", + "method": "GET", + "ret": "bin", + "paytoqs": "query", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "insecureHTTPParser": false, + "authType": "", + "senderr": false, + "headers": [], + "x": 1190, + "y": 120, + "wires": [ + [ + "e250cc2f901bea9f" + ] + ] + }, + { + "id": "3f9797c13c3a11a1", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "9113c5ebf5c089e3", + "name": "Door Status Message", + "func": "// Extract contact state and sensor name\nlet contact = msg.payload;\nlet topic = msg.topic || '';\nlet rawName = topic.split('/')[1] || 'Unknown';\nlet cleanName = rawName.replace(/sensor/i, '').trim();\n\n// Create message\nmsg.payload = {\n chatId: -4747176681,\n type: 'message',\n content: `🚪 ${cleanName} just ${contact === true ? \"CLOSED\" : \"OPENED\"}`\n};\n\nreturn msg;\n\n\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 940, + "y": 60, + "wires": [ + [] + ] + }, + { + "id": "c57ea029ee569428", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "a73395db2426b4fc", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Front Water Valve (SWV)", + "device_id": "0x0ceff6fffe84e4bc", + "state": "state", + "outputAtStartup": false, + "filterChanges": true, + "enableMultiple": false, + "x": 610, + "y": 540, + "wires": [ + [ + "fe7372c7f33d96f3" + ] + ] + }, + { + "id": "fe7372c7f33d96f3", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "a73395db2426b4fc", + "name": "Status Message", + "func": "let state = msg.payload;\nlet status = state === 'ON' ? \"OPENED\" : \"CLOSED\";\n\nmsg.payload = {\n chatId: -4747176681, // your group chat ID\n type: 'message',\n content: `💧 Front Water Valve is now ${status}`\n};\n\nreturn msg;\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 840, + "y": 540, + "wires": [ + [ + "18454d6cad2042dc" + ] + ] + }, + { + "id": "c1d2a4dc34b63730", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "a73395db2426b4fc", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Back Water Valve (SWV)", + "device_id": "0x7cc6b6fffe38a415", + "state": "state", + "outputAtStartup": false, + "filterChanges": true, + "enableMultiple": false, + "x": 610, + "y": 600, + "wires": [ + [ + "141f635c6c9d380f" + ] + ] + }, + { + "id": "141f635c6c9d380f", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "a73395db2426b4fc", + "name": "Status Message", + "func": "let state = msg.payload;\nlet status = state === 'ON' ? \"OPENED\" : \"CLOSED\";\n\nmsg.payload = {\n chatId: -4747176681, // your group chat ID\n type: 'message',\n content: `💧 Back Water Valve is now ${status}`\n};\n\nreturn msg;\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 840, + "y": 600, + "wires": [ + [ + "18454d6cad2042dc" + ] + ] + }, + { + "id": "77f3053cbdcdb875", + "type": "debug", + "z": "480553bb6f5b2b51", + "name": "debug 9", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 2540, + "y": 260, + "wires": [] + }, + { + "id": "55c50c70b3d0a0bb", + "type": "telegram sender", + "z": "480553bb6f5b2b51", + "name": "", + "bot": "3dbb887330025103", + "haserroroutput": false, + "outputs": 1, + "x": 2330, + "y": 260, + "wires": [ + [ + "77f3053cbdcdb875" + ] + ] + }, + { + "id": "41d29e978759e26e", + "type": "link in", + "z": "480553bb6f5b2b51", + "name": "telegram sender", + "links": [ + "7301d71523d87e94", + "18454d6cad2042dc", + "70f797398b0e6783", + "81d16e7a5307e9cc", + "bd7fed2198a7b950", + "8f15067daf678e66", + "1ba4ee81e40e5c51" + ], + "x": 2145, + "y": 260, + "wires": [ + [ + "55c50c70b3d0a0bb" + ] + ] + }, + { + "id": "7301d71523d87e94", + "type": "link out", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "link out 16", + "mode": "link", + "links": [ + "41d29e978759e26e" + ], + "x": 1555, + "y": 120, + "wires": [] + }, + { + "id": "18454d6cad2042dc", + "type": "link out", + "z": "480553bb6f5b2b51", + "g": "a73395db2426b4fc", + "name": "link out 17", + "mode": "link", + "links": [ + "41d29e978759e26e" + ], + "x": 1055, + "y": 540, + "wires": [] + }, + { + "id": "4055425d6c42dd13", + "type": "api-current-state", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "", + "server": "29eb84a2708d96e0", + "version": 3, + "outputs": 2, + "halt_if": "home", + "halt_if_type": "str", + "halt_if_compare": "is", + "entity_id": "person.nearchos_paraskeva", + "state_type": "str", + "blockInputOverrides": false, + "outputProperties": [], + "for": "", + "forType": "num", + "forUnits": "seconds", + "override_topic": false, + "state_location": "payload", + "override_payload": "msg", + "entity_location": "data", + "override_data": "msg", + "x": 570, + "y": 120, + "wires": [ + [], + [ + "a9d786b1ba52704b", + "f6adcaa35a793e58" + ] + ] + }, + { + "id": "ada6fe8339eab29b", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Veranda Door Sensor (E2013)", + "device_id": "0x048727fffea287ba", + "state": "contact", + "outputAtStartup": true, + "filterChanges": true, + "enableMultiple": false, + "x": 180, + "y": 260, + "wires": [ + [ + "4055425d6c42dd13" + ] + ] + }, + { + "id": "8f42e8fdfef40579", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Pantry water leak Sensor (ZG-222ZA)", + "device_id": "0xa4c138aeed18e61c", + "state": "water_leak", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 230, + "y": 620, + "wires": [ + [ + "1164cfc4fd2e5382" + ] + ] + }, + { + "id": "58ee1ac91f379cb9", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Status Message", + "func": "let state = msg.payload;\n\nif (state === true) {\n msg.payload = {\n chatId: -4747176681, // your group chat ID\n type: 'animation',\n content: 'https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExeWoxNjV6Z2Fna3MxMjcxd3RsaWxjMTA3cWhraW9kbGYzbGFtNzUzYiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/lVBtp4SRW6rvDHf1b6/giphy.gif', // Replace with your preferred GIF URL\n caption: '🚨 WATER LEAK DETECTED in Pantry Sink!'\n };\n return msg;\n}\n\nreturn null;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1000, + "y": 460, + "wires": [ + [ + "70f797398b0e6783" + ] + ] + }, + { + "id": "a1f1e1ea2cf90f61", + "type": "inject", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Simulate Leak (true)", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "true", + "payloadType": "bool", + "x": 290, + "y": 420, + "wires": [ + [ + "22740e52f6cc3399" + ] + ] + }, + { + "id": "3242605bce7f6f92", + "type": "inject", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Simulate No Leak (false)", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "false", + "payloadType": "bool", + "x": 270, + "y": 460, + "wires": [ + [ + "22740e52f6cc3399" + ] + ] + }, + { + "id": "22740e52f6cc3399", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Prepare trigger/reset", + "func": "// Pass through leak state\n// Add reset flag when false\n\nif (msg.payload === false) {\n msg.reset = true;\n}\n\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 560, + "y": 420, + "wires": [ + [ + "53799a37bb6cf7c6" + ] + ] + }, + { + "id": "53799a37bb6cf7c6", + "type": "trigger", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Repeat every 1 min", + "op1": "true", + "op2": "true", + "op1type": "bool", + "op2type": "bool", + "duration": "1", + "extend": true, + "overrideDelay": false, + "units": "min", + "reset": "reset", + "bytopic": "all", + "topic": "topic", + "outputs": 1, + "x": 780, + "y": 420, + "wires": [ + [ + "8b9761ea2f86b4f1" + ] + ] + }, + { + "id": "8b9761ea2f86b4f1", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Status Message", + "func": "let state = msg.payload;\n\nif (state === true) {\n msg.payload = {\n chatId: -4747176681, // your group chat ID\n type: 'animation',\n content: 'https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExeWoxNjV6Z2Fna3MxMjcxd3RsaWxjMTA3cWhraW9kbGYzbGFtNzUzYiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/lVBtp4SRW6rvDHf1b6/giphy.gif', // Replace with your preferred GIF URL\n caption: '🚨 WATER LEAK DETECTED in Kitchen Sink!'\n };\n return msg;\n}\n\nreturn null;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1000, + "y": 420, + "wires": [ + [ + "70f797398b0e6783" + ] + ] + }, + { + "id": "70f797398b0e6783", + "type": "link out", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "link out 19", + "mode": "link", + "links": [ + "41d29e978759e26e" + ], + "x": 1575, + "y": 440, + "wires": [] + }, + { + "id": "2157cdd444dfec91", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "kitchen water leak sensor (E2202)", + "device_id": "0xf84477fffeec19e9", + "state": "water_leak", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 240, + "y": 500, + "wires": [ + [ + "22740e52f6cc3399" + ] + ] + }, + { + "id": "8b2f06afcf0d773e", + "type": "inject", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Simulate Leak (true)", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "true", + "payloadType": "bool", + "x": 290, + "y": 540, + "wires": [ + [ + "1164cfc4fd2e5382" + ] + ] + }, + { + "id": "e31ec1ce332c4c99", + "type": "inject", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Simulate No Leak (false)", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "false", + "payloadType": "bool", + "x": 270, + "y": 580, + "wires": [ + [ + "1164cfc4fd2e5382" + ] + ] + }, + { + "id": "1164cfc4fd2e5382", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Prepare trigger/reset", + "func": "// Pass through leak state\n// Add reset flag when false\n\nif (msg.payload === false) {\n msg.reset = true;\n}\n\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 560, + "y": 460, + "wires": [ + [ + "748c39a404648158" + ] + ] + }, + { + "id": "748c39a404648158", + "type": "trigger", + "z": "480553bb6f5b2b51", + "g": "5c95d99a679a5e00", + "name": "Repeat every 1 min", + "op1": "true", + "op2": "true", + "op1type": "bool", + "op2type": "bool", + "duration": "1", + "extend": true, + "overrideDelay": false, + "units": "min", + "reset": "reset", + "bytopic": "all", + "topic": "topic", + "outputs": 1, + "x": 790, + "y": 460, + "wires": [ + [ + "58ee1ac91f379cb9" + ] + ] + }, + { + "id": "eb721beae24b271b", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "27 selected", + "device_id": [ + "0xa4c138ff324b5b7e", + "0xa4c138a5b9771b05", + "0xa4c13873de3faa66", + "0xa4c138cc9b54cec9", + "0xa4c138112a12d55f", + "0xa4c13854734f3a20", + "0xa4c1387dd1c2d40e", + "0xa4c138332cc3da08", + "0xa4c1388b74d83cfa", + "0xa4c138c2ae9ed430", + "0xa4c1380bab38d546", + "0xa4c1383b7c24483a", + "0x8c65a3fffee879a6", + "0xa4c138ceaa5681d5", + "0xa4c138d65f53037c", + "0xa4c1383d7921827a", + "0xa4c138eb9239e0de", + "0xa4c13891821386eb", + "0xa4c138f4b1035c1d", + "0x0ceff6fffe84e4bc", + "0x7cc6b6fffe38a415", + "0x048727fffea28deb", + "0x6cfd22fffe2cc318", + "0x6cfd22fffe66db44", + "0xa4c138aeed18e61c", + "0xf84477fffeec19e9", + "0x048727fffea287ba" + ], + "state": null, + "outputAtStartup": false, + "filterChanges": false, + "enableMultiple": true, + "x": 150, + "y": 820, + "wires": [ + [ + "41f1e9feaf1ea6aa", + "a9f755806e4e8a75" + ] + ] + }, + { + "id": "41f1e9feaf1ea6aa", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "function 5", + "func": "let result = [];\nlet devices = msg.payload;\nlet now = Date.now();\nlet dayMs = 24 * 60 * 60 * 1000;\n\n// Use flow context to track last alerts\nlet lastAlerts = flow.get(\"battery_alerts\") || {};\n\nfor (let ieee in devices) {\n let dev = devices[ieee];\n let battery = dev.payload?.battery ?? dev.item?.current_values?.battery;\n let name = dev.item?.friendly_name || dev.topic || ieee;\n\n if (typeof battery === 'number' && battery < 15) {\n let lastAlert = lastAlerts[ieee];\n\n if (!lastAlert || now - lastAlert > dayMs) {\n result.push(`🔋 ${name} is at ${battery}%`);\n lastAlerts[ieee] = now; // update timestamp\n }\n }\n}\n\n// Save updated state\nflow.set(\"battery_alerts\", lastAlerts);\n\nif (result.length > 0) {\n let time = new Date().toLocaleString();\n msg.payload = `Low battery alert(s) [${time}]:\\n` + result.join('\\n');\n return msg;\n}\n\nreturn null;\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 600, + "y": 740, + "wires": [ + [ + "c60307d3b68ebe34", + "5c2272c244ffc8b0" + ] + ] + }, + { + "id": "c60307d3b68ebe34", + "type": "function", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "function 6", + "func": "let battery = msg.payload;\n\n\nmsg.payload = {\n chatId: -4747176681, // your group chat ID\n type: 'message',\n content: `${battery}`\n};\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1040, + "y": 740, + "wires": [ + [ + "81d16e7a5307e9cc", + "6d60df5c0c6920d2" + ] + ] + }, + { + "id": "81d16e7a5307e9cc", + "type": "link out", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "link out 18", + "mode": "link", + "links": [ + "41d29e978759e26e" + ], + "x": 1575, + "y": 740, + "wires": [] + }, + { + "id": "209d79611a5414cc", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "1c73b8aaec035f06", + "name": "function 7", + "func": "let result = [];\nlet devices = msg.payload;\n\nfor (let ieee in devices) {\n let dev = devices[ieee];\n\n // Try to get battery from payload or fallback to item.current_values\n let battery = dev.payload?.battery ?? dev.item?.current_values?.battery;\n\n // Get friendly name or fallback to topic or IEEE\n let name = dev.item?.friendly_name || dev.topic || ieee;\n // if (typeof battery === 'number') {\n if (typeof battery === 'number' && battery < 15) {\n result.push(`🔋 ${name} is at ${battery}%`);\n }\n}\n\nif (result.length > 0) {\n msg.payload = `Low battery alert(s):\\n` + result.join('\\n');\n return msg;\n}\n\nreturn null;\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 600, + "y": 780, + "wires": [ + [] + ] + }, + { + "id": "e6a93b0cbc48af98", + "type": "inject", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "Test Device", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "zigbee2mqtt/Test Device", + "payload": "{\"0xdeadbeef00000001\":{\"item\":{\"friendly_name\":\"Low Battery Sensor\"},\"topic\":\"zigbee2mqtt/Low Battery Sensor\",\"payload\":{\"battery\":9}},\"0xdeadbeef00000002\":{\"item\":{\"friendly_name\":\"Good Battery Sensor\"},\"topic\":\"zigbee2mqtt/Good Battery Sensor\",\"payload\":{\"battery\":87}}}", + "payloadType": "json", + "x": 170, + "y": 740, + "wires": [ + [ + "41f1e9feaf1ea6aa", + "a9f755806e4e8a75" + ] + ] + }, + { + "id": "a9f755806e4e8a75", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "debug 2", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 160, + "y": 780, + "wires": [] + }, + { + "id": "5c2272c244ffc8b0", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "debug 3", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 760, + "y": 760, + "wires": [] + }, + { + "id": "6d60df5c0c6920d2", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "1c73b8aaec035f06", + "name": "debug 4", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1200, + "y": 760, + "wires": [] + }, + { + "id": "8e0d5ec04f029b9a", + "type": "zigbee2mqtt-in", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "Door Sensors", + "server": "4e20fc347c658518", + "friendly_name": "4 selected", + "device_id": [ + "0xa4c138eb9239e0de", + "0xa4c138f4b1035c1d", + "0x048727fffea28deb", + "0x048727fffea287ba" + ], + "state": null, + "outputAtStartup": false, + "filterChanges": false, + "enableMultiple": true, + "x": 130, + "y": 80, + "wires": [ + [ + "eb25ece689ea1045", + "63cb264d89898c6a" + ] + ] + }, + { + "id": "eb25ece689ea1045", + "type": "rbe", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "", + "func": "rbe", + "gap": "", + "start": "", + "inout": "out", + "septopics": true, + "property": "payload", + "topi": "topic", + "x": 350, + "y": 80, + "wires": [ + [ + "bd4b7dd437c1aaaf" + ] + ] + }, + { + "id": "63cb264d89898c6a", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "debug 5", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 270, + "y": 40, + "wires": [] + }, + { + "id": "bd4b7dd437c1aaaf", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "debug 8", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 470, + "y": 60, + "wires": [] + }, + { + "id": "f6adcaa35a793e58", + "type": "debug", + "z": "480553bb6f5b2b51", + "g": "9113c5ebf5c089e3", + "name": "debug 11", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 850, + "y": 200, + "wires": [] + }, + { + "id": "8656a450e0ae020d", + "type": "mqtt in", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "", + "topic": "#", + "qos": "2", + "datatype": "auto-detect", + "broker": "a1235a09a8d34dea", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 150, + "y": 1200, + "wires": [ + [ + "7ff8359abb38dca4" + ] + ] + }, + { + "id": "7ff8359abb38dca4", + "type": "switch", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "", + "property": "topic", + "propertyType": "msg", + "rules": [ + { + "t": "cont", + "v": "zigbee2mqtt", + "vt": "str" + }, + { + "t": "eq", + "v": "frigate/doorbell/person", + "vt": "str" + }, + { + "t": "eq", + "v": "frigate/veranda_door/person", + "vt": "str" + }, + { + "t": "eq", + "v": "frigate/basement_driveway/person", + "vt": "str" + }, + { + "t": "cont", + "v": "frigate", + "vt": "str" + }, + { + "t": "else" + } + ], + "checkall": "true", + "repair": false, + "outputs": 6, + "x": 330, + "y": 1200, + "wires": [ + [], + [ + "doorbell_state_check" + ], + [ + "48bfe0ad8a973a84" + ], + [ + "365adc2679063bc8" + ], + [], + [] + ] + }, + { + "id": "doorbell_state_check", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Check State Change", + "func": "// Get current payload value\nvar currentValue = parseInt(msg.payload);\n\n// Get previous state from context (default to 0 if not set)\nvar previousValue = context.get('previousDoorbellState') || 0;\n\n// Store current value for next comparison\ncontext.set('previousDoorbellState', currentValue);\n\n// Only proceed if current value is 1 AND previous value was 0\nif (currentValue === 1 && previousValue === 0) {\n msg.payload = \"🚪 Person detected at doorbell!\";\n return msg;\n}\n\n// Don't send message for other cases\nreturn null;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 700, + "y": 1200, + "wires": [ + [ + "78a1c2a40934ef20" + ] + ] + }, + { + "id": "bd7fed2198a7b950", + "type": "link out", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "link out 22", + "mode": "link", + "links": [ + "41d29e978759e26e" + ], + "x": 2335, + "y": 1200, + "wires": [] + }, + { + "id": "78a1c2a40934ef20", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Generate Doorbell Camera URL", + "func": "// Hardcoded doorbell camera IP\nconst doorbellCameraIP = \"10.20.30.30\";\n\n// Generate random string for cache busting\nconst randomString = Math.random().toString(36).substring(7);\n\n// Construct camera snapshot URL\nmsg.url = `http://${doorbellCameraIP}/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=${randomString}&user=admin&password=sh1pb0x1`;\n\n// Clear payload for HTTP request\nmsg.payload = null;\n\n// Add camera info to message\nmsg.camera_ip = doorbellCameraIP;\nmsg.camera_location = \"Doorbell Camera\";\n\nnode.status({fill:\"blue\", shape:\"dot\", text:`URL: ${doorbellCameraIP}`});\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 990, + "y": 1200, + "wires": [ + [ + "05f0d77a9b605f22" + ] + ] + }, + { + "id": "05f0d77a9b605f22", + "type": "http request", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Fetch Camera Snapshot", + "method": "GET", + "ret": "bin", + "paytoqs": "query", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "insecureHTTPParser": false, + "authType": "", + "senderr": false, + "headers": [], + "x": 1270, + "y": 1200, + "wires": [ + [ + "19d0e8d8c5addb1f" + ] + ] + }, + { + "id": "19d0e8d8c5addb1f", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Send Doorbell Alert to Telegram", + "func": "// Validate that we received a proper image buffer\nif (!Buffer.isBuffer(msg.payload)) {\n node.warn(\"❌ Doorbell snapshot fetch failed — not a binary image buffer\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Snapshot failed\"});\n return null;\n}\n\n// Check if buffer has reasonable size (basic validation)\nif (msg.payload.length < 1000) {\n node.warn(\"❌ Doorbell snapshot too small — likely corrupted\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Image too small\"});\n return null;\n}\n\n// Format timestamp for display\nconst timestamp = new Date(msg.timestamp).toLocaleString();\n\n// Prepare Telegram message payload\nmsg.payload = {\n chatId: -4747176681, // Telegram group chat ID\n type: 'photo',\n content: msg.payload, // Binary image buffer\n caption: `🔔 DOORBELL ALERT! 🔔\\n\\n📍 Someone is at the ${msg.location}\\n⏰ Time: ${timestamp}\\n📷 Camera: ${msg.camera_location} (${msg.camera_ip})`\n};\n\nnode.status({fill:\"green\", shape:\"dot\", text:\"Alert sent!\"});\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1540, + "y": 1200, + "wires": [ + [ + "bd7fed2198a7b950" + ] + ] + }, + { + "id": "eff49303c6d84242", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Send Doorbell Alert to Telegram", + "func": "// Validate that we received a proper image buffer\nif (!Buffer.isBuffer(msg.payload)) {\n node.warn(\"❌ Doorbell snapshot fetch failed — not a binary image buffer\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Snapshot failed\"});\n return null;\n}\n\n// Check if buffer has reasonable size (basic validation)\nif (msg.payload.length < 1000) {\n node.warn(\"❌ Doorbell snapshot too small — likely corrupted\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Image too small\"});\n return null;\n}\n\n// Format timestamp for display\nconst timestamp = new Date(msg.timestamp).toLocaleString();\n\n// Prepare Telegram message payload\nmsg.payload = {\n chatId: -4747176681, // Telegram group chat ID\n type: 'photo',\n content: msg.payload, // Binary image buffer\n caption: `🔔 Veranda ALERT! 🔔\\n\\n📍 Someone is at the ${msg.location}\\n⏰ Time: ${timestamp}\\n📷 Camera: ${msg.camera_location} (${msg.camera_ip})`\n};\n\nnode.status({fill:\"green\", shape:\"dot\", text:\"Alert sent!\"});\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1550, + "y": 1260, + "wires": [ + [ + "bd7fed2198a7b950" + ] + ] + }, + { + "id": "c74ddf6b84ef7a95", + "type": "http request", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Fetch Camera Snapshot", + "method": "GET", + "ret": "bin", + "paytoqs": "query", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "insecureHTTPParser": false, + "authType": "", + "senderr": false, + "headers": [], + "x": 1270, + "y": 1260, + "wires": [ + [ + "eff49303c6d84242", + "88d855e525ab78f0" + ] + ] + }, + { + "id": "d496d62222d1a073", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Generate Doorbell Camera URL", + "func": "// Hardcoded doorbell camera IP\nconst CameraIP = \"10.20.30.33\";\n\n// Generate random string for cache busting\nconst randomString = Math.random().toString(36).substring(7);\n\n// Construct camera snapshot URL\nmsg.url = `http://${CameraIP}/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=${randomString}&user=admin&password=sh1pb0x1`;\n\n// Clear payload for HTTP request\nmsg.payload = null;\n\n// Add camera info to message\nmsg.camera_ip = CameraIP;\nmsg.camera_location = \"Veranda Door Camera\";\n\nnode.status({fill:\"blue\", shape:\"dot\", text:`URL: ${CameraIP}`});\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 990, + "y": 1260, + "wires": [ + [ + "c74ddf6b84ef7a95" + ] + ] + }, + { + "id": "48bfe0ad8a973a84", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Check State Change", + "func": "// Get current payload value\nvar currentValue = parseInt(msg.payload);\n\n// Get previous state from context (default to 0 if not set)\nvar previousValue = context.get('previousDoorbellState') || 0;\n\n// Store current value for next comparison\ncontext.set('previousDoorbellState', currentValue);\n\n// Only proceed if current value is 1 AND previous value was 0\nif (currentValue === 1 && previousValue === 0) {\n msg.payload = \"🚪 Person detected at Veranda Door!\";\n return msg;\n}\n\n// Don't send message for other cases\nreturn null;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 700, + "y": 1260, + "wires": [ + [ + "d496d62222d1a073" + ] + ] + }, + { + "id": "88d855e525ab78f0", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "function 8", + "func": "// Try decoding buffer into string so we can read JSON errors\ntry {\n msg.debug_text = msg.payload.toString();\n node.warn(\"Camera returned text: \" + msg.debug_text);\n} catch (e) {\n node.warn(\"Could not decode payload\");\n}\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1620, + "y": 1300, + "wires": [ + [] + ] + }, + { + "id": "a4752681740af47e", + "type": "inject", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Simulate Person Detected", + "props": [ + { + "p": "payload", + "v": "1", + "vt": "num" + }, + { + "p": "timestamp", + "v": "", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "x": 170, + "y": 1300, + "wires": [ + [ + "7e88e31d094e343d" + ] + ] + }, + { + "id": "7e88e31d094e343d", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Add Current Timestamp", + "func": "// Add timestamp\nmsg.timestamp = Date.now();\nreturn msg;", + "outputs": 1, + "x": 170, + "y": 1360, + "wires": [ + [ + "365adc2679063bc8" + ] + ] + }, + { + "id": "365adc2679063bc8", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Check Doorbell State", + "func": "// Get previous state from context\nvar prev = context.get('prev') || 0;\nvar curr = parseInt(msg.payload);\ncontext.set('prev', curr);\n\nif(curr===1 && prev===0){\n msg.payload = \"🚪 Person detected at Basement Driveway!\";\n return msg;\n}\nreturn null;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 700, + "y": 1360, + "wires": [ + [ + "842a49fa21a0587a" + ] + ] + }, + { + "id": "842a49fa21a0587a", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Reolink Login", + "func": "const ip = '10.20.30.34';\nconst username = 'admin';\nconst password = 'sh1pb0x1';\nmsg.url = `http://${ip}/cgi-bin/api.cgi?cmd=Login&token=null`;\nmsg.method = 'POST';\nmsg.headers = { 'Content-Type': 'application/json' };\nmsg.payload = JSON.stringify([\n { cmd: 'Login', param: { User: { userName: username, password: password } } }\n]);\nmsg.camera_ip = ip;\nmsg.camera_location = 'Basement Driveway Camera';\nmsg.nextRandom = Math.random().toString(36).substring(7);\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 950, + "y": 1360, + "wires": [ + [ + "bedee5f9397c27f1" + ] + ] + }, + { + "id": "bedee5f9397c27f1", + "type": "http request", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Send Login Request", + "method": "use", + "ret": "obj", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "x": 1190, + "y": 1360, + "wires": [ + [ + "3f9c2db4dfea3095" + ] + ] + }, + { + "id": "3f9c2db4dfea3095", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Fetch Snapshot", + "func": "// Extract token from login response\nif(!msg.payload || !msg.payload[0] || !msg.payload[0].value){\n node.error('Login failed');\n return null;\n}\nconst token = msg.payload[0].value.Token.name;\nif(!token){ node.error('Token missing'); return null; }\n\n// Build snapshot URL\nmsg.url = `http://${msg.camera_ip}/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=${msg.nextRandom}&token=${token}`;\nmsg.method = 'GET';\nmsg.headers = {};\nmsg.payload = null;\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1430, + "y": 1360, + "wires": [ + [ + "0446448ed8c8da8a" + ] + ] + }, + { + "id": "0446448ed8c8da8a", + "type": "http request", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Get Camera Snapshot", + "method": "use", + "ret": "bin", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "x": 1670, + "y": 1360, + "wires": [ + [ + "d241b1f4991cfc05" + ] + ] + }, + { + "id": "d241b1f4991cfc05", + "type": "function", + "z": "480553bb6f5b2b51", + "d": true, + "g": "ce79bea4667a77e2", + "name": "Send Doorbell Alert to Telegram", + "func": "// Validate that we received a proper image buffer\nif (!Buffer.isBuffer(msg.payload)) {\n node.warn(\"❌ Basement Driveway snapshot fetch failed — not a binary image buffer\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Snapshot failed\"});\n return null;\n}\n\n// Check if buffer has reasonable size (basic validation)\nif (msg.payload.length < 1000) {\n node.warn(\"❌ Basement Driveway snapshot too small — likely corrupted\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Image too small\"});\n return null;\n}\n\n// Format timestamp for display\nconst timestamp = new Date(msg.timestamp).toLocaleString();\n\n// Prepare Telegram message payload\nmsg.payload = {\n chatId: -4747176681, // Telegram group chat ID\n type: 'photo',\n content: msg.payload, // Binary image buffer\n caption: `🔔 Basement Driveway ALERT! 🔔\\n\\n📍 Someone is at the ${msg.location}\\n⏰ Time: ${timestamp}\\n📷 Camera: ${msg.camera_location} (${msg.camera_ip})`\n};\n\nnode.status({fill:\"green\", shape:\"dot\", text:\"Alert sent!\"});\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1950, + "y": 1360, + "wires": [ + [ + "bd7fed2198a7b950" + ] + ] + }, + { + "id": "ca1a1f7f8b90cfc9", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "d": true, + "g": "b5ffb4a4212da258", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Office Switch (TS0042)", + "device_id": "0xa4c138a5b9771b05", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 220, + "y": 120, + "wires": [ + [ + "b8f3d3242fb39bdd" + ] + ] + }, + { + "id": "b8f3d3242fb39bdd", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "d": true, + "g": "b5ffb4a4212da258", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "bedroom_1.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 120, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "629cbd7ce15bb9e3", + "type": "link out", + "z": "f9a2fb576f0d360d", + "name": "link out 1", + "mode": "link", + "links": [ + "c6162cb59e21491f" + ], + "x": 1785, + "y": 1340, + "wires": [] + }, + { + "id": "6654c150377ed2bb", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "4b06cbc568091f6f", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Hallway Switch (TS0042)", + "device_id": "0xa4c13873de3faa66", + "state": "0", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 230, + "y": 220, + "wires": [ + [ + "83bbabef65831016" + ] + ] + }, + { + "id": "1aa9e0ff37d1afee", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "795d604329d51272", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Hallway Door Switch (TS0042)", + "device_id": "0xa4c138112a12d55f", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 250, + "y": 320, + "wires": [ + [ + "b9c07d22cbaae6a2" + ] + ] + }, + { + "id": "a6cf1bd4ae01ee56", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "39f557d8eec2fbae", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Master Bedroom Switch (TS0044)", + "device_id": "0xa4c13854734f3a20", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 260, + "y": 420, + "wires": [ + [ + "16eaca15570ff092", + "2d6bcc49a2785fd6" + ] + ] + }, + { + "id": "675ba75711ef84e7", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "68db2262f7c7313e", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Master bathroom switch (TS0042)", + "device_id": "0xa4c1387dd1c2d40e", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 260, + "y": 900, + "wires": [ + [ + "84272551b322cde2" + ] + ] + }, + { + "id": "becbc51cb810ff3d", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "5d202845936f0275", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Pantry Switch (TS0042)", + "device_id": "0xa4c138c2ae9ed430", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 220, + "y": 1000, + "wires": [ + [ + "4673e2e3f2c0626f" + ] + ] + }, + { + "id": "0bd6051323b66072", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "74596bdef53b62cf", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Kitchen Switch (TS0044)", + "device_id": "0xa4c1380bab38d546", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 230, + "y": 1100, + "wires": [ + [ + "6aac621e2a9acf79" + ] + ] + }, + { + "id": "6e30880884a2cdb2", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "5b22d521e762f0da", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Kitchen Veranda Switch (TS0043)", + "device_id": "0xa4c1383b7c24483a", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 250, + "y": 1200, + "wires": [ + [ + "da7ab159be01b35c" + ] + ] + }, + { + "id": "83bbabef65831016", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "4b06cbc568091f6f", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "hallway.sw_2", + "type": "str" + }, + { + "name": "switch_2", + "value": "hallway.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 220, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "b9c07d22cbaae6a2", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "795d604329d51272", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "outSide.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 320, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "16eaca15570ff092", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "39f557d8eec2fbae", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "masterBedroom.sw_1", + "type": "str" + }, + { + "name": "switch_2", + "value": "", + "type": "str" + }, + { + "name": "switch_3", + "value": "masterBathroom.sw_1", + "type": "str" + }, + { + "name": "switch_4", + "value": "masterBedroom.sw_2", + "type": "str" + } + ], + "x": 1270, + "y": 420, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "84272551b322cde2", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "68db2262f7c7313e", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "masterBathroom.sw_1", + "type": "str" + }, + { + "name": "switch_2", + "value": "masterBathroom.sw_2", + "type": "str" + } + ], + "x": 1270, + "y": 900, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "4673e2e3f2c0626f", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "5d202845936f0275", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "hallway.sw_1", + "type": "str" + }, + { + "name": "switch_2", + "value": "pantry.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 1000, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "6aac621e2a9acf79", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "74596bdef53b62cf", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "kitchen.sw_1", + "type": "str" + }, + { + "name": "switch_2", + "value": "kitchen.sw_2", + "type": "str" + }, + { + "name": "switch_3", + "value": "kitchen.sw_3", + "type": "str" + } + ], + "x": 1270, + "y": 1100, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "da7ab159be01b35c", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "5b22d521e762f0da", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "livingRoom.sw_1", + "type": "str" + }, + { + "name": "switch_3", + "value": "outVeranda.sw_2", + "type": "str" + } + ], + "x": 1270, + "y": 1200, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "6f028b285108ff17", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "fb2d97888a83fc98", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "bedroom_2.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 1300, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "ef6df11582a22110", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "1166ae03751d953e", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "bathroom.sw_1", + "type": "str" + }, + { + "name": "single", + "value": "bathroom.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 1400, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "82c1d5f7ae54b348", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "fb2d97888a83fc98", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Bedroom (TS0042)", + "device_id": "0xa4c1388b74d83cfa", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 210, + "y": 1300, + "wires": [ + [ + "6f028b285108ff17" + ] + ] + }, + { + "id": "774c15def7e6cf96", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "1166ae03751d953e", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Shower Switch (TS0041)", + "device_id": "0xa4c138cc9b54cec9", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 230, + "y": 1400, + "wires": [ + [ + "ef6df11582a22110" + ] + ] + }, + { + "id": "25cf5d71108cbbb4", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "5270af8a6312b29d", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "dinningRoom.sw_1", + "type": "str" + }, + { + "name": "switch_3", + "value": "entrance.sw_1", + "type": "str" + }, + { + "name": "switch_4", + "value": "entrance.sw_2", + "type": "str" + } + ], + "x": 1270, + "y": 1500, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "a330bf2f4d26c536", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "6809b1c79783ab0c", + "name": "", + "env": [ + { + "name": "single", + "value": "guestWc.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 1640, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "cbcd3d741247b12b", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "5270af8a6312b29d", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Entrance Switch (TS0044)", + "device_id": "0xa4c138ceaa5681d5", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 230, + "y": 1500, + "wires": [ + [ + "25cf5d71108cbbb4", + "0bf96ec77f6d1809" + ] + ] + }, + { + "id": "398c06a48e4daa13", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "6809b1c79783ab0c", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Guest WC Switch (TS0041)", + "device_id": "0xa4c138d65f53037c", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 240, + "y": 1640, + "wires": [ + [ + "a330bf2f4d26c536" + ] + ] + }, + { + "id": "1633e8e6861be2e0", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "3db6f7ccc63a5f8b", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Basement Switch (TS0043)", + "device_id": "0xa4c13891821386eb", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 230, + "y": 1740, + "wires": [ + [ + "9c36d1335b0f8ebb", + "7f48ed25c8ec1fce" + ] + ] + }, + { + "id": "9c36d1335b0f8ebb", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "3db6f7ccc63a5f8b", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "dinningRoom.sw_4", + "type": "str" + }, + { + "name": "single", + "value": "guestWc.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 1740, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "36d1d2c1e10100e7", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Master Bedroom RODRET (E2201)", + "device_id": "0x6cfd22fffe66db44", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 260, + "y": 2060, + "wires": [ + [ + "4c584ffba6a17ccb" + ] + ] + }, + { + "id": "4c584ffba6a17ccb", + "type": "switch", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "on", + "vt": "str" + }, + { + "t": "eq", + "v": "off", + "vt": "str" + }, + { + "t": "eq", + "v": "brightness_move_up", + "vt": "str" + }, + { + "t": "eq", + "v": "brightness_move_down", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 4, + "x": 590, + "y": 2120, + "wires": [ + [ + "6dd69a9285fc9583" + ], + [ + "6dd69a9285fc9583" + ], + [ + "c53543186fd45d8b" + ], + [ + "aadcf8d1921d77e6" + ] + ] + }, + { + "id": "c53543186fd45d8b", + "type": "change", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "", + "rules": [ + { + "t": "set", + "p": "topic", + "pt": "msg", + "to": "masterBedroom", + "tot": "str" + }, + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "sw_1", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 920, + "y": 2100, + "wires": [ + [ + "33eb6e0725214c0b" + ] + ] + }, + { + "id": "33eb6e0725214c0b", + "type": "link out", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "link out 15", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1325, + "y": 2100, + "wires": [] + }, + { + "id": "5df52e7f8217a6d9", + "type": "zigbee2mqtt-out", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Master Bedroom Spots (GL-C-006P)", + "device_id": "0xa4c138883e5d795c", + "command": "brightness", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1190, + "y": 2060, + "wires": [] + }, + { + "id": "6dd69a9285fc9583", + "type": "function", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "function 4", + "func": "// Initialize the counter\nlet counter = context.get('counter') || 10;\n\nif (msg.payload === \"on\") {\n msg.payload = counter;\n counter += 10;\n context.set('counter', counter);\n return msg;\n}\n\nif (msg.payload === \"off\") {\n counter -= 10;\n msg.payload = counter;\n context.set('counter', counter);\n return msg;\n}\n\n// Ignore other payloads\nreturn null;\n", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 840, + "y": 2060, + "wires": [ + [ + "5df52e7f8217a6d9" + ] + ] + }, + { + "id": "3e5fabb6aa93449c", + "type": "zigbee2mqtt-out", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Master Bedroom Spots (GL-C-006P)", + "device_id": "0xa4c138883e5d795c", + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1190, + "y": 2140, + "wires": [] + }, + { + "id": "aadcf8d1921d77e6", + "type": "change", + "z": "f9a2fb576f0d360d", + "g": "41609eab5db77f0a", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "off", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 920, + "y": 2140, + "wires": [ + [ + "3e5fabb6aa93449c" + ] + ] + }, + { + "id": "109c390c131b09a8", + "type": "change", + "z": "f9a2fb576f0d360d", + "g": "39f557d8eec2fbae", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "toggle", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 880, + "y": 460, + "wires": [ + [ + "d02a9d4e0468bc67" + ] + ] + }, + { + "id": "d02a9d4e0468bc67", + "type": "zigbee2mqtt-out", + "z": "f9a2fb576f0d360d", + "g": "39f557d8eec2fbae", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Master Bedroom Spots (GL-C-006P)", + "device_id": "0xa4c138883e5d795c", + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1190, + "y": 460, + "wires": [] + }, + { + "id": "07e63813fb04b906", + "type": "change", + "z": "f9a2fb576f0d360d", + "g": "5270af8a6312b29d", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "toggle", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 820, + "y": 1540, + "wires": [ + [ + "4d3d4a82a512ca10" + ] + ] + }, + { + "id": "4d3d4a82a512ca10", + "type": "zigbee2mqtt-out", + "z": "f9a2fb576f0d360d", + "g": "5270af8a6312b29d", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Dining Room Spots (GL-C-009P)", + "device_id": "0xa4c138df201f25a1", + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1200, + "y": 1540, + "wires": [] + }, + { + "id": "46517eb4342b3d18", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "b6271f4a3284f8d9", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Living Room Door Switch (TS0044)", + "device_id": "0xa4c1383d7921827a", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 260, + "y": 1920, + "wires": [ + [ + "a3d5be29d6effa34", + "701c1cd70874e615" + ] + ] + }, + { + "id": "a3d5be29d6effa34", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "b6271f4a3284f8d9", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "livingRoom.sw_1", + "type": "str" + }, + { + "name": "switch_2", + "value": "", + "type": "str" + }, + { + "name": "switch_3", + "value": "", + "type": "str" + }, + { + "name": "switch_4", + "value": "", + "type": "str" + }, + { + "name": "single", + "value": "", + "type": "str" + } + ], + "x": 550, + "y": 1920, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "d23c32a704af639d", + "type": "change", + "z": "f9a2fb576f0d360d", + "g": "b6271f4a3284f8d9", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "toggle", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 720, + "y": 1960, + "wires": [ + [ + "68f7d3015de2dbab" + ] + ] + }, + { + "id": "68f7d3015de2dbab", + "type": "zigbee2mqtt-out", + "z": "f9a2fb576f0d360d", + "g": "b6271f4a3284f8d9", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Living Room Spots (GL-C-006P)", + "device_id": "0xa4c13837f72f43ab", + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1210, + "y": 1960, + "wires": [] + }, + { + "id": "fe5612a41399f1f5", + "type": "api-call-service", + "z": "f9a2fb576f0d360d", + "g": "3db6f7ccc63a5f8b", + "name": "", + "server": "29eb84a2708d96e0", + "version": 7, + "debugenabled": false, + "action": "switch.toggle", + "floorId": [], + "areaId": [], + "deviceId": [], + "entityId": [ + "switch.basement_lights_switch_1" + ], + "labelId": [], + "data": "", + "dataType": "jsonata", + "mergeContext": "", + "mustacheAltTags": false, + "outputProperties": [], + "queue": "none", + "blockInputOverrides": true, + "domain": "switch", + "service": "toggle", + "x": 1270, + "y": 1780, + "wires": [ + [] + ] + }, + { + "id": "7f48ed25c8ec1fce", + "type": "switch", + "z": "f9a2fb576f0d360d", + "g": "3db6f7ccc63a5f8b", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "2_single", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 850, + "y": 1780, + "wires": [ + [ + "fe5612a41399f1f5" + ] + ] + }, + { + "id": "ab17cf6a7d65fcb9", + "type": "inject", + "z": "f9a2fb576f0d360d", + "g": "3db6f7ccc63a5f8b", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 840, + "y": 1820, + "wires": [ + [ + "fe5612a41399f1f5" + ] + ] + }, + { + "id": "0bf96ec77f6d1809", + "type": "switch", + "z": "f9a2fb576f0d360d", + "g": "5270af8a6312b29d", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "2_single", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 550, + "y": 1540, + "wires": [ + [ + "07e63813fb04b906" + ] + ] + }, + { + "id": "2d6bcc49a2785fd6", + "type": "switch", + "z": "f9a2fb576f0d360d", + "g": "39f557d8eec2fbae", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "2_single", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 630, + "y": 460, + "wires": [ + [ + "109c390c131b09a8" + ] + ] + }, + { + "id": "701c1cd70874e615", + "type": "switch", + "z": "f9a2fb576f0d360d", + "g": "b6271f4a3284f8d9", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "2_single", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 530, + "y": 1960, + "wires": [ + [ + "d23c32a704af639d" + ] + ] + }, + { + "id": "0b112f9f314fa0bb", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "ef555db28dba4c46", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "0xa4c138332cc3da08 (TS0044)", + "device_id": "0xa4c138332cc3da08", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 250, + "y": 560, + "wires": [ + [ + "a1f5a94baf1fbcd7", + "f0de9c882920d07c" + ] + ] + }, + { + "id": "a1f5a94baf1fbcd7", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "ef555db28dba4c46", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "masterBedroom.sw_1", + "type": "str" + }, + { + "name": "switch_2", + "value": "", + "type": "str" + }, + { + "name": "switch_3", + "value": "masterBedroom.sw_3", + "type": "str" + }, + { + "name": "switch_4", + "value": "masterBedroom.sw_5", + "type": "str" + } + ], + "x": 1270, + "y": 560, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "ae314d3d3457b679", + "type": "change", + "z": "f9a2fb576f0d360d", + "g": "ef555db28dba4c46", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "toggle", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 880, + "y": 600, + "wires": [ + [ + "0c35b08a3c7d347f" + ] + ] + }, + { + "id": "0c35b08a3c7d347f", + "type": "zigbee2mqtt-out", + "z": "f9a2fb576f0d360d", + "g": "ef555db28dba4c46", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Master Bedroom Spots (GL-C-006P)", + "device_id": [], + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1190, + "y": 600, + "wires": [] + }, + { + "id": "f0de9c882920d07c", + "type": "switch", + "z": "f9a2fb576f0d360d", + "g": "ef555db28dba4c46", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "2_single", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 630, + "y": 600, + "wires": [ + [ + "ae314d3d3457b679" + ] + ] + }, + { + "id": "310f749a07eded9d", + "type": "zigbee2mqtt-in", + "z": "f9a2fb576f0d360d", + "g": "1756bd5d4ff2c23b", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "basement_switch_2 (TS0043)", + "device_id": "0xa4c138a1968db065", + "state": "action", + "outputAtStartup": true, + "filterChanges": false, + "enableMultiple": false, + "x": 240, + "y": 2240, + "wires": [ + [ + "d0febe343ef897ac", + "8cce4a0a7061ac87" + ] + ] + }, + { + "id": "d0febe343ef897ac", + "type": "subflow:4dc13919bcc9d676", + "z": "f9a2fb576f0d360d", + "g": "1756bd5d4ff2c23b", + "name": "", + "env": [ + { + "name": "switch_1", + "value": "dinningRoom.sw_4", + "type": "str" + }, + { + "name": "single", + "value": "guestWc.sw_1", + "type": "str" + } + ], + "x": 1270, + "y": 2240, + "wires": [ + [ + "629cbd7ce15bb9e3" + ] + ] + }, + { + "id": "e1965a78e430acad", + "type": "api-call-service", + "z": "f9a2fb576f0d360d", + "g": "1756bd5d4ff2c23b", + "name": "", + "server": "29eb84a2708d96e0", + "version": 7, + "debugenabled": false, + "action": "switch.toggle", + "floorId": [], + "areaId": [], + "deviceId": [], + "entityId": [ + "switch.basement_lights_switch_1" + ], + "labelId": [], + "data": "", + "dataType": "jsonata", + "mergeContext": "", + "mustacheAltTags": false, + "outputProperties": [], + "queue": "none", + "blockInputOverrides": true, + "domain": "switch", + "service": "toggle", + "x": 1270, + "y": 2280, + "wires": [ + [] + ] + }, + { + "id": "8cce4a0a7061ac87", + "type": "switch", + "z": "f9a2fb576f0d360d", + "g": "1756bd5d4ff2c23b", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "2_single", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 850, + "y": 2280, + "wires": [ + [ + "e1965a78e430acad" + ] + ] + }, + { + "id": "d5cad6d8ca5fe08c", + "type": "inject", + "z": "f9a2fb576f0d360d", + "g": "1756bd5d4ff2c23b", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 840, + "y": 2320, + "wires": [ + [ + "e1965a78e430acad" + ] + ] + }, + { + "id": "05ecf6f492d4a8cf", + "type": "inject", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "ON", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "ON", + "payloadType": "str", + "x": 230, + "y": 1020, + "wires": [ + [ + "301e955ac79a9326" + ] + ] + }, + { + "id": "ad81a0918c31a862", + "type": "inject", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "OFF", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "OFF", + "payloadType": "str", + "x": 230, + "y": 1140, + "wires": [ + [ + "301e955ac79a9326" + ] + ] + }, + { + "id": "7049d2e629127b0e", + "type": "api-current-state", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "", + "server": "29eb84a2708d96e0", + "version": 3, + "outputs": 2, + "halt_if": "off", + "halt_if_type": "str", + "halt_if_compare": "is", + "entity_id": "binary_sensor.front_lights", + "state_type": "str", + "blockInputOverrides": true, + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "outVeranda", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "for": "0", + "forType": "num", + "forUnits": "minutes", + "override_topic": false, + "state_location": "payload", + "override_payload": "msg", + "entity_location": "data", + "override_data": "msg", + "x": 840, + "y": 1040, + "wires": [ + [ + "e44fa1b081b4aba7" + ], + [] + ] + }, + { + "id": "e44fa1b081b4aba7", + "type": "link out", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "link out 21", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1085, + "y": 1060, + "wires": [] + }, + { + "id": "532476a808456423", + "type": "api-current-state", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "", + "server": "29eb84a2708d96e0", + "version": 3, + "outputs": 2, + "halt_if": "on", + "halt_if_type": "str", + "halt_if_compare": "is", + "entity_id": "binary_sensor.front_lights", + "state_type": "str", + "blockInputOverrides": true, + "outputProperties": [ + { + "property": "topic", + "propertyType": "msg", + "value": "outVeranda", + "valueType": "str" + }, + { + "property": "payload", + "propertyType": "msg", + "value": "sw_1", + "valueType": "str" + } + ], + "for": "0", + "forType": "num", + "forUnits": "minutes", + "override_topic": false, + "state_location": "payload", + "override_payload": "msg", + "entity_location": "data", + "override_data": "msg", + "x": 840, + "y": 1100, + "wires": [ + [ + "e44fa1b081b4aba7" + ], + [] + ] + }, + { + "id": "9e01966beafd582b", + "type": "cronplus", + "z": "6722d185624fd57a", + "d": true, + "g": "cc230a0894b116d3", + "name": "", + "outputField": "payload", + "timeZone": "", + "storeName": "", + "commandResponseMsgOutput": "fanOut", + "defaultLocation": "", + "defaultLocationType": "default", + "outputs": 4, + "options": [ + { + "name": "ON", + "topic": "topic1", + "payloadType": "str", + "payload": "on", + "expressionType": "cron", + "expression": "0 0 20 * * *", + "location": "", + "offset": "0", + "solarType": "all", + "solarEvents": "sunrise,sunset" + }, + { + "name": "OFF", + "topic": "topic2", + "payloadType": "str", + "payload": "off", + "expressionType": "cron", + "expression": "0 0 00 * * *", + "location": "", + "offset": "0", + "solarType": "all", + "solarEvents": "sunrise,sunset" + } + ], + "x": 400, + "y": 1160, + "wires": [ + [], + [], + [], + [] + ] + }, + { + "id": "905629455f4dd222", + "type": "zigbee2mqtt-out", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Front Water Valve (SWV)", + "device_id": "0x0ceff6fffe84e4bc", + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1350, + "y": 460, + "wires": [] + }, + { + "id": "33e972f98673a6b6", + "type": "cronplus", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "Olive Trees every 2 days at 19:00", + "outputField": "payload", + "timeZone": "", + "storeName": "file", + "commandResponseMsgOutput": "fanOut", + "defaultLocation": "", + "defaultLocationType": "default", + "outputs": 4, + "options": [ + { + "name": "ON", + "topic": "front", + "payloadType": "default", + "payload": "", + "expressionType": "cron", + "expression": "20 6 */7 * *", + "location": "", + "offset": "0", + "solarType": "all", + "solarEvents": "sunrise,sunset" + }, + { + "name": "OFF", + "topic": "front", + "payloadType": "default", + "payload": "", + "expressionType": "cron", + "expression": "30 6 */7 * *", + "location": "", + "offset": "0", + "solarType": "all", + "solarEvents": "sunrise,sunset" + } + ], + "x": 240, + "y": 440, + "wires": [ + [ + "ab7182ad5c37be48" + ], + [ + "d51ae77e82bb86f0" + ], + [], + [] + ] + }, + { + "id": "ab7182ad5c37be48", + "type": "change", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "on", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "on", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 690, + "y": 420, + "wires": [ + [ + "905629455f4dd222" + ] + ] + }, + { + "id": "d51ae77e82bb86f0", + "type": "change", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "off", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "off", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 690, + "y": 460, + "wires": [ + [ + "905629455f4dd222" + ] + ] + }, + { + "id": "b43dc5183a67d128", + "type": "zigbee2mqtt-out", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "", + "server": "4e20fc347c658518", + "friendly_name": "Back Water Valve (SWV)", + "device_id": "0x7cc6b6fffe38a415", + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1350, + "y": 540, + "wires": [] + }, + { + "id": "2896d687417bbc1d", + "type": "cronplus", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "Citrus Trees every 2 days at 06:00", + "outputField": "payload", + "timeZone": "", + "storeName": "file", + "commandResponseMsgOutput": "fanOut", + "defaultLocation": "", + "defaultLocationType": "default", + "outputs": 4, + "options": [ + { + "name": "ON", + "topic": "front", + "payloadType": "default", + "payload": "", + "expressionType": "cron", + "expression": "0 6 */2 * *", + "location": "", + "offset": "0", + "solarType": "all", + "solarEvents": "sunrise,sunset" + }, + { + "name": "OFF", + "topic": "front", + "payloadType": "default", + "payload": "", + "expressionType": "cron", + "expression": "15 6 */2 * *", + "location": "", + "offset": "0", + "solarType": "all", + "solarEvents": "sunrise,sunset" + } + ], + "x": 240, + "y": 560, + "wires": [ + [ + "1199fd928d0abd32" + ], + [ + "86153b2dba01756f" + ], + [], + [] + ] + }, + { + "id": "1199fd928d0abd32", + "type": "change", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "on", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "on", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 690, + "y": 540, + "wires": [ + [ + "b43dc5183a67d128" + ] + ] + }, + { + "id": "86153b2dba01756f", + "type": "change", + "z": "6722d185624fd57a", + "g": "b73455843d7a5a9f", + "name": "off", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "off", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 690, + "y": 580, + "wires": [ + [ + "b43dc5183a67d128" + ] + ] + }, + { + "id": "301e955ac79a9326", + "type": "bigtimer", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "outtopic": "", + "outpayload1": "", + "outpayload2": "", + "name": "Big Timer", + "comment": "", + "lat": "34.660694", + "lon": "32.964306", + "starttime": "5004", + "endtime": "60", + "starttime2": "0", + "endtime2": "0", + "startoff": 0, + "endoff": 0, + "startoff2": 0, + "endoff2": 0, + "offs": 0, + "outtext1": "ON", + "outtext2": "OFF", + "timeout": 1440, + "sun": true, + "mon": true, + "tue": true, + "wed": true, + "thu": true, + "fri": true, + "sat": true, + "jan": true, + "feb": true, + "mar": true, + "apr": true, + "may": true, + "jun": true, + "jul": true, + "aug": true, + "sep": true, + "oct": true, + "nov": true, + "dec": true, + "day1": 0, + "month1": 0, + "day2": 0, + "month2": 0, + "day3": 0, + "month3": 0, + "day4": 0, + "month4": 0, + "day5": 0, + "month5": 0, + "day6": 0, + "month6": 0, + "day7": 0, + "month7": 0, + "day8": 0, + "month8": 0, + "day9": 0, + "month9": 0, + "day10": 0, + "month10": 0, + "day11": 0, + "month11": 0, + "day12": 0, + "month12": 0, + "d1": 0, + "w1": 0, + "d2": 0, + "w2": 0, + "d3": 0, + "w3": 0, + "d4": 0, + "w4": 0, + "d5": 0, + "w5": 0, + "d6": 0, + "w6": 0, + "xday1": 0, + "xmonth1": 0, + "xday2": 0, + "xmonth2": 0, + "xday3": 0, + "xmonth3": 0, + "xday4": 0, + "xmonth4": 0, + "xday5": 0, + "xmonth5": 0, + "xday6": 0, + "xmonth6": 0, + "xday7": 0, + "xmonth7": 0, + "xday8": 0, + "xmonth8": 0, + "xday9": 0, + "xmonth9": 0, + "xday10": 0, + "xmonth10": 0, + "xday11": 0, + "xmonth11": 0, + "xday12": 0, + "xmonth12": 0, + "xd1": 0, + "xw1": 0, + "xd2": 0, + "xw2": 0, + "xd3": 0, + "xw3": 0, + "xd4": 0, + "xw4": 0, + "xd5": 0, + "xw5": 0, + "xd6": 0, + "xw6": 0, + "suspend": false, + "random": false, + "randon1": false, + "randoff1": false, + "randon2": false, + "randoff2": false, + "repeat": true, + "atstart": true, + "odd": false, + "even": false, + "x": 400, + "y": 1080, + "wires": [ + [ + "7049d2e629127b0e", + "532476a808456423", + "434ef7e821c63223" + ], + [ + "0fdf497d70900d11" + ], + [ + "54161a78e09f85c3", + "7049d2e629127b0e", + "532476a808456423" + ] + ] + }, + { + "id": "54161a78e09f85c3", + "type": "debug", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "debug 14", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 560, + "y": 1140, + "wires": [] + }, + { + "id": "0fdf497d70900d11", + "type": "debug", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "debug 21", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 680, + "y": 980, + "wires": [] + }, + { + "id": "434ef7e821c63223", + "type": "debug", + "z": "6722d185624fd57a", + "g": "cc230a0894b116d3", + "name": "debug 22", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 450, + "y": 980, + "wires": [] + }, + { + "id": "d97b1b86e0e0040e", + "type": "zigbee2mqtt-get", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "Get Current Plug State", + "server": "4e20fc347c658518", + "friendly_name": "Living Room Plug (TS011F_plug_3)", + "device_id": "0xa4c138c205230a46", + "state": "state", + "enableMultiple": false, + "x": 1080, + "y": 1860, + "wires": [ + [ + "6fdd0a95dd76a2e8", + "be86177040153c9f" + ] + ] + }, + { + "id": "3737a8c897de2221", + "type": "function", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "Compare Desired vs Actual", + "func": "let input = msg.input || null;\n\nif (input === 1) {\n // BigTimer sets desired state\n // Validate state value before storing\n if (msg.payload === \"ON\" || msg.payload === \"OFF\") {\n flow.set(\"desired_state\", msg.payload);\n node.status({fill:\"blue\", shape:\"dot\", text:`Desired: ${msg.payload}`});\n node.warn(`[BigTimer] Desired state set to: ${msg.payload}`);\n } else {\n node.error(`[BigTimer] Invalid state value: ${msg.payload}`);\n node.status({fill:\"red\", shape:\"ring\", text:\"Invalid state\"});\n }\n return null;\n}\n\nif (input === 2) {\n // Zigbee GET input - check and sync state\n let desired = flow.get(\"desired_state\");\n let current = msg.state || \n msg.payload || \n msg.payload?.state || \n msg.data?.state ||\n null;\n\n // Log current status\n node.warn(`[Zigbee Check] Desired: ${desired}, Current: ${current}`);\n\n // Check if states are available\n if (!desired) {\n node.warn(\"[Zigbee Check] No desired state set yet\");\n node.status({fill:\"yellow\", shape:\"ring\", text:\"No desired state\"});\n return null;\n }\n\n if (!current) {\n node.error(\"[Zigbee Check] Current state is undefined\");\n node.status({fill:\"red\", shape:\"ring\", text:\"No current state\"});\n return null;\n }\n\n // Check if sync is needed\n if (desired === \"ON\" && current !== \"ON\") {\n node.warn(`[Zigbee Check] State mismatch - sending ON command`);\n node.status({fill:\"green\", shape:\"dot\", text:\"Syncing → ON\"});\n return { payload: \"ON\" };\n }\n \n if (desired === \"OFF\" && current !== \"OFF\") {\n node.warn(`[Zigbee Check] State mismatch - sending OFF command`);\n node.status({fill:\"green\", shape:\"dot\", text:\"Syncing → OFF\"});\n return { payload: \"OFF\" };\n }\n\n // States match - no action needed\n node.status({fill:\"green\", shape:\"ring\", text:`Synced: ${current}`});\n node.warn(`[Zigbee Check] States match - no action needed`);\n return null;\n}\n\n// Invalid input\nnode.error(`Invalid input value: ${input}`);\nnode.status({fill:\"red\", shape:\"ring\", text:\"Invalid input\"});\nreturn null;\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1580, + "y": 1880, + "wires": [ + [ + "ca74d224370a0b5a", + "893d9228d166c0d2" + ] + ] + }, + { + "id": "ca74d224370a0b5a", + "type": "zigbee2mqtt-out", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "Set Plug State", + "server": "4e20fc347c658518", + "friendly_name": "Living Room Plug (TS011F_plug_3)", + "device_id": "0xa4c138c205230a46", + "command": "state", + "commandType": "z2m_cmd", + "payload": "payload", + "payloadType": "msg", + "optionsValue": "", + "optionsType": "nothing", + "x": 1820, + "y": 1880, + "wires": [] + }, + { + "id": "3a8262144b91d661", + "type": "inject", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "OFF", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "OFF", + "payloadType": "str", + "x": 750, + "y": 1960, + "wires": [ + [ + "01df5278eead8a98" + ] + ] + }, + { + "id": "d20a28e1dea2fcde", + "type": "inject", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "ON", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "ON", + "payloadType": "str", + "x": 750, + "y": 1840, + "wires": [ + [ + "01df5278eead8a98" + ] + ] + }, + { + "id": "01df5278eead8a98", + "type": "bigtimer", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "outtopic": "", + "outpayload1": "", + "outpayload2": "", + "name": "Big Timer", + "comment": "", + "lat": "34.660694", + "lon": "32.964306", + "starttime": "960", + "endtime": "60", + "starttime2": "0", + "endtime2": "0", + "startoff": 0, + "endoff": 0, + "startoff2": 0, + "endoff2": 0, + "offs": 0, + "outtext1": "ON", + "outtext2": "OFF", + "timeout": 1440, + "sun": true, + "mon": true, + "tue": true, + "wed": true, + "thu": true, + "fri": true, + "sat": true, + "jan": true, + "feb": true, + "mar": true, + "apr": true, + "may": true, + "jun": true, + "jul": true, + "aug": true, + "sep": true, + "oct": true, + "nov": true, + "dec": true, + "day1": 0, + "month1": 0, + "day2": 0, + "month2": 0, + "day3": 0, + "month3": 0, + "day4": 0, + "month4": 0, + "day5": 0, + "month5": 0, + "day6": 0, + "month6": 0, + "day7": 0, + "month7": 0, + "day8": 0, + "month8": 0, + "day9": 0, + "month9": 0, + "day10": 0, + "month10": 0, + "day11": 0, + "month11": 0, + "day12": 0, + "month12": 0, + "d1": 0, + "w1": 0, + "d2": 0, + "w2": 0, + "d3": 0, + "w3": 0, + "d4": 0, + "w4": 0, + "d5": 0, + "w5": 0, + "d6": 0, + "w6": 0, + "xday1": 0, + "xmonth1": 0, + "xday2": 0, + "xmonth2": 0, + "xday3": 0, + "xmonth3": 0, + "xday4": 0, + "xmonth4": 0, + "xday5": 0, + "xmonth5": 0, + "xday6": 0, + "xmonth6": 0, + "xday7": 0, + "xmonth7": 0, + "xday8": 0, + "xmonth8": 0, + "xday9": 0, + "xmonth9": 0, + "xday10": 0, + "xmonth10": 0, + "xday11": 0, + "xmonth11": 0, + "xday12": 0, + "xmonth12": 0, + "xd1": 0, + "xw1": 0, + "xd2": 0, + "xw2": 0, + "xd3": 0, + "xw3": 0, + "xd4": 0, + "xw4": 0, + "xd5": 0, + "xw5": 0, + "xd6": 0, + "xw6": 0, + "suspend": false, + "random": false, + "randon1": false, + "randoff1": false, + "randon2": false, + "randoff2": false, + "repeat": true, + "atstart": true, + "odd": false, + "even": false, + "x": 880, + "y": 1900, + "wires": [ + [], + [], + [ + "d97b1b86e0e0040e", + "1460ed0d062f8b73" + ] + ] + }, + { + "id": "1460ed0d062f8b73", + "type": "change", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "Tag BigTimer Input", + "rules": [ + { + "t": "set", + "p": "input", + "pt": "msg", + "to": "1", + "tot": "num" + } + ], + "x": 1310, + "y": 1900, + "wires": [ + [ + "3737a8c897de2221", + "bb1639973b4a6a05" + ] + ] + }, + { + "id": "6fdd0a95dd76a2e8", + "type": "change", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "Tag Zigbee Input", + "rules": [ + { + "t": "set", + "p": "input", + "pt": "msg", + "to": "2", + "tot": "num" + } + ], + "x": 1310, + "y": 1860, + "wires": [ + [ + "3737a8c897de2221", + "800cb2f6a21c882f" + ] + ] + }, + { + "id": "bb1639973b4a6a05", + "type": "debug", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "debug 23", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1560, + "y": 2020, + "wires": [] + }, + { + "id": "800cb2f6a21c882f", + "type": "debug", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "debug 24", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1540, + "y": 1960, + "wires": [] + }, + { + "id": "893d9228d166c0d2", + "type": "debug", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "debug 25", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1750, + "y": 1980, + "wires": [] + }, + { + "id": "be86177040153c9f", + "type": "debug", + "z": "6722d185624fd57a", + "g": "860e99939cf4fb5c", + "name": "debug 26", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1230, + "y": 1720, + "wires": [] + }, + { + "id": "6fb9cc41c7b14fac", + "type": "link out", + "z": "31693504fcbbd954", + "name": "link out 23", + "mode": "link", + "links": [ + "c6162cb59e21491f" + ], + "x": 2105, + "y": 880, + "wires": [] + }, + { + "id": "func_node", + "type": "function", + "z": "31693504fcbbd954", + "name": "Build JSON true/false", + "func": "let input = msg.payload;\nlet parts = input.split(\".\");\nif (parts.length !== 2) {\n node.error(\"Invalid input format. Expected room.switch\", msg);\n return null;\n}\n\nlet room = parts[0];\nlet sw = parts[1];\n\nlet onMsg = { payload: { [room]: { [sw]: true } } };\nnode.send(onMsg);\n\nlet offMsg = { payload: { [room]: { [sw]: false } } };\nsetTimeout(() => { node.send(offMsg); }, 1000);\n\nreturn null;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1920, + "y": 880, + "wires": [ + [ + "debug_node", + "6fb9cc41c7b14fac" + ] + ] + }, + { + "id": "debug_node", + "type": "debug", + "z": "31693504fcbbd954", + "name": "Output", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "x": 2110, + "y": 920, + "wires": [] + }, + { + "id": "btn_masterBedroom_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom all_off", + "group": "group_masterBedroom", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "masterBedroom.all_off", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBedroom_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom all_on", + "group": "group_masterBedroom", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "masterBedroom.all_on", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBedroom_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom sw_1", + "group": "group_masterBedroom", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "masterBedroom.sw_1", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBedroom_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom sw_2", + "group": "group_masterBedroom", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "masterBedroom.sw_2", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBedroom_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom sw_3", + "group": "group_masterBedroom", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "masterBedroom.sw_3", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBedroom_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom sw_4", + "group": "group_masterBedroom", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "masterBedroom.sw_4", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBedroom_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom sw_5", + "group": "group_masterBedroom", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "masterBedroom.sw_5", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBedroom_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBedroom sw_6", + "group": "group_masterBedroom", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "masterBedroom.sw_6", + "payloadType": "str", + "topic": "", + "x": 140, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom all_off", + "group": "group_masterBathroom", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "masterBathroom.all_off", + "payloadType": "str", + "topic": "", + "x": 390, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom all_on", + "group": "group_masterBathroom", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "masterBathroom.all_on", + "payloadType": "str", + "topic": "", + "x": 390, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom sw_1", + "group": "group_masterBathroom", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "masterBathroom.sw_1", + "payloadType": "str", + "topic": "", + "x": 380, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom sw_2", + "group": "group_masterBathroom", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "masterBathroom.sw_2", + "payloadType": "str", + "topic": "", + "x": 380, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom sw_3", + "group": "group_masterBathroom", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "masterBathroom.sw_3", + "payloadType": "str", + "topic": "", + "x": 380, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom sw_4", + "group": "group_masterBathroom", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "masterBathroom.sw_4", + "payloadType": "str", + "topic": "", + "x": 380, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom sw_5", + "group": "group_masterBathroom", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "masterBathroom.sw_5", + "payloadType": "str", + "topic": "", + "x": 380, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_masterBathroom_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "masterBathroom sw_6", + "group": "group_masterBathroom", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "masterBathroom.sw_6", + "payloadType": "str", + "topic": "", + "x": 380, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 all_off", + "group": "group_bedroom_1", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "bedroom_1.all_off", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 all_on", + "group": "group_bedroom_1", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "bedroom_1.all_on", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 sw_1", + "group": "group_bedroom_1", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "bedroom_1.sw_1", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 sw_2", + "group": "group_bedroom_1", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "bedroom_1.sw_2", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 sw_3", + "group": "group_bedroom_1", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "bedroom_1.sw_3", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 sw_4", + "group": "group_bedroom_1", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "bedroom_1.sw_4", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 sw_5", + "group": "group_bedroom_1", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "bedroom_1.sw_5", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_1_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_1 sw_6", + "group": "group_bedroom_1", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "bedroom_1.sw_6", + "payloadType": "str", + "topic": "", + "x": 630, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 all_off", + "group": "group_bedroom_2", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "bedroom_2.all_off", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 all_on", + "group": "group_bedroom_2", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "bedroom_2.all_on", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 sw_1", + "group": "group_bedroom_2", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "bedroom_2.sw_1", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 sw_2", + "group": "group_bedroom_2", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "bedroom_2.sw_2", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 sw_3", + "group": "group_bedroom_2", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "bedroom_2.sw_3", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 sw_4", + "group": "group_bedroom_2", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "bedroom_2.sw_4", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 sw_5", + "group": "group_bedroom_2", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "bedroom_2.sw_5", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bedroom_2_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bedroom_2 sw_6", + "group": "group_bedroom_2", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "bedroom_2.sw_6", + "payloadType": "str", + "topic": "", + "x": 1050, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom all_off", + "group": "group_bathroom", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "bathroom.all_off", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom all_on", + "group": "group_bathroom", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "bathroom.all_on", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom sw_1", + "group": "group_bathroom", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "bathroom.sw_1", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom sw_2", + "group": "group_bathroom", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "bathroom.sw_2", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom sw_3", + "group": "group_bathroom", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "bathroom.sw_3", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom sw_4", + "group": "group_bathroom", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "bathroom.sw_4", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom sw_5", + "group": "group_bathroom", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "bathroom.sw_5", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_bathroom_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "bathroom sw_6", + "group": "group_bathroom", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "bathroom.sw_6", + "payloadType": "str", + "topic": "", + "x": 1680, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway all_off", + "group": "group_hallway", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "hallway.all_off", + "payloadType": "str", + "topic": "", + "x": 840, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway all_on", + "group": "group_hallway", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "hallway.all_on", + "payloadType": "str", + "topic": "", + "x": 840, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway sw_1", + "group": "group_hallway", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "hallway.sw_1", + "payloadType": "str", + "topic": "", + "x": 830, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway sw_2", + "group": "group_hallway", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "hallway.sw_2", + "payloadType": "str", + "topic": "", + "x": 830, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway sw_3", + "group": "group_hallway", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "hallway.sw_3", + "payloadType": "str", + "topic": "", + "x": 830, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway sw_4", + "group": "group_hallway", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "hallway.sw_4", + "payloadType": "str", + "topic": "", + "x": 830, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway sw_5", + "group": "group_hallway", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "hallway.sw_5", + "payloadType": "str", + "topic": "", + "x": 830, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_hallway_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "hallway sw_6", + "group": "group_hallway", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "hallway.sw_6", + "payloadType": "str", + "topic": "", + "x": 830, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry all_off", + "group": "group_pantry", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "pantry.all_off", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry all_on", + "group": "group_pantry", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "pantry.all_on", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry sw_1", + "group": "group_pantry", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "pantry.sw_1", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry sw_2", + "group": "group_pantry", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "pantry.sw_2", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry sw_3", + "group": "group_pantry", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "pantry.sw_3", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry sw_4", + "group": "group_pantry", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "pantry.sw_4", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry sw_5", + "group": "group_pantry", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "pantry.sw_5", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_pantry_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "pantry sw_6", + "group": "group_pantry", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "pantry.sw_6", + "payloadType": "str", + "topic": "", + "x": 1470, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen all_off", + "group": "group_kitchen", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "kitchen.all_off", + "payloadType": "str", + "topic": "", + "x": 1580, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen all_on", + "group": "group_kitchen", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "kitchen.all_on", + "payloadType": "str", + "topic": "", + "x": 1580, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen sw_1", + "group": "group_kitchen", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "kitchen.sw_1", + "payloadType": "str", + "topic": "", + "x": 1570, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen sw_2", + "group": "group_kitchen", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "kitchen.sw_2", + "payloadType": "str", + "topic": "", + "x": 1570, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen sw_3", + "group": "group_kitchen", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "kitchen.sw_3", + "payloadType": "str", + "topic": "", + "x": 1570, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen sw_4", + "group": "group_kitchen", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "kitchen.sw_4", + "payloadType": "str", + "topic": "", + "x": 1570, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen sw_5", + "group": "group_kitchen", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "kitchen.sw_5", + "payloadType": "str", + "topic": "", + "x": 1570, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_kitchen_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "kitchen sw_6", + "group": "group_kitchen", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "kitchen.sw_6", + "payloadType": "str", + "topic": "", + "x": 1570, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom all_off", + "group": "group_livingRoom", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "livingRoom.all_off", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom all_on", + "group": "group_livingRoom", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "livingRoom.all_on", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom sw_1", + "group": "group_livingRoom", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "livingRoom.sw_1", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom sw_2", + "group": "group_livingRoom", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "livingRoom.sw_2", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom sw_3", + "group": "group_livingRoom", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "livingRoom.sw_3", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom sw_4", + "group": "group_livingRoom", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "livingRoom.sw_4", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom sw_5", + "group": "group_livingRoom", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "livingRoom.sw_5", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_livingRoom_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "livingRoom sw_6", + "group": "group_livingRoom", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "livingRoom.sw_6", + "payloadType": "str", + "topic": "", + "x": 1350, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom all_off", + "group": "group_dinningRoom", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "dinningRoom.all_off", + "payloadType": "str", + "topic": "", + "x": 1150, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom all_on", + "group": "group_dinningRoom", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "dinningRoom.all_on", + "payloadType": "str", + "topic": "", + "x": 1140, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom sw_1", + "group": "group_dinningRoom", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "dinningRoom.sw_1", + "payloadType": "str", + "topic": "", + "x": 1150, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom sw_2", + "group": "group_dinningRoom", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "dinningRoom.sw_2", + "payloadType": "str", + "topic": "", + "x": 1150, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom sw_3", + "group": "group_dinningRoom", + "order": 5, + "width": 0, + "height": 0, + "passthru": false, + "label": "sw_3", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "dinningRoom.sw_3", + "payloadType": "str", + "topic": "", + "topicType": "str", + "x": 1150, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom sw_4", + "group": "group_dinningRoom", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "dinningRoom.sw_4", + "payloadType": "str", + "topic": "", + "x": 1150, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom sw_5", + "group": "group_dinningRoom", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "dinningRoom.sw_5", + "payloadType": "str", + "topic": "", + "x": 1150, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_dinningRoom_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "dinningRoom sw_6", + "group": "group_dinningRoom", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "dinningRoom.sw_6", + "payloadType": "str", + "topic": "", + "x": 1150, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance all_off", + "group": "group_entrance", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "entrance.all_off", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 940, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance all_on", + "group": "group_entrance", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "entrance.all_on", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 980, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance sw_1", + "group": "group_entrance", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "entrance.sw_1", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 1020, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance sw_2", + "group": "group_entrance", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "entrance.sw_2", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 1060, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance sw_3", + "group": "group_entrance", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "entrance.sw_3", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 1100, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance sw_4", + "group": "group_entrance", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "entrance.sw_4", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 1140, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance sw_5", + "group": "group_entrance", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "entrance.sw_5", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 1180, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_entrance_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "entrance sw_6", + "group": "group_entrance", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "entrance.sw_6", + "payloadType": "str", + "topic": "", + "x": 1300, + "y": 1220, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc all_off", + "group": "group_guestWc", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "guestWc.all_off", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc all_on", + "group": "group_guestWc", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "guestWc.all_on", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc sw_1", + "group": "group_guestWc", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "guestWc.sw_1", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc sw_2", + "group": "group_guestWc", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "guestWc.sw_2", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc sw_3", + "group": "group_guestWc", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "guestWc.sw_3", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc sw_4", + "group": "group_guestWc", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "guestWc.sw_4", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc sw_5", + "group": "group_guestWc", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "guestWc.sw_5", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_guestWc_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "guestWc sw_6", + "group": "group_guestWc", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "guestWc.sw_6", + "payloadType": "str", + "topic": "", + "x": 940, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda all_off", + "group": "group_outVeranda", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "outVeranda.all_off", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda all_on", + "group": "group_outVeranda", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "outVeranda.all_on", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda sw_1", + "group": "group_outVeranda", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "outVeranda.sw_1", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda sw_2", + "group": "group_outVeranda", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "outVeranda.sw_2", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda sw_3", + "group": "group_outVeranda", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "outVeranda.sw_3", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda sw_4", + "group": "group_outVeranda", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "outVeranda.sw_4", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda sw_5", + "group": "group_outVeranda", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "outVeranda.sw_5", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outVeranda_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outVeranda sw_6", + "group": "group_outVeranda", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "outVeranda.sw_6", + "payloadType": "str", + "topic": "", + "x": 730, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront all_off", + "group": "group_outFront", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "outFront.all_off", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront all_on", + "group": "group_outFront", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "outFront.all_on", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront sw_1", + "group": "group_outFront", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "outFront.sw_1", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront sw_2", + "group": "group_outFront", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "outFront.sw_2", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront sw_3", + "group": "group_outFront", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "outFront.sw_3", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront sw_4", + "group": "group_outFront", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "outFront.sw_4", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront sw_5", + "group": "group_outFront", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "outFront.sw_5", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outFront_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outFront sw_6", + "group": "group_outFront", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "outFront.sw_6", + "payloadType": "str", + "topic": "", + "x": 520, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide all_off", + "group": "group_outSide", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "outSide.all_off", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide all_on", + "group": "group_outSide", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "outSide.all_on", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide sw_1", + "group": "group_outSide", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "outSide.sw_1", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide sw_2", + "group": "group_outSide", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "outSide.sw_2", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide sw_3", + "group": "group_outSide", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "outSide.sw_3", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide sw_4", + "group": "group_outSide", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "outSide.sw_4", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide sw_5", + "group": "group_outSide", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "outSide.sw_5", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outSide_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outSide sw_6", + "group": "group_outSide", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "outSide.sw_6", + "payloadType": "str", + "topic": "", + "x": 340, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_all_off", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack all_off", + "group": "group_outBack", + "order": 1, + "width": 0, + "height": 0, + "label": "all_off", + "payload": "outBack.all_off", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 580, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_all_on", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack all_on", + "group": "group_outBack", + "order": 2, + "width": 0, + "height": 0, + "label": "all_on", + "payload": "outBack.all_on", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 620, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_sw_1", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack sw_1", + "group": "group_outBack", + "order": 3, + "width": 0, + "height": 0, + "label": "sw_1", + "payload": "outBack.sw_1", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 660, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_sw_2", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack sw_2", + "group": "group_outBack", + "order": 4, + "width": 0, + "height": 0, + "label": "sw_2", + "payload": "outBack.sw_2", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 700, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_sw_3", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack sw_3", + "group": "group_outBack", + "order": 5, + "width": 0, + "height": 0, + "label": "sw_3", + "payload": "outBack.sw_3", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 740, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_sw_4", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack sw_4", + "group": "group_outBack", + "order": 6, + "width": 0, + "height": 0, + "label": "sw_4", + "payload": "outBack.sw_4", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 780, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_sw_5", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack sw_5", + "group": "group_outBack", + "order": 7, + "width": 0, + "height": 0, + "label": "sw_5", + "payload": "outBack.sw_5", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 820, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "btn_outBack_sw_6", + "type": "ui_button", + "z": "31693504fcbbd954", + "name": "outBack sw_6", + "group": "group_outBack", + "order": 8, + "width": 0, + "height": 0, + "label": "sw_6", + "payload": "outBack.sw_6", + "payloadType": "str", + "topic": "", + "x": 120, + "y": 860, + "wires": [ + [ + "func_node" + ] + ] + }, + { + "id": "timer30min_btn", + "type": "ha-button", + "z": "50971c50479dfd7a", + "name": "Timer 30 Min", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "timer30min_config", + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "30", + "valueType": "num" + } + ], + "x": 630, + "y": 680, + "wires": [ + [ + "260bf6fdd821f1b9" + ] + ] + }, + { + "id": "timer1hour_btn", + "type": "ha-button", + "z": "50971c50479dfd7a", + "name": "Timer 1 Hour", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "timer1hour_config", + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "60", + "valueType": "num" + } + ], + "x": 790, + "y": 680, + "wires": [ + [ + "260bf6fdd821f1b9" + ] + ] + }, + { + "id": "timer2hours_btn", + "type": "ha-button", + "z": "50971c50479dfd7a", + "name": "Timer 2 Hours", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "timer2hours_config", + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "120", + "valueType": "num" + } + ], + "x": 990, + "y": 680, + "wires": [ + [ + "260bf6fdd821f1b9" + ] + ] + }, + { + "id": "timerstop_btn", + "type": "ha-button", + "z": "50971c50479dfd7a", + "name": "Stop Timer", + "version": 0, + "debugenabled": false, + "outputs": 1, + "entityConfig": "timerstop_config", + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "stop", + "valueType": "str" + } + ], + "x": 980, + "y": 740, + "wires": [ + [ + "a57a98c852762ebf" + ] + ] + }, + { + "id": "water_boiler_timer_handler", + "type": "function", + "z": "50971c50479dfd7a", + "name": "Water Boiler Timer Handler", + "func": "// Helper function to format remaining time\nfunction formatTime(remainingMs) {\n if (remainingMs <= 0) return '0m 0s';\n const totalSeconds = Math.floor(remainingMs / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n const hours = Math.floor(minutes / 60);\n const mins = minutes % 60;\n \n let timeStr = '';\n if (hours > 0) timeStr += hours + 'h ';\n if (mins > 0 || hours === 0) timeStr += mins + 'm ';\n timeStr += seconds + 's';\n return timeStr.trim();\n}\n\n// Check if this is a timer command (numeric payload or \"stop\")\nconst payload = msg.payload;\nconst isTimerCommand = typeof payload === 'number' || payload === 'stop';\n\nif (isTimerCommand) {\n // Cancel any existing timer and interval\n const existingTimer = flow.get('water_boiler_timer');\n if (existingTimer) {\n clearTimeout(existingTimer);\n flow.set('water_boiler_timer', null);\n }\n \n const existingInterval = flow.get('water_boiler_timer_interval');\n if (existingInterval) {\n clearInterval(existingInterval);\n flow.set('water_boiler_timer_interval', null);\n }\n \n if (payload === 'stop') {\n // Stop command - turn off water boiler immediately\n const msgFalse = {\n topic: \"bathroom\",\n payload: \"sw_6\",\n value: false\n };\n \n // Send 0 remaining time\n const msgTime = {\n payload: '0m 0s'\n };\n \n node.status({fill:\"yellow\", shape:\"dot\", text:\"Timer stopped\"});\n return [msgFalse, null, msgTime];\n } else {\n // Timer duration in minutes - convert to milliseconds\n const durationMs = payload * 60 * 1000;\n const startTime = Date.now();\n \n // Store start time and duration\n flow.set('water_boiler_timer_start', startTime);\n flow.set('water_boiler_timer_duration', durationMs);\n \n // Turn ON water boiler first\n const msgTrue = {\n topic: \"bathroom\",\n payload: \"sw_6\",\n value: true\n };\n \n // Function to send remaining time\n const sendRemainingTime = function() {\n const elapsed = Date.now() - startTime;\n const remaining = Math.max(0, durationMs - elapsed);\n const timeStr = formatTime(remaining);\n \n const msgTime = {\n payload: timeStr\n };\n node.send([null, null, msgTime]);\n \n // Update status\n node.status({fill:\"blue\", shape:\"dot\", text:`Timer: ${timeStr}`});\n \n // Clear interval if timer expired\n if (remaining <= 0) {\n const intervalId = flow.get('water_boiler_timer_interval');\n if (intervalId) {\n clearInterval(intervalId);\n flow.set('water_boiler_timer_interval', null);\n }\n }\n };\n \n // Send initial remaining time\n sendRemainingTime();\n \n // Set up interval to update remaining time every second (live refresh)\n const intervalId = setInterval(sendRemainingTime, 1000);\n flow.set('water_boiler_timer_interval', intervalId);\n \n // Set timer to turn OFF after duration\n const timerId = setTimeout(function() {\n const msgOff = {\n topic: \"bathroom\",\n payload: \"sw_6\",\n value: false\n };\n \n // Send final time (0m 0s)\n const msgTime = {\n payload: '0m 0s'\n };\n \n node.send([msgOff, null, msgTime]);\n flow.set('water_boiler_timer', null);\n flow.set('water_boiler_timer_start', null);\n flow.set('water_boiler_timer_duration', null);\n \n // Clear interval\n const intervalId = flow.get('water_boiler_timer_interval');\n if (intervalId) {\n clearInterval(intervalId);\n flow.set('water_boiler_timer_interval', null);\n }\n \n node.status({fill:\"green\", shape:\"ring\", text:\"Timer completed\"});\n }, durationMs);\n \n // Store timer ID\n flow.set('water_boiler_timer', timerId);\n \n const minutes = payload;\n const hours = Math.floor(minutes / 60);\n const mins = minutes % 60;\n let timeStr = '';\n if (hours > 0) timeStr += hours + 'h ';\n if (mins > 0 || hours === 0) timeStr += mins + 'm';\n \n // Return ON command on output 1, nothing on output 2, initial time on output 3\n const msgTime = {\n payload: formatTime(durationMs)\n };\n return [msgTrue, null, msgTime];\n }\n} else {\n // Regular button press - pass through to existing function on output 2\n return [null, msg, null];\n}\n\n", + "outputs": 3, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1620, + "y": 680, + "wires": [ + [ + "1d0a46b9d5255560" + ], + [], + [ + "timer_remaining_sensor" + ] + ] + }, + { + "id": "1d0a46b9d5255560", + "type": "link out", + "z": "50971c50479dfd7a", + "name": "link out 24", + "mode": "link", + "links": [ + "0bbcb269da9b75c7" + ], + "x": 1845, + "y": 560, + "wires": [] + }, + { + "id": "timer_remaining_sensor", + "type": "ha-sensor", + "z": "50971c50479dfd7a", + "name": "Timer Remaining", + "entityConfig": "timer_remaining_config", + "version": 1, + "state": "payload", + "stateType": "msg", + "attributes": [], + "x": 1890, + "y": 620, + "wires": [ + [] + ] + }, + { + "id": "a57a98c852762ebf", + "type": "api-current-state", + "z": "50971c50479dfd7a", + "name": "", + "server": "29eb84a2708d96e0", + "version": 3, + "outputs": 2, + "halt_if": "on", + "halt_if_type": "str", + "halt_if_compare": "is", + "entity_id": "binary_sensor.water_boiler", + "state_type": "str", + "blockInputOverrides": false, + "outputProperties": [], + "for": "0", + "forType": "num", + "forUnits": "minutes", + "override_topic": false, + "state_location": "payload", + "override_payload": "msg", + "entity_location": "data", + "override_data": "msg", + "x": 1280, + "y": 740, + "wires": [ + [ + "water_boiler_timer_handler" + ], + [] + ] + }, + { + "id": "260bf6fdd821f1b9", + "type": "api-current-state", + "z": "50971c50479dfd7a", + "name": "", + "server": "29eb84a2708d96e0", + "version": 3, + "outputs": 2, + "halt_if": "off", + "halt_if_type": "str", + "halt_if_compare": "is", + "entity_id": "binary_sensor.water_boiler", + "state_type": "str", + "blockInputOverrides": false, + "outputProperties": [], + "for": "0", + "forType": "num", + "forUnits": "minutes", + "override_topic": false, + "state_location": "payload", + "override_payload": "msg", + "entity_location": "data", + "override_data": "msg", + "x": 1280, + "y": 680, + "wires": [ + [ + "water_boiler_timer_handler" + ], + [] + ] + }, + { + "id": "0ad5d7e5c99c2675", + "type": "poll-state", + "z": "50971c50479dfd7a", + "name": "", + "server": "29eb84a2708d96e0", + "version": 3, + "exposeAsEntityConfig": "", + "updateInterval": "30", + "updateIntervalType": "num", + "updateIntervalUnits": "seconds", + "outputInitially": true, + "outputOnChanged": true, + "entityId": "binary_sensor.water_boiler", + "stateType": "str", + "ifState": "on", + "ifStateType": "str", + "ifStateOperator": "is", + "outputs": 2, + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "entityState" + }, + { + "property": "data", + "propertyType": "msg", + "value": "", + "valueType": "entity" + }, + { + "property": "topic", + "propertyType": "msg", + "value": "", + "valueType": "triggerId" + } + ], + "x": 1070, + "y": 600, + "wires": [ + [ + "2472daff4bc82fb2", + "f12fa3448a1b5872" + ], + [] + ] + }, + { + "id": "2472daff4bc82fb2", + "type": "function", + "z": "50971c50479dfd7a", + "name": "Safety Check Function", + "func": "// Safety Check Function\n// This function checks the water boiler state on Node-RED startup ONLY\n// It's connected to the poll-state node which outputs initially when Node-RED starts\n// If the boiler is ON, it turns it off and clears timer state\n// Uses a flag to ensure it only runs once on startup, not on every poll\n\n// Check if safety check has already been performed (only runs once on startup)\nconst safetyCheckDone = flow.get('water_boiler_safety_check_done');\nif (safetyCheckDone === true) {\n // Safety check already performed, ignore this message (from periodic polls)\n // Return nothing to both outputs\n return [null, null];\n}\n\n// Mark safety check as done (so it won't run again until Node-RED restarts)\nflow.set('water_boiler_safety_check_done', true);\n\n// Get the current state from the poll-state node\nconst currentState = msg.payload; // 'on' or 'off'\n\n// Clear any timer state (safety measure on restart)\nflow.set('water_boiler_timer', null);\nflow.set('water_boiler_timer_interval', null);\nflow.set('water_boiler_timer_start', null);\nflow.set('water_boiler_timer_duration', null);\n\n// Only turn off if the boiler is currently ON\nif (currentState === 'on') {\n // Boiler is ON - turn it off for safety\n const msgOff = {\n topic: \"bathroom\",\n payload: \"sw_6\",\n value: false\n };\n \n const msgTime = {\n payload: '0m 0s'\n };\n \n node.status({fill:\"red\", shape:\"ring\", text:\"Safety: Boiler was ON, turned off\"});\n return [msgOff, msgTime];\n} else {\n // Boiler is already off, just reset timer display\n const msgTime = {\n payload: '0m 0s'\n };\n \n node.status({fill:\"green\", shape:\"ring\", text:\"Safety: Boiler already off\"});\n return [null, msgTime];\n}\n\n", + "outputs": 2, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1600, + "y": 620, + "wires": [ + [ + "1d0a46b9d5255560" + ], + [ + "timer_remaining_sensor" + ] + ] + }, + { + "id": "f96bf3ff41b2e8f7", + "type": "server-state-changed", + "z": "50971c50479dfd7a", + "d": true, + "name": "Monitor Boiler (3h Safety)", + "server": "29eb84a2708d96e0", + "version": 6, + "outputs": 1, + "exposeAsEntityConfig": "", + "entities": { + "entity": [ + "binary_sensor.water_boiler" + ], + "substring": [], + "regex": [] + }, + "outputInitially": true, + "stateType": "str", + "ifState": "", + "ifStateType": "str", + "ifStateOperator": "is", + "outputOnlyOnStateChange": true, + "for": "0", + "forType": "num", + "forUnits": "minutes", + "ignorePrevStateNull": true, + "ignorePrevStateUnknown": true, + "ignorePrevStateUnavailable": false, + "ignoreCurrentStateUnknown": false, + "ignoreCurrentStateUnavailable": false, + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "entityState" + }, + { + "property": "data", + "propertyType": "msg", + "value": "", + "valueType": "eventData" + }, + { + "property": "topic", + "propertyType": "msg", + "value": "", + "valueType": "triggerId" + } + ], + "x": 1030, + "y": 560, + "wires": [ + [ + "3hour_safety_function_events" + ] + ] + }, + { + "id": "3hour_safety_function_events", + "type": "function", + "z": "50971c50479dfd7a", + "d": true, + "name": "3-Hour Safety Timer", + "func": "// Water Boiler 3-Hour Safety Timer\n// Uses server-state-changed node which provides old_state and new_state in event data\n// Event-driven (real-time) - no polling needed!\n\n// Get state information from server-state-changed node\n// msg.data contains the full event object with old_state and new_state\nconst eventData = msg.data;\nconst newState = eventData.new_state ? eventData.new_state.state : msg.payload;\nconst oldState = eventData.old_state ? eventData.old_state.state : 'unknown';\n\n// Get existing safety timer if any\nconst safetyTimer = flow.get('water_boiler_3hour_timer');\n\n// Check if state changed from off/unknown to on\nif (newState === 'on' && oldState !== 'on') {\n // Boiler just turned ON - start 3-hour safety timer\n \n // Cancel any existing timer (safety measure)\n if (safetyTimer) {\n clearTimeout(safetyTimer);\n flow.set('water_boiler_3hour_timer', null);\n }\n \n // Start 3-hour timer (3 hours = 3 * 60 * 60 * 1000 milliseconds)\n const threeHours = 3 * 60 * 60 * 1000;\n const timerId = setTimeout(function() {\n // 3 hours elapsed - turn off boiler for safety\n const msgOff = {\n topic: \"bathroom\",\n payload: \"sw_6\",\n value: false\n };\n \n // Clear timer state\n flow.set('water_boiler_3hour_timer', null);\n flow.set('water_boiler_3hour_start_time', null);\n \n // Send turn-off command\n node.send(msgOff);\n node.status({fill:\"red\", shape:\"ring\", text:\"Safety: 3h limit reached, turned off\"});\n node.warn(\"Water boiler has been ON for 3 hours - automatically turned off for safety\");\n }, threeHours);\n \n // Store timer ID and start time\n flow.set('water_boiler_3hour_timer', timerId);\n flow.set('water_boiler_3hour_start_time', Date.now());\n \n // Update status\n node.status({fill:\"yellow\", shape:\"dot\", text:\"3h safety timer: 3h remaining\"});\n node.log(\"Water boiler turned ON - 3-hour safety timer started\");\n \n // Don't send any message (just monitoring)\n return null;\n \n} else if (newState === 'off' && oldState === 'on') {\n // Boiler just turned OFF - cancel safety timer\n \n if (safetyTimer) {\n clearTimeout(safetyTimer);\n flow.set('water_boiler_3hour_timer', null);\n \n const startTime = flow.get('water_boiler_3hour_start_time');\n if (startTime) {\n const runtime = Math.floor((Date.now() - startTime) / 1000 / 60); // minutes\n node.log(`Water boiler turned OFF - 3-hour safety timer cancelled (was on for ${runtime} minutes)`);\n }\n \n flow.set('water_boiler_3hour_start_time', null);\n node.status({fill:\"green\", shape:\"ring\", text:\"3h safety timer cancelled\"});\n } else {\n node.status({fill:\"grey\", shape:\"ring\", text:\"Boiler off\"});\n }\n \n // Don't send any message\n return null;\n \n} else {\n // Other state changes (on->on, off->off, initial state, etc.) - ignore\n return null;\n}", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1600, + "y": 560, + "wires": [ + [ + "1d0a46b9d5255560" + ] + ] + }, + { + "id": "f12fa3448a1b5872", + "type": "debug", + "z": "50971c50479dfd7a", + "name": "debug 28", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1290, + "y": 460, + "wires": [] + }, + { + "id": "inject-person", + "type": "inject", + "z": "a6a28d6b6c0b4f4a", + "name": "Simulate Person Detected", + "props": [ + { + "p": "payload", + "v": "1", + "vt": "num" + }, + { + "p": "timestamp", + "v": "", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "x": 410, + "y": 860, + "wires": [ + [ + "function-add-timestamp" + ] + ] + }, + { + "id": "function-add-timestamp", + "type": "function", + "z": "a6a28d6b6c0b4f4a", + "name": "Add Current Timestamp", + "func": "// Add timestamp\nmsg.timestamp = Date.now();\nreturn msg;", + "outputs": 1, + "x": 670, + "y": 860, + "wires": [ + [ + "function-check-state" + ] + ] + }, + { + "id": "function-check-state", + "type": "function", + "z": "a6a28d6b6c0b4f4a", + "name": "Check Doorbell State", + "func": "// Get previous state from context\nvar prev = context.get('prev') || 0;\nvar curr = parseInt(msg.payload);\ncontext.set('prev', curr);\n\nif(curr===1 && prev===0){\n msg.payload = \"🚪 Person detected at Basement Driveway!\";\n return msg;\n}\nreturn null;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 910, + "y": 860, + "wires": [ + [ + "function-login" + ] + ] + }, + { + "id": "function-login", + "type": "function", + "z": "a6a28d6b6c0b4f4a", + "name": "Reolink Login", + "func": "const ip = '10.20.30.34';\nconst username = 'admin';\nconst password = 'sh1pb0x1';\nmsg.url = `http://${ip}/cgi-bin/api.cgi?cmd=Login&token=null`;\nmsg.method = 'POST';\nmsg.headers = { 'Content-Type': 'application/json' };\nmsg.payload = JSON.stringify([\n { cmd: 'Login', param: { User: { userName: username, password: password } } }\n]);\nmsg.camera_ip = ip;\nmsg.camera_location = 'Basement Driveway Camera';\nmsg.nextRandom = Math.random().toString(36).substring(7);\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1150, + "y": 860, + "wires": [ + [ + "http-login" + ] + ] + }, + { + "id": "http-login", + "type": "http request", + "z": "a6a28d6b6c0b4f4a", + "name": "Send Login Request", + "method": "use", + "ret": "obj", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "x": 1390, + "y": 860, + "wires": [ + [ + "function-snap" + ] + ] + }, + { + "id": "function-snap", + "type": "function", + "z": "a6a28d6b6c0b4f4a", + "name": "Fetch Snapshot", + "func": "// Extract token from login response\nif(!msg.payload || !msg.payload[0] || !msg.payload[0].value){\n node.error('Login failed');\n return null;\n}\nconst token = msg.payload[0].value.Token.name;\nif(!token){ node.error('Token missing'); return null; }\n\n// Build snapshot URL\nmsg.url = `http://${msg.camera_ip}/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=${msg.nextRandom}&token=${token}`;\nmsg.method = 'GET';\nmsg.headers = {};\nmsg.payload = null;\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1630, + "y": 860, + "wires": [ + [ + "http-snap" + ] + ] + }, + { + "id": "http-snap", + "type": "http request", + "z": "a6a28d6b6c0b4f4a", + "name": "Get Camera Snapshot", + "method": "use", + "ret": "bin", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "x": 1870, + "y": 860, + "wires": [ + [ + "b95aab8dab0c2fb4" + ] + ] + }, + { + "id": "b95aab8dab0c2fb4", + "type": "function", + "z": "a6a28d6b6c0b4f4a", + "name": "Send Doorbell Alert to Telegram", + "func": "// Validate that we received a proper image buffer\nif (!Buffer.isBuffer(msg.payload)) {\n node.warn(\"❌ Basement Driveway snapshot fetch failed — not a binary image buffer\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Snapshot failed\"});\n return null;\n}\n\n// Check if buffer has reasonable size (basic validation)\nif (msg.payload.length < 1000) {\n node.warn(\"❌ Basement Driveway snapshot too small — likely corrupted\");\n node.status({fill:\"red\", shape:\"ring\", text:\"Image too small\"});\n return null;\n}\n\n// Format timestamp for display\nconst timestamp = new Date(msg.timestamp).toLocaleString();\n\n// Prepare Telegram message payload\nmsg.payload = {\n chatId: -4747176681, // Telegram group chat ID\n type: 'photo',\n content: msg.payload, // Binary image buffer\n caption: `🔔 Basement Driveway ALERT! 🔔\\n\\n📍 Someone is at the ${msg.location}\\n⏰ Time: ${timestamp}\\n📷 Camera: ${msg.camera_location} (${msg.camera_ip})`\n};\n\nnode.status({fill:\"green\", shape:\"dot\", text:\"Alert sent!\"});\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 2150, + "y": 860, + "wires": [ + [ + "8f15067daf678e66" + ] + ] + }, + { + "id": "8f15067daf678e66", + "type": "link out", + "z": "a6a28d6b6c0b4f4a", + "name": "link out 25", + "mode": "link", + "links": [ + "41d29e978759e26e" + ], + "x": 2365, + "y": 860, + "wires": [] + }, + { + "id": "dbea7ee9936e4747", + "type": "mqtt in", + "z": "b673e10beab091ff", + "name": "MQTT Input", + "topic": "#", + "qos": "2", + "datatype": "auto-detect", + "broker": "a1235a09a8d34dea", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 330, + "y": 1040, + "wires": [ + [ + "b10d3c44bca37652" + ] + ] + }, + { + "id": "b10d3c44bca37652", + "type": "function", + "z": "b673e10beab091ff", + "name": "Camera Config & Topic Parser", + "func": "// Camera configuration - Add/remove cameras here\n// authMethod: 'url' for URL-based auth, 'reolink' for Reolink API login\nconst cameras = [\n {\n name: \"Doorbell Camera\",\n ip: \"10.20.30.30\",\n mqttTopic: \"frigate/doorbell/person\",\n username: \"admin\",\n password: \"sh1pb0x1\",\n authMethod: \"url\"\n },\n {\n name: \"Veranda Door Camera\",\n ip: \"10.20.30.33\",\n mqttTopic: \"frigate/veranda_door/person\",\n username: \"admin\",\n password: \"sh1pb0x1\",\n authMethod: \"url\"\n },\n {\n name: \"Basement Driveway Camera\",\n ip: \"10.20.30.34\",\n mqttTopic: \"frigate/basement_driveway/person\",\n username: \"admin\",\n password: \"sh1pb0x1\",\n authMethod: \"reolink\"\n }\n];\n\n// Find matching camera based on MQTT topic\nconst topic = msg.topic;\nconst camera = cameras.find(cam => topic === cam.mqttTopic);\n\n// Only process if it's a person detection topic for a known camera\nif (!camera) {\n return null; // Not a camera we care about\n}\n\n// Store camera config in message\nmsg.camera_name = camera.name;\nmsg.camera_ip = camera.ip;\nmsg.camera_username = camera.username;\nmsg.camera_password = camera.password;\nmsg.camera_location = camera.name;\nmsg.authMethod = camera.authMethod;\n\n// Add timestamp if not present\nif (!msg.timestamp) {\n msg.timestamp = Date.now();\n}\n\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 670, + "y": 1040, + "wires": [ + [ + "a1e7837147954108", + "50b554a0642ab4e4" + ] + ] + }, + { + "id": "a1e7837147954108", + "type": "function", + "z": "b673e10beab091ff", + "name": "Check State Change", + "func": "// Get current payload value\nvar currentValue = parseInt(msg.payload);\n\n// Use camera name as unique key for state tracking\nvar stateKey = 'previousState_' + msg.camera_name.replace(/\\s+/g, '_');\n\n// Get previous state from context (default to 0 if not set)\nvar previousValue = context.get(stateKey) || 0;\n\n// Store current value for next comparison\ncontext.set(stateKey, currentValue);\n\n// Only proceed if current value is 1 AND previous value was 0\nif (currentValue === 1 && previousValue === 0) {\n node.status({fill:\"blue\", shape:\"dot\", text:`${msg.camera_name}: Person detected!`});\n return msg;\n}\n\n// Don't send message for other cases\nreturn null;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 930, + "y": 1040, + "wires": [ + [ + "90f6a26b7b04cc23", + "ec7ded527bfe8ef2" + ] + ] + }, + { + "id": "90f6a26b7b04cc23", + "type": "switch", + "z": "b673e10beab091ff", + "name": "Route by Auth Method", + "property": "authMethod", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "url", + "vt": "str" + }, + { + "t": "eq", + "v": "reolink", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 2, + "x": 1180, + "y": 1040, + "wires": [ + [ + "d723df03bdda6ca5", + "43983177055e72b1" + ], + [ + "0339e277385e3213", + "6cafa4c162199702" + ] + ] + }, + { + "id": "d723df03bdda6ca5", + "type": "function", + "z": "b673e10beab091ff", + "name": "URL Auth Snapshot", + "func": "// Validate camera info\nif (!msg.camera_ip || !msg.camera_username || !msg.camera_password) {\n node.error(`Missing camera credentials for ${msg.camera_name || 'unknown'}`);\n node.status({fill:\"red\", shape:\"ring\", text:\"Missing credentials\"});\n return null;\n}\n\n// Generate random string for cache busting\nconst randomString = Math.random().toString(36).substring(7);\n\n// URL encode credentials to handle special characters safely\nconst encodedUser = encodeURIComponent(msg.camera_username);\nconst encodedPassword = encodeURIComponent(msg.camera_password);\n\n// Construct camera snapshot URL with credentials\nmsg.url = `http://${msg.camera_ip}/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=${randomString}&user=${encodedUser}&password=${encodedPassword}`;\nmsg.method = 'GET';\nmsg.headers = {};\nmsg.payload = null;\n\nnode.status({fill:\"blue\", shape:\"dot\", text:`Fetching ${msg.camera_name}...`});\n\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1450, + "y": 960, + "wires": [ + [ + "267ea942b61eb093", + "7b685fbbf92cce4f" + ] + ] + }, + { + "id": "0339e277385e3213", + "type": "function", + "z": "b673e10beab091ff", + "name": "Reolink Login", + "func": "// Prepare login request for Reolink API\nconst ip = msg.camera_ip;\nconst username = msg.camera_username;\nconst password = msg.camera_password;\n\n// Build login URL\nmsg.url = `http://${ip}/cgi-bin/api.cgi?cmd=Login&token=null`;\nmsg.method = 'POST';\nmsg.headers = { 'Content-Type': 'application/json' };\nmsg.payload = JSON.stringify([\n { cmd: 'Login', param: { User: { userName: username, password: password } } }\n]);\n\n// Generate random string for cache busting (for snapshot request)\nmsg.nextRandom = Math.random().toString(36).substring(7);\n\n// Preserve camera info\n// msg.camera_ip, msg.camera_name, msg.camera_location already set\n\nnode.status({fill:\"yellow\", shape:\"dot\", text:`Logging in to ${msg.camera_name}...`});\n\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1290, + "y": 1160, + "wires": [ + [ + "36ef8809abd6c068", + "ad8ae18947a58945" + ] + ] + }, + { + "id": "36ef8809abd6c068", + "type": "http request", + "z": "b673e10beab091ff", + "name": "Send Login Request", + "method": "use", + "ret": "obj", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "senderr": true, + "x": 1450, + "y": 1040, + "wires": [ + [ + "e24c5bb10ec49f08" + ] + ] + }, + { + "id": "e24c5bb10ec49f08", + "type": "function", + "z": "b673e10beab091ff", + "name": "Fetch Snapshot", + "func": "// Check for HTTP errors first\nif (msg.statusCode && msg.statusCode !== 200) {\n node.error(`HTTP ${msg.statusCode} - Login failed for ${msg.camera_name}`);\n node.status({fill:\"red\", shape:\"ring\", text:\"HTTP Error\"});\n return null;\n}\n\n// Extract token from login response\nif(!msg.payload || !msg.payload[0] || !msg.payload[0].value){\n node.error('Login failed for ' + msg.camera_name + ' - Invalid response');\n node.status({fill:\"red\", shape:\"ring\", text:\"Login failed\"});\n return null;\n}\n\nconst token = msg.payload[0].value.Token.name;\nif(!token){ \n node.error('Token missing for ' + msg.camera_name); \n node.status({fill:\"red\", shape:\"ring\", text:\"Token missing\"});\n return null; \n}\n\n// Build snapshot URL\nmsg.url = `http://${msg.camera_ip}/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=${msg.nextRandom}&token=${token}`;\nmsg.method = 'GET';\nmsg.headers = {};\nmsg.payload = null;\n\nnode.status({fill:\"blue\", shape:\"dot\", text:`Fetching snapshot from ${msg.camera_name}...`});\n\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1660, + "y": 1040, + "wires": [ + [ + "267ea942b61eb093" + ] + ] + }, + { + "id": "267ea942b61eb093", + "type": "http request", + "z": "b673e10beab091ff", + "name": "Get Camera Snapshot", + "method": "use", + "ret": "bin", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "senderr": true, + "x": 1910, + "y": 1040, + "wires": [ + [ + "c52d97f3e3de82d8" + ] + ] + }, + { + "id": "b9939417e38d7ffa", + "type": "function", + "z": "b673e10beab091ff", + "name": "Error Handler", + "func": "// Handle HTTP errors\nconst cameraName = msg.camera_name || 'Unknown Camera';\nconst errorMsg = msg.error || msg.statusCode || msg.message || 'Unknown error';\n\n// Check if it's a timeout\nif (msg.error && (msg.error.message && msg.error.message.includes('timeout') || msg.error.code === 'ETIMEDOUT')) {\n node.error(`⏱️ ${cameraName} request timed out`);\n node.status({fill:\"red\", shape:\"ring\", text:\"Timeout\"});\n} else {\n node.error(`❌ ${cameraName} error: ${errorMsg}`);\n node.status({fill:\"red\", shape:\"ring\", text:\"Error\"});\n}\n\n// Pass error info to debug\nmsg.error_details = {\n camera: cameraName,\n error: errorMsg,\n statusCode: msg.statusCode,\n url: msg.url,\n timestamp: new Date().toISOString()\n};\n\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1700, + "y": 1200, + "wires": [ + [ + "df63eb3ba1bbfb73" + ] + ] + }, + { + "id": "df63eb3ba1bbfb73", + "type": "debug", + "z": "b673e10beab091ff", + "name": "Error Debug", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "error_details", + "statusVal": "", + "statusType": "auto", + "x": 1910, + "y": 1200, + "wires": [] + }, + { + "id": "c52d97f3e3de82d8", + "type": "function", + "z": "b673e10beab091ff", + "name": "Send Alert to Telegram", + "func": "// Validate that we received a proper image buffer\nif (!Buffer.isBuffer(msg.payload)) {\n node.warn(`❌ ${msg.camera_name} snapshot fetch failed — not a binary image buffer`);\n node.status({fill:\"red\", shape:\"ring\", text:\"Snapshot failed\"});\n return null;\n}\n\n// Check if buffer has reasonable size (basic validation)\nif (msg.payload.length < 1000) {\n node.warn(`❌ ${msg.camera_name} snapshot too small — likely corrupted`);\n node.status({fill:\"red\", shape:\"ring\", text:\"Image too small\"});\n return null;\n}\n\n// Format timestamp for display\nconst timestamp = new Date(msg.timestamp).toLocaleString();\n\n// Prepare Telegram message payload\nmsg.payload = {\n chatId: -4747176681, // Telegram group chat ID\n type: 'photo',\n content: msg.payload, // Binary image buffer\n caption: `🔔 ${msg.camera_name.toUpperCase()} ALERT! 🔔\\n\\n📍 Someone detected at ${msg.camera_location}\\n⏰ Time: ${timestamp}\\n📷 Camera: ${msg.camera_name} (${msg.camera_ip})`\n};\n\nnode.status({fill:\"green\", shape:\"dot\", text:\"Alert sent!\"});\n\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 2190, + "y": 1040, + "wires": [ + [ + "1ba4ee81e40e5c51", + "8df5c69617ed9e92" + ] + ] + }, + { + "id": "1ba4ee81e40e5c51", + "type": "link out", + "z": "b673e10beab091ff", + "name": "link out 22", + "mode": "link", + "links": [ + "41d29e978759e26e" + ], + "x": 2325, + "y": 860, + "wires": [] + }, + { + "id": "8df5c69617ed9e92", + "type": "debug", + "z": "b673e10beab091ff", + "name": "debug 12", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 2330, + "y": 1120, + "wires": [] + }, + { + "id": "50b554a0642ab4e4", + "type": "debug", + "z": "b673e10beab091ff", + "name": "debug 13", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 830, + "y": 1160, + "wires": [] + }, + { + "id": "ad8ae18947a58945", + "type": "debug", + "z": "b673e10beab091ff", + "name": "debug 17", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1430, + "y": 1260, + "wires": [] + }, + { + "id": "9618c8df3ea2d338", + "type": "inject", + "z": "b673e10beab091ff", + "name": "Test Veranda Door", + "props": [ + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "frigate/veranda_door/person", + "x": 210, + "y": 1180, + "wires": [ + [ + "498fac7ac1d2e87b" + ] + ] + }, + { + "id": "43983177055e72b1", + "type": "debug", + "z": "b673e10beab091ff", + "name": "debug 18", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1300, + "y": 880, + "wires": [] + }, + { + "id": "7b685fbbf92cce4f", + "type": "debug", + "z": "b673e10beab091ff", + "name": "debug 19", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1700, + "y": 880, + "wires": [] + }, + { + "id": "6cafa4c162199702", + "type": "debug", + "z": "b673e10beab091ff", + "name": "debug 20", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1100, + "y": 1240, + "wires": [] + }, + { + "id": "ec7ded527bfe8ef2", + "type": "debug", + "z": "b673e10beab091ff", + "name": "debug 30", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1080, + "y": 960, + "wires": [] + }, + { + "id": "498fac7ac1d2e87b", + "type": "function", + "z": "b673e10beab091ff", + "name": "function 9", + "func": "// Send payload 0 first, then payload 1 after a short delay\n// This tests the state change detection (0 -> 1)\n\n// Get topic from incoming message (set in inject node)\nconst topic = msg.topic;\n\n// Send payload 0 immediately\nconst msg0 = {\n topic: topic,\n payload: 0,\n timestamp: Date.now()\n};\n\n// Send payload 1 after 500ms delay\nsetTimeout(() => {\n const msg1 = {\n topic: topic,\n payload: 1,\n timestamp: Date.now()\n };\n node.send(msg1);\n}, 500);\n\n// Return the first message (payload 0)\nreturn msg0;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 420, + "y": 1180, + "wires": [ + [ + "b10d3c44bca37652" + ] + ] + }, + { + "id": "85ca22727431531a", + "type": "inject", + "z": "b673e10beab091ff", + "name": "Test Veranda Door", + "props": [ + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "frigate/basement_driveway/person", + "x": 210, + "y": 1220, + "wires": [ + [ + "498fac7ac1d2e87b" + ] + ] + }, + { + "id": "lr-ha-to-nvl", + "type": "function", + "z": "24e9a56732cfa6f1", + "name": "HA to NVL", + "func": "const ROOM_NAME = 'livingRoom';\nconst entityId = (msg.topic || '').toString();\nconst parts = entityId.split('_');\nconst last = parts[parts.length - 1] || '';\nconst num = parseInt(last, 10);\nconst lightNum = (!isNaN(num) && num >= 1) ? Math.min(6, num) : 1;\nconst isOn = (msg.payload === 'on' || msg.payload === true);\nif (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });\nconst state = flow.get('nvlInState');\nif (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};\nconst r = state.rooms[ROOM_NAME];\nr['ha_l' + lightNum + '_on'] = false;\nr['ha_l' + lightNum + '_off'] = false;\nif (isOn) r['ha_l' + lightNum + '_on'] = true;\nelse r['ha_l' + lightNum + '_off'] = true;\nflow.set('nvlInState', state);\nmsg.payload = { buildAndSend: true };\nreturn msg;", + "outputs": 1, + "x": 540, + "y": 220, + "wires": [ + [ + "lr-build-nvl", + "59ce11298eda23b6" + ] + ] + }, + { + "id": "lr-zigbee-to-nvl", + "type": "function", + "z": "24e9a56732cfa6f1", + "name": "Zigbee to NVL", + "func": "const ROOM_NAME = 'livingRoom';\nconst actionToSwitch = { single: 1, double: 2, hold: 3, release: 4, triple: 5, quad: 6 };\nconst payload = msg.payload || {};\nconst action = (payload.action || payload.click || '').toLowerCase();\nconst swIndex = actionToSwitch[action];\nif (swIndex == null) return null;\nif (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });\nconst state = flow.get('nvlInState');\nif (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};\nstate.rooms[ROOM_NAME]['zigbee_sw' + swIndex] = true;\nflow.set('nvlInState', state);\nmsg.payload = { buildAndSend: true };\nmsg.zigbeeClear = { room: ROOM_NAME, key: 'zigbee_sw' + swIndex };\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 540, + "y": 320, + "wires": [ + [ + "lr-build-nvl", + "lr-delay" + ] + ] + }, + { + "id": "lr-delay", + "type": "delay", + "z": "24e9a56732cfa6f1", + "name": "80ms", + "pauseType": "delay", + "timeout": "80", + "timeoutUnits": "milliseconds", + "rate": "1", + "nbRateUnits": "1", + "rateUnits": "seconds", + "randomFirst": "1", + "randomLast": "5", + "randomUnits": "seconds", + "drop": true, + "outputs": 1, + "x": 760, + "y": 320, + "wires": [ + [ + "lr-clear-zigbee" + ] + ] + }, + { + "id": "lr-clear-zigbee", + "type": "function", + "z": "24e9a56732cfa6f1", + "name": "Clear zigbee edge", + "func": "if (msg.zigbeeClear) {\n const state = flow.get('nvlInState') || { rooms: {}, boiler: {} };\n const r = state.rooms[msg.zigbeeClear.room];\n if (r) r[msg.zigbeeClear.key] = false;\n flow.set('nvlInState', state);\n}\nmsg.payload = { buildAndSend: true };\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 970, + "y": 320, + "wires": [ + [ + "lr-build-nvl" + ] + ] + }, + { + "id": "lr-build-nvl", + "type": "function", + "z": "24e9a56732cfa6f1", + "name": "Build NVL_In", + "func": "const roomSize = 20;\nconst roomNames = ['masterBedroom','masterBathroom','bedroom_1','bedroom_2','bathroom','guestWc','kitchen','pantry','livingRoom','dinningRoom','entrance','hallway','outVeranda','outFront','outBack','outSide'];\nconst state = flow.get('nvlInState') || { rooms: {}, boiler: {} };\nconst rooms = state.rooms || {};\nconst boiler = state.boiler || {};\nconst buf = Buffer.alloc(328);\nfor (let r = 0; r < 16; r++) {\n const c = rooms[roomNames[r]] || {};\n const base = r * roomSize;\n buf[base+0]=c.ha_l1_on?1:0;buf[base+1]=c.ha_l1_off?1:0;buf[base+2]=c.ha_l2_on?1:0;buf[base+3]=c.ha_l2_off?1:0;buf[base+4]=c.ha_l3_on?1:0;buf[base+5]=c.ha_l3_off?1:0;buf[base+6]=c.ha_l4_on?1:0;buf[base+7]=c.ha_l4_off?1:0;buf[base+8]=c.ha_l5_on?1:0;buf[base+9]=c.ha_l5_off?1:0;buf[base+10]=c.ha_l6_on?1:0;buf[base+11]=c.ha_l6_off?1:0;\n for (let i = 0; i < 6; i++) buf[base+12+i] = c['zigbee_sw'+(i+1)] ? 1 : 0;\n buf[base+18]=c.ha_all_on?1:0;buf[base+19]=c.ha_all_off?1:0;\n}\nconst bo = 320;\nbuf[bo+0]=boiler.ha_on?1:0;buf[bo+1]=boiler.ha_off?1:0;buf[bo+2]=boiler.schedule_on?1:0;buf[bo+3]=boiler.schedule_off?1:0;buf[bo+4]=boiler.emergency_stop?1:0;buf.writeInt16LE(boiler.max_on_time_minutes??480,bo+6);\nmsg.payload = buf;\nreturn msg;", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 790, + "y": 260, + "wires": [ + [ + "9d20e54acf4079aa" + ] + ] + }, + { + "id": "59ce11298eda23b6", + "type": "debug", + "z": "24e9a56732cfa6f1", + "name": "debug 33", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 730, + "y": 200, + "wires": [] + }, + { + "id": "9d20e54acf4079aa", + "type": "debug", + "z": "24e9a56732cfa6f1", + "name": "debug 35", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 950, + "y": 200, + "wires": [] + }, + { + "id": "5925b9fedc7dc1e1", + "type": "debug", + "z": "24e9a56732cfa6f1", + "name": "debug 38", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 750, + "y": 560, + "wires": [] + }, + { + "id": "ed2071aadf923b50", + "type": "inject", + "z": "24e9a56732cfa6f1", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 140, + "y": 520, + "wires": [ + [] + ] + } +] \ No newline at end of file diff --git a/node-red/config_files/node-red-contrib-home-assistant-websocket.json b/node-red/config_files/node-red-contrib-home-assistant-websocket.json new file mode 100644 index 0000000..0581cb4 --- /dev/null +++ b/node-red/config_files/node-red-contrib-home-assistant-websocket.json @@ -0,0 +1,12 @@ +{ + "nodes": { + "6d1793815598b149": { + "isEnabled": true + } + }, + "issues": { + "hidden": [ + "fe5612a41399f1f5" + ] + } +} \ No newline at end of file diff --git a/node-red/config_files/package-lock.json b/node-red/config_files/package-lock.json new file mode 100644 index 0000000..a970207 --- /dev/null +++ b/node-red/config_files/package-lock.json @@ -0,0 +1,4101 @@ +{ + "name": "node-red-project", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node-red-project", + "version": "0.0.1", + "dependencies": { + "node-red-contrib-bigtimer": "~2.8.6", + "node-red-contrib-cron-plus": "~2.2.4", + "node-red-contrib-home-assistant-websocket": "~0.80.3", + "node-red-contrib-nvl": "~1.2.1", + "node-red-contrib-telegrambot": "~17.0.5", + "node-red-contrib-watchdirectory": "~1.0.14", + "node-red-contrib-zigbee2mqtt": "~2.7.5", + "node-red-dashboard": "~3.6.6", + "node-red-timer-scheduler": "~1.0.6" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cypress/request": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.8.tgz", + "integrity": "sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==", + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "6.14.0", + "safe-buffer": "^5.1.2", + "tough-cookie": "^5.0.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/request-promise": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cypress/request-promise/-/request-promise-5.0.0.tgz", + "integrity": "sha512-eKdYVpa9cBEw2kTBlHeu1PP16Blwtum6QHg/u9s/MoHkZfuo1pRGka1VlUHXF5kdew82BvOJVVGk0x8X0nbp+w==", + "license": "ISC", + "dependencies": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^4.1.3" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "@cypress/request": "^3.0.0" + } + }, + "node_modules/@cypress/request-promise/node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@cypress/request/node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/request/node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@cypress/request/node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "node_modules/@cypress/request/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@cypress/request/node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@cypress/request/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "license": "MIT" + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.13.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", + "integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.18.tgz", + "integrity": "sha512-21jK/1j+Wg+7jVw1xnSwy/2Q1VgVjWuFssbYGTREPUBeZ+rqVFl2udq0IkxzPC0ZhOzVceUbyIACFZKLqKEBlA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findindex": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.findindex/-/array.prototype.findindex-2.2.4.tgz", + "integrity": "sha512-LLm4mhxa9v8j0A/RPnpQAP4svXToJFh+Hp1pNYl5ZD5qpB4zdx/D4YjpVcETkhFbUKWO3iGMVLvrOnnmkAJT6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.0.tgz", + "integrity": "sha512-ClDyJGQkc8ZtzdAAbAwBmhMSpwN/sC9HA8jxdYm6nVUbCfZbe2mgza4qh7AuEYyEPB/c4Kznf9s66bnsKMQDjw==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brackets2dots": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brackets2dots/-/brackets2dots-1.1.0.tgz", + "integrity": "sha512-DEIJz+ebFQ2SYPpXd8owCjy+8H+9N2Pd9DeSf0J33oavLyBYpAtjLg/Z/RmdJdTeHmKVva+L411HjnvyV2rSOA==", + "license": "MIT" + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/coord-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/coord-parser/-/coord-parser-1.0.0.tgz", + "integrity": "sha512-rketzalr6NS1IE8QjVf90djYvfxQ97aL3onUj8VlJsGsEBbsKf6SoKK3awYTrqynLr9VEBbCkHrwayGU4XjU2Q==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cronosjs": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/cronosjs/-/cronosjs-1.7.1.tgz", + "integrity": "sha512-d6S6+ep7dJxsAG8OQQCdKuByI/S/AV64d9OF5mtmcykOyPu92cAkAnF3Tbc9s5oOaLQBYYQmTNvjqYRkPJ/u5Q==", + "license": "ISC", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cronstrue": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-3.11.0.tgz", + "integrity": "sha512-nXST7NEkjPF6loOTJtCwbLHB6cOVR5Ofuq/VZ4UGsZw/0HTvxy6mRCrDHOJ4xoLPcuS3ooruBLyUk3INaidKwg==", + "license": "MIT", + "bin": { + "cronstrue": "bin/cli.js" + } + }, + "node_modules/curry2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/curry2/-/curry2-1.0.3.tgz", + "integrity": "sha512-2vXqPLsITt0ccyczu1BFl3tc8Q6BOCsTHt+NZYasd8wp60RQIYhGM3Beis5h5FgJPT11M1rfiKOR7dPL6cL14Q==", + "license": "MIT", + "dependencies": { + "fast-bind": "^1.0.0" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotsplit.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dotsplit.js/-/dotsplit.js-1.1.0.tgz", + "integrity": "sha512-oFVx9VEE+M3yM4oUkaiDa+U2RhOmjXWyXwtfdc5UiHDSZWleE96FS3nx3yXMVuhLJOdI2GMThvaegkwRYPgAFQ==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-bind/-/fast-bind-1.0.0.tgz", + "integrity": "sha512-kna1xVU4nn4HW4RVwh6VYSWoii+u8EkWKS3I6YZluncEvtQwahHKhZTRPFHOOkeJK4m0/Tz2Ir9n10tARqeiXw==", + "license": "ISC" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/geolib": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/geolib/-/geolib-3.3.4.tgz", + "integrity": "sha512-EicrlLLL3S42gE9/wde+11uiaYAaeSVDwCUIv2uMIoRBfNJCn8EsSI+6nS3r4TCKDO6+RQNM9ayLq2at+oZQWQ==", + "license": "MIT" + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gridstack": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/gridstack/-/gridstack-0.6.4.tgz", + "integrity": "sha512-4ToCnneNg5Uw+ms3xHtPVvsNXdvwQhngdlyNgGkARwvooQu+gLL6xkwPqLU59TsZP/LVvofb2QhEuXyh/ocL8w==", + "license": "MIT", + "dependencies": { + "jquery": "^1.8 || 2 || 3" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, + "node_modules/home-assistant-js-websocket": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/home-assistant-js-websocket/-/home-assistant-js-websocket-9.6.0.tgz", + "integrity": "sha512-TK8HWW6UjCsUdjIBQRB2sBbEya0VniOBFqFjgqSznJiB7YriNgN8jghyThxOkgxwrnt2eN6oWoZMCTSp1o7H+w==", + "license": "Apache-2.0" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/iec-61131-3": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/iec-61131-3/-/iec-61131-3-1.1.0.tgz", + "integrity": "sha512-ypECAZP6g9FETSOCDH0dC+dnHDSGnzHtvdORGwq60LNqR7GpXZGSDaY2kTlynmothyxdm5YfxGQuVCqjhPQk5w==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.5.2" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/jsonata": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.1.0.tgz", + "integrity": "sha512-OCzaRMK8HobtX8fp37uIVmL8CY1IGc/a6gLsDqz3quExFR09/U78HUzWYr7T31UEB6+Eu0/8dkVD5fFDOl9a8w==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/lowdb": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mqtt": { + "version": "5.10.4", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.4.tgz", + "integrity": "sha512-wN+SuhT2/ZaG6NPxca0N6YtRivnMxk6VflxQUEeqDH4erKdj+wPAGhHmcTLzvqfE4sJRxrEJ+XJxUc0No0E7eQ==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.18", + "@types/ws": "^8.5.14", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.0", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.1", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "reinterval": "^1.1.0", + "rfdc": "^1.4.1", + "split2": "^4.2.0", + "worker-timers": "^7.1.8", + "ws": "^8.18.0" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-red-contrib-bigtimer": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/node-red-contrib-bigtimer/-/node-red-contrib-bigtimer-2.8.6.tgz", + "integrity": "sha512-vxIEr91C6ogc9hG5Usc+vx210xP/Q4ZG4oaa0GxQPkV82Ip/WVEaMA70Pi0rL7/JuopUARS6fLPrP2DiUO49eA==", + "license": "MIT", + "dependencies": { + "suncalc": "1.*" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/node-red-contrib-cron-plus": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/node-red-contrib-cron-plus/-/node-red-contrib-cron-plus-2.2.4.tgz", + "integrity": "sha512-aQ8sq13XM/qazZTAemnSqVi9sds8sP7DwFJ4dGSe24W0dVW2vBbd61Bh1Q51/zzv/b4J6wkJzc+Xf2a1KBQAjA==", + "license": "MIT", + "dependencies": { + "coord-parser": "^1.0.0", + "cronosjs": "^1.7.1", + "cronstrue": "^3.2.0", + "pretty-ms": "^7.0.1", + "suncalc2": "^1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/node-red-contrib-home-assistant-websocket": { + "version": "0.80.3", + "resolved": "https://registry.npmjs.org/node-red-contrib-home-assistant-websocket/-/node-red-contrib-home-assistant-websocket-0.80.3.tgz", + "integrity": "sha512-ZOo+Xoq13SpDe9KzECZboWx+KvFcwPIhk4W7RaUdkWhZR5qRBKMufGOuIZ+oMEFOr1KuOeYIXf8VaK04nDkgJw==", + "license": "MIT", + "dependencies": { + "axios": "^1.13.2", + "bonjour-service": "^1.3.0", + "compare-versions": "^6.1.1", + "cronosjs": "^1.7.1", + "debug": "^4.4.3", + "flat": "^5.0.2", + "geolib": "^3.3.4", + "home-assistant-js-websocket": "^9.5.0", + "joi": "^17.13.3", + "jsonata": "2.1.0", + "lodash": "^4.17.21", + "lowdb": "^1.0.0", + "mustache": "4.2.0", + "selectn": "^1.3.0", + "slugify": "^1.6.6", + "timestring": "^7.0.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=18.2.0" + } + }, + "node_modules/node-red-contrib-nvl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-red-contrib-nvl/-/node-red-contrib-nvl-1.2.1.tgz", + "integrity": "sha512-DZE1hBLi71+3Re/oLyA5NXLgfM1+8qw3WCGV9wRvFqhOfZt3WcjVIIwRGUKaF2LHHfetIDLbqtB37WiknsXv+A==", + "license": "MIT", + "dependencies": { + "iec-61131-3": "^1.0.0", + "lodash.merge": "^4.6.2" + } + }, + "node_modules/node-red-contrib-telegrambot": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/node-red-contrib-telegrambot/-/node-red-contrib-telegrambot-17.0.5.tgz", + "integrity": "sha512-W9jw37ildwn5YTQxQDzhcLXP5pLtakQemCOWVCWZPFAqIjxAIcsLyAS7vWUFFJF4JWMQ5r+SaQ3QEYjoWkbX5w==", + "license": "MIT", + "dependencies": { + "bluebird": "^3.7.2", + "node-telegram-bot-api": "^0.66.0", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/node-red-contrib-watchdirectory": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/node-red-contrib-watchdirectory/-/node-red-contrib-watchdirectory-1.0.14.tgz", + "integrity": "sha512-BVqFolsq4ISAH7shzljC2C0Y7KetSYYNGpethR3DgaWZrlo21RcE8n0qlhYWMlRQU5LlHL33PGP5qA5SEFDAWg==", + "license": "ISC", + "dependencies": { + "chokidar": "^3.3.1" + } + }, + "node_modules/node-red-contrib-zigbee2mqtt": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/node-red-contrib-zigbee2mqtt/-/node-red-contrib-zigbee2mqtt-2.7.5.tgz", + "integrity": "sha512-R17CD/aYHCYiWgfIGjTw5rnFC/EsUC/1ENapdH8jnfm3npM69L4T7JSX38fAdP6AHJdfW4pqWLqxn1Kot5sHPw==", + "license": "GPL", + "dependencies": { + "eventsource": "^2.0.2", + "mqtt": "^5.9.1", + "request": "^2.88.2", + "viz.js": "^2.1.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/node-red-dashboard": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/node-red-dashboard/-/node-red-dashboard-3.6.6.tgz", + "integrity": "sha512-+77TiX0CwFQkEVKqYZz5Xacsf2ycGl8ZVXXXIVvm51bol9ILzaOh0WyxWjWHrpYk8hPUhs2H34avD8l6Lx9ELw==", + "deprecated": "No longer maintained - please look at alternatives such as @flowfuse/node-red-dashboard or node-red-contrib-uibuilder", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "compression": "^1.8.1", + "gridstack": "^0.6.4", + "serve-static": "^1.16.2", + "socket.io": "^4.8.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-red-timer-scheduler": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-red-timer-scheduler/-/node-red-timer-scheduler-1.0.6.tgz", + "integrity": "sha512-uec17CvPrKOMht+lKETparhWgPCGgtAUwYCLJ3zMxXlg7/vhfGBvuF+FjNezvmsx8v6QdNUeTXEXyHe2T3UjoA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/node-telegram-bot-api": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/node-telegram-bot-api/-/node-telegram-bot-api-0.66.0.tgz", + "integrity": "sha512-s4Hrg5q+VPl4/tJVG++pImxF6eb8tNJNj4KnDqAOKL6zGU34lo9RXmyAN158njwGN+v8hdNf8s9fWIYW9hPb5A==", + "license": "MIT", + "dependencies": { + "@cypress/request": "^3.0.1", + "@cypress/request-promise": "^5.0.0", + "array.prototype.findindex": "^2.0.2", + "bl": "^1.2.3", + "debug": "^3.2.7", + "eventemitter3": "^3.0.0", + "file-type": "^3.9.0", + "mime": "^1.6.0", + "pump": "^2.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/node-telegram-bot-api/node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/node-telegram-bot-api/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/node-telegram-bot-api/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/node-telegram-bot-api/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/node-telegram-bot-api/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "license": "MIT", + "dependencies": { + "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==", + "license": "MIT" + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "license": "ISC", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/selectn": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/selectn/-/selectn-1.3.0.tgz", + "integrity": "sha512-mid0kbUHd1kZ2hMYIMTqPMJ4xpLiaGnpXqkiMuwSBH9GiS0jkE8wGjlMG9D6ICZ4Nj8iln75wn3oCZQf9Gudpw==", + "license": "MIT", + "dependencies": { + "brackets2dots": "^1.1.0", + "curry2": "^1.0.0", + "dotsplit.js": "^1.0.3" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "license": "ISC", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/steno": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", + "integrity": "sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.3" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/suncalc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.9.0.tgz", + "integrity": "sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A==" + }, + "node_modules/suncalc2": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/suncalc2/-/suncalc2-1.8.1.tgz", + "integrity": "sha512-tNoAni1LqWzBzJRX3NRCsD50gVsuejH1jd9Y+XyKO3WkpPIU+QNyWF4Zf2JnUQKW2LKLWIBgVDQCrgsQwqs0tA==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "license": "MIT" + }, + "node_modules/timestring": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/timestring/-/timestring-7.0.0.tgz", + "integrity": "sha512-U7ttxEdKWqHYJ96OGoJJR5gU8Nwkl3tlY0n7Jr4vcpLD2RkVZLE1Ph9k8ZRrZ7LYX9QCtd3M9OUaR9P8Z37QNg==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/viz.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/viz.js/-/viz.js-2.1.2.tgz", + "integrity": "sha512-UO6CPAuEMJ8oNR0gLLNl+wUiIzQUsyUOp8SyyDKTqVRBtq7kk1VnFmIZW8QufjxGrGEuI+LVR7p/C7uEKy0LQw==", + "deprecated": "2.x is no longer supported, 3.x published as @viz-js/viz", + "license": "MIT" + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/node-red/config_files/package.json b/node-red/config_files/package.json new file mode 100644 index 0000000..71821c9 --- /dev/null +++ b/node-red/config_files/package.json @@ -0,0 +1,17 @@ +{ + "name": "node-red-project", + "description": "A Node-RED Project", + "version": "0.0.1", + "private": true, + "dependencies": { + "node-red-contrib-bigtimer": "~2.8.6", + "node-red-contrib-cron-plus": "~2.2.4", + "node-red-contrib-home-assistant-websocket": "~0.80.3", + "node-red-contrib-nvl": "~1.2.1", + "node-red-contrib-telegrambot": "~17.0.5", + "node-red-contrib-watchdirectory": "~1.0.14", + "node-red-contrib-zigbee2mqtt": "~2.7.5", + "node-red-dashboard": "~3.6.6", + "node-red-timer-scheduler": "~1.0.6" + } +} diff --git a/node-red/room-config.js b/node-red/config_files/room-config.js similarity index 99% rename from node-red/room-config.js rename to node-red/config_files/room-config.js index 366ebeb..622a941 100644 --- a/node-red/room-config.js +++ b/node-red/config_files/room-config.js @@ -1,4 +1,4 @@ -/** +ccc/** * Single source of truth for rooms and lights. * Lives on the Node-RED server at /root/.node-red/room-config.js * All function nodes use: global.get('roomConfig') diff --git a/node-red/config_files/settings.js b/node-red/config_files/settings.js new file mode 100644 index 0000000..4acc0af --- /dev/null +++ b/node-red/config_files/settings.js @@ -0,0 +1,594 @@ +/** + * This is the default settings file provided by Node-RED. + * + * It can contain any valid JavaScript code that will get run when Node-RED + * is started. + * + * Lines that start with // are commented out. + * Each entry should be separated from the entries above and below by a comma ',' + * + * For more information about individual settings, refer to the documentation: + * https://nodered.org/docs/user-guide/runtime/configuration + * + * The settings are split into the following sections: + * - Flow File and User Directory Settings + * - Security + * - Server Settings + * - Runtime Settings + * - Editor Settings + * - Node Settings + * + **/ + +module.exports = { + +/******************************************************************************* + * Flow File and User Directory Settings + * - flowFile + * - credentialSecret + * - flowFilePretty + * - userDir + * - nodesDir + ******************************************************************************/ + + /** The file containing the flows. If not set, defaults to flows_.json **/ + flowFile: 'flows.json', + + /** By default, credentials are encrypted in storage using a generated key. To + * specify your own secret, set the following property. + * If you want to disable encryption of credentials, set this property to false. + * Note: once you set this property, do not change it - doing so will prevent + * node-red from being able to decrypt your existing credentials and they will be + * lost. + */ + //credentialSecret: "a-secret-key", + + /** By default, the flow JSON will be formatted over multiple lines making + * it easier to compare changes when using version control. + * To disable pretty-printing of the JSON set the following property to false. + */ + flowFilePretty: true, + + /** By default, all user data is stored in a directory called `.node-red` under + * the user's home directory. To use a different location, the following + * property can be used + */ + //userDir: '/home/nol/.node-red/', + + /** Node-RED scans the `nodes` directory in the userDir to find local node files. + * The following property can be used to specify an additional directory to scan. + */ + //nodesDir: '/home/nol/.node-red/nodes', + +/******************************************************************************* + * Security + * - adminAuth + * - https + * - httpsRefreshInterval + * - requireHttps + * - httpNodeAuth + * - httpStaticAuth + ******************************************************************************/ + + /** To password protect the Node-RED editor and admin API, the following + * property can be used. See https://nodered.org/docs/security.html for details. + */ + //adminAuth: { + // type: "credentials", + // users: [{ + // username: "admin", + // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", + // permissions: "*" + // }] + //}, + + /** The following property can be used to enable HTTPS + * This property can be either an object, containing both a (private) key + * and a (public) certificate, or a function that returns such an object. + * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener + * for details of its contents. + */ + + /** Option 1: static object */ + //https: { + // key: require("fs").readFileSync('privkey.pem'), + // cert: require("fs").readFileSync('cert.pem') + //}, + + /** Option 2: function that returns the HTTP configuration object */ + // https: function() { + // // This function should return the options object, or a Promise + // // that resolves to the options object + // return { + // key: require("fs").readFileSync('privkey.pem'), + // cert: require("fs").readFileSync('cert.pem') + // } + // }, + + /** If the `https` setting is a function, the following setting can be used + * to set how often, in hours, the function will be called. That can be used + * to refresh any certificates. + */ + //httpsRefreshInterval : 12, + + /** The following property can be used to cause insecure HTTP connections to + * be redirected to HTTPS. + */ + //requireHttps: true, + + /** To password protect the node-defined HTTP endpoints (httpNodeRoot), + * including node-red-dashboard, or the static content (httpStatic), the + * following properties can be used. + * The `pass` field is a bcrypt hash of the password. + * See https://nodered.org/docs/security.html#generating-the-password-hash + */ + //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, + //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, + +/******************************************************************************* + * Server Settings + * - uiPort + * - uiHost + * - apiMaxLength + * - httpServerOptions + * - httpAdminRoot + * - httpAdminMiddleware + * - httpAdminCookieOptions + * - httpNodeRoot + * - httpNodeCors + * - httpNodeMiddleware + * - httpStatic + * - httpStaticRoot + * - httpStaticCors + ******************************************************************************/ + + /** the tcp port that the Node-RED web server is listening on */ + uiPort: process.env.PORT || 1880, + + /** By default, the Node-RED UI accepts connections on all IPv4 interfaces. + * To listen on all IPv6 addresses, set uiHost to "::", + * The following property can be used to listen on a specific interface. For + * example, the following would only allow connections from the local machine. + */ + //uiHost: "127.0.0.1", + + /** The maximum size of HTTP request that will be accepted by the runtime api. + * Default: 5mb + */ + //apiMaxLength: '5mb', + + /** The following property can be used to pass custom options to the Express.js + * server used by Node-RED. For a full list of available options, refer + * to http://expressjs.com/en/api.html#app.settings.table + */ + //httpServerOptions: { }, + + /** By default, the Node-RED UI is available at http://localhost:1880/ + * The following property can be used to specify a different root path. + * If set to false, this is disabled. + */ + //httpAdminRoot: '/admin', + + /** The following property can be used to add a custom middleware function + * in front of all admin http routes. For example, to set custom http + * headers. It can be a single function or an array of middleware functions. + */ + // httpAdminMiddleware: function(req,res,next) { + // // Set the X-Frame-Options header to limit where the editor + // // can be embedded + // //res.set('X-Frame-Options', 'sameorigin'); + // next(); + // }, + + /** The following property can be used to set addition options on the session + * cookie used as part of adminAuth authentication system + * Available options are documented here: https://www.npmjs.com/package/express-session#cookie + */ + // httpAdminCookieOptions: { }, + + /** Some nodes, such as HTTP In, can be used to listen for incoming http requests. + * By default, these are served relative to '/'. The following property + * can be used to specify a different root path. If set to false, this is + * disabled. + */ + //httpNodeRoot: '/red-nodes', + + /** The following property can be used to configure cross-origin resource sharing + * in the HTTP nodes. + * See https://github.com/troygoode/node-cors#configuration-options for + * details on its contents. The following is a basic permissive set of options: + */ + //httpNodeCors: { + // origin: "*", + // methods: "GET,PUT,POST,DELETE" + //}, + + /** If you need to set an http proxy please set an environment variable + * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. + * For example - http_proxy=http://myproxy.com:8080 + * (Setting it here will have no effect) + * You may also specify no_proxy (or NO_PROXY) to supply a comma separated + * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk + */ + + /** The following property can be used to add a custom middleware function + * in front of all http in nodes. This allows custom authentication to be + * applied to all http in nodes, or any other sort of common request processing. + * It can be a single function or an array of middleware functions. + */ + //httpNodeMiddleware: function(req,res,next) { + // // Handle/reject the request, or pass it on to the http in node by calling next(); + // // Optionally skip our rawBodyParser by setting this to true; + // //req.skipRawBodyParser = true; + // next(); + //}, + + /** When httpAdminRoot is used to move the UI to a different root path, the + * following property can be used to identify a directory of static content + * that should be served at http://localhost:1880/. + * When httpStaticRoot is set differently to httpAdminRoot, there is no need + * to move httpAdminRoot + */ + //httpStatic: '/home/nol/node-red-static/', //single static source + /** + * OR multiple static sources can be created using an array of objects... + * Each object can also contain an options object for further configuration. + * See https://expressjs.com/en/api.html#express.static for available options. + * They can also contain an option `cors` object to set specific Cross-Origin + * Resource Sharing rules for the source. `httpStaticCors` can be used to + * set a default cors policy across all static routes. + */ + //httpStatic: [ + // {path: '/home/nol/pics/', root: "/img/"}, + // {path: '/home/nol/reports/', root: "/doc/"}, + // {path: '/home/nol/videos/', root: "/vid/", options: {maxAge: '1d'}} + //], + + /** + * All static routes will be appended to httpStaticRoot + * e.g. if httpStatic = "/home/nol/docs" and httpStaticRoot = "/static/" + * then "/home/nol/docs" will be served at "/static/" + * e.g. if httpStatic = [{path: '/home/nol/pics/', root: "/img/"}] + * and httpStaticRoot = "/static/" + * then "/home/nol/pics/" will be served at "/static/img/" + */ + //httpStaticRoot: '/static/', + + /** The following property can be used to configure cross-origin resource sharing + * in the http static routes. + * See https://github.com/troygoode/node-cors#configuration-options for + * details on its contents. The following is a basic permissive set of options: + */ + //httpStaticCors: { + // origin: "*", + // methods: "GET,PUT,POST,DELETE" + //}, + + /** The following property can be used to modify proxy options */ + // proxyOptions: { + // mode: "legacy", // legacy mode is for non-strict previous proxy determination logic (node-red < v4 compatible) + // }, + +/******************************************************************************* + * Runtime Settings + * - lang + * - runtimeState + * - diagnostics + * - logging + * - contextStorage + * - exportGlobalContextKeys + * - externalModules + ******************************************************************************/ + + /** Uncomment the following to run node-red in your preferred language. + * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko + * Some languages are more complete than others. + */ + // lang: "de", + + /** Configure diagnostics options + * - enabled: When `enabled` is `true` (or unset), diagnostics data will + * be available at http://localhost:1880/diagnostics + * - ui: When `ui` is `true` (or unset), the action `show-system-info` will + * be available to logged in users of node-red editor + */ + diagnostics: { + /** enable or disable diagnostics endpoint. Must be set to `false` to disable */ + enabled: true, + /** enable or disable diagnostics display in the node-red editor. Must be set to `false` to disable */ + ui: true, + }, + /** Configure runtimeState options + * - enabled: When `enabled` is `true` flows runtime can be Started/Stopped + * by POSTing to available at http://localhost:1880/flows/state + * - ui: When `ui` is `true`, the action `core:start-flows` and + * `core:stop-flows` will be available to logged in users of node-red editor + * Also, the deploy menu (when set to default) will show a stop or start button + */ + runtimeState: { + /** enable or disable flows/state endpoint. Must be set to `false` to disable */ + enabled: false, + /** show or hide runtime stop/start options in the node-red editor. Must be set to `false` to hide */ + ui: false, + }, + /** Configure the logging output */ + logging: { + /** Only console logging is currently supported */ + console: { + /** Level of logging to be recorded. Options are: + * fatal - only those errors which make the application unusable should be recorded + * error - record errors which are deemed fatal for a particular request + fatal errors + * warn - record problems which are non fatal + errors + fatal errors + * info - record information about the general running of the application + warn + error + fatal errors + * debug - record information which is more verbose than info + info + warn + error + fatal errors + * trace - record very detailed logging + debug + info + warn + error + fatal errors + * off - turn off all logging (doesn't affect metrics or audit) + */ + level: "info", + /** Whether or not to include metric events in the log output */ + metrics: false, + /** Whether or not to include audit events in the log output */ + audit: false + } + }, + + /** Context Storage + * The following property can be used to enable context storage. The configuration + * provided here will enable file-based context that flushes to disk every 30 seconds. + * Refer to the documentation for further options: https://nodered.org/docs/api/context/ + */ + //contextStorage: { + // default: { + // module:"localfilesystem" + // }, + //}, + + /** `global.keys()` returns a list of all properties set in global context. + * This allows them to be displayed in the Context Sidebar within the editor. + * In some circumstances it is not desirable to expose them to the editor. The + * following property can be used to hide any property set in `functionGlobalContext` + * from being list by `global.keys()`. + * By default, the property is set to false to avoid accidental exposure of + * their values. Setting this to true will cause the keys to be listed. + */ + exportGlobalContextKeys: false, + + /** Configure how the runtime will handle external npm modules. + * This covers: + * - whether the editor will allow new node modules to be installed + * - whether nodes, such as the Function node are allowed to have their + * own dynamically configured dependencies. + * The allow/denyList options can be used to limit what modules the runtime + * will install/load. It can use '*' as a wildcard that matches anything. + */ + externalModules: { + // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ + // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ + // palette: { /** Configuration for the Palette Manager */ + // allowInstall: true, /** Enable the Palette Manager in the editor */ + // allowUpdate: true, /** Allow modules to be updated in the Palette Manager */ + // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ + // allowList: ['*'], + // denyList: [], + // allowUpdateList: ['*'], + // denyUpdateList: [] + // }, + // modules: { /** Configuration for node-specified modules */ + // allowInstall: true, + // allowList: [], + // denyList: [] + // } + }, + + +/******************************************************************************* + * Editor Settings + * - disableEditor + * - editorTheme + ******************************************************************************/ + + /** The following property can be used to disable the editor. The admin API + * is not affected by this option. To disable both the editor and the admin + * API, use either the httpRoot or httpAdminRoot properties + */ + //disableEditor: false, + + /** Customising the editor + * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes + * for all available options. + */ + editorTheme: { + /** The following property can be used to set a custom theme for the editor. + * See https://github.com/node-red-contrib-themes/theme-collection for + * a collection of themes to chose from. + */ + //theme: "", + + /** To disable the 'Welcome to Node-RED' tour that is displayed the first + * time you access the editor for each release of Node-RED, set this to false + */ + //tours: false, + + palette: { + /** The following property can be used to order the categories in the editor + * palette. If a node's category is not in the list, the category will get + * added to the end of the palette. + * If not set, the following default order is used: + */ + //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], + }, + + projects: { + /** To enable the Projects feature, set this value to true */ + enabled: false, + workflow: { + /** Set the default projects workflow mode. + * - manual - you must manually commit changes + * - auto - changes are automatically committed + * This can be overridden per-user from the 'Git config' + * section of 'User Settings' within the editor + */ + mode: "manual" + } + }, + + codeEditor: { + /** Select the text editor component used by the editor. + * As of Node-RED V3, this defaults to "monaco", but can be set to "ace" if desired + */ + lib: "monaco", + options: { + /** The follow options only apply if the editor is set to "monaco" + * + * theme - must match the file name of a theme in + * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme + * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme" + */ + // theme: "vs", + /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc. + * for the full list, see https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneEditorConstructionOptions.html + */ + //fontSize: 14, + //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", + //fontLigatures: true, + } + }, + + markdownEditor: { + mermaid: { + /** enable or disable mermaid diagram in markdown document + */ + enabled: true + } + }, + + multiplayer: { + /** To enable the Multiplayer feature, set this value to true */ + enabled: false + }, + }, + +/******************************************************************************* + * Node Settings + * - fileWorkingDirectory + * - functionGlobalContext + * - functionExternalModules + * - functionTimeout + * - nodeMessageBufferMaxLength + * - ui (for use with Node-RED Dashboard) + * - debugUseColors + * - debugMaxLength + * - debugStatusLength + * - execMaxBufferSize + * - httpRequestTimeout + * - mqttReconnectTime + * - serialReconnectTime + * - socketReconnectTime + * - socketTimeout + * - tcpMsgQueueSize + * - inboundWebSocketTimeout + * - tlsConfigDisableLocalFiles + * - webSocketNodeVerifyClient + ******************************************************************************/ + + /** The working directory to handle relative file paths from within the File nodes + * defaults to the working directory of the Node-RED process. + */ + //fileWorkingDirectory: "", + + /** Allow the Function node to load additional npm modules directly */ + functionExternalModules: true, + + /** Default timeout, in seconds, for the Function node. 0 means no timeout is applied */ + functionTimeout: 0, + + /** The following property can be used to set predefined values in Global Context. + * This allows extra node modules to be made available with in Function node. + * For example, the following: + * functionGlobalContext: { os:require('os') } + * will allow the `os` module to be accessed in a Function node using: + * global.get("os") + */ + functionGlobalContext: { + roomConfig: require('/root/.node-red/room-config.js'), + // os:require('os'), + }, + + /** The maximum number of messages nodes will buffer internally as part of their + * operation. This applies across a range of nodes that operate on message sequences. + * defaults to no limit. A value of 0 also means no limit is applied. + */ + //nodeMessageBufferMaxLength: 0, + + /** If you installed the optional node-red-dashboard you can set it's path + * relative to httpNodeRoot + * Other optional properties include + * readOnly:{boolean}, + * middleware:{function or array}, (req,res,next) - http middleware + * ioMiddleware:{function or array}, (socket,next) - socket.io middleware + */ + //ui: { path: "ui" }, + + /** Colourise the console output of the debug node */ + //debugUseColors: true, + + /** The maximum length, in characters, of any message sent to the debug sidebar tab */ + debugMaxLength: 1000, + + /** The maximum length, in characters, of status messages under the debug node */ + //debugStatusLength: 32, + + /** Maximum buffer size for the exec node. Defaults to 10Mb */ + //execMaxBufferSize: 10000000, + + /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */ + //httpRequestTimeout: 120000, + + /** Retry time in milliseconds for MQTT connections */ + mqttReconnectTime: 15000, + + /** Retry time in milliseconds for Serial port connections */ + serialReconnectTime: 15000, + + /** Retry time in milliseconds for TCP socket connections */ + //socketReconnectTime: 10000, + + /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */ + //socketTimeout: 120000, + + /** Maximum number of messages to wait in queue while attempting to connect to TCP socket + * defaults to 1000 + */ + //tcpMsgQueueSize: 2000, + + /** Timeout in milliseconds for inbound WebSocket connections that do not + * match any configured node. Defaults to 5000 + */ + //inboundWebSocketTimeout: 5000, + + /** To disable the option for using local files for storing keys and + * certificates in the TLS configuration node, set this to true. + */ + //tlsConfigDisableLocalFiles: true, + + /** The following property can be used to verify WebSocket connection attempts. + * This allows, for example, the HTTP request headers to be checked to ensure + * they include valid authentication information. + */ + //webSocketNodeVerifyClient: function(info) { + // /** 'info' has three properties: + // * - origin : the value in the Origin header + // * - req : the HTTP request + // * - secure : true if req.connection.authorized or req.connection.encrypted is set + // * + // * The function should return true if the connection should be accepted, false otherwise. + // * + // * Alternatively, if this function is defined to accept a second argument, callback, + // * it can be used to verify the client asynchronously. + // * The callback takes three arguments: + // * - result : boolean, whether to accept the connection or not + // * - code : if result is false, the HTTP error status to return + // * - reason: if result is false, the HTTP reason string to return + // */ + //}, +} diff --git a/node-red/room-config-loader.js b/node-red/room-config-loader.js index 5a281b3..9fd17d2 100644 --- a/node-red/room-config-loader.js +++ b/node-red/room-config-loader.js @@ -1,5 +1,5 @@ // Load room config into global (no require - Function nodes can't use it). -// 1. Open node-red/room-config.js in the repo. +// 1. Open node-red/config_files/room-config.js in the repo (mirror of server file). // 2. Copy the whole object: from "const ROOM_CONFIG = {" up to "};" (do NOT copy "module.exports"). // 3. Replace the ROOM_CONFIG below with your paste. Keep the two lines at the end.