node-red-contrib-knx-ultimate
Version:
Control your KNX and KNX Secure intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control, ETS group address importer, and KNX routing between interfaces. Easy to use and highly configurable.
145 lines (144 loc) • 8.94 kB
JSON
[
{
"id": "tab_osc_knx_fn",
"type": "tab",
"label": "OSC -> KNX (Function)",
"disabled": false,
"info": ""
},
{
"id": "cmt_osc_knx_fn_intro",
"type": "comment",
"z": "tab_osc_knx_fn",
"name": "Receive OSC in a Function node and map to two KNX DPTs",
"info": "Prerequisites:\n1) In Node-RED settings.js set functionExternalModules: true and restart Node-RED.\n2) Deploy this flow: Node-RED will install node-osc for this Function node.\n\nOSC mappings handled by the Function node:\n- /knx/switch <value> -> KNX Device DPT 1.001 (true/false)\n- /knx/value <value> -> KNX Device DPT 5.001 (0..255 integer)\n\nExample OSC values:\n- /knx/switch 1\n- /knx/switch false\n- /knx/value 120",
"x": 410,
"y": 40,
"wires": []
},
{
"id": "fn_osc_receiver",
"type": "function",
"z": "tab_osc_knx_fn",
"name": "OSC Receiver (node-osc)",
"func": "// Input messages are ignored: this node emits only from the OSC callback defined in Setup.\nreturn null;",
"outputs": 2,
"timeout": 0,
"noerr": 0,
"initialize": "const listenPort = 8001;\nconst listenHost = \"0.0.0.0\";\n\nconst closeServer = (server) => {\n if (!server || typeof server.close !== \"function\") {\n return Promise.resolve();\n }\n\n try {\n if (typeof server.removeAllListeners === \"function\") {\n server.removeAllListeners(\"message\");\n server.removeAllListeners(\"bundle\");\n server.removeAllListeners(\"error\");\n server.removeAllListeners(\"listening\");\n }\n } catch (err) {\n node.warn(\"Error removing OSC listeners: \" + err.message);\n }\n\n return new Promise((resolve) => {\n let doneCalled = false;\n const done = () => {\n if (doneCalled) return;\n doneCalled = true;\n resolve();\n };\n\n try {\n const maybePromise = server.close((err) => {\n if (err) node.warn(\"OSC close callback error: \" + err.message);\n done();\n });\n\n if (maybePromise && typeof maybePromise.then === \"function\") {\n maybePromise.then(done).catch((err) => {\n node.warn(\"OSC close promise error: \" + err.message);\n done();\n });\n } else {\n setTimeout(done, 150);\n }\n } catch (err) {\n node.warn(\"OSC close throw: \" + err.message);\n done();\n }\n });\n};\n\nif (!nodeOsc || typeof nodeOsc.Server !== \"function\") {\n node.status({ fill: \"red\", shape: \"ring\", text: \"node-osc missing\" });\n node.error(\"node-osc not available. Enable functionExternalModules and deploy again.\");\n return;\n}\n\nconst normalizeBoolean = (value) => {\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"number\") return value !== 0;\n const text = String(value).trim().toLowerCase();\n return text === \"true\" || text === \"1\" || text === \"on\";\n};\n\nconst previousServer = context.get(\"oscServer\");\ncontext.set(\"oscServer\", null);\nnode.status({ fill: \"yellow\", shape: \"ring\", text: \"restarting OSC...\" });\n\nreturn closeServer(previousServer).then(() => {\n const oscServer = new nodeOsc.Server(listenPort, listenHost, function () {\n node.status({ fill: \"green\", shape: \"dot\", text: \"OSC \" + listenHost + \":\" + listenPort });\n });\n\n context.set(\"oscServer\", oscServer);\n\n oscServer.on(\"error\", function (err) {\n node.warn(\"OSC server error: \" + (err && err.message ? err.message : err));\n });\n\n oscServer.on(\"message\", function (oscMsg) {\n if (!Array.isArray(oscMsg) || oscMsg.length < 2) return;\n\n const address = String(oscMsg[0] || \"\");\n const value = oscMsg[1];\n\n if (address === \"/knx/switch\") {\n node.send([{\n payload: normalizeBoolean(value),\n oscAddress: address,\n rawOsc: oscMsg\n }, null]);\n return;\n }\n\n if (address === \"/knx/value\") {\n const parsed = Number(value);\n if (!Number.isFinite(parsed)) {\n node.warn(\"OSC /knx/value not numeric: \" + value);\n return;\n }\n\n node.send([null, {\n payload: Math.max(0, Math.min(255, Math.round(parsed))),\n oscAddress: address,\n rawOsc: oscMsg\n }]);\n }\n });\n}).catch((err) => {\n node.status({ fill: \"red\", shape: \"ring\", text: \"OSC start error\" });\n node.error(\"Unable to start OSC server: \" + (err && err.message ? err.message : err));\n});",
"finalize": "const closeServer = (server) => {\n if (!server || typeof server.close !== \"function\") {\n return Promise.resolve();\n }\n\n try {\n if (typeof server.removeAllListeners === \"function\") {\n server.removeAllListeners(\"message\");\n server.removeAllListeners(\"bundle\");\n server.removeAllListeners(\"error\");\n server.removeAllListeners(\"listening\");\n }\n } catch (err) {\n node.warn(\"Error removing OSC listeners in finalize: \" + err.message);\n }\n\n return new Promise((resolve) => {\n let doneCalled = false;\n const done = () => {\n if (doneCalled) return;\n doneCalled = true;\n resolve();\n };\n\n try {\n const maybePromise = server.close((err) => {\n if (err) node.warn(\"OSC finalize close callback error: \" + err.message);\n done();\n });\n\n if (maybePromise && typeof maybePromise.then === \"function\") {\n maybePromise.then(done).catch((err) => {\n node.warn(\"OSC finalize close promise error: \" + err.message);\n done();\n });\n } else {\n setTimeout(done, 150);\n }\n } catch (err) {\n node.warn(\"OSC finalize close throw: \" + err.message);\n done();\n }\n });\n};\n\nconst oscServer = context.get(\"oscServer\");\ncontext.set(\"oscServer\", null);\nnode.status({ fill: \"yellow\", shape: \"ring\", text: \"stopping OSC...\" });\n\nreturn closeServer(oscServer).then(() => {\n node.status({});\n});",
"libs": [
{
"var": "nodeOsc",
"module": "node-osc"
}
],
"x": 230,
"y": 180,
"wires": [
[
"knx_bool_1"
],
[
"knx_int_1"
]
]
},
{
"id": "knx_bool_1",
"type": "knxUltimate",
"z": "tab_osc_knx_fn",
"server": "osc_knx_cfg_1",
"topic": "1/1/1",
"outputtopic": "",
"dpt": "1.001",
"initialread": false,
"notifyreadrequest": false,
"notifyresponse": true,
"notifywrite": true,
"notifyreadrequestalsorespondtobus": false,
"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
"listenallga": false,
"name": "KNX Switch (DPT 1.001)",
"outputtype": "write",
"outputRBE": false,
"inputRBE": false,
"x": 520,
"y": 150,
"wires": [
[
"dbg_bool_out"
]
]
},
{
"id": "knx_int_1",
"type": "knxUltimate",
"z": "tab_osc_knx_fn",
"server": "osc_knx_cfg_1",
"topic": "1/1/2",
"outputtopic": "",
"dpt": "5.001",
"initialread": false,
"notifyreadrequest": false,
"notifyresponse": true,
"notifywrite": true,
"notifyreadrequestalsorespondtobus": false,
"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
"listenallga": false,
"name": "KNX Value (DPT 5.001)",
"outputtype": "write",
"outputRBE": false,
"inputRBE": false,
"x": 510,
"y": 220,
"wires": [
[
"dbg_int_out"
]
]
},
{
"id": "dbg_bool_out",
"type": "debug",
"z": "tab_osc_knx_fn",
"name": "Bool KNX out",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"x": 720,
"y": 150,
"wires": []
},
{
"id": "dbg_int_out",
"type": "debug",
"z": "tab_osc_knx_fn",
"name": "Int KNX out",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"x": 710,
"y": 220,
"wires": []
},
{
"id": "osc_knx_cfg_1",
"type": "knxUltimate-config",
"z": "",
"host": "224.0.23.12",
"port": "3671",
"physAddr": "15.15.203",
"suppressACKRequest": false,
"csv": "",
"KNXEthInterface": "Auto",
"KNXEthInterfaceManuallyInput": "",
"autoReconnect": "yes"
}
]