

Control your KNX intallation via Node.js!
This is the official engine of Node-Red node KNX-Ultimate (https://flows.nodered.org/node/KNXUltimate)
I had many users asking for a node.js release of that engine, so here is it.
The node will be KNX Secure compatible. I'm already working on that.
CHANGELOG
Properties to be passed to the connection(see the knxUltimateClientProperties variable below)
ipAddr (string) | The IP of your KNX router/interface (for Routers, use "224.0.23.12") |
ipPort (string) | The port, default is "3671" |
physAddr (string) | The physical address to be identified in the KNX bus |
suppress_ack_ldatareq (bool) | Avoid sending/receive the ACK telegram. Leave false. If you encounter issues with old interface, set it to true |
loglevel (string) | The log level. "info", "error", "debug" or "trace" |
localEchoInTunneling (bool) | Leave true forever. This is used only in Node-Red KNX-Ultimate node |
hostProtocol (string) | "Multicast" if you're connecting to a KNX Router. "TunnelUDP" for KNX Interfaces, or "TunnelTCP" for secure KNX Interfaces (not yet implemented) |
isSecureKNXEnabled (bool) | True: Enables the secure connection. Leave false until KNX-Secure has been released. |
jKNXSecureKeyring (string) | ETS Keyring JSON file content (leave blank until KNX-Secure has been released) |
localIPAddress (string) | The local IP address to be used to connect to the KNX/IP Bus. Leave blank, will be automatically filled by KNXUltimate |
KNXEthInterface (string) | "Auto": Bind to the first avaiable local interfavce. "Manual": if you wish to specify the interface (for example eth1); in this case, set the property interface to the interface name (interface:"eth1") |
interface (string) | Specifies the local eth interface to be used to connect to the KNX Bus. Do not add this parameter if you've set KNXEthInterface to "Auto" |
Supported Datapoints
Datapoints supporter are huge. Please run the sample.js file to view all datapoints in the console window.
For each Datapoint, there is a sample on how to format the payload (telegram) to be passed.
For example, pass a true for datapoint 1.001, or { red: 125, green: 0, blue: 0 } for datapoijnt 232.600.
Commands to be used to write to the KNX BUS
See the examples also.
.write (GA, payload, datapoint) | Sends a WRITE telegram to the BUS. GA is the group address (for example "0/0/1"), payload is the value you want to send (for example true), datapoint is a string representing the datapoint (for example "5.001") |
.respond (GA, payload, datapoint) | Sends a RESPONSE telegram to the BUS. GA is the group address (for example "0/0/1"), payload is the value you want to send (for example true), datapoint is a string representing the datapoint (for example "5.001") |
.read (GA) | Sends a READ telegram to the BUS. GA is the group address (for example "0/0/1"). |
Simple sample (you can find this sample in the "simpleSample.js" file):
const knx = require("./index.js");
let knxUltimateClientProperties = {
ipAddr: "224.0.23.12",
ipPort: "3671",
physAddr: "1.1.100",
suppress_ack_ldatareq: false,
loglevel: "error",
localEchoInTunneling: true,
hostProtocol: "Multicast",
isSecureKNXEnabled: false,
jKNXSecureKeyring: "",
localIPAddress: "",
KNXEthInterface: "Auto",
};
const knxUltimateClient = new knx.KNXClient(knxUltimateClientProperties);
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.indication, function (_datagram, _echoed) {
let _evt = "";
if (_datagram.cEMIMessage.npdu.isGroupRead) _evt = "GroupValue_Read";
if (_datagram.cEMIMessage.npdu.isGroupResponse) _evt = "GroupValue_Response";
if (_datagram.cEMIMessage.npdu.isGroupWrite) _evt = "GroupValue_Write";
console.log("src: " + _datagram.cEMIMessage.srcAddress.toString() + " dest: " + _datagram.cEMIMessage.dstAddress.toString(), " event: " + _evt);
});
knxUltimateClient.Connect();
knxUltimateClient.write("0/1/1", false, "1.001");
Full featured sample (you can find this sample in the "sample.js" file):
const knx = require("./index.js");
const dptlib = require('./src/dptlib');
sortBy = (field) => (a, b) => {
if (a[field] > b[field]) { return 1 } else { return -1 }
};
onlyDptKeys = (kv) => {
return kv[0].startsWith("DPT")
};
extractBaseNo = (kv) => {
return {
subtypes: kv[1].subtypes,
base: parseInt(kv[1].id.replace("DPT", ""))
}
};
convertSubtype = (baseType) => (kv) => {
let value = `${baseType.base}.${kv[0]}`;
let sRet = value + " " + kv[1].name;
return {
value: value
, text: sRet
}
}
toConcattedSubtypes = (acc, baseType) => {
let subtypes =
Object.entries(baseType.subtypes)
.sort(sortBy(0))
.map(convertSubtype(baseType))
return acc.concat(subtypes)
};
dptGetHelp = dpt => {
var sDPT = dpt.split(".")[0];
var jRet;
if (sDPT == "0") {
jRet = {
"help":
``, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki"
};
return(jRet);
}
jRet = { "help": "No sample currently avaiable", "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome" };
const dpts =
Object.entries(dptlib)
.filter(onlyDptKeys)
for (let index = 0; index < dpts.length; index++) {
if (dpts[index][0].toUpperCase() === "DPT" + sDPT) {
jRet = { "help": (dpts[index][1].basetype.hasOwnProperty("help") ? dpts[index][1].basetype.help : "No sample currently avaiable, just pass the payload as is it"), "helplink": (dpts[index][1].basetype.hasOwnProperty("helplink") ? dpts[index][1].basetype.helplink : "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome") };
break;
}
}
return (jRet);
}
const dpts =
Object.entries(dptlib)
.filter(onlyDptKeys)
.map(extractBaseNo)
.sort(sortBy("base"))
.reduce(toConcattedSubtypes, [])
dpts.forEach(element => {
console.log(element.text + " USAGE: " + dptGetHelp(element.value).help);
console.log(" ");
});
let knxUltimateClientProperties = {
ipAddr: "224.0.23.12",
ipPort: "3671",
physAddr: "1.1.100",
suppress_ack_ldatareq: false,
loglevel: "error",
localEchoInTunneling: true,
hostProtocol: "Multicast",
isSecureKNXEnabled: false,
jKNXSecureKeyring: "",
localIPAddress: "",
KNXEthInterface: "Auto",
};
const knxUltimateClient = new knx.KNXClient(knxUltimateClientProperties);
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.indication, handleBusEvents);
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.error, err => {
console.log("Error", err)
});
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.disconnected, info => {
console.log("Disconnected", info)
});
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.close, info => {
console.log("Closed", info)
});
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.connected, info => {
console.log("Connected. On Duty", info)
});
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.connecting, info => {
console.log("Connecting...", info)
});
knxUltimateClient.Connect();
function handleBusEvents(_datagram, _echoed) {
let _evt = "";
if (_datagram.cEMIMessage.npdu.isGroupRead) _evt = "GroupValue_Read";
if (_datagram.cEMIMessage.npdu.isGroupResponse) _evt = "GroupValue_Response";
if (_datagram.cEMIMessage.npdu.isGroupWrite) _evt = "GroupValue_Write";
console.log("src: " + _datagram.cEMIMessage.srcAddress.toString() + " dest: " + _datagram.cEMIMessage.dstAddress.toString(), " event: " + _evt);
}
console.log("WARNING: I'm about to write to your BUS in 10 seconds! Press Control+C to abort!")
console.log("WARNING: I'm about to write to your BUS in 10 seconds! Press Control+C to abort!")
console.log("WARNING: I'm about to write to your BUS in 10 seconds! Press Control+C to abort!")
console.log("WARNING: I'm about to write to your BUS in 10 seconds! Press Control+C to abort!")
console.log("WARNING: I'm about to write to your BUS in 10 seconds! Press Control+C to abort!")
setTimeout(() => {
console.log("Clear to send: " + knxUltimateClient._getClearToSend())
let payload = true;
knxUltimateClient.write("0/1/1", payload, "1.001");
payload = { red: 125, green: 0, blue: 0 };
knxUltimateClient.write("0/1/2", payload, "232.600");
knxUltimateClient.read("0/0/1");
payload = false;
knxUltimateClient.respond("0/0/1", payload, "1.001");
}, 10000);
Why not to try Node-Red https://nodered.org and the awesome KNX-Ultimate node https://github.com/Supergiovane/KNXUltimate ?
