UNPKG

node-red-contrib-knx-ultimate

Version:

Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable.

198 lines (174 loc) 9.87 kB
const ping = require('ping'); module.exports = function (RED) { function knxUltimateWatchDog(config) { RED.nodes.createNode(this, config); var node = this; node.server = RED.nodes.getNode(config.server); node.dpt = "1.001"; node.notifyreadrequestalsorespondtobus = "false"; node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ""; node.notifyreadrequest = true; node.notifyresponse = true; node.notifywrite = false; node.initialread = false; node.listenallga = false; node.outputtype = "write"; node.outputRBE = false; node.inputRBE = false; node.currentPayload = ""; node.topic = config.topic !== undefined ? config.topic : ""; node.retryInterval = config.retryInterval !== undefined ? config.retryInterval * 1000 : 10000; node.maxRetry = config.maxRetry !== undefined ? config.maxRetry : 6; node.autoStart = config.autoStart !== undefined ? config.autoStart : true; node.beatNumber = 0; // Telegram counter node.timerWatchDog = null; node.isWatchDog = true; node.checkLevel = config.checkLevel !== undefined ? config.checkLevel : "Ethernet"; node.icountMessageInWindow = 0; // Used to call the status update from the config node. node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => { if (node.server == null) { node.status({ fill: "red", shape: "dot", text: "[NO GATEWAY SELECTED]" }); return; } if (node.icountMessageInWindow == -999) return; // Locked out, doesn't change status. var dDate = new Date(); // 30/08/2019 Display only the things selected in the config GA = (typeof GA == "undefined" || GA == "") ? "" : "(" + GA + ") "; devicename = devicename || ""; dpt = (typeof dpt == "undefined" || dpt == "") ? "" : " DPT" + dpt; node.status({ fill: fill, shape: shape, text: GA + payload + ((node.listenallga && node.server.statusDisplayDeviceNameWhenALL) === true ? " " + devicename : "") + (node.server.statusDisplayDataPoint === true ? dpt : "") + (node.server.statusDisplayLastUpdate === true ? " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" : "") + " " + text }); } if (!node.server) return; function handleTheDog() { node.beatNumber += 1; if (node.beatNumber > node.maxRetry) { // Confirmed connection error node.beatNumber = 0; // Reset Counter let msg = { type: "BUSError", checkPerformed: node.checkLevel, nodeid: node.id, payload: true, description: "Watchdog elapsed with no response." } node.send(msg) } else { if (node.checkLevel === "Ethernet") { // 23/05/2020 using ping var cfg = { timeout: 2 }; ping.sys.probe(node.server.host, function (isAlive) { if (isAlive) { node.watchDogTimerReset() } else { node.setNodeStatus({ fill: "yellow", shape: "dot", text: "Check level " + node.checkLevel + ", failed ping " + node.beatNumber + " of " + node.maxRetry, payload: "", GA: "", dpt: "", devicename: "" }); }; }, cfg); } else { // Issue a read request if (node.server.knxConnection) { node.server.writeQueueAdd({ grpaddr: node.topic, payload: "", dpt: "", outputtype: "read" }); node.setNodeStatus({ fill: "grey", shape: "dot", text: "Checking level " + node.checkLevel + ", with beat telegram " + node.beatNumber + " of " + node.maxRetry, payload: "", GA: "", dpt: "", devicename: "" }); }; } }; }; // This function is called by the knx-ultimate config node. node.watchDogTimerReset = () => { // Resets the watchdog, means all is OK if (node.checkLevel === "Ethernet") { node.beatNumber = 0; // Reset counter let t = setTimeout(() => { // 21/03/2022 fixed possible memory leak. Previously was setTimeout without "let t = ". node.setNodeStatus({ fill: "green", shape: "dot", text: "Basic check level unicast " + node.checkLevel + " - Interface OK.", payload: "", GA: node.topic, dpt: "", devicename: "" }); }, 500); } else { // With this check level "Ethernet + KNX Twisted Pair", i need to obtain the "Response" from the physical device, otherwise the connection TP is broken. node.beatNumber = 0; // Reset counter let t = setTimeout(() => { // 21/03/2022 fixed possible memory leak. Previously was setTimeout without "let t = ". node.setNodeStatus({ fill: "green", shape: "dot", text: "Full check level " + node.checkLevel + " - KNX BUS OK.", payload: "", GA: node.topic, dpt: "", devicename: "" }); }, 500); }; }; // 16/02/2020 This function is called by the knx-ultimate config node. node.signalNodeErrorCalledByConfigNode = _oError => { // Report an error from knx-ultimate node. // let oError = {nodeid:node.id,topic:node.outputtopic,devicename:devicename,GA:GA,text:text}; let msg = { type: "NodeError", checkPerformed: "Self KNX-Ultimate node reporting a red color status", nodeid: _oError.nodeid, payload: true, description: _oError.text, completeError: _oError }; node.send(msg); }; node.StartWatchDogTimer = () => { node.beatNumber = 0; if (node.timerWatchDog !== null) clearInterval(node.timerWatchDog); node.timerWatchDog = setInterval(handleTheDog, node.retryInterval); // 02/01/2020 Start the timer that handles the queue of telegrams node.setNodeStatus({ fill: "green", shape: "dot", text: "WatchDog started.", payload: "", GA: "", dpt: "", devicename: "" }) } // This function is called by the knx-ultimate config node, to output a msg.payload. node.handleSend = msg => { node.send(msg); }; node.on("input", function (msg) { if (typeof msg === "undefined") return; if (msg.hasOwnProperty("start")) { if (msg.start === true) { node.StartWatchDogTimer(); } else { if (node.timerWatchDog !== null) clearInterval(node.timerWatchDog); node.setNodeStatus({ fill: "grey", shape: "ring", text: "WatchDog stopped.", payload: "", GA: "", dpt: "", devicename: "" }) }; }; if (node.server === undefined) return; // 01/02/2020 Dinamic change of the KNX Gateway IP, Port and Physical Address // This new thing has been requested by proServ RealKNX staff. if (msg.hasOwnProperty("setGatewayConfig")) { node.server.setGatewayConfig(msg.setGatewayConfig.IP, msg.setGatewayConfig.Port, msg.setGatewayConfig.PhysicalAddress, msg.setGatewayConfig.BindToEthernetInterface, msg.setGatewayConfig.Protocol); let ret = { type: "setGatewayConfig", checkPerformed: "The Watchdog node changed the gateway configuration.", nodeid: node.id, payload: true, description: "New Config issued to the gateway. IP:" + (msg.setGatewayConfig.IP || "Unchanged") + " Port:" + (msg.setGatewayConfig.Port || "Unchanged") + " PhysicalAddress:" + (msg.setGatewayConfig.PhysicalAddress || "Unchanged") + " Protocol:" + (msg.setGatewayConfig.Protocol || "Unchanged") + " BindLocalInterface:" + (msg.setGatewayConfig.BindToEthernetInterface || "Unchanged"), completeError: "" }; node.send(ret); }; // 05/05/2021 force connection/disconnectio of the gateway if (msg.hasOwnProperty("connectGateway")) { node.server.connectGateway(msg.connectGateway); let ret = { type: "connectGateway", checkPerformed: "The Watchdog issued a connection/disconnection to the gateway.", nodeid: node.id, payload: msg.connectGateway, description: "Connection", completeError: "" }; node.send(ret); } }); node.on("close", function (done) { if (node.timerWatchDog !== null) clearInterval(node.timerWatchDog); if (node.server) { node.server.removeClient(node) }; done(); }); // On each deploy, unsubscribe+resubscribe // Unsubscribe(Subscribe) if (node.server) { if (node.timerWatchDog !== null) clearInterval(node.timerWatchDog); node.server.removeClient(node); if (node.topic || node.listenallga) { node.server.addClient(node); if (node.autoStart) node.StartWatchDogTimer(); // Autostart watchdog } } } RED.nodes.registerType("knxUltimateWatchDog", knxUltimateWatchDog) }