node-red-contrib-knx-ultimate 1.3.10 → 1.3.14

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,6 +4,22 @@
4
4
 
5
5
  <br/>
6
6
  <p>
7
+ <b>Version 1.3.14</b> - 26 December 2021<br/>
8
+ - KNXEngine: ACK management: the not acknowledged message will be re-transmitted once, then the connection will be dropped, as per KNX specs.<br/>
9
+ - KNXEngine: ACK management: the telegram's queue to be sent to the KNX BUS will be paused during the ACK waiting.<br/>
10
+ - KNXEngine: Routing: now the routing_busy and routing_lost_messages telegrams sent by the KNX/IP Router are handled.<br/>
11
+ </p>
12
+ <p>
13
+ <b>Version 1.3.13</b> - 25 December 2021<br/>
14
+ - KNXEngine: when in tunneling and suppress ACK request is disabled, the error is raised only after 3° failed ACK reception instead of 1°.<br/>
15
+ - KNXEngine: at disconnection, delete all pending ACK requests timer.<br/>
16
+ - 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/>
17
+ </p>
18
+ <p>
19
+ <b>Version 1.3.12</b> - December 2021<br/>
20
+ - KNX-Ultimate DEVICE node: added the validation of Group Address while deploy, with support for modern addressing up to 31/7/255.<br/>
21
+ </p>
22
+ <p>
7
23
  <b>Version 1.3.10</b> - December 2021<br/>
8
24
  - FIX: fixed a stupid "Disconnected by Message length mismatch 8/16" error due to a dumb find/replace error in the code.<br/>
9
25
  - Added some more log to help resolving issues.<br/>
@@ -86,7 +86,7 @@ class KNXClient extends EventEmitter {
86
86
  }
87
87
 
88
88
  super();
89
- this._clientTunnelSeqNumber = 0;
89
+ this._clientTunnelSeqNumber = -1;
90
90
  this._options = options;//Object.assign(optionsDefaults, options);
91
91
  this._options.connectionKeepAliveTimeout = KNXConstants.KNX_CONSTANTS.CONNECTION_ALIVE_TIME,
92
92
  this._localPort = null;
@@ -169,10 +169,11 @@ class KNXClient extends EventEmitter {
169
169
  });
170
170
  }
171
171
 
172
- this._clientTunnelSeqNumber = 0;
172
+ this._clientTunnelSeqNumber = -1;
173
173
  this._channelID = null;
174
174
  this._connectionState = STATE.DISCONNECTED;
175
- this._tunnelReqTimer = new Map();
175
+ this._tunnelReqTimer = null;
176
+ this._numFailedTelegramACK = 0; // 25/12/2021 Keep count of the failed tunnelig ACK telegrams
176
177
 
177
178
  }
178
179
  get channelID() {
@@ -239,6 +240,7 @@ class KNXClient extends EventEmitter {
239
240
  });
240
241
  }
241
242
  send(knxPacket) {
243
+
242
244
  // Logging
243
245
  if (this.sysLogger !== undefined && this.sysLogger !== null) {
244
246
  try {
@@ -341,9 +343,11 @@ class KNXClient extends EventEmitter {
341
343
  cEMIMessage.control.priority = 3;
342
344
  cEMIMessage.control.addressType = 1;
343
345
  cEMIMessage.control.hopCount = 6;
346
+ this._incSeqNumber(); // 26/12/2021
344
347
  const seqNum = this._getSeqNumber();
345
348
  const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
346
- if (!this._options.suppress_ack_ldatareq) this._setTimerAndCallback(knxPacketRequest);
349
+ if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
350
+ //if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("this._tunnelReqTimer "+ this._tunnelReqTimer.size);
347
351
  this.send(knxPacketRequest);
348
352
  // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
349
353
  try {
@@ -385,9 +389,10 @@ class KNXClient extends EventEmitter {
385
389
  cEMIMessage.control.priority = 3;
386
390
  cEMIMessage.control.addressType = 1;
387
391
  cEMIMessage.control.hopCount = 6;
392
+ this._incSeqNumber(); // 26/12/2021
388
393
  const seqNum = this._getSeqNumber();
389
394
  const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
390
- if (!this._options.suppress_ack_ldatareq) this._setTimerAndCallback(knxPacketRequest);
395
+ if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
391
396
  this.send(knxPacketRequest);
392
397
  // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
393
398
  try {
@@ -426,9 +431,10 @@ class KNXClient extends EventEmitter {
426
431
  cEMIMessage.control.priority = 3;
427
432
  cEMIMessage.control.addressType = 1;
428
433
  cEMIMessage.control.hopCount = 6;
434
+ this._incSeqNumber(); // 26/12/2021
429
435
  const seqNum = this._getSeqNumber();
430
436
  const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
431
- if (!this._options.suppress_ack_ldatareq) this._setTimerAndCallback(knxPacketRequest);
437
+ if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
432
438
  this.send(knxPacketRequest);
433
439
  // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
434
440
  try {
@@ -474,9 +480,10 @@ class KNXClient extends EventEmitter {
474
480
  cEMIMessage.control.priority = 3;
475
481
  cEMIMessage.control.addressType = 1;
476
482
  cEMIMessage.control.hopCount = 6;
483
+ this._incSeqNumber(); // 26/12/2021
477
484
  const seqNum = this._getSeqNumber();
478
485
  const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
479
- if (!this._options.suppress_ack_ldatareq) this._setTimerAndCallback(knxPacketRequest);
486
+ if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
480
487
  this.send(knxPacketRequest);
481
488
  // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
482
489
  try {
@@ -544,6 +551,8 @@ class KNXClient extends EventEmitter {
544
551
  }
545
552
 
546
553
  this._connectionState = STATE.CONNECTING;
554
+ this._numFailedTelegramACK = 0; // 25/12/2021 Reset the failed ACK counter
555
+ this._clearToSend = true; // 26/12/2021 allow to send
547
556
 
548
557
  if (this._timer !== null) clearTimeout(this._timer);
549
558
 
@@ -564,7 +573,7 @@ class KNXClient extends EventEmitter {
564
573
 
565
574
  }, 1000 * KNXConstants.KNX_CONSTANTS.CONNECT_REQUEST_TIMEOUT);
566
575
  this._awaitingResponseType = KNXConstants.KNX_CONSTANTS.CONNECT_RESPONSE;
567
- this._clientTunnelSeqNumber = 0;
576
+ this._clientTunnelSeqNumber = -1;
568
577
  this._sendConnectRequestMessage(new TunnelCRI.TunnelCRI(knxLayer));
569
578
 
570
579
  } else if (this._options.hostProtocol === "TunnelTCP") {
@@ -582,12 +591,11 @@ class KNXClient extends EventEmitter {
582
591
  if (conn._options.isSecureKNXEnabled) conn._sendSecureSessionRequestMessage(new TunnelCRI.TunnelCRI(knxLayer));
583
592
  });
584
593
 
585
-
586
594
  } else {
587
595
 
588
596
  // Multicast
589
597
  this._connectionState = STATE.CONNECTED;
590
- this._clientTunnelSeqNumber = 0;
598
+ this._clientTunnelSeqNumber = -1;
591
599
  try {
592
600
  this.emit(KNXClientEvents.connected, this._options);
593
601
  } catch (error) {
@@ -645,6 +653,7 @@ class KNXClient extends EventEmitter {
645
653
  if (this._timerTimeoutSendDisconnectRequestMessagetimer !== null) clearTimeout(this._timerTimeoutSendDisconnectRequestMessagetimer);
646
654
  this._timerTimeoutSendDisconnectRequestMessage = null;
647
655
  if (this._timer !== null) clearTimeout(this._timer);
656
+ if (this._tunnelReqTimer !== null) clearTimeout(this._tunnelReqTimer);
648
657
  this.stopHeartBeat();
649
658
  this._connectionState = STATE.DISCONNECTED;
650
659
  try {
@@ -652,9 +661,10 @@ class KNXClient extends EventEmitter {
652
661
  } catch (error) {
653
662
  }
654
663
 
655
- this._clientTunnelSeqNumber = 0;
664
+ this._clientTunnelSeqNumber = -1;
665
+ this._clearToSend = true; // 26/12/2021 allow to send
656
666
  this._channelID = null;
657
- this._tunnelReqTimer = new Map();
667
+
658
668
  // 08/12/2021
659
669
  try {
660
670
  this._clientSocket.close();
@@ -672,46 +682,77 @@ class KNXClient extends EventEmitter {
672
682
  _getSeqNumber() {
673
683
  return this._clientTunnelSeqNumber;
674
684
  }
685
+ // 26/12/2021 Handle the busy state, for example while waiting for ACK
686
+ _getClearToSend() {
687
+ return (this._clearToSend !== undefined ? this._clearToSend : true);
688
+ }
689
+
675
690
  _incSeqNumber(seq) {
676
- this._clientTunnelSeqNumber = seq ? seq + 1 : this._clientTunnelSeqNumber + 1;
691
+ this._clientTunnelSeqNumber++;
677
692
  if (this._clientTunnelSeqNumber > 255) {
678
693
  this._clientTunnelSeqNumber = 0;
679
694
  }
680
695
  return this._clientTunnelSeqNumber;
681
696
  }
682
-
683
697
  _keyFromCEMIMessage(cEMIMessage) {
684
698
  return cEMIMessage.dstAddress.toString();
685
699
  }
686
- _setTimerAndCallback(knxTunnelingRequest) {
700
+ _setTimerWaitingForACK(knxTunnelingRequest) {
687
701
  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
- this._tunnelReqTimer.set(knxTunnelingRequest.seqCounter, setTimeout(() => {
690
- this._tunnelReqTimer.delete(knxTunnelingRequest.seqCounter);
691
- if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("KNXClient: _setTimerAndCallback: " + (timeoutErr.message || "Undef error"));
702
+ if (this._tunnelReqTimer !== null) clearTimeout(this._tunnelReqTimer);
703
+ this._clearToSend = false; // 26/12/2021 stop sending until ACK received
704
+ this._tunnelReqTimer = setTimeout(() => {
692
705
  try {
693
- this.emit(KNXClientEvents.error, timeoutErr);
694
- } catch (error) {
695
- }
696
- }, KNXConstants.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000));
706
+ this._numFailedTelegramACK += 1;
707
+ if (this._numFailedTelegramACK > 2) {
708
+ this._numFailedTelegramACK = 0;
709
+ this._clearToSend = true;
710
+ this.emit(KNXClientEvents.error, timeoutErr);
711
+ } else {
712
+ // 26/12/2021 // If no ACK received, resend the datagram once with the same sequence number
713
+ this._setTimerWaitingForACK(knxTunnelingRequest);
714
+ this.send(knxTunnelingRequest);
715
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("KNXClient: _setTimerWaitingForACK: " + (timeoutErr.message || "Undef error") + " no ACK received. Retransmit datagram with seqNumber " + this._getSeqNumber());
716
+ }
717
+ } catch (error) { }
718
+ }, KNXConstants.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000);
719
+
697
720
  }
698
721
  _processInboundMessage(msg, rinfo) {
699
722
 
700
723
  try {
701
724
 
702
725
  // Composing debug string
703
- var sProcessInboundLog = "???";
704
- try {
705
- sProcessInboundLog = "Data received: " + msg.toString("hex");
706
- sProcessInboundLog += " srcAddress: " + JSON.stringify(rinfo);
707
- } catch (error) { }
708
726
  try {
709
- if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.trace("Received KNX packet: _processInboundMessage, " + sProcessInboundLog + " ChannelID:" + this._channelID || "??" + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
727
+ if (this.sysLogger !== undefined && this.sysLogger !== null) {
728
+ var sProcessInboundLog = "???";
729
+ try {
730
+ sProcessInboundLog = "Data received: " + msg.toString("hex");
731
+ sProcessInboundLog += " srcAddress: " + JSON.stringify(rinfo);
732
+ } catch (error) { }
733
+ this.sysLogger.trace("Received KNX packet: _processInboundMessage, " + sProcessInboundLog + " ChannelID:" + this._channelID || "??" + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
734
+ }
710
735
  } catch (error) { }
711
736
 
712
737
  const { knxHeader, knxMessage } = KNXProtocol.KNXProtocol.parseMessage(msg);
713
738
 
739
+ // 26/12/2021 ROUTING LOST MESSAGE OR BUSY
740
+ if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.ROUTING_LOST_MESSAGE) {
741
+ try {
742
+ this.emit(KNXClientEvents.error, new Error('ROUTING_LOST_MESSAGE'));
743
+ this._setDisconnected();
744
+ return;
745
+ } catch (error) { }
746
+ } else if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.ROUTING_BUSY) {
747
+ try {
748
+ this.emit(KNXClientEvents.error, new Error('ROUTING_BUSY'));
749
+ this._setDisconnected();
750
+ return;
751
+ } catch (error) { }
752
+ }
753
+
714
754
  if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.SEARCH_RESPONSE) {
755
+
715
756
  if (this._discovery_timer == null) {
716
757
  return;
717
758
  }
@@ -729,8 +770,7 @@ class KNXClient extends EventEmitter {
729
770
  if (knxConnectResponse.status !== KNXConstants.ConnectionStatus.E_NO_ERROR) {
730
771
  try {
731
772
  this.emit(KNXClientEvents.error, KNXConnectResponse.KNXConnectResponse.statusToString(knxConnectResponse.status));
732
- } catch (error) {
733
- }
773
+ } catch (error) { }
734
774
  this._setDisconnected();
735
775
  return;
736
776
  }
@@ -785,34 +825,38 @@ class KNXClient extends EventEmitter {
785
825
  if (knxTunnelingRequest.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_IND) {
786
826
 
787
827
  // Composing debug string
788
- let sDebugString = "???";
789
828
  try {
790
- sDebugString = "Data: " + JSON.stringify(knxTunnelingRequest.cEMIMessage.npdu);
791
- sDebugString += " srcAddress: " + knxTunnelingRequest.cEMIMessage.srcAddress.toString();
792
- sDebugString += " dstAddress: " + knxTunnelingRequest.cEMIMessage.dstAddress.toString();
793
- } catch (error) { }
794
-
795
- try {
796
- if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.debug("Received KNX packet: TUNNELING: L_DATA_IND, " + sDebugString + " ChannelID:" + this._channelID + " seqCounter:" + knxTunnelingRequest.seqCounter + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
829
+ if (this.sysLogger !== undefined && this.sysLogger !== null) {
830
+ let sDebugString = "???";
831
+ try {
832
+ sDebugString = "Data: " + JSON.stringify(knxTunnelingRequest.cEMIMessage.npdu);
833
+ sDebugString += " srcAddress: " + knxTunnelingRequest.cEMIMessage.srcAddress.toString();
834
+ sDebugString += " dstAddress: " + knxTunnelingRequest.cEMIMessage.dstAddress.toString();
835
+ } catch (error) { }
836
+ this.sysLogger.debug("Received KNX packet: TUNNELING: L_DATA_IND, " + sDebugString + " ChannelID:" + this._channelID + " seqCounter:" + knxTunnelingRequest.seqCounter + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
837
+ }
797
838
  } catch (error) { }
798
839
 
799
840
  try {
800
841
  this.emit(KNXClientEvents.indication, knxTunnelingRequest, false, msg.toString("hex"));
801
- } catch (error) {
802
- }
842
+ } catch (error) { }
803
843
 
804
- }
805
- else if (knxTunnelingRequest.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_CON) {
844
+ } else if (knxTunnelingRequest.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_CON) {
806
845
 
807
846
  try {
808
847
  if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.debug("Received KNX packet: TUNNELING: L_DATA_CON, ChannelID:" + this._channelID + " seqCounter:" + knxTunnelingRequest.seqCounter + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
809
848
  } catch (error) { }
810
849
 
811
850
  }
851
+
852
+ // 26/12/2021 send the ACK if the server requestet that
853
+ // Then REMOVED, because some interfaces sets the "ack request" always to 0 even if it needs ack.
854
+ //if (knxMessage.cEMIMessage.control.ack){
812
855
  const knxTunnelAck = KNXProtocol.KNXProtocol.newKNXTunnelingACK(knxTunnelingRequest.channelID, knxTunnelingRequest.seqCounter, KNXConstants.KNX_CONSTANTS.E_NO_ERROR);
813
856
  this.send(knxTunnelAck);
814
- }
815
- else if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.TUNNELING_ACK) {
857
+ //}
858
+
859
+ } else if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.TUNNELING_ACK) {
816
860
  //const knxTunnelingAck = lodash.cloneDeep(knxMessage);
817
861
  const knxTunnelingAck = knxMessage;
818
862
  if (knxTunnelingAck.channelID !== this._channelID) {
@@ -823,19 +867,17 @@ class KNXClient extends EventEmitter {
823
867
  if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.debug("Received KNX packet: TUNNELING: TUNNELING_ACK, ChannelID:" + this._channelID + " seqCounter:" + knxTunnelingAck.seqCounter + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
824
868
  } catch (error) { }
825
869
 
826
- this._incSeqNumber(knxTunnelingAck.seqCounter);
827
-
828
- 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);
831
- try {
832
- 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
- } catch (error) { }
834
- }
835
- else {
836
-
837
- // Avoid warning if the KNXEngine is set to ignore ACK's telegrams
838
- if (!this._options.suppress_ack_ldatareq) {
870
+ // Check the received ACK sequence number
871
+ if (!this._options.suppress_ack_ldatareq) {
872
+ if (knxTunnelingAck.seqCounter === this._getSeqNumber()) {
873
+ if (this._tunnelReqTimer !== null) clearTimeout(this._tunnelReqTimer);
874
+ this._numFailedTelegramACK = 0; // 25/12/2021 clear the current ACK failed telegram number
875
+ this._clearToSend = true; // I'm ready to send a new datagram now
876
+ try {
877
+ 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);
878
+ } catch (error) { }
879
+ } else {
880
+ // Inform that i received an ACK with an unexpected sequence number
839
881
  try {
840
882
  if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = " + knxTunnelingAck.seqCounter);
841
883
  } catch (error) { }
@@ -850,15 +892,16 @@ class KNXClient extends EventEmitter {
850
892
  if (knxRoutingInd.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_IND) {
851
893
 
852
894
  // Composing debug string
853
- let sDebugString = "???";
854
- try {
855
- sDebugString = "Data: " + JSON.stringify(knxRoutingInd.cEMIMessage.npdu);
856
- sDebugString += " srcAddress: " + knxRoutingInd.cEMIMessage.srcAddress.toString();
857
- sDebugString += " dstAddress: " + knxRoutingInd.cEMIMessage.dstAddress.toString();
858
- } catch (error) { }
859
-
860
895
  try {
861
- if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.debug("Received KNX packet: ROUTING: L_DATA_IND, " + sDebugString + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
896
+ if (this.sysLogger !== undefined && this.sysLogger !== null) {
897
+ let sDebugString = "???";
898
+ try {
899
+ sDebugString = "Data: " + JSON.stringify(knxRoutingInd.cEMIMessage.npdu);
900
+ sDebugString += " srcAddress: " + knxRoutingInd.cEMIMessage.srcAddress.toString();
901
+ sDebugString += " dstAddress: " + knxRoutingInd.cEMIMessage.dstAddress.toString();
902
+ } catch (error) { }
903
+ this.sysLogger.debug("Received KNX packet: ROUTING: L_DATA_IND, " + sDebugString + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
904
+ }
862
905
  } catch (error) { }
863
906
 
864
907
  try {
@@ -915,6 +958,7 @@ class KNXClient extends EventEmitter {
915
958
  } catch (error) { }
916
959
  try {
917
960
  this.emit(KNXClientEvents.error, e);
961
+ this._setDisconnected();
918
962
  } catch (error) { }
919
963
 
920
964
  }
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KNXConnectResponse = void 0;
4
- const KNXConstants_1 = require("./KNXConstants");
5
- const KNXPacket_1 = require("./KNXPacket");
6
- const HPAI_1 = require("./HPAI");
7
- const CRD_1 = require("./CRD");
8
- class KNXConnectResponse extends KNXPacket_1.KNXPacket {
4
+ const KNXConstants = require("./KNXConstants");
5
+ const KNXPacket = require("./KNXPacket");
6
+ const HPAI = require("./HPAI");
7
+ const CRD = require("./CRD");
8
+ class KNXConnectResponse extends KNXPacket.KNXPacket {
9
9
  constructor(channelID, status, hpai, crd) {
10
- super(KNXConstants_1.KNX_CONSTANTS.CONNECT_RESPONSE, hpai == null ? 2 : 2 + hpai.length + crd.length);
10
+ super(KNXConstants.KNX_CONSTANTS.CONNECT_RESPONSE, hpai == null ? 2 : 2 + hpai.length + crd.length);
11
11
  this.channelID = channelID;
12
12
  this.status = status;
13
13
  this.hpai = hpai;
@@ -21,27 +21,27 @@ class KNXConnectResponse extends KNXPacket_1.KNXPacket {
21
21
  const status = buffer.readUInt8(offset++);
22
22
  let hpai, crd;
23
23
  if (offset < buffer.length) {
24
- hpai = HPAI_1.HPAI.createFromBuffer(buffer, offset);
24
+ hpai = HPAI.HPAI.createFromBuffer(buffer, offset);
25
25
  offset += hpai.length;
26
- crd = CRD_1.CRD.createFromBuffer(buffer, offset);
26
+ crd = CRD.CRD.createFromBuffer(buffer, offset);
27
27
  }
28
28
  return new KNXConnectResponse(channelID, status, hpai, crd);
29
29
  }
30
30
  static statusToString(status) {
31
31
  switch (status) {
32
- case KNXConstants_1.KNX_CONSTANTS.E_SEQUENCE_NUMBER:
32
+ case KNXConstants.KNX_CONSTANTS.E_SEQUENCE_NUMBER:
33
33
  return 'Invalid Sequence Number';
34
- case KNXConstants_1.KNX_CONSTANTS.E_CONNECTION_TYPE:
34
+ case KNXConstants.KNX_CONSTANTS.E_CONNECTION_TYPE:
35
35
  return 'Invalid Connection Type';
36
- case KNXConstants_1.KNX_CONSTANTS.E_CONNECTION_OPTION:
36
+ case KNXConstants.KNX_CONSTANTS.E_CONNECTION_OPTION:
37
37
  return 'Invalid Connection Option';
38
- case KNXConstants_1.KNX_CONSTANTS.E_NO_MORE_CONNECTIONS:
38
+ case KNXConstants.KNX_CONSTANTS.E_NO_MORE_CONNECTIONS:
39
39
  return 'No More Connections';
40
- case KNXConstants_1.KNX_CONSTANTS.E_DATA_CONNECTION:
40
+ case KNXConstants.KNX_CONSTANTS.E_DATA_CONNECTION:
41
41
  return 'Invalid Data Connection';
42
- case KNXConstants_1.KNX_CONSTANTS.E_KNX_CONNECTION:
42
+ case KNXConstants.KNX_CONSTANTS.E_KNX_CONNECTION:
43
43
  return 'Invalid KNX Connection';
44
- case KNXConstants_1.KNX_CONSTANTS.E_TUNNELING_LAYER:
44
+ case KNXConstants.KNX_CONSTANTS.E_TUNNELING_LAYER:
45
45
  return 'Invalid Tunneling Layer';
46
46
  default:
47
47
  return `Unknown error ${status}`;
@@ -18,6 +18,7 @@ exports.KNX_CONSTANTS = {
18
18
  DEVICE_CONFIGURATION_ACK: 0x0311,
19
19
  TUNNELING_REQUEST: 0x0420,
20
20
  TUNNELING_ACK: 0x0421,
21
+ ROUTING_BUSY: 0x0532,
21
22
  ROUTING_INDICATION: 0x0530,
22
23
  ROUTING_LOST_MESSAGE: 0x0531,
23
24
  DEVICE_MGMT_CONNECTION: 0x03,
@@ -61,15 +62,15 @@ exports.KNX_CONSTANTS = {
61
62
  KNX_IP: '224.0.23.12',
62
63
  IPV4_ADDRESS_LENGTH: 4,
63
64
  // Search for KNX IP Secure Unicasts Setups
64
- SECURE_SEARCH_REQUEST : 0x20b,
65
- SECURE_SEARCH_RESPONSE : 0x20c,
65
+ SECURE_SEARCH_REQUEST: 0x20b,
66
+ SECURE_SEARCH_RESPONSE: 0x20c,
66
67
  // KNX IP Secure
67
- SECURE_WRAPPER : 0x0950,
68
- SECURE_SESSION_REQUEST : 0x0951,
69
- SECURE_SESSION_RESPONSE : 0x0952,
70
- SECURE_SESSION_AUTH : 0x0953,
71
- SECURE_SESSION_STATUS : 0x0954,
72
- SECURE_GROUP_SYNC : 0x0955
68
+ SECURE_WRAPPER: 0x0950,
69
+ SECURE_SESSION_REQUEST: 0x0951,
70
+ SECURE_SESSION_RESPONSE: 0x0952,
71
+ SECURE_SESSION_AUTH: 0x0953,
72
+ SECURE_SESSION_STATUS: 0x0954,
73
+ SECURE_GROUP_SYNC: 0x0955
73
74
  };
74
75
 
75
76
  var ConnectionStatus;
@@ -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;
@@ -82,7 +82,7 @@ class ControlField {
82
82
  }
83
83
  this.control2 = (this.control2 & 0xF0) | Number(format);
84
84
  }
85
- get framrFormat() {
85
+ get frameFormat() {
86
86
  return this.control2 & 0xF;
87
87
  }
88
88
  static get DEFAULT_CONTROL1() {
@@ -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: "" });
@@ -814,7 +801,7 @@ return msg;`, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-
814
801
  //function handleBusEvents(_evt, _src, _dest, _rawValue, _datagram, _isRepeated) {
815
802
  function handleBusEvents(_datagram, _echoed, _CEMI) {
816
803
 
817
-
804
+
818
805
  // _rawValue
819
806
  try {
820
807
  _rawValue = _datagram.cEMIMessage.npdu.dataValue;
@@ -848,7 +835,7 @@ return msg;`, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-
848
835
  // I'm receiving a telegram from the BUS
849
836
  try {
850
837
  var iStart = _datagram._header._headerLength; //+ 4;
851
- _cemiETS = _CEMI.substring(iStart*2 );
838
+ _cemiETS = _CEMI.substring(iStart * 2);
852
839
  //_cemiETS = datagram.cEMIMessage.srcAddress.toBuffer().toString("hex") + _datagram.cEMIMessage.dstAddress.toBuffer().toString("hex") + "01" + _datagram.cEMIMessage.npdu._tpci.toString(16)
853
840
  } catch (error) { }
854
841
 
@@ -1100,7 +1087,6 @@ return msg;`, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-
1100
1087
 
1101
1088
  function handleTelegramQueue() {
1102
1089
  if (node.knxConnection !== null || node.host.toUpperCase() === "EMULATE") {
1103
- //console.log("BANANA handleTelegramQueueSONO BLOCCATO ? ",node.lockHandleTelegramQueue, "CONNECTED ? ", node.linkStatus )
1104
1090
  if (node.lockHandleTelegramQueue === true) return; // Exits if the funtion is busy
1105
1091
  node.lockHandleTelegramQueue = true; // Lock the function. It cannot be called again until finished.
1106
1092
 
@@ -1111,6 +1097,15 @@ return msg;`, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-
1111
1097
  return;
1112
1098
  }
1113
1099
 
1100
+ // 26/12/2021 If the KNXEngine is busy waiting for telegram's ACK, exit
1101
+ if (!node.knxConnection._getClearToSend()) {
1102
+ node.lockHandleTelegramQueue = false; // Unlock the function
1103
+ if (node.telegramsQueue.length > 0) {
1104
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn("knxUltimate-config: handleTelegramQueue: the KNXEngine is busy or is waiting for a telegram ACK with seqNumner " + node.knxConnection._getSeqNumber() + ". Delay handling queue.");
1105
+ }
1106
+ return;
1107
+ }
1108
+
1114
1109
  // Retrieving oKNXMessage { grpaddr, payload,dpt,outputtype (write or response),nodecallerid (node caller)}. 06/03/2020 "Read" request does have the lower priority in the queue, so firstly, i search for "read" telegrams and i move it on the top of the queue pile.
1115
1110
  var aTelegramsFiltered = [];
1116
1111
  aTelegramsFiltered = node.telegramsQueue.filter(a => a.outputtype !== "read");
@@ -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.10",
3
+ "version": "1.3.14",
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",