node-red-contrib-knx-ultimate 1.3.9 → 1.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,17 @@
4
4
 
5
5
  <br/>
6
6
  <p>
7
- <b>Version 1.3.9</b> - December 2021<br/>
7
+ <b>Version 1.3.13</b> - 25 December 2021<br/>
8
+ - KNXEngine: when in tunneling and suppress ACK request is disabled, the error is raised only after 3° failed ACK reception instead of 1°.<br/>
9
+ - KNXEngine: at disconnection, delete all pending ACK requests timer.<br/>
10
+ - Warning: if you've suppressed the ACK requests, in some cases the node cannot detect the disconnection. In this case, please use the KNX Watchdog to detect the disconnecitons and reconnect.<br/>
11
+ </p>
12
+ <p>
13
+ <b>Version 1.3.12</b> - December 2021<br/>
14
+ - KNX-Ultimate DEVICE node: added the validation of Group Address while deploy, with support for modern addressing up to 31/7/255.<br/>
15
+ </p>
16
+ <p>
17
+ <b>Version 1.3.10</b> - December 2021<br/>
8
18
  - FIX: fixed a stupid "Disconnected by Message length mismatch 8/16" error due to a dumb find/replace error in the code.<br/>
9
19
  - Added some more log to help resolving issues.<br/>
10
20
  </p>
@@ -173,6 +173,7 @@ class KNXClient extends EventEmitter {
173
173
  this._channelID = null;
174
174
  this._connectionState = STATE.DISCONNECTED;
175
175
  this._tunnelReqTimer = new Map();
176
+ this._numFailedTelegramACK = 0; // 25/12/2021 Keep count of the failed tunnelig ACK telegrams
176
177
 
177
178
  }
178
179
  get channelID() {
@@ -544,6 +545,7 @@ class KNXClient extends EventEmitter {
544
545
  }
545
546
 
546
547
  this._connectionState = STATE.CONNECTING;
548
+ this._numFailedTelegramACK = 0; // 25/12/2021 Reset the failed ACK counter
547
549
 
548
550
  if (this._timer !== null) clearTimeout(this._timer);
549
551
 
@@ -652,9 +654,17 @@ class KNXClient extends EventEmitter {
652
654
  } catch (error) {
653
655
  }
654
656
 
657
+ // 25/12/2021 Stops all awaiting ACK timers
658
+ for (const [key, oTimer] of this._tunnelReqTimer.entries()) {
659
+ try {
660
+ clearTimeout(oTimer);
661
+ } catch (error) { }
662
+ }
663
+ this._tunnelReqTimer.clear();
655
664
  this._clientTunnelSeqNumber = 0;
656
665
  this._channelID = null;
657
666
  this._tunnelReqTimer = new Map();
667
+
658
668
  // 08/12/2021
659
669
  try {
660
670
  this._clientSocket.close();
@@ -685,12 +695,16 @@ class KNXClient extends EventEmitter {
685
695
  }
686
696
  _setTimerAndCallback(knxTunnelingRequest) {
687
697
  const timeoutErr = new errors.RequestTimeoutError(`RequestTimeoutError seqCounter:${knxTunnelingRequest.seqCounter}, DestAddr:${knxTunnelingRequest.cEMIMessage.dstAddress.toString() || "Non definito"}, AckRequested:${knxTunnelingRequest.cEMIMessage.control.ack}, timed out waiting telegram acknowledge by ${this._options.ipAddr || "No Peer host detected"}`);
688
- //const key = this._keyFromCEMIMessage(knxTunnelingRequest.cEMIMessage);
689
698
  this._tunnelReqTimer.set(knxTunnelingRequest.seqCounter, setTimeout(() => {
690
699
  this._tunnelReqTimer.delete(knxTunnelingRequest.seqCounter);
691
- if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("KNXClient: _setTimerAndCallback: " + (timeoutErr.message || "Undef error"));
692
700
  try {
693
- this.emit(KNXClientEvents.error, timeoutErr);
701
+ this._numFailedTelegramACK += 1;
702
+ if (this._numFailedTelegramACK > 3) {
703
+ this._numFailedTelegramACK = 0;
704
+ this.emit(KNXClientEvents.error, timeoutErr);
705
+ } else {
706
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("KNXClient: _setTimerAndCallback: " + (timeoutErr.message || "Undef error") + " " + this._numFailedTelegramACK + " of 3 times before emitting error.");
707
+ }
694
708
  } catch (error) {
695
709
  }
696
710
  }, KNXConstants.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000));
@@ -826,8 +840,11 @@ class KNXClient extends EventEmitter {
826
840
  this._incSeqNumber(knxTunnelingAck.seqCounter);
827
841
 
828
842
  if (this._tunnelReqTimer.has(knxTunnelingAck.seqCounter)) {
829
- if (this._tunnelReqTimer.get(knxTunnelingAck.seqCounter) !== null) clearTimeout(this._tunnelReqTimer.get(knxTunnelingAck.seqCounter));
830
- this._tunnelReqTimer.delete(knxTunnelingAck.seqCounter);
843
+ if (this._tunnelReqTimer.get(knxTunnelingAck.seqCounter) !== null) {
844
+ clearTimeout(this._tunnelReqTimer.get(knxTunnelingAck.seqCounter));
845
+ this._tunnelReqTimer.delete(knxTunnelingAck.seqCounter);
846
+ }
847
+ this._numFailedTelegramACK = 0; // 25/12/2021 clear the current ACK failed telegram number
831
848
  try {
832
849
  if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.debug("Received KNX packet: TUNNELING: DELETED_TUNNELING_ACK FROM PENDING ACK's, ChannelID:" + this._channelID + " seqCounter:" + knxTunnelingAck.seqCounter + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
833
850
  } catch (error) { }
@@ -29,10 +29,7 @@ const determineLogLevel = options => {
29
29
 
30
30
  module.exports = {
31
31
  get: function (options) {
32
- if (!logger) {
33
- //console.log('BANANA new logger, level',determineLogLevel(options),(options && options.debug && 'debug') ||
34
- //(options && options.loglevel) ||
35
- //'error');
32
+ if ((!logger) || (logger && options)) {
36
33
  logger = require('log-driver')({
37
34
  levels: ['silent', 'error', 'warn', 'info', 'debug', 'trace'],
38
35
  level: determineLogLevel(options),
@@ -58,7 +55,7 @@ module.exports = {
58
55
  }
59
56
  }
60
57
  })
61
- }
58
+ }
62
59
  return (logger)
63
60
  },
64
61
  destroy: function () {
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KNXHeader = void 0;
4
4
  const KNXConstants = require("./KNXConstants");
5
+ const sysLogger = require("./../KnxLog.js").get(); // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
6
+
5
7
 
6
8
  class KNXHeader {
7
9
  constructor(type, length) {
@@ -35,9 +37,9 @@ class KNXHeader {
35
37
  const length = buffer.readUInt16BE(offset);
36
38
  if (length !== buffer.length) {
37
39
  try {
38
- console.log(`Received KNX packet: KNXHeader: createFromBuffer: Message length mismatch ${length}/${buffer.length} Data processed: ${buffer.toString("hex") || "??"}`);
40
+ if (sysLogger !== undefined && sysLogger !== null) sysLogger.error(`Received KNX packet: KNXHeader: createFromBuffer: Message length mismatch ${length}/${buffer.length} Data processed: ${buffer.toString("hex") || "??"}`);
39
41
  } catch (error) { }
40
- // throw new Error(`Message length mismatch ${length}/${buffer.length} Data processed: ${buffer.toString("hex") || "??"}`);
42
+ // throw new Error(`Message length mismatch ${length}/${buffer.length} Data processed: ${buffer.toString("hex") || "??"}`);
41
43
  }
42
44
  return new KNXHeader(type, length - header_length);
43
45
  }
@@ -13,17 +13,36 @@ const splitIP = (ip, name = 'ip') => {
13
13
  };
14
14
  exports.splitIP = splitIP;
15
15
  const validateKNXAddress = (address, isGroup = false) => {
16
+ // 22/12/2021 Supergiovane: https://support.knx.org/hc/en-us/articles/115003188109-Group-Addresses
16
17
  if (typeof (address) === 'string') {
17
18
  const digits = address.split(/[./]/);
18
19
  if (digits.length < 2 || digits.length > 3) {
19
- throw new Error(`Invalid address format: ${address}`);
20
+ throw new Error(`Invalid address format: ${address} Only 3 level addresses are allowed`);
20
21
  }
22
+ if ((digits.length === 3 && address === "0/0/0") || (digits.length === 1 && address === "0/0")) throw new Error(`Invalid address: ${address}`);
23
+
21
24
  let count = 0;
22
25
  let newAddress = 0;
23
26
  for (let i = digits.length - 1; i >= 0; i--, count++) {
24
27
  const digit = Number(digits[i]);
25
- if (isNaN(digit) || (count > 1 && digit > 15) || (count === 0 && digit > 255)) {
26
- throw new Error(`Invalid digit at pos ${i} inside address: ${address}`);
28
+ if (isGroup && digits.length === 3) {
29
+ // Validating Group Address
30
+ if (isNaN(digit) || (count === 2 && digit > 31) || (count === 1 && digit > 7) || (count === 0 && digit > 255)) {
31
+ // 22/12/2021 Supergiovane disabled digits validation
32
+ throw new Error(`Invalid 3 levels GA digit ${digit} inside address: ${address}`);
33
+ }
34
+ } else if (isGroup && digits.length === 2) {
35
+ // Validating Group Address
36
+ if (isNaN(digit) || (count === 1 && digit > 31) || (count === 0 && digit > 2047)) {
37
+ // 22/12/2021 Supergiovane disabled digits validation
38
+ throw new Error(`Invalid 2 levels GA digit ${digit} inside address: ${address}`);
39
+ }
40
+ } else {
41
+ // Validating KNX Device Address
42
+ if (isNaN(digit) || (count > 1 && digit > 15) || (count === 0 && digit > 255)) {
43
+ // 22/12/2021 Supergiovane disabled digits validation
44
+ throw new Error(`Invalid Individual Address digit ${digit} inside address: ${address}`);
45
+ }
27
46
  }
28
47
  if (count === 0) {
29
48
  newAddress = digit;
@@ -8,7 +8,7 @@
8
8
  physAddr: { value: "15.15.22", required: true },
9
9
  hostProtocol: { value: "Multicast", required: false }, // TunnelUDP/TunnelTCP/Multicast
10
10
  // enable this option to suppress the acknowledge flag with outgoing L_Data.req requests. LoxOne needs this
11
- suppressACKRequest: { value: true },
11
+ suppressACKRequest: { value: false },
12
12
  csv: { value: "", required: false },
13
13
  KNXEthInterface: { value: "Auto" },
14
14
  KNXEthInterfaceManuallyInput: { value: "" },
@@ -425,19 +425,6 @@ return msg;`, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-
425
425
  node.addClient = (_Node) => {
426
426
  // Check if node already exists
427
427
  if (node.nodeClients.filter(x => x.id === _Node.id).length === 0) {
428
- // Check if the node has a valid topic and dpt
429
- if (_Node.listenallga === false) {
430
- if (_Node.topic === undefined || _Node.dpt === undefined) {
431
- _Node.setNodeStatus({ fill: "red", shape: "dot", text: "Empty Group Addr. or datapoint.", payload: "", GA: "", dpt: "", devicename: "" })
432
- return;
433
- } else {
434
- // topic must be in format x/x/x
435
- if (_Node.topic.split("\/").length < 3) {
436
- _Node.setNodeStatus({ fill: "red", shape: "dot", text: "Wrong group address (topic: " + _Node.topic + ") format.", payload: "", GA: "", dpt: "", devicename: "" })
437
- return;
438
- }
439
- }
440
- }
441
428
  // Add _Node to the clients array
442
429
  if (node.autoReconnect) {
443
430
  _Node.setNodeStatus({ fill: "grey", shape: "ring", text: "Node initialized.", payload: "", GA: "", dpt: "", devicename: "" });
@@ -1,5 +1,6 @@
1
1
  module.exports = function (RED) {
2
2
  const _ = require("lodash");
3
+ const KNXUtils = require("./../KNXEngine/protocol/KNXUtils");
3
4
 
4
5
  function knxUltimate(config) {
5
6
  RED.nodes.createNode(this, config)
@@ -57,15 +58,16 @@ module.exports = function (RED) {
57
58
  }
58
59
 
59
60
  // Check if the node has a valid topic and dpt
60
- if (node.listenallga == false) {
61
- if (typeof node.topic == "undefined" || typeof node.dpt == "undefined") {
61
+ if (node.listenallga === false) {
62
+ if (node.topic === undefined || node.dpt === undefined) {
62
63
  node.setNodeStatus({ fill: "red", shape: "dot", text: "Empty Group Addr. or datapoint.", payload: "", GA: "", dpt: "", devicename: "" })
63
64
  return;
64
65
  } else {
65
-
66
- // topic must be in formar x/x/x
67
- if (node.topic.split("\/").length < 3) {
68
- node.setNodeStatus({ fill: "red", shape: "dot", text: "Wrong group address format.", payload: "", GA: node.topic, dpt: "", devicename: "" })
66
+ // Validate the Address
67
+ try {
68
+ KNXUtils.validateKNXAddress(node.topic, true)
69
+ } catch (error) {
70
+ node.setNodeStatus({ fill: "red", shape: "dot", text: error.message, payload: "", GA: node.topic, dpt: "", devicename: "" })
69
71
  return;
70
72
  }
71
73
  }
@@ -23,8 +23,8 @@
23
23
  "show_date_status": "Tag und Uhrzeit des letzten Updates im Status anzeigen",
24
24
  "show_device_status": "Gerätenamen im Status anzeigen (\"Alle Gruppenadressen abhören\" erforderlich)",
25
25
  "show_datapoint_status": "Datenpunkt im Status anzeigen",
26
- "suppress_ack": "ACK-Anfrage unterdrücken",
27
- "suppress_ack_help": "Diese Option unterstützt die Kompatibilität mit der alten Firmware des IP-Interface SWG1 148-1AB22 von Siemens",
26
+ "suppress_ack": "ACK-Anfrage unterdrücken in Tunnel Modus",
27
+ "suppress_ack_help": "Diese Option unterstützt die Kompatibilität mit der alten Firmware des alten IP-Interfaces.Achtung: Der Knoten kann die Trennung vom Gateway möglicherweise nicht bemerken. Verwenden Sie in diesem Fall den Watchdog im Ethernet+KNX-Twisted-Pair Modus.",
28
28
  "ignoreTelegramsWithRepeatedFlag": "Wiederholt (R-Flag) telegramme vom BUS unterdrücken",
29
29
  "log_level": "Loglevel",
30
30
  "nodes_list_title": "Liste aller KNX-Ultimate Nodes in allen Flows",
@@ -23,8 +23,8 @@
23
23
  "show_date_status": "Show last update day and time in status",
24
24
  "show_device_status": "Show device name in status (require \"Listen all Group Address\" )",
25
25
  "show_datapoint_status": "Show datapoint in status",
26
- "suppress_ack": "Suppress ACK request",
27
- "suppress_ack_help": "The option above helps old KNX/IP Interfaces compatibility",
26
+ "suppress_ack": "Suppress ACK request in tunneling mode",
27
+ "suppress_ack_help": "The option above helps old KNX/IP Interfaces compatibility. Warning: the node may not notice the disconnection from the gateway. In this case, use the watchdog in Ethernet+KNX twisted pair mode.",
28
28
  "ignoreTelegramsWithRepeatedFlag": "Suppress repeated (R-Flag) telegrams fom BUS",
29
29
  "log_level": "Loglevel",
30
30
  "nodes_list_title": "List of your nodes in all flows",
@@ -23,8 +23,8 @@
23
23
  "show_date_status": "Visualizza data e ora ultimo aggiornamento, nello stato",
24
24
  "show_device_status": "Visualizza il nome del dispositivo nello stato (richiede \"Modalità universale\" )",
25
25
  "show_datapoint_status": "Visualizza il DPT nello stato",
26
- "suppress_ack": "Sopprimi richieste ACK",
27
- "suppress_ack_help": "L'opzione ACK sopra aiuta la compatibilità con vecchie interfacce",
26
+ "suppress_ack": "Sopprimi richieste ACK in modalità tunnel",
27
+ "suppress_ack_help": "L'opzione ACK sopra aiuta la compatibilità con vecchie interfacce. Attenzione: il nodo potrebbe non accorgersi della disconnessione al gateway. In questo caso, usare il watchdog in modalità Ethernet+KNX twisted pair.",
28
28
  "ignoreTelegramsWithRepeatedFlag": "Sopprimi telegrammi dal BUS ripetuti (Flag R)",
29
29
  "log_level": "Livello log",
30
30
  "nodes_list_title": "Elenco nodi in tutti i flows",
@@ -23,8 +23,8 @@
23
23
  "show_date_status": "在状态中显示上次更新日期和时间",
24
24
  "show_device_status": "在状态中显示设备的名称 (需要 \"监听所有组地址\" )",
25
25
  "show_datapoint_status": "在状态中显示数据类型",
26
- "suppress_ack": "抑制 ACK 请求",
27
- "suppress_ack_help": "上面的选项有助于旧的 KNX/IP 接口兼容性",
26
+ "suppress_ack": "抑制 ACK 请求 in tunneling mode",
27
+ "suppress_ack_help": "上面的选项有助于旧的 KNX/IP 接口兼容性 Warning: the node may not notice the disconnection from the gateway. In this case, use the watchdog in Ethernet+KNX twisted pair mode.",
28
28
  "ignoreTelegramsWithRepeatedFlag": "抑制重复的 (R-Flag) 来自总线的电报",
29
29
  "log_level": "日志等级",
30
30
  "nodes_list_title": "所有流中的节点列表",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-knx-ultimate",
3
- "version": "1.3.9",
3
+ "version": "1.3.13",
4
4
  "description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable.",
5
5
  "dependencies": {
6
6
  "fs": "0.0.1-security",