node-red-contrib-knx-ultimate 1.3.13 → 1.3.18
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 +22 -0
- package/KNXEngine/KNXClient.js +138 -111
- package/KNXEngine/protocol/KNXConnectResponse.js +15 -15
- package/KNXEngine/protocol/KNXConstants.js +9 -8
- package/KNXEngine/protocol/KNXHeader.js +3 -0
- package/KNXEngine/protocol/KNXProtocol.js +4 -3
- package/KNXEngine/protocol/KNXSecureSessionRequest.js +12 -8
- package/KNXEngine/protocol/cEMI/ControlField.js +1 -1
- package/KNXEngine/protocol/cEMI/NPDU.js +30 -5
- package/README.md +14 -3
- package/img/wiki/SceneControllerSample1.png +0 -0
- package/img/wiki/SceneControllerSample2.png +0 -0
- package/nodes/knxUltimate-config.html +8 -8
- package/nodes/knxUltimate-config.js +54 -32
- package/nodes/knxUltimate.html +225 -204
- package/nodes/knxUltimate.js +1 -1
- package/nodes/knxUltimateGlobalContext.js +1 -1
- package/nodes/knxUltimateSceneController.js +3 -5
- package/nodes/locales/de/knxUltimate-config.json +1 -1
- package/nodes/locales/de/knxUltimate.json +1 -0
- package/nodes/locales/de/knxUltimateSceneController.json +1 -1
- package/nodes/locales/en-US/knxUltimate.json +1 -0
- package/nodes/locales/en-US/knxUltimateSceneController.json +1 -1
- package/nodes/locales/it/knxUltimate.json +1 -0
- package/nodes/locales/it/knxUltimateSceneController.json +1 -1
- package/nodes/locales/zh-CN/knxUltimate.json +1 -0
- package/nodes/locales/zh-CN/knxUltimateSceneController.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@
|
|
|
4
4
|
|
|
5
5
|
<br/>
|
|
6
6
|
<p>
|
|
7
|
+
<b>Version 1.3.18</b> - Januar 2022<br/>
|
|
8
|
+
- FIX: Scene Controller: fixed an issue preventing the node to work if you haven't set the RECALL and SAVE group addresses.<br/>
|
|
9
|
+
- WIKI: updated the samples in the scene controller node.<br/>
|
|
10
|
+
</p>
|
|
11
|
+
<p>
|
|
12
|
+
<b>Version 1.3.16</b> - Januar 2022<br/>
|
|
13
|
+
- KNXEngine: there are some weird KNX gateways out there, either sending malformed header or CEMI messages. Now KNX-Ultimate will simply ignore these bad messages. Prior, it was disconnecting.<br/>
|
|
14
|
+
- KNXEngine: KNX-Secure packets are silently discarded for now, until KNX Secure will be ready.<br/>
|
|
15
|
+
- KNXEngine: added more logs to for troubleshooting pourposes.<br/>
|
|
16
|
+
</p>
|
|
17
|
+
<p>
|
|
18
|
+
<b>Version 1.3.15</b> - Januar 2022<br/>
|
|
19
|
+
- KNXEngine: better handling of disconnection in UDP mode, allowing very old grandpa KNX/IP interfaces enough time to understand what's happening, avoiding it to go crazy.<br/>
|
|
20
|
+
- KNXEngine: corrected Curve Crypto in KNX-Secure (KNX Secure is not enabled yet!).<br/>
|
|
21
|
+
</p>
|
|
22
|
+
<p>
|
|
23
|
+
<b>Version 1.3.14</b> - 26 December 2021<br/>
|
|
24
|
+
- KNXEngine: ACK management: the not acknowledged message will be re-transmitted once, then the connection will be dropped, as per KNX specs.<br/>
|
|
25
|
+
- KNXEngine: ACK management: the telegram's queue to be sent to the KNX BUS will be paused during the ACK waiting.<br/>
|
|
26
|
+
- KNXEngine: Routing: now the routing_busy and routing_lost_messages telegrams sent by the KNX/IP Router are handled.<br/>
|
|
27
|
+
</p>
|
|
28
|
+
<p>
|
|
7
29
|
<b>Version 1.3.13</b> - 25 December 2021<br/>
|
|
8
30
|
- 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
31
|
- KNXEngine: at disconnection, delete all pending ACK requests timer.<br/>
|
package/KNXEngine/KNXClient.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Made with love by Supergiovane
|
|
2
|
+
|
|
1
3
|
"use strict";
|
|
2
4
|
|
|
3
5
|
|
|
@@ -17,7 +19,7 @@ const ipAddressHelper = require("./util/ipAddressHelper");
|
|
|
17
19
|
const KNXAddress = require("./protocol/KNXAddress").KNXAddress;
|
|
18
20
|
const KNXDataBuffer = require("./protocol/KNXDataBuffer").KNXDataBuffer;
|
|
19
21
|
const DPTLib = require('./dptlib');
|
|
20
|
-
const
|
|
22
|
+
const KNXsecureKeyring = require("./KNXsecureKeyring.js");
|
|
21
23
|
//const lodash = require("lodash");
|
|
22
24
|
|
|
23
25
|
var STATE;
|
|
@@ -74,7 +76,8 @@ const optionsDefaults = {
|
|
|
74
76
|
loglevel: "info",
|
|
75
77
|
localEchoInTunneling: true,
|
|
76
78
|
localIPAddress: "",
|
|
77
|
-
interface: ""
|
|
79
|
+
interface: "",
|
|
80
|
+
jKNXSecureKeyring: {}
|
|
78
81
|
};
|
|
79
82
|
|
|
80
83
|
class KNXClient extends EventEmitter {
|
|
@@ -86,13 +89,13 @@ class KNXClient extends EventEmitter {
|
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
super();
|
|
89
|
-
this._clientTunnelSeqNumber =
|
|
92
|
+
this._clientTunnelSeqNumber = -1;
|
|
90
93
|
this._options = options;//Object.assign(optionsDefaults, options);
|
|
91
94
|
this._options.connectionKeepAliveTimeout = KNXConstants.KNX_CONSTANTS.CONNECTION_ALIVE_TIME,
|
|
92
95
|
this._localPort = null;
|
|
93
96
|
this._peerHost = this._options.ipAddr;
|
|
94
97
|
this._peerPort = this._options.ipPort;
|
|
95
|
-
this.
|
|
98
|
+
this._connectionTimeoutTimer = null;
|
|
96
99
|
this._heartbeatFailures = 0;
|
|
97
100
|
this.max_HeartbeatFailures = 3;
|
|
98
101
|
this._heartbeatTimer = null;
|
|
@@ -101,6 +104,7 @@ class KNXClient extends EventEmitter {
|
|
|
101
104
|
this._processInboundMessage = this._processInboundMessage.bind(this);
|
|
102
105
|
this._clientSocket = null;
|
|
103
106
|
this.sysLogger = null;
|
|
107
|
+
this.jKNXSecureKeyring = this._options.jKNXSecureKeyring; // 28/12/2021 Contains the Keyring JSON object
|
|
104
108
|
try {
|
|
105
109
|
this.sysLogger = require("./KnxLog.js").get({ loglevel: this._options.loglevel }); // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
|
|
106
110
|
} catch (error) {
|
|
@@ -169,17 +173,17 @@ class KNXClient extends EventEmitter {
|
|
|
169
173
|
});
|
|
170
174
|
}
|
|
171
175
|
|
|
172
|
-
this._clientTunnelSeqNumber =
|
|
176
|
+
this._clientTunnelSeqNumber = -1;
|
|
173
177
|
this._channelID = null;
|
|
174
178
|
this._connectionState = STATE.DISCONNECTED;
|
|
175
|
-
this._tunnelReqTimer =
|
|
179
|
+
this._tunnelReqTimer = null;
|
|
176
180
|
this._numFailedTelegramACK = 0; // 25/12/2021 Keep count of the failed tunnelig ACK telegrams
|
|
177
181
|
|
|
178
182
|
}
|
|
179
183
|
get channelID() {
|
|
180
184
|
return this._channelID;
|
|
181
185
|
}
|
|
182
|
-
// Transform the plain value "data" into a KNXDataBuffer. The datapoints without "null" are SixBits
|
|
186
|
+
// 16/12/2021 Transform the plain value "data" into a KNXDataBuffer. The datapoints without "null" are SixBits
|
|
183
187
|
// dataPointsSixBits = {
|
|
184
188
|
// DPT1,
|
|
185
189
|
// DPT2,
|
|
@@ -240,6 +244,7 @@ class KNXClient extends EventEmitter {
|
|
|
240
244
|
});
|
|
241
245
|
}
|
|
242
246
|
send(knxPacket) {
|
|
247
|
+
|
|
243
248
|
// Logging
|
|
244
249
|
if (this.sysLogger !== undefined && this.sysLogger !== null) {
|
|
245
250
|
try {
|
|
@@ -279,7 +284,7 @@ class KNXClient extends EventEmitter {
|
|
|
279
284
|
} catch (error) {
|
|
280
285
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("Sending KNX packet: Send UDP Catch error: " + error.message + " " + typeof (knxPacket) + " seqCounter:" + knxPacket.seqCounter);
|
|
281
286
|
try {
|
|
282
|
-
|
|
287
|
+
this.emit(KNXClientEvents.error, error);
|
|
283
288
|
} catch (error) {
|
|
284
289
|
}
|
|
285
290
|
|
|
@@ -292,15 +297,12 @@ class KNXClient extends EventEmitter {
|
|
|
292
297
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("Sending KNX packet: Send TCP sending error: " + err.message || "Undef error");
|
|
293
298
|
this.emit(KNXClientEvents.error, err);
|
|
294
299
|
}
|
|
295
|
-
|
|
296
300
|
});
|
|
297
301
|
} catch (error) {
|
|
298
302
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("Sending KNX packet: Send TCP Catch error: " + error.message || "Undef error");
|
|
299
303
|
try {
|
|
300
|
-
|
|
301
|
-
} catch (error) {
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
+
this.emit(KNXClientEvents.error, error);
|
|
305
|
+
} catch (error) { }
|
|
304
306
|
}
|
|
305
307
|
}
|
|
306
308
|
}
|
|
@@ -342,9 +344,11 @@ class KNXClient extends EventEmitter {
|
|
|
342
344
|
cEMIMessage.control.priority = 3;
|
|
343
345
|
cEMIMessage.control.addressType = 1;
|
|
344
346
|
cEMIMessage.control.hopCount = 6;
|
|
347
|
+
this._incSeqNumber(); // 26/12/2021
|
|
345
348
|
const seqNum = this._getSeqNumber();
|
|
346
349
|
const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
|
|
347
|
-
if (!this._options.suppress_ack_ldatareq) this.
|
|
350
|
+
if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
|
|
351
|
+
//if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("this._tunnelReqTimer "+ this._tunnelReqTimer.size);
|
|
348
352
|
this.send(knxPacketRequest);
|
|
349
353
|
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
|
|
350
354
|
try {
|
|
@@ -386,9 +390,10 @@ class KNXClient extends EventEmitter {
|
|
|
386
390
|
cEMIMessage.control.priority = 3;
|
|
387
391
|
cEMIMessage.control.addressType = 1;
|
|
388
392
|
cEMIMessage.control.hopCount = 6;
|
|
393
|
+
this._incSeqNumber(); // 26/12/2021
|
|
389
394
|
const seqNum = this._getSeqNumber();
|
|
390
395
|
const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
|
|
391
|
-
if (!this._options.suppress_ack_ldatareq) this.
|
|
396
|
+
if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
|
|
392
397
|
this.send(knxPacketRequest);
|
|
393
398
|
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
|
|
394
399
|
try {
|
|
@@ -427,9 +432,10 @@ class KNXClient extends EventEmitter {
|
|
|
427
432
|
cEMIMessage.control.priority = 3;
|
|
428
433
|
cEMIMessage.control.addressType = 1;
|
|
429
434
|
cEMIMessage.control.hopCount = 6;
|
|
435
|
+
this._incSeqNumber(); // 26/12/2021
|
|
430
436
|
const seqNum = this._getSeqNumber();
|
|
431
437
|
const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
|
|
432
|
-
if (!this._options.suppress_ack_ldatareq) this.
|
|
438
|
+
if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
|
|
433
439
|
this.send(knxPacketRequest);
|
|
434
440
|
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
|
|
435
441
|
try {
|
|
@@ -475,9 +481,10 @@ class KNXClient extends EventEmitter {
|
|
|
475
481
|
cEMIMessage.control.priority = 3;
|
|
476
482
|
cEMIMessage.control.addressType = 1;
|
|
477
483
|
cEMIMessage.control.hopCount = 6;
|
|
484
|
+
this._incSeqNumber(); // 26/12/2021
|
|
478
485
|
const seqNum = this._getSeqNumber();
|
|
479
486
|
const knxPacketRequest = KNXProtocol.KNXProtocol.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage);
|
|
480
|
-
if (!this._options.suppress_ack_ldatareq) this.
|
|
487
|
+
if (!this._options.suppress_ack_ldatareq) this._setTimerWaitingForACK(knxPacketRequest);
|
|
481
488
|
this.send(knxPacketRequest);
|
|
482
489
|
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false
|
|
483
490
|
try {
|
|
@@ -523,8 +530,8 @@ class KNXClient extends EventEmitter {
|
|
|
523
530
|
if (this._clientSocket == null) {
|
|
524
531
|
throw new Error('No client socket defined');
|
|
525
532
|
}
|
|
526
|
-
this.
|
|
527
|
-
this.
|
|
533
|
+
this._connectionTimeoutTimer = setTimeout(() => {
|
|
534
|
+
this._connectionTimeoutTimer = null;
|
|
528
535
|
}, 1000 * KNXConstants.KNX_CONSTANTS.DEVICE_CONFIGURATION_REQUEST_TIMEOUT);
|
|
529
536
|
this._awaitingResponseType = KNXConstants.KNX_CONSTANTS.DESCRIPTION_RESPONSE;
|
|
530
537
|
this._sendDescriptionRequestMessage(host, port);
|
|
@@ -546,8 +553,9 @@ class KNXClient extends EventEmitter {
|
|
|
546
553
|
|
|
547
554
|
this._connectionState = STATE.CONNECTING;
|
|
548
555
|
this._numFailedTelegramACK = 0; // 25/12/2021 Reset the failed ACK counter
|
|
556
|
+
this._clearToSend = true; // 26/12/2021 allow to send
|
|
549
557
|
|
|
550
|
-
if (this.
|
|
558
|
+
if (this._connectionTimeoutTimer !== null) clearTimeout(this._connectionTimeoutTimer);
|
|
551
559
|
|
|
552
560
|
// Emit connecting
|
|
553
561
|
this.emit(KNXClientEvents.connecting, this._options);
|
|
@@ -557,8 +565,8 @@ class KNXClient extends EventEmitter {
|
|
|
557
565
|
|
|
558
566
|
// Unicast, need to explicitly create the connection
|
|
559
567
|
const timeoutError = new Error(`Connection timeout to ${this._peerHost}:${this._peerPort}`);
|
|
560
|
-
this.
|
|
561
|
-
this.
|
|
568
|
+
this._connectionTimeoutTimer = setTimeout(() => {
|
|
569
|
+
this._connectionTimeoutTimer = null;
|
|
562
570
|
try {
|
|
563
571
|
this.emit(KNXClientEvents.error, timeoutError);
|
|
564
572
|
} catch (error) {
|
|
@@ -566,7 +574,7 @@ class KNXClient extends EventEmitter {
|
|
|
566
574
|
|
|
567
575
|
}, 1000 * KNXConstants.KNX_CONSTANTS.CONNECT_REQUEST_TIMEOUT);
|
|
568
576
|
this._awaitingResponseType = KNXConstants.KNX_CONSTANTS.CONNECT_RESPONSE;
|
|
569
|
-
this._clientTunnelSeqNumber =
|
|
577
|
+
this._clientTunnelSeqNumber = -1;
|
|
570
578
|
this._sendConnectRequestMessage(new TunnelCRI.TunnelCRI(knxLayer));
|
|
571
579
|
|
|
572
580
|
} else if (this._options.hostProtocol === "TunnelTCP") {
|
|
@@ -581,15 +589,14 @@ class KNXClient extends EventEmitter {
|
|
|
581
589
|
// }, 1000 * KNXConstants.KNX_CONSTANTS.CONNECT_REQUEST_TIMEOUT);
|
|
582
590
|
conn._awaitingResponseType = KNXConstants.KNX_CONSTANTS.CONNECT_RESPONSE;
|
|
583
591
|
conn._clientTunnelSeqNumber = 0;
|
|
584
|
-
if (conn._options.isSecureKNXEnabled) conn._sendSecureSessionRequestMessage(new TunnelCRI.TunnelCRI(knxLayer));
|
|
592
|
+
if (conn._options.isSecureKNXEnabled) conn._sendSecureSessionRequestMessage(new TunnelCRI.TunnelCRI(knxLayer), conn.jKNXSecureKeyring);
|
|
585
593
|
});
|
|
586
594
|
|
|
587
|
-
|
|
588
595
|
} else {
|
|
589
596
|
|
|
590
597
|
// Multicast
|
|
591
598
|
this._connectionState = STATE.CONNECTED;
|
|
592
|
-
this._clientTunnelSeqNumber =
|
|
599
|
+
this._clientTunnelSeqNumber = -1;
|
|
593
600
|
try {
|
|
594
601
|
this.emit(KNXClientEvents.connected, this._options);
|
|
595
602
|
} catch (error) {
|
|
@@ -607,7 +614,7 @@ class KNXClient extends EventEmitter {
|
|
|
607
614
|
this._heartbeatTimer = setTimeout(() => {
|
|
608
615
|
this._heartbeatTimer = null;
|
|
609
616
|
try {
|
|
610
|
-
|
|
617
|
+
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("KNXClient: getConnectionStatus Timeout " + this._heartbeatFailures + " out of " + this.max_HeartbeatFailures);
|
|
611
618
|
//this.emit(KNXClientEvents.error, timeoutError);
|
|
612
619
|
} catch (error) {
|
|
613
620
|
}
|
|
@@ -631,14 +638,11 @@ class KNXClient extends EventEmitter {
|
|
|
631
638
|
}
|
|
632
639
|
this.stopHeartBeat();
|
|
633
640
|
this._connectionState = STATE.DISCONNECTING;
|
|
634
|
-
this._timer = setTimeout(() => {
|
|
635
|
-
this._timer = null;
|
|
636
|
-
}, 1000 * KNXConstants.KNX_CONSTANTS.CONNECT_REQUEST_TIMEOUT);
|
|
637
641
|
this._awaitingResponseType = KNXConstants.KNX_CONSTANTS.DISCONNECT_RESPONSE;
|
|
638
642
|
this._sendDisconnectRequestMessage(this._channelID);
|
|
639
|
-
this._timerTimeoutSendDisconnectRequestMessage = setTimeout(() => {
|
|
640
|
-
|
|
641
|
-
}, 1000 * KNXConstants.KNX_CONSTANTS.CONNECT_REQUEST_TIMEOUT);
|
|
643
|
+
//this._timerTimeoutSendDisconnectRequestMessage = setTimeout(() => {
|
|
644
|
+
this._setDisconnected();
|
|
645
|
+
//}, 1000 * KNXConstants.KNX_CONSTANTS.CONNECT_REQUEST_TIMEOUT);
|
|
642
646
|
}
|
|
643
647
|
isConnected() {
|
|
644
648
|
return this._connectionState === STATE.CONNECTED;
|
|
@@ -646,7 +650,8 @@ class KNXClient extends EventEmitter {
|
|
|
646
650
|
_setDisconnected() {
|
|
647
651
|
if (this._timerTimeoutSendDisconnectRequestMessagetimer !== null) clearTimeout(this._timerTimeoutSendDisconnectRequestMessagetimer);
|
|
648
652
|
this._timerTimeoutSendDisconnectRequestMessage = null;
|
|
649
|
-
if (this.
|
|
653
|
+
if (this._connectionTimeoutTimer !== null) clearTimeout(this._connectionTimeoutTimer);
|
|
654
|
+
if (this._tunnelReqTimer !== null) clearTimeout(this._tunnelReqTimer);
|
|
650
655
|
this.stopHeartBeat();
|
|
651
656
|
this._connectionState = STATE.DISCONNECTED;
|
|
652
657
|
try {
|
|
@@ -654,16 +659,9 @@ class KNXClient extends EventEmitter {
|
|
|
654
659
|
} catch (error) {
|
|
655
660
|
}
|
|
656
661
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
try {
|
|
660
|
-
clearTimeout(oTimer);
|
|
661
|
-
} catch (error) { }
|
|
662
|
-
}
|
|
663
|
-
this._tunnelReqTimer.clear();
|
|
664
|
-
this._clientTunnelSeqNumber = 0;
|
|
662
|
+
this._clientTunnelSeqNumber = -1;
|
|
663
|
+
this._clearToSend = true; // 26/12/2021 allow to send
|
|
665
664
|
this._channelID = null;
|
|
666
|
-
this._tunnelReqTimer = new Map();
|
|
667
665
|
|
|
668
666
|
// 08/12/2021
|
|
669
667
|
try {
|
|
@@ -682,50 +680,79 @@ class KNXClient extends EventEmitter {
|
|
|
682
680
|
_getSeqNumber() {
|
|
683
681
|
return this._clientTunnelSeqNumber;
|
|
684
682
|
}
|
|
683
|
+
// 26/12/2021 Handle the busy state, for example while waiting for ACK
|
|
684
|
+
_getClearToSend() {
|
|
685
|
+
return (this._clearToSend !== undefined ? this._clearToSend : true);
|
|
686
|
+
}
|
|
687
|
+
|
|
685
688
|
_incSeqNumber(seq) {
|
|
686
|
-
this._clientTunnelSeqNumber
|
|
689
|
+
this._clientTunnelSeqNumber++;
|
|
687
690
|
if (this._clientTunnelSeqNumber > 255) {
|
|
688
691
|
this._clientTunnelSeqNumber = 0;
|
|
689
692
|
}
|
|
690
693
|
return this._clientTunnelSeqNumber;
|
|
691
694
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
_setTimerAndCallback(knxTunnelingRequest) {
|
|
695
|
+
// _keyFromCEMIMessage(cEMIMessage) {
|
|
696
|
+
// return cEMIMessage.dstAddress.toString();
|
|
697
|
+
// }
|
|
698
|
+
_setTimerWaitingForACK(knxTunnelingRequest) {
|
|
697
699
|
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"}`);
|
|
698
|
-
this._tunnelReqTimer
|
|
699
|
-
|
|
700
|
+
if (this._tunnelReqTimer !== null) clearTimeout(this._tunnelReqTimer);
|
|
701
|
+
this._clearToSend = false; // 26/12/2021 stop sending until ACK received
|
|
702
|
+
this._tunnelReqTimer = setTimeout(() => {
|
|
700
703
|
try {
|
|
701
704
|
this._numFailedTelegramACK += 1;
|
|
702
|
-
if (this._numFailedTelegramACK >
|
|
705
|
+
if (this._numFailedTelegramACK > 2) {
|
|
703
706
|
this._numFailedTelegramACK = 0;
|
|
707
|
+
this._clearToSend = true;
|
|
704
708
|
this.emit(KNXClientEvents.error, timeoutErr);
|
|
705
709
|
} else {
|
|
706
|
-
|
|
710
|
+
// 26/12/2021 // If no ACK received, resend the datagram once with the same sequence number
|
|
711
|
+
this._setTimerWaitingForACK(knxTunnelingRequest);
|
|
712
|
+
this.send(knxTunnelingRequest);
|
|
713
|
+
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());
|
|
707
714
|
}
|
|
708
|
-
} catch (error) {
|
|
709
|
-
|
|
710
|
-
|
|
715
|
+
} catch (error) { }
|
|
716
|
+
}, KNXConstants.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000);
|
|
717
|
+
|
|
711
718
|
}
|
|
712
719
|
_processInboundMessage(msg, rinfo) {
|
|
713
720
|
|
|
714
721
|
try {
|
|
715
|
-
|
|
716
722
|
// Composing debug string
|
|
717
|
-
var sProcessInboundLog = "???";
|
|
718
723
|
try {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
+
if (this.sysLogger !== undefined && this.sysLogger !== null) {
|
|
725
|
+
var sProcessInboundLog = "???";
|
|
726
|
+
try {
|
|
727
|
+
sProcessInboundLog = "Data received: " + msg.toString("hex");
|
|
728
|
+
sProcessInboundLog += " srcAddress: " + JSON.stringify(rinfo);
|
|
729
|
+
} catch (error) { }
|
|
730
|
+
this.sysLogger.trace("Received KNX packet: _processInboundMessage, " + sProcessInboundLog + " ChannelID:" + this._channelID || "??" + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
|
|
731
|
+
}
|
|
724
732
|
} catch (error) { }
|
|
725
733
|
|
|
734
|
+
// BUGFIXING https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/162
|
|
735
|
+
//msg = Buffer.from("0610053000102900b06011fe11150080","hex");
|
|
736
|
+
|
|
726
737
|
const { knxHeader, knxMessage } = KNXProtocol.KNXProtocol.parseMessage(msg);
|
|
727
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
|
+
|
|
728
754
|
if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.SEARCH_RESPONSE) {
|
|
755
|
+
|
|
729
756
|
if (this._discovery_timer == null) {
|
|
730
757
|
return;
|
|
731
758
|
}
|
|
@@ -737,14 +764,13 @@ class KNXClient extends EventEmitter {
|
|
|
737
764
|
}
|
|
738
765
|
else if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.CONNECT_RESPONSE) {
|
|
739
766
|
if (this._connectionState === STATE.CONNECTING) {
|
|
740
|
-
if (this.
|
|
741
|
-
this.
|
|
767
|
+
if (this._connectionTimeoutTimer !== null) clearTimeout(this._connectionTimeoutTimer);
|
|
768
|
+
this._connectionTimeoutTimer = null;
|
|
742
769
|
const knxConnectResponse = knxMessage;
|
|
743
770
|
if (knxConnectResponse.status !== KNXConstants.ConnectionStatus.E_NO_ERROR) {
|
|
744
771
|
try {
|
|
745
772
|
this.emit(KNXClientEvents.error, KNXConnectResponse.KNXConnectResponse.statusToString(knxConnectResponse.status));
|
|
746
|
-
} catch (error) {
|
|
747
|
-
}
|
|
773
|
+
} catch (error) { }
|
|
748
774
|
this._setDisconnected();
|
|
749
775
|
return;
|
|
750
776
|
}
|
|
@@ -799,34 +825,38 @@ class KNXClient extends EventEmitter {
|
|
|
799
825
|
if (knxTunnelingRequest.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_IND) {
|
|
800
826
|
|
|
801
827
|
// Composing debug string
|
|
802
|
-
let sDebugString = "???";
|
|
803
828
|
try {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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
|
+
}
|
|
811
838
|
} catch (error) { }
|
|
812
839
|
|
|
813
840
|
try {
|
|
814
841
|
this.emit(KNXClientEvents.indication, knxTunnelingRequest, false, msg.toString("hex"));
|
|
815
|
-
} catch (error) {
|
|
816
|
-
}
|
|
842
|
+
} catch (error) { }
|
|
817
843
|
|
|
818
|
-
}
|
|
819
|
-
else if (knxTunnelingRequest.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_CON) {
|
|
844
|
+
} else if (knxTunnelingRequest.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_CON) {
|
|
820
845
|
|
|
821
846
|
try {
|
|
822
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);
|
|
823
848
|
} catch (error) { }
|
|
824
849
|
|
|
825
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){
|
|
826
855
|
const knxTunnelAck = KNXProtocol.KNXProtocol.newKNXTunnelingACK(knxTunnelingRequest.channelID, knxTunnelingRequest.seqCounter, KNXConstants.KNX_CONSTANTS.E_NO_ERROR);
|
|
827
856
|
this.send(knxTunnelAck);
|
|
828
|
-
|
|
829
|
-
|
|
857
|
+
//}
|
|
858
|
+
|
|
859
|
+
} else if (knxHeader.service_type === KNXConstants.KNX_CONSTANTS.TUNNELING_ACK) {
|
|
830
860
|
//const knxTunnelingAck = lodash.cloneDeep(knxMessage);
|
|
831
861
|
const knxTunnelingAck = knxMessage;
|
|
832
862
|
if (knxTunnelingAck.channelID !== this._channelID) {
|
|
@@ -837,22 +867,17 @@ class KNXClient extends EventEmitter {
|
|
|
837
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);
|
|
838
868
|
} catch (error) { }
|
|
839
869
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
this.
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
}
|
|
852
|
-
else {
|
|
853
|
-
|
|
854
|
-
// Avoid warning if the KNXEngine is set to ignore ACK's telegrams
|
|
855
|
-
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
|
|
856
881
|
try {
|
|
857
882
|
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = " + knxTunnelingAck.seqCounter);
|
|
858
883
|
} catch (error) { }
|
|
@@ -867,15 +892,16 @@ class KNXClient extends EventEmitter {
|
|
|
867
892
|
if (knxRoutingInd.cEMIMessage.msgCode === CEMIConstants.CEMIConstants.L_DATA_IND) {
|
|
868
893
|
|
|
869
894
|
// Composing debug string
|
|
870
|
-
let sDebugString = "???";
|
|
871
|
-
try {
|
|
872
|
-
sDebugString = "Data: " + JSON.stringify(knxRoutingInd.cEMIMessage.npdu);
|
|
873
|
-
sDebugString += " srcAddress: " + knxRoutingInd.cEMIMessage.srcAddress.toString();
|
|
874
|
-
sDebugString += " dstAddress: " + knxRoutingInd.cEMIMessage.dstAddress.toString();
|
|
875
|
-
} catch (error) { }
|
|
876
|
-
|
|
877
895
|
try {
|
|
878
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null)
|
|
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
|
+
}
|
|
879
905
|
} catch (error) { }
|
|
880
906
|
|
|
881
907
|
try {
|
|
@@ -916,7 +942,7 @@ class KNXClient extends EventEmitter {
|
|
|
916
942
|
}
|
|
917
943
|
}
|
|
918
944
|
else {
|
|
919
|
-
if (this.
|
|
945
|
+
if (this._connectionTimeoutTimer !== null) clearTimeout(this._connectionTimeoutTimer);
|
|
920
946
|
}
|
|
921
947
|
}
|
|
922
948
|
try {
|
|
@@ -928,10 +954,12 @@ class KNXClient extends EventEmitter {
|
|
|
928
954
|
}
|
|
929
955
|
catch (e) {
|
|
930
956
|
try {
|
|
931
|
-
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("Received KNX packet: Error processing inbound message: " + e.message + " " + sProcessInboundLog + " ChannelID:" + this._channelID + " Host:" + this._options.ipAddr + ":" + this._options.ipPort);
|
|
957
|
+
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error("Received KNX packet: Error processing inbound message: " + e.message + " " + sProcessInboundLog + " ChannelID:" + this._channelID + " Host:" + this._options.ipAddr + ":" + this._options.ipPort + ". This means that KNX-Ultimate received a malformed Header or CEMI message from your KNX Gateway.");
|
|
932
958
|
} catch (error) { }
|
|
933
959
|
try {
|
|
934
|
-
|
|
960
|
+
// 05/01/2022 Avoid disconnecting, because there are many bugged knx gateways out there!
|
|
961
|
+
//this.emit(KNXClientEvents.error, e);
|
|
962
|
+
//this._setDisconnected();
|
|
935
963
|
} catch (error) { }
|
|
936
964
|
|
|
937
965
|
}
|
|
@@ -942,7 +970,6 @@ class KNXClient extends EventEmitter {
|
|
|
942
970
|
this.send(KNXProtocol.KNXProtocol.newKNXDescriptionRequest(new HPAI.HPAI(this._options.localIPAddress)));
|
|
943
971
|
}
|
|
944
972
|
_sendSearchRequestMessage() {
|
|
945
|
-
console.log('_sendSearchRequestMessage', this._options.localIPAddress, this._localPort);
|
|
946
973
|
this.send(KNXProtocol.KNXProtocol.newKNXSearchRequest(new HPAI.HPAI(this._options.localIPAddress, this._localPort)), KNXConstants.KNX_CONSTANTS.KNX_PORT, KNXConstants.KNX_CONSTANTS.KNX_IP);
|
|
947
974
|
}
|
|
948
975
|
_sendConnectRequestMessage(cri) {
|
|
@@ -957,9 +984,9 @@ class KNXClient extends EventEmitter {
|
|
|
957
984
|
_sendDisconnectResponseMessage(channelID, status = KNXConstants.ConnectionStatus.E_NO_ERROR) {
|
|
958
985
|
this.send(KNXProtocol.KNXProtocol.newKNXDisconnectResponse(channelID, status));
|
|
959
986
|
}
|
|
960
|
-
_sendSecureSessionRequestMessage(cri) {
|
|
987
|
+
_sendSecureSessionRequestMessage(cri, jKNXSecureKeyring) {
|
|
961
988
|
let oHPAI = new HPAI.HPAI("0.0.0.0", 0, this._options.hostProtocol === "TunnelTCP" ? KNXConstants.KNX_CONSTANTS.IPV4_TCP : KNXConstants.KNX_CONSTANTS.IPV4_UDP);
|
|
962
|
-
this.send(KNXProtocol.KNXProtocol.newKNXSecureSessionRequest(cri, oHPAI));
|
|
989
|
+
this.send(KNXProtocol.KNXProtocol.newKNXSecureSessionRequest(cri, oHPAI, jKNXSecureKeyring));
|
|
963
990
|
}
|
|
964
991
|
}
|
|
965
992
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.KNXConnectResponse = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
class KNXConnectResponse extends
|
|
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(
|
|
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 =
|
|
24
|
+
hpai = HPAI.HPAI.createFromBuffer(buffer, offset);
|
|
25
25
|
offset += hpai.length;
|
|
26
|
-
crd =
|
|
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
|
|
32
|
+
case KNXConstants.KNX_CONSTANTS.E_SEQUENCE_NUMBER:
|
|
33
33
|
return 'Invalid Sequence Number';
|
|
34
|
-
case
|
|
34
|
+
case KNXConstants.KNX_CONSTANTS.E_CONNECTION_TYPE:
|
|
35
35
|
return 'Invalid Connection Type';
|
|
36
|
-
case
|
|
36
|
+
case KNXConstants.KNX_CONSTANTS.E_CONNECTION_OPTION:
|
|
37
37
|
return 'Invalid Connection Option';
|
|
38
|
-
case
|
|
38
|
+
case KNXConstants.KNX_CONSTANTS.E_NO_MORE_CONNECTIONS:
|
|
39
39
|
return 'No More Connections';
|
|
40
|
-
case
|
|
40
|
+
case KNXConstants.KNX_CONSTANTS.E_DATA_CONNECTION:
|
|
41
41
|
return 'Invalid Data Connection';
|
|
42
|
-
case
|
|
42
|
+
case KNXConstants.KNX_CONSTANTS.E_KNX_CONNECTION:
|
|
43
43
|
return 'Invalid KNX Connection';
|
|
44
|
-
case
|
|
44
|
+
case KNXConstants.KNX_CONSTANTS.E_TUNNELING_LAYER:
|
|
45
45
|
return 'Invalid Tunneling Layer';
|
|
46
46
|
default:
|
|
47
47
|
return `Unknown error ${status}`;
|