Flat-structure generator + Rego guards, rename Laser & Fog to DJ Booth
Closes #1. Generator (scripts/flow2json.py): - Emit all nodes at top level with z field (no wrapped tabs) - Set icon: "" on ui-button to avoid Dashboard 2.0 prependIcon TypeError - Set z field on ui-* widgets so they render in Dashboard 2.0 SPA - Use ui-text label from spec, not name Policy (policy/flow.rego) — 9 new deny rules: - deny_wrapped_tab: tab with internal nodes[] (nodered 5.0 bug) - deny_missing_wires: missing or non-array wires (Flow.js rewireNode crash) - deny_no_autoconnect: mqtt-broker must reconnect - deny_invalid_protocol: protocolVersion must be 3/4/5 - deny_unused_broker: broker declared but never referenced - Plus deny_broker_port / deny_group_ref / deny_page_ref / deny_wire_ref refinements Flow (zigbee-monitor.*): - 22-node flat flow: button-in → btn-filter → 4 parallel plug-outs - 4 ui-text widgets + ui-button for permit-join (60s) - Renamed plug: "Laser & Fog" → "DJ Booth" (matches z2m friendly_name) Repo hygiene: - justfile: fix host IP 192.168.9.147 → 192.168.81.147, /ui → /dashboard - README.md: document 8 generation invariants and rule table Verified end-to-end: publishing {"action":"single"} on zigbee2mqtt/Door Button triggers {"state":"TOGGLE"} on all four .../set topics including the renamed DJ Booth. 462/462 conftest tests pass.
This commit is contained in:
+114
-56
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "br9dfef2b2",
|
||||
"id": "braff51339",
|
||||
"type": "mqtt-broker",
|
||||
"name": "mosquitto",
|
||||
"broker": "mosquitto",
|
||||
@@ -22,7 +22,7 @@
|
||||
"willPayload": ""
|
||||
},
|
||||
{
|
||||
"id": "uibba9906e7",
|
||||
"id": "uibab06e3f4",
|
||||
"type": "ui-base",
|
||||
"name": "Dashboard",
|
||||
"path": "/dashboard",
|
||||
@@ -36,7 +36,7 @@
|
||||
"titleBarStyle": "default"
|
||||
},
|
||||
{
|
||||
"id": "uitcdb20b5e",
|
||||
"id": "uit4a70230a",
|
||||
"type": "ui-theme",
|
||||
"name": "Default Theme",
|
||||
"colors": {
|
||||
@@ -54,13 +54,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "uipb062fd21",
|
||||
"id": "uip2851213f",
|
||||
"type": "ui-page",
|
||||
"name": "Home",
|
||||
"ui": "uibba9906e7",
|
||||
"ui": "uibab06e3f4",
|
||||
"icon": "home",
|
||||
"path": "/home",
|
||||
"theme": "uitcdb20b5e",
|
||||
"theme": "uit4a70230a",
|
||||
"layout": "flex",
|
||||
"order": 0,
|
||||
"className": "",
|
||||
@@ -68,10 +68,10 @@
|
||||
"disabled": false
|
||||
},
|
||||
{
|
||||
"id": "uig1ba3cd58",
|
||||
"id": "uig93b85217",
|
||||
"type": "ui-group",
|
||||
"name": "Plugs",
|
||||
"page": "uipb062fd21",
|
||||
"page": "uip2851213f",
|
||||
"width": "6",
|
||||
"height": "1",
|
||||
"order": 1,
|
||||
@@ -80,9 +80,16 @@
|
||||
"visible": true,
|
||||
"disabled": false
|
||||
},
|
||||
{
|
||||
"id": "fd235da06",
|
||||
"type": "tab",
|
||||
"label": "Zigbee Monitor",
|
||||
"disabled": false,
|
||||
"info": "Single button toggles all plugs",
|
||||
"env": []
|
||||
},
|
||||
{
|
||||
"id": "zigbee-state-squiggle",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt in",
|
||||
"wires": [
|
||||
[
|
||||
@@ -92,14 +99,14 @@
|
||||
"name": "",
|
||||
"x": 170,
|
||||
"y": 80,
|
||||
"broker": "br9dfef2b2",
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/Squiggle",
|
||||
"qos": "0",
|
||||
"datatype": "auto"
|
||||
"datatype": "auto",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "zigbee-state-sideboard",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt in",
|
||||
"wires": [
|
||||
[
|
||||
@@ -109,14 +116,14 @@
|
||||
"name": "",
|
||||
"x": 170,
|
||||
"y": 80,
|
||||
"broker": "br9dfef2b2",
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/Sideboard Lamp",
|
||||
"qos": "0",
|
||||
"datatype": "auto"
|
||||
"datatype": "auto",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "zigbee-state-bamboo",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt in",
|
||||
"wires": [
|
||||
[
|
||||
@@ -126,14 +133,31 @@
|
||||
"name": "",
|
||||
"x": 170,
|
||||
"y": 80,
|
||||
"broker": "br9dfef2b2",
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/Bamboo Lights",
|
||||
"qos": "0",
|
||||
"datatype": "auto"
|
||||
"datatype": "auto",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "zigbee-state-laser",
|
||||
"type": "mqtt in",
|
||||
"wires": [
|
||||
[
|
||||
"text-laser"
|
||||
]
|
||||
],
|
||||
"name": "",
|
||||
"x": 170,
|
||||
"y": 220,
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/DJ Booth",
|
||||
"qos": "0",
|
||||
"datatype": "auto",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "button-in",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt in",
|
||||
"wires": [
|
||||
[
|
||||
@@ -143,23 +167,24 @@
|
||||
"name": "",
|
||||
"x": 170,
|
||||
"y": 220,
|
||||
"broker": "br9dfef2b2",
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/Door Button",
|
||||
"qos": "0",
|
||||
"datatype": "auto"
|
||||
"datatype": "auto",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "btn-filter",
|
||||
"z": "f11689cf1",
|
||||
"type": "function",
|
||||
"wires": [
|
||||
[
|
||||
"plug-out-squiggle",
|
||||
"plug-out-sideboard",
|
||||
"plug-out-bamboo"
|
||||
"plug-out-bamboo",
|
||||
"plug-out-laser"
|
||||
]
|
||||
],
|
||||
"name": "Single press only",
|
||||
"name": "Single press",
|
||||
"x": 450,
|
||||
"y": 220,
|
||||
"func": "try {\n const p = JSON.parse(msg.payload);\n if (p.action === 'single') {\n msg.payload = { state: 'TOGGLE' };\n return msg;\n }\n} catch (e) {}\nreturn null;\n",
|
||||
@@ -167,101 +192,131 @@
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": []
|
||||
"libs": [],
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "plug-out-squiggle",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt out",
|
||||
"wires": [],
|
||||
"name": "",
|
||||
"x": 770,
|
||||
"y": 220,
|
||||
"broker": "br9dfef2b2",
|
||||
"y": 360,
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/Squiggle/set",
|
||||
"qos": "2",
|
||||
"retain": false
|
||||
"retain": false,
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "plug-out-sideboard",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt out",
|
||||
"wires": [],
|
||||
"name": "",
|
||||
"x": 770,
|
||||
"y": 360,
|
||||
"broker": "br9dfef2b2",
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/Sideboard Lamp/set",
|
||||
"qos": "2",
|
||||
"retain": false
|
||||
"retain": false,
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "plug-out-bamboo",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt out",
|
||||
"wires": [],
|
||||
"name": "",
|
||||
"x": 770,
|
||||
"y": 360,
|
||||
"broker": "br9dfef2b2",
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/Bamboo Lights/set",
|
||||
"qos": "2",
|
||||
"retain": false
|
||||
"retain": false,
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "plug-out-laser",
|
||||
"type": "mqtt out",
|
||||
"wires": [],
|
||||
"name": "",
|
||||
"x": 770,
|
||||
"y": 500,
|
||||
"broker": "braff51339",
|
||||
"topic": "zigbee2mqtt/DJ Booth/set",
|
||||
"qos": "2",
|
||||
"retain": false,
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "text-squiggle",
|
||||
"z": "f11689cf1",
|
||||
"type": "ui-text",
|
||||
"wires": [],
|
||||
"name": "Squiggle",
|
||||
"x": 770,
|
||||
"y": 360,
|
||||
"group": "uig1ba3cd58",
|
||||
"y": 500,
|
||||
"group": "uig93b85217",
|
||||
"width": "6",
|
||||
"height": "2",
|
||||
"order": 1,
|
||||
"label": "Squiggle",
|
||||
"format": "{{msg.payload.state}}",
|
||||
"layout": "row-spread",
|
||||
"className": ""
|
||||
"className": "",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "text-sideboard",
|
||||
"z": "f11689cf1",
|
||||
"type": "ui-text",
|
||||
"wires": [],
|
||||
"name": "Sideboard Lamp",
|
||||
"x": 770,
|
||||
"y": 500,
|
||||
"group": "uig1ba3cd58",
|
||||
"group": "uig93b85217",
|
||||
"width": "6",
|
||||
"height": "2",
|
||||
"order": 1,
|
||||
"order": 2,
|
||||
"label": "Sideboard Lamp",
|
||||
"format": "{{msg.payload.state}}",
|
||||
"layout": "row-spread",
|
||||
"className": ""
|
||||
"className": "",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "text-bamboo",
|
||||
"z": "f11689cf1",
|
||||
"type": "ui-text",
|
||||
"wires": [],
|
||||
"name": "Bamboo Lights",
|
||||
"x": 770,
|
||||
"y": 500,
|
||||
"group": "uig1ba3cd58",
|
||||
"y": 640,
|
||||
"group": "uig93b85217",
|
||||
"width": "6",
|
||||
"height": "2",
|
||||
"order": 1,
|
||||
"order": 3,
|
||||
"label": "Bamboo Lights",
|
||||
"format": "{{msg.payload.state}}",
|
||||
"layout": "row-spread",
|
||||
"className": ""
|
||||
"className": "",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "text-laser",
|
||||
"type": "ui-text",
|
||||
"wires": [],
|
||||
"name": "DJ Booth",
|
||||
"x": 770,
|
||||
"y": 640,
|
||||
"group": "uig93b85217",
|
||||
"width": "6",
|
||||
"height": "2",
|
||||
"order": 4,
|
||||
"label": "DJ Booth",
|
||||
"format": "{{msg.payload.state}}",
|
||||
"layout": "row-spread",
|
||||
"className": "",
|
||||
"z": "fd235da06"
|
||||
},
|
||||
{
|
||||
"id": "permit-btn",
|
||||
"z": "f11689cf1",
|
||||
"type": "ui-button",
|
||||
"wires": [
|
||||
[
|
||||
@@ -270,27 +325,30 @@
|
||||
],
|
||||
"name": "Permit Join (60s)",
|
||||
"x": 170,
|
||||
"y": 500,
|
||||
"group": "uig1ba3cd58",
|
||||
"y": 640,
|
||||
"group": "uig93b85217",
|
||||
"payload": "{\"value\": true, \"time\": 60}",
|
||||
"payloadType": "json",
|
||||
"topic": "zigbee2mqtt/bridge/request/permit_join",
|
||||
"width": "3",
|
||||
"height": "1",
|
||||
"order": 1,
|
||||
"className": ""
|
||||
"order": 5,
|
||||
"className": "",
|
||||
"z": "fd235da06",
|
||||
"icon": "",
|
||||
"label": "Permit Join (60s)"
|
||||
},
|
||||
{
|
||||
"id": "mqtt-out",
|
||||
"z": "f11689cf1",
|
||||
"type": "mqtt out",
|
||||
"wires": [],
|
||||
"name": "",
|
||||
"x": 770,
|
||||
"y": 640,
|
||||
"broker": "br9dfef2b2",
|
||||
"y": 780,
|
||||
"broker": "braff51339",
|
||||
"topic": "",
|
||||
"qos": "2",
|
||||
"retain": false
|
||||
"retain": false,
|
||||
"z": "fd235da06"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user