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 +11 -1
- package/KNXEngine/KNXClient.js +22 -5
- package/KNXEngine/KnxLog.js +2 -5
- package/KNXEngine/protocol/KNXHeader.js +4 -2
- package/KNXEngine/protocol/KNXUtils.js +22 -3
- package/nodes/knxUltimate-config.html +1 -1
- package/nodes/knxUltimate-config.js +0 -13
- package/nodes/knxUltimate.js +8 -6
- package/nodes/locales/de/knxUltimate-config.json +2 -2
- package/nodes/locales/en-US/knxUltimate-config.json +2 -2
- package/nodes/locales/it/knxUltimate-config.json +2 -2
- package/nodes/locales/zh-CN/knxUltimate-config.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
<br/>
|
|
6
6
|
<p>
|
|
7
|
-
<b>Version 1.3.
|
|
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>
|
package/KNXEngine/KNXClient.js
CHANGED
|
@@ -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.
|
|
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)
|
|
830
|
-
|
|
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) { }
|
package/KNXEngine/KnxLog.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 (
|
|
26
|
-
|
|
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:
|
|
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: "" });
|
package/nodes/knxUltimate.js
CHANGED
|
@@ -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
|
|
61
|
-
if (
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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-
|
|
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.
|
|
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",
|