node-red-contrib-knx-ultimate 5.2.5 → 6.0.0

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/nodes/commonFunctions.js +40 -3
  3. package/nodes/knxUltimate-config.js +379 -335
  4. package/nodes/knxUltimate.html +8 -9
  5. package/nodes/knxUltimate.js +5 -5
  6. package/nodes/knxUltimateAI.js +1 -1
  7. package/nodes/knxUltimateHueLight.html +3 -3
  8. package/nodes/knxUltimateHueLight.js +1 -1
  9. package/nodes/knxUltimateMatterBridge.html +321 -247
  10. package/nodes/knxUltimateMatterBridge.js +48 -1
  11. package/nodes/knxUltimateMatterControllerDevice.html +2119 -0
  12. package/nodes/knxUltimateMatterControllerDevice.js +1701 -0
  13. package/nodes/locales/de/knxUltimateMatterBridge.html +6 -2
  14. package/nodes/locales/de/knxUltimateMatterBridge.json +14 -1
  15. package/nodes/locales/de/knxUltimateMatterControllerDevice.html +27 -0
  16. package/nodes/locales/de/knxUltimateMatterControllerDevice.json +97 -0
  17. package/nodes/locales/de/matter-config.json +5 -1
  18. package/nodes/locales/de/matterbridge-config.json +1 -0
  19. package/nodes/locales/en/knxUltimateMatterBridge.html +6 -2
  20. package/nodes/locales/en/knxUltimateMatterBridge.json +14 -1
  21. package/nodes/locales/en/knxUltimateMatterControllerDevice.html +27 -0
  22. package/nodes/locales/en/knxUltimateMatterControllerDevice.json +97 -0
  23. package/nodes/locales/en/matter-config.json +6 -1
  24. package/nodes/locales/en/matterbridge-config.json +1 -0
  25. package/nodes/locales/es/knxUltimateMatterBridge.html +6 -2
  26. package/nodes/locales/es/knxUltimateMatterBridge.json +14 -1
  27. package/nodes/locales/es/knxUltimateMatterControllerDevice.html +27 -0
  28. package/nodes/locales/es/knxUltimateMatterControllerDevice.json +97 -0
  29. package/nodes/locales/es/matter-config.json +5 -1
  30. package/nodes/locales/es/matterbridge-config.json +1 -0
  31. package/nodes/locales/fr/knxUltimateMatterBridge.html +6 -2
  32. package/nodes/locales/fr/knxUltimateMatterBridge.json +14 -1
  33. package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +27 -0
  34. package/nodes/locales/fr/knxUltimateMatterControllerDevice.json +89 -0
  35. package/nodes/locales/fr/matter-config.json +5 -1
  36. package/nodes/locales/fr/matterbridge-config.json +1 -0
  37. package/nodes/locales/it/knxUltimateMatterBridge.html +6 -2
  38. package/nodes/locales/it/knxUltimateMatterBridge.json +14 -1
  39. package/nodes/locales/it/knxUltimateMatterControllerDevice.html +27 -0
  40. package/nodes/locales/it/knxUltimateMatterControllerDevice.json +97 -0
  41. package/nodes/locales/it/matter-config.json +6 -1
  42. package/nodes/locales/it/matterbridge-config.json +1 -0
  43. package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +6 -2
  44. package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +14 -1
  45. package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +27 -0
  46. package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.json +89 -0
  47. package/nodes/locales/zh-CN/matter-config.json +5 -1
  48. package/nodes/locales/zh-CN/matterbridge-config.json +1 -0
  49. package/nodes/matter-config.html +58 -6
  50. package/nodes/matter-config.js +7 -2
  51. package/nodes/matterbridge-config.html +1 -0
  52. package/nodes/utils/lightEngines/canonicalLight.js +75 -0
  53. package/nodes/utils/lightEngines/hueLightEngine.js +51 -0
  54. package/nodes/utils/lightEngines/index.js +25 -0
  55. package/nodes/utils/lightEngines/knxLightCommand.js +79 -0
  56. package/nodes/utils/lightEngines/lightEngine.js +46 -0
  57. package/nodes/utils/lightEngines/matterHueShim.js +59 -0
  58. package/nodes/utils/lightEngines/matterLightEngine.js +117 -0
  59. package/nodes/utils/matterEngine.mjs +185 -14
  60. package/nodes/utils/matterKnxConverter.js +1 -1
  61. package/package.json +11 -3
  62. package/resources/KNXSendSnippets.js +53 -53
  63. package/resources/htmlUtils.js +6 -4
  64. package/nodes/knxUltimateMatterDevice.html +0 -632
  65. package/nodes/knxUltimateMatterDevice.js +0 -308
  66. package/nodes/locales/de/knxUltimateMatterDevice.html +0 -56
  67. package/nodes/locales/de/knxUltimateMatterDevice.json +0 -109
  68. package/nodes/locales/en/knxUltimateMatterDevice.html +0 -56
  69. package/nodes/locales/en/knxUltimateMatterDevice.json +0 -109
  70. package/nodes/locales/es/knxUltimateMatterDevice.html +0 -56
  71. package/nodes/locales/es/knxUltimateMatterDevice.json +0 -109
  72. package/nodes/locales/fr/knxUltimateMatterDevice.html +0 -56
  73. package/nodes/locales/fr/knxUltimateMatterDevice.json +0 -109
  74. package/nodes/locales/it/knxUltimateMatterDevice.html +0 -56
  75. package/nodes/locales/it/knxUltimateMatterDevice.json +0 -109
  76. package/nodes/locales/zh-CN/knxUltimateMatterDevice.html +0 -56
  77. package/nodes/locales/zh-CN/knxUltimateMatterDevice.json +0 -109
@@ -879,6 +879,19 @@ module.exports = (RED) => {
879
879
  }
880
880
  }
881
881
 
882
+ function getUniversalNodeAcceptedGAs (_oNode) {
883
+ const raw = _oNode?.knxUltimateAcceptedGAs
884
+ if (raw === undefined || raw === null) return null
885
+ const values = raw instanceof Set ? Array.from(raw) : Array.isArray(raw) ? raw : typeof raw === 'string' ? raw.split(/[,\s;]+/) : []
886
+ return values.map((ga) => String(ga || '').trim()).filter((ga) => ga !== '')
887
+ }
888
+
889
+ function universalNodeAcceptsGA (_oNode, _dest) {
890
+ const acceptedGAs = getUniversalNodeAcceptedGAs(_oNode)
891
+ if (acceptedGAs === null || acceptedGAs.length === 0) return true
892
+ return acceptedGAs.includes(String(_dest || '').trim())
893
+ }
894
+
882
895
  // 17/02/2020 Do initial read (called by node.timerDoInitialRead timer)
883
896
  function DoInitialReadFromKNXBusOrFile () {
884
897
  if (node.linkStatus !== 'connected') return // 29/08/2019 If not connected, exit
@@ -1000,18 +1013,36 @@ module.exports = (RED) => {
1000
1013
  } else if (_oClient.hasOwnProperty('isLoadControlNode') && _oClient.isLoadControlNode) {
1001
1014
  _oClient.initialReadAllDevicesInRules()
1002
1015
  } else if (_oClient.listenallga === true) {
1003
- for (let index = 0; index < node.csv.length; index++) {
1004
- const element = node.csv[index]
1005
- if (!readHistory.includes(element.ga)) {
1006
- node.sendKNXTelegramToKNXEngine({
1007
- grpaddr: element.ga,
1008
- payload: '',
1009
- dpt: '',
1010
- outputtype: 'read',
1011
- nodecallerid: element.id
1012
- })
1013
- readHistory.push(element.ga)
1014
- node.sysLogger?.debug('DoInitialReadFromKNXBusOrFile from Universal Node: sent read request to GA ' + element.ga)
1016
+ const acceptedGAs = getUniversalNodeAcceptedGAs(_oClient)
1017
+ if (acceptedGAs === null) {
1018
+ for (let index = 0; index < node.csv.length; index++) {
1019
+ const element = node.csv[index]
1020
+ if (!readHistory.includes(element.ga)) {
1021
+ node.sendKNXTelegramToKNXEngine({
1022
+ grpaddr: element.ga,
1023
+ payload: '',
1024
+ dpt: '',
1025
+ outputtype: 'read',
1026
+ nodecallerid: element.id
1027
+ })
1028
+ readHistory.push(element.ga)
1029
+ node.sysLogger?.debug('DoInitialReadFromKNXBusOrFile from Universal Node: sent read request to GA ' + element.ga)
1030
+ }
1031
+ }
1032
+ } else {
1033
+ for (let index = 0; index < acceptedGAs.length; index++) {
1034
+ const ga = acceptedGAs[index]
1035
+ if (!readHistory.includes(ga)) {
1036
+ node.sendKNXTelegramToKNXEngine({
1037
+ grpaddr: ga,
1038
+ payload: '',
1039
+ dpt: '',
1040
+ outputtype: 'read',
1041
+ nodecallerid: _oClient.id
1042
+ })
1043
+ readHistory.push(ga)
1044
+ node.sysLogger?.debug('DoInitialReadFromKNXBusOrFile from filtered Universal Node: sent read request to GA ' + ga)
1045
+ }
1015
1046
  }
1016
1047
  }
1017
1048
  } else {
@@ -1261,6 +1292,10 @@ module.exports = (RED) => {
1261
1292
  await node.knxConnection.Disconnect()
1262
1293
  node.sysLogger?.debug('removing old handlers. Node ' + node.name)
1263
1294
  node.knxConnection.removeAllListeners()
1295
+ // The orphaned client may still emit a late async 'error' (e.g. the KNX Secure
1296
+ // IA discovery timing out): with no listener, emit('error') would CRASH the
1297
+ // whole process. Keep a swallow-all handler on it.
1298
+ node.knxConnection.on(knx.KNXClientEvents.error, () => { })
1264
1299
  }
1265
1300
  } catch (error) {
1266
1301
  node.sysLogger?.info('BANANA ERRORINO', error)
@@ -1453,200 +1488,202 @@ module.exports = (RED) => {
1453
1488
  node.nodeClients
1454
1489
  .filter((_input) => _input.notifywrite === true)
1455
1490
  .forEach((_input) => {
1456
- if (_input.hasOwnProperty('isMultiRouting')) {
1457
- const msg = {
1458
- topic: _input.outputtopic || _dest,
1459
- payload: {
1491
+ try {
1492
+ if (_input.hasOwnProperty('isMultiRouting')) {
1493
+ const msg = {
1494
+ topic: _input.outputtopic || _dest,
1495
+ payload: {
1496
+ knx: {
1497
+ event: _evt,
1498
+ source: _src,
1499
+ destination: _dest,
1500
+ apdu: { data: _apduData, bitlength: _apduBitLength, hex: _apduHex },
1501
+ cemi: { hex: _cemiETS },
1502
+ echoed: _echoed,
1503
+ repeated: isRepeated,
1504
+ repeat: isRepeated
1505
+ },
1506
+ knxMultiRouting: {
1507
+ gateway: { id: node.id, name: node.name || '', physAddr: node.physAddr || '' },
1508
+ receivedAt: Date.now()
1509
+ }
1510
+ }
1511
+ }
1512
+ _input.setNodeStatus({ fill: 'green', shape: 'dot', text: 'RAW', payload: _evt, GA: _dest, dpt: '', devicename: _src })
1513
+ _input.handleSend(msg)
1514
+ } else
1515
+ // 21/10/2024 check wether is a HUE device
1516
+ if (_input.type.includes('knxUltimateHue')) {
1517
+ const msg = {
1460
1518
  knx: {
1461
1519
  event: _evt,
1462
- source: _src,
1463
1520
  destination: _dest,
1464
- apdu: { data: _apduData, bitlength: _apduBitLength, hex: _apduHex },
1465
- cemi: { hex: _cemiETS },
1466
- echoed: _echoed,
1521
+ rawValue: _rawValue,
1467
1522
  repeated: isRepeated,
1468
1523
  repeat: isRepeated
1469
- },
1470
- knxMultiRouting: {
1471
- gateway: { id: node.id, name: node.name || '', physAddr: node.physAddr || '' },
1472
- receivedAt: Date.now()
1473
1524
  }
1474
1525
  }
1475
- }
1476
- _input.setNodeStatus({ fill: 'green', shape: 'dot', text: 'RAW', payload: _evt, GA: _dest, dpt: '', devicename: _src })
1477
- _input.handleSend(msg)
1478
- } else
1479
- // 21/10/2024 check wether is a HUE device
1480
- if (_input.type.includes('knxUltimateHue')) {
1481
- const msg = {
1482
- knx: {
1483
- event: _evt,
1484
- destination: _dest,
1485
- rawValue: _rawValue,
1486
- repeated: isRepeated,
1487
- repeat: isRepeated
1488
- }
1489
- }
1490
- _input.handleSend(msg)
1491
- } else if (_input.hasOwnProperty('isSceneController')) { // 19/03/2020 in the middle of coronavirus. Whole italy is red zone, closed down. Scene Controller implementation
1526
+ _input.handleSend(msg)
1527
+ } else if (_input.hasOwnProperty('isSceneController')) { // 19/03/2020 in the middle of coronavirus. Whole italy is red zone, closed down. Scene Controller implementation
1492
1528
  // 12/08/2020 Check wether is a learn (save) command or a activate (play) command.
1493
- if (_dest === _input.topic || _dest === _input.topicSave) {
1529
+ if (_dest === _input.topic || _dest === _input.topicSave) {
1494
1530
  // Prepare the two messages to be evaluated directly into the Scene Controller node.
1495
- new Promise((resolve) => {
1496
- if (_dest === _input.topic) {
1497
- try {
1498
- const msgRecall = buildInputMessage({
1499
- _srcGA: _src,
1500
- _destGA: _dest,
1501
- _event: _evt,
1502
- _Rawvalue: _rawValue,
1503
- _inputDpt: _input.dpt,
1504
- _devicename: _input.name ? _input.name : '',
1505
- _outputtopic: _input.outputtopic,
1506
- _oNode: null,
1507
- _echoed,
1508
- _repeated: isRepeated
1509
- })
1510
- _input.RecallScene(msgRecall.payload, false)
1511
- } catch (error) { }
1512
- } // 12/08/2020 Do NOT use "else", because both topics must be evaluated in case both recall and save have same group address.
1513
- if (_dest === _input.topicSave) {
1514
- try {
1515
- const msgSave = buildInputMessage({
1516
- _srcGA: _src,
1517
- _destGA: _dest,
1518
- _event: _evt,
1519
- _Rawvalue: _rawValue,
1520
- _inputDpt: _input.dptSave,
1521
- _devicename: _input.name || '',
1522
- _outputtopic: _dest,
1523
- _oNode: null,
1524
- _echoed,
1525
- _repeated: isRepeated
1526
- })
1527
- _input.SaveScene(msgSave.payload, false)
1528
- } catch (error) { }
1529
- }
1530
- resolve(true) // fulfilled
1531
+ new Promise((resolve) => {
1532
+ if (_dest === _input.topic) {
1533
+ try {
1534
+ const msgRecall = buildInputMessage({
1535
+ _srcGA: _src,
1536
+ _destGA: _dest,
1537
+ _event: _evt,
1538
+ _Rawvalue: _rawValue,
1539
+ _inputDpt: _input.dpt,
1540
+ _devicename: _input.name ? _input.name : '',
1541
+ _outputtopic: _input.outputtopic,
1542
+ _oNode: null,
1543
+ _echoed,
1544
+ _repeated: isRepeated
1545
+ })
1546
+ _input.RecallScene(msgRecall.payload, false)
1547
+ } catch (error) { }
1548
+ } // 12/08/2020 Do NOT use "else", because both topics must be evaluated in case both recall and save have same group address.
1549
+ if (_dest === _input.topicSave) {
1550
+ try {
1551
+ const msgSave = buildInputMessage({
1552
+ _srcGA: _src,
1553
+ _destGA: _dest,
1554
+ _event: _evt,
1555
+ _Rawvalue: _rawValue,
1556
+ _inputDpt: _input.dptSave,
1557
+ _devicename: _input.name || '',
1558
+ _outputtopic: _dest,
1559
+ _oNode: null,
1560
+ _echoed,
1561
+ _repeated: isRepeated
1562
+ })
1563
+ _input.SaveScene(msgSave.payload, false)
1564
+ } catch (error) { }
1565
+ }
1566
+ resolve(true) // fulfilled
1531
1567
  // reject("error"); // rejected
1532
- })
1533
- .then(function () { })
1534
- .catch(function () { })
1535
- } else {
1568
+ })
1569
+ .then(function () { })
1570
+ .catch(function () { })
1571
+ } else {
1536
1572
  // 19/03/2020 Check and Update value if the input is part of a scene controller
1537
- new Promise((resolve) => {
1573
+ new Promise((resolve) => {
1538
1574
  // Check and update the values of each device in the scene and update the rule array accordingly.
1539
- for (let i = 0; i < _input.rules.length; i++) {
1575
+ for (let i = 0; i < _input.rules.length; i++) {
1540
1576
  // rule is { topic: rowRuleTopic, devicename: rowRuleDeviceName, dpt:rowRuleDPT, send: rowRuleSend}
1541
- const oDevice = _input.rules[i]
1542
- if (typeof oDevice !== 'undefined' && oDevice.topic == _dest) {
1543
- const msg = buildInputMessage({
1544
- _srcGA: _src,
1545
- _destGA: _dest,
1546
- _event: _evt,
1547
- _Rawvalue: _rawValue,
1548
- _inputDpt: oDevice.dpt,
1549
- _devicename: oDevice.name || '',
1550
- _outputtopic: oDevice.outputtopic,
1551
- _oNode: null,
1552
- _echoed,
1553
- _repeated: isRepeated
1554
- })
1555
- oDevice.currentPayload = msg.payload
1556
- _input.setNodeStatus({
1557
- fill: 'grey',
1558
- shape: 'dot',
1559
- text: 'Update dev in scene',
1560
- payload: oDevice.currentPayload,
1561
- GA: oDevice.topic,
1562
- dpt: oDevice.dpt,
1563
- devicename: oDevice.devicename || ''
1564
- })
1565
- break
1577
+ const oDevice = _input.rules[i]
1578
+ if (typeof oDevice !== 'undefined' && oDevice.topic == _dest) {
1579
+ const msg = buildInputMessage({
1580
+ _srcGA: _src,
1581
+ _destGA: _dest,
1582
+ _event: _evt,
1583
+ _Rawvalue: _rawValue,
1584
+ _inputDpt: oDevice.dpt,
1585
+ _devicename: oDevice.name || '',
1586
+ _outputtopic: oDevice.outputtopic,
1587
+ _oNode: null,
1588
+ _echoed,
1589
+ _repeated: isRepeated
1590
+ })
1591
+ oDevice.currentPayload = msg.payload
1592
+ _input.setNodeStatus({
1593
+ fill: 'grey',
1594
+ shape: 'dot',
1595
+ text: 'Update dev in scene',
1596
+ payload: oDevice.currentPayload,
1597
+ GA: oDevice.topic,
1598
+ dpt: oDevice.dpt,
1599
+ devicename: oDevice.devicename || ''
1600
+ })
1601
+ break
1602
+ }
1566
1603
  }
1567
- }
1568
- resolve(true) // fulfilled
1604
+ resolve(true) // fulfilled
1569
1605
  // reject("error"); // rejected
1570
- })
1571
- .then(function () { })
1572
- .catch(function () { })
1573
- }
1574
- } else if (_input.hasOwnProperty('isLogger')) {
1606
+ })
1607
+ .then(function () { })
1608
+ .catch(function () { })
1609
+ }
1610
+ } else if (_input.hasOwnProperty('isLogger')) {
1575
1611
  // 26/03/2020 Coronavirus is slightly decreasing the affected numer of people. Logger Node
1576
1612
  // 24/03/2021 Logger Node, i'll pass cemiETS
1577
- if (_cemiETS !== undefined) {
1613
+ if (_cemiETS !== undefined) {
1578
1614
  // new Promise((resolve, reject) => {
1579
- _input.handleSend(_cemiETS)
1615
+ _input.handleSend(_cemiETS)
1580
1616
  // resolve(true); // fulfilled
1581
1617
  // reject("error"); // rejected
1582
1618
  // }).then(function () { }).catch(function () { });
1583
- }
1584
- } else if (_input.listenallga === true) {
1619
+ }
1620
+ } else if (_input.listenallga === true && universalNodeAcceptsGA(_input, _dest)) {
1585
1621
  // 25/10/2019 TRY TO AUTO DECODE IF Group address not found in the CSV
1586
- const msg = buildInputMessage({
1587
- _srcGA: _src,
1588
- _destGA: _dest,
1589
- _event: _evt,
1590
- _Rawvalue: _rawValue,
1591
- _outputtopic: _dest,
1592
- _oNode: _input,
1593
- _echoed,
1594
- _repeated: isRepeated
1595
- })
1596
- _input.setNodeStatus({
1597
- fill: 'green',
1598
- shape: 'dot',
1599
- text: '',
1600
- payload: msg.payload,
1601
- GA: msg.knx.destination,
1602
- dpt: msg.knx.dpt,
1603
- devicename: msg.devicename
1604
- })
1605
- _input.handleSend(msg)
1606
- } else if (_input.topic == _dest) {
1607
- if (_input.hasOwnProperty('isWatchDog')) {
1608
- // 04/02/2020 Watchdog implementation
1609
- // Is a watchdog node
1610
- } else {
1611
1622
  const msg = buildInputMessage({
1612
1623
  _srcGA: _src,
1613
1624
  _destGA: _dest,
1614
1625
  _event: _evt,
1615
1626
  _Rawvalue: _rawValue,
1616
- _inputDpt: _input.dpt,
1617
- _devicename: _input.name ? _input.name : '',
1618
- _outputtopic: _input.outputtopic,
1627
+ _outputtopic: _dest,
1619
1628
  _oNode: _input,
1620
1629
  _echoed,
1621
1630
  _repeated: isRepeated
1622
1631
  })
1623
- // Check RBE INPUT from KNX Bus, to avoid send the payload to the flow, if it's equal to the current payload
1624
- if (!checkRBEInputFromKNXBusAllowSend(_input, msg.payload)) {
1625
- _input.setNodeStatus({
1626
- fill: 'grey',
1627
- shape: 'ring',
1628
- text: 'rbe block (' + msg.payload + ') from KNX',
1629
- payload: '',
1630
- GA: '',
1631
- dpt: '',
1632
- devicename: ''
1633
- })
1634
- return
1635
- }
1636
- msg.previouspayload = typeof _input.currentPayload !== 'undefined' ? _input.currentPayload : '' // 24/01/2020 Added previous payload
1637
- _input.currentPayload = msg.payload // Set the current value for the RBE input
1638
1632
  _input.setNodeStatus({
1639
1633
  fill: 'green',
1640
1634
  shape: 'dot',
1641
1635
  text: '',
1642
1636
  payload: msg.payload,
1643
- GA: _input.topic,
1644
- dpt: _input.dpt,
1645
- devicename: ''
1637
+ GA: msg.knx.destination,
1638
+ dpt: msg.knx.dpt,
1639
+ devicename: msg.devicename
1646
1640
  })
1647
1641
  _input.handleSend(msg)
1642
+ } else if (_input.topic == _dest) {
1643
+ if (_input.hasOwnProperty('isWatchDog')) {
1644
+ // 04/02/2020 Watchdog implementation
1645
+ // Is a watchdog node
1646
+ } else {
1647
+ const msg = buildInputMessage({
1648
+ _srcGA: _src,
1649
+ _destGA: _dest,
1650
+ _event: _evt,
1651
+ _Rawvalue: _rawValue,
1652
+ _inputDpt: _input.dpt,
1653
+ _devicename: _input.name ? _input.name : '',
1654
+ _outputtopic: _input.outputtopic,
1655
+ _oNode: _input,
1656
+ _echoed,
1657
+ _repeated: isRepeated
1658
+ })
1659
+ // Check RBE INPUT from KNX Bus, to avoid send the payload to the flow, if it's equal to the current payload
1660
+ if (!checkRBEInputFromKNXBusAllowSend(_input, msg.payload)) {
1661
+ _input.setNodeStatus({
1662
+ fill: 'grey',
1663
+ shape: 'ring',
1664
+ text: 'rbe block (' + msg.payload + ') from KNX',
1665
+ payload: '',
1666
+ GA: '',
1667
+ dpt: '',
1668
+ devicename: ''
1669
+ })
1670
+ return
1671
+ }
1672
+ msg.previouspayload = typeof _input.currentPayload !== 'undefined' ? _input.currentPayload : '' // 24/01/2020 Added previous payload
1673
+ _input.currentPayload = msg.payload // Set the current value for the RBE input
1674
+ _input.setNodeStatus({
1675
+ fill: 'green',
1676
+ shape: 'dot',
1677
+ text: '',
1678
+ payload: msg.payload,
1679
+ GA: _input.topic,
1680
+ dpt: _input.dpt,
1681
+ devicename: ''
1682
+ })
1683
+ _input.handleSend(msg)
1684
+ }
1648
1685
  }
1649
- }
1686
+ } catch (errDispatch) { node.sysLogger?.error('KNX dispatch client error: ' + (errDispatch && errDispatch.message)) }
1650
1687
  })
1651
1688
  // console.timeEnd('GroupValue_Write');
1652
1689
  break
@@ -1655,103 +1692,105 @@ module.exports = (RED) => {
1655
1692
  node.nodeClients
1656
1693
  .filter((_input) => _input.notifyresponse === true)
1657
1694
  .forEach((_input) => {
1658
- if (_input.hasOwnProperty('isMultiRouting')) {
1659
- const msg = {
1660
- topic: _input.outputtopic || _dest,
1661
- payload: {
1662
- knx: {
1663
- event: _evt,
1664
- source: _src,
1665
- destination: _dest,
1666
- apdu: { data: _apduData, bitlength: _apduBitLength, hex: _apduHex },
1667
- cemi: { hex: _cemiETS },
1668
- echoed: _echoed,
1669
- repeated: isRepeated,
1670
- repeat: isRepeated
1671
- },
1672
- knxMultiRouting: {
1673
- gateway: { id: node.id, name: node.name || '', physAddr: node.physAddr || '' },
1674
- receivedAt: Date.now()
1695
+ try {
1696
+ if (_input.hasOwnProperty('isMultiRouting')) {
1697
+ const msg = {
1698
+ topic: _input.outputtopic || _dest,
1699
+ payload: {
1700
+ knx: {
1701
+ event: _evt,
1702
+ source: _src,
1703
+ destination: _dest,
1704
+ apdu: { data: _apduData, bitlength: _apduBitLength, hex: _apduHex },
1705
+ cemi: { hex: _cemiETS },
1706
+ echoed: _echoed,
1707
+ repeated: isRepeated,
1708
+ repeat: isRepeated
1709
+ },
1710
+ knxMultiRouting: {
1711
+ gateway: { id: node.id, name: node.name || '', physAddr: node.physAddr || '' },
1712
+ receivedAt: Date.now()
1713
+ }
1675
1714
  }
1676
1715
  }
1677
- }
1678
- _input.setNodeStatus({ fill: 'blue', shape: 'dot', text: 'RAW', payload: _evt, GA: _dest, dpt: '', devicename: _src })
1679
- _input.handleSend(msg)
1680
- } else
1681
- if (_input.hasOwnProperty('isLogger')) {
1716
+ _input.setNodeStatus({ fill: 'blue', shape: 'dot', text: 'RAW', payload: _evt, GA: _dest, dpt: '', devicename: _src })
1717
+ _input.handleSend(msg)
1718
+ } else
1719
+ if (_input.hasOwnProperty('isLogger')) {
1682
1720
  // 26/03/2020 Coronavirus is slightly decreasing the affected numer of people. Logger Node
1683
1721
  // 24/03/2021 Logger Node, i'll pass cemiETS
1684
- if (_cemiETS !== undefined) {
1722
+ if (_cemiETS !== undefined) {
1685
1723
  // new Promise((resolve, reject) => {
1686
- _input.handleSend(_cemiETS)
1724
+ _input.handleSend(_cemiETS)
1687
1725
  // resolve(true); // fulfilled
1688
1726
  // reject("error"); // rejected
1689
1727
  // }).then(function () { }).catch(function () { });
1690
- }
1691
- } else if (_input.listenallga === true) {
1692
- const msg = buildInputMessage({
1693
- _srcGA: _src,
1694
- _destGA: _dest,
1695
- _event: _evt,
1696
- _Rawvalue: _rawValue,
1697
- _outputtopic: _dest,
1698
- _oNode: _input,
1699
- _echoed,
1700
- _repeated: isRepeated
1701
- })
1702
- _input.setNodeStatus({
1703
- fill: 'blue',
1704
- shape: 'dot',
1705
- text: '',
1706
- payload: msg.payload,
1707
- GA: msg.knx.destination,
1708
- dpt: msg.knx.dpt,
1709
- devicename: msg.devicename
1710
- })
1711
- _input.handleSend(msg)
1712
- } else if (_input.topic === _dest) {
1713
- // 04/02/2020 Watchdog implementation
1714
- if (_input.hasOwnProperty('isWatchDog')) {
1715
- // Is a watchdog node
1716
- _input.watchDogTimerReset()
1717
- } else {
1728
+ }
1729
+ } else if (_input.listenallga === true && universalNodeAcceptsGA(_input, _dest)) {
1718
1730
  const msg = buildInputMessage({
1719
1731
  _srcGA: _src,
1720
1732
  _destGA: _dest,
1721
1733
  _event: _evt,
1722
1734
  _Rawvalue: _rawValue,
1723
- _inputDpt: _input.dpt,
1724
- _devicename: _input.name ? _input.name : '',
1725
- _outputtopic: _input.outputtopic,
1735
+ _outputtopic: _dest,
1726
1736
  _oNode: _input,
1727
1737
  _echoed,
1728
1738
  _repeated: isRepeated
1729
1739
  })
1730
- // Check RBE INPUT from KNX Bus, to avoid send the payload to the flow, if it's equal to the current payload
1731
- if (!checkRBEInputFromKNXBusAllowSend(_input, msg.payload)) {
1732
- _input.setNodeStatus({
1733
- fill: 'grey',
1734
- shape: 'ring',
1735
- text: 'rbe INPUT filter applied on ' + msg.payload,
1736
- payload: msg.payload,
1737
- GA: _dest
1738
- })
1739
- return
1740
- }
1741
- msg.previouspayload = typeof _input.currentPayload !== 'undefined' ? _input.currentPayload : '' // 24/01/2020 Added previous payload
1742
- _input.currentPayload = msg.payload // Set the current value for the RBE input
1743
1740
  _input.setNodeStatus({
1744
1741
  fill: 'blue',
1745
1742
  shape: 'dot',
1746
1743
  text: '',
1747
1744
  payload: msg.payload,
1748
- GA: _input.topic,
1745
+ GA: msg.knx.destination,
1749
1746
  dpt: msg.knx.dpt,
1750
1747
  devicename: msg.devicename
1751
1748
  })
1752
1749
  _input.handleSend(msg)
1750
+ } else if (_input.topic === _dest) {
1751
+ // 04/02/2020 Watchdog implementation
1752
+ if (_input.hasOwnProperty('isWatchDog')) {
1753
+ // Is a watchdog node
1754
+ _input.watchDogTimerReset()
1755
+ } else {
1756
+ const msg = buildInputMessage({
1757
+ _srcGA: _src,
1758
+ _destGA: _dest,
1759
+ _event: _evt,
1760
+ _Rawvalue: _rawValue,
1761
+ _inputDpt: _input.dpt,
1762
+ _devicename: _input.name ? _input.name : '',
1763
+ _outputtopic: _input.outputtopic,
1764
+ _oNode: _input,
1765
+ _echoed,
1766
+ _repeated: isRepeated
1767
+ })
1768
+ // Check RBE INPUT from KNX Bus, to avoid send the payload to the flow, if it's equal to the current payload
1769
+ if (!checkRBEInputFromKNXBusAllowSend(_input, msg.payload)) {
1770
+ _input.setNodeStatus({
1771
+ fill: 'grey',
1772
+ shape: 'ring',
1773
+ text: 'rbe INPUT filter applied on ' + msg.payload,
1774
+ payload: msg.payload,
1775
+ GA: _dest
1776
+ })
1777
+ return
1778
+ }
1779
+ msg.previouspayload = typeof _input.currentPayload !== 'undefined' ? _input.currentPayload : '' // 24/01/2020 Added previous payload
1780
+ _input.currentPayload = msg.payload // Set the current value for the RBE input
1781
+ _input.setNodeStatus({
1782
+ fill: 'blue',
1783
+ shape: 'dot',
1784
+ text: '',
1785
+ payload: msg.payload,
1786
+ GA: _input.topic,
1787
+ dpt: msg.knx.dpt,
1788
+ devicename: msg.devicename
1789
+ })
1790
+ _input.handleSend(msg)
1791
+ }
1753
1792
  }
1754
- }
1793
+ } catch (errDispatch) { node.sysLogger?.error('KNX dispatch client error: ' + (errDispatch && errDispatch.message)) }
1755
1794
  })
1756
1795
  break
1757
1796
 
@@ -1759,133 +1798,135 @@ module.exports = (RED) => {
1759
1798
  node.nodeClients
1760
1799
  .filter((_input) => _input.notifyreadrequest === true)
1761
1800
  .forEach((_input) => {
1762
- if (_input.hasOwnProperty('isMultiRouting')) {
1763
- const msg = {
1764
- topic: _input.outputtopic || _dest,
1765
- payload: {
1766
- knx: {
1767
- event: _evt,
1768
- source: _src,
1769
- destination: _dest,
1770
- apdu: { data: null, bitlength: 0, hex: '' },
1771
- cemi: { hex: _cemiETS },
1772
- echoed: _echoed,
1773
- repeated: isRepeated,
1774
- repeat: isRepeated
1775
- },
1776
- knxMultiRouting: {
1777
- gateway: { id: node.id, name: node.name || '', physAddr: node.physAddr || '' },
1778
- receivedAt: Date.now()
1801
+ try {
1802
+ if (_input.hasOwnProperty('isMultiRouting')) {
1803
+ const msg = {
1804
+ topic: _input.outputtopic || _dest,
1805
+ payload: {
1806
+ knx: {
1807
+ event: _evt,
1808
+ source: _src,
1809
+ destination: _dest,
1810
+ apdu: { data: null, bitlength: 0, hex: '' },
1811
+ cemi: { hex: _cemiETS },
1812
+ echoed: _echoed,
1813
+ repeated: isRepeated,
1814
+ repeat: isRepeated
1815
+ },
1816
+ knxMultiRouting: {
1817
+ gateway: { id: node.id, name: node.name || '', physAddr: node.physAddr || '' },
1818
+ receivedAt: Date.now()
1819
+ }
1779
1820
  }
1780
1821
  }
1781
- }
1782
- _input.setNodeStatus({ fill: 'grey', shape: 'dot', text: 'RAW Read', payload: '', GA: _dest, dpt: '', devicename: _src })
1783
- _input.handleSend(msg)
1784
- } else
1785
- if (_input.hasOwnProperty('isLogger')) {
1822
+ _input.setNodeStatus({ fill: 'grey', shape: 'dot', text: 'RAW Read', payload: '', GA: _dest, dpt: '', devicename: _src })
1823
+ _input.handleSend(msg)
1824
+ } else
1825
+ if (_input.hasOwnProperty('isLogger')) {
1786
1826
  // 26/03/2020 Coronavirus is slightly decreasing the affected numer of people. Logger Node
1787
1827
  // node.sysLogger?.info("BANANA isLogger", _evt, _src, _dest, _rawValue, _cemiETS);
1788
1828
  // 24/03/2021 Logger Node, i'll pass cemiETS
1789
- if (_cemiETS !== undefined) {
1829
+ if (_cemiETS !== undefined) {
1790
1830
  // new Promise((resolve, reject) => {
1791
- _input.handleSend(_cemiETS)
1831
+ _input.handleSend(_cemiETS)
1792
1832
  // resolve(true); // fulfilled
1793
1833
  // reject("error"); // rejected
1794
1834
  // }).then(function () { }).catch(function () { });
1795
- }
1796
- } else if (_input.listenallga === true) {
1835
+ }
1836
+ } else if (_input.listenallga === true && universalNodeAcceptsGA(_input, _dest)) {
1797
1837
  // Read Request
1798
- const msg = buildInputMessage({
1799
- _srcGA: _src,
1800
- _destGA: _dest,
1801
- _event: _evt,
1802
- _Rawvalue: null,
1803
- _outputtopic: _dest,
1804
- _oNode: _input,
1805
- _echoed,
1806
- _repeated: isRepeated
1807
- })
1808
- _input.setNodeStatus({
1809
- fill: 'grey',
1810
- shape: 'dot',
1811
- text: 'Read',
1812
- payload: '',
1813
- GA: msg.knx.destination,
1814
- dpt: msg.knx.dpt,
1815
- devicename: msg.devicename
1816
- })
1817
- _input.handleSend(msg)
1818
- } else if (_input.topic === _dest) {
1819
- // 04/02/2020 Watchdog implementation
1820
- if (_input.hasOwnProperty('isWatchDog')) {
1821
- // Is a watchdog node
1822
- } else {
1823
- // Read Request
1824
1838
  const msg = buildInputMessage({
1825
1839
  _srcGA: _src,
1826
1840
  _destGA: _dest,
1827
1841
  _event: _evt,
1828
1842
  _Rawvalue: null,
1829
- _inputDpt: _input.dpt,
1830
- _devicename: _input.name || '',
1831
- _outputtopic: _input.outputtopic,
1843
+ _outputtopic: _dest,
1832
1844
  _oNode: _input,
1833
1845
  _echoed,
1834
1846
  _repeated: isRepeated
1835
1847
  })
1836
- msg.previouspayload = typeof _input.currentPayload !== 'undefined' ? _input.currentPayload : '' // 24/01/2020 Reset previous payload
1837
- // 24/09/2019 Autorespond to BUS
1838
- if (_input.hasOwnProperty('notifyreadrequestalsorespondtobus') && _input.notifyreadrequestalsorespondtobus === true) {
1839
- if (typeof _input.currentPayload === 'undefined' || _input.currentPayload === '' || _input.currentPayload === null) {
1848
+ _input.setNodeStatus({
1849
+ fill: 'grey',
1850
+ shape: 'dot',
1851
+ text: 'Read',
1852
+ payload: '',
1853
+ GA: msg.knx.destination,
1854
+ dpt: msg.knx.dpt,
1855
+ devicename: msg.devicename
1856
+ })
1857
+ _input.handleSend(msg)
1858
+ } else if (_input.topic === _dest) {
1859
+ // 04/02/2020 Watchdog implementation
1860
+ if (_input.hasOwnProperty('isWatchDog')) {
1861
+ // Is a watchdog node
1862
+ } else {
1863
+ // Read Request
1864
+ const msg = buildInputMessage({
1865
+ _srcGA: _src,
1866
+ _destGA: _dest,
1867
+ _event: _evt,
1868
+ _Rawvalue: null,
1869
+ _inputDpt: _input.dpt,
1870
+ _devicename: _input.name || '',
1871
+ _outputtopic: _input.outputtopic,
1872
+ _oNode: _input,
1873
+ _echoed,
1874
+ _repeated: isRepeated
1875
+ })
1876
+ msg.previouspayload = typeof _input.currentPayload !== 'undefined' ? _input.currentPayload : '' // 24/01/2020 Reset previous payload
1877
+ // 24/09/2019 Autorespond to BUS
1878
+ if (_input.hasOwnProperty('notifyreadrequestalsorespondtobus') && _input.notifyreadrequestalsorespondtobus === true) {
1879
+ if (typeof _input.currentPayload === 'undefined' || _input.currentPayload === '' || _input.currentPayload === null) {
1840
1880
  // 14/08/2021 Added || input.currentPayload === null
1841
- node.sendKNXTelegramToKNXEngine({
1842
- grpaddr: _dest,
1843
- payload: _input.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized,
1844
- dpt: _input.dpt,
1845
- outputtype: 'response',
1846
- nodecallerid: _input.id
1847
- })
1848
- _input.setNodeStatus({
1849
- fill: 'blue',
1850
- shape: 'ring',
1851
- text: 'Read & Autorespond with default',
1852
- payload: _input.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized,
1853
- GA: _input.topic,
1854
- dpt: msg.knx.dpt,
1855
- devicename: ''
1856
- })
1881
+ node.sendKNXTelegramToKNXEngine({
1882
+ grpaddr: _dest,
1883
+ payload: _input.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized,
1884
+ dpt: _input.dpt,
1885
+ outputtype: 'response',
1886
+ nodecallerid: _input.id
1887
+ })
1888
+ _input.setNodeStatus({
1889
+ fill: 'blue',
1890
+ shape: 'ring',
1891
+ text: 'Read & Autorespond with default',
1892
+ payload: _input.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized,
1893
+ GA: _input.topic,
1894
+ dpt: msg.knx.dpt,
1895
+ devicename: ''
1896
+ })
1897
+ } else {
1898
+ node.sendKNXTelegramToKNXEngine({
1899
+ grpaddr: _dest,
1900
+ payload: _input.currentPayload,
1901
+ dpt: _input.dpt,
1902
+ outputtype: 'response',
1903
+ nodecallerid: _input.id
1904
+ })
1905
+ _input.setNodeStatus({
1906
+ fill: 'blue',
1907
+ shape: 'ring',
1908
+ text: 'Read & Autorespond with default',
1909
+ payload: _input.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized,
1910
+ GA: _input.topic,
1911
+ dpt: msg.knx.dpt,
1912
+ devicename: ''
1913
+ })
1914
+ }
1857
1915
  } else {
1858
- node.sendKNXTelegramToKNXEngine({
1859
- grpaddr: _dest,
1860
- payload: _input.currentPayload,
1861
- dpt: _input.dpt,
1862
- outputtype: 'response',
1863
- nodecallerid: _input.id
1864
- })
1865
1916
  _input.setNodeStatus({
1866
- fill: 'blue',
1867
- shape: 'ring',
1868
- text: 'Read & Autorespond with default',
1869
- payload: _input.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized,
1917
+ fill: 'grey',
1918
+ shape: 'dot',
1919
+ text: 'Read',
1920
+ payload: msg.payload,
1870
1921
  GA: _input.topic,
1871
1922
  dpt: msg.knx.dpt,
1872
1923
  devicename: ''
1873
1924
  })
1874
1925
  }
1875
- } else {
1876
- _input.setNodeStatus({
1877
- fill: 'grey',
1878
- shape: 'dot',
1879
- text: 'Read',
1880
- payload: msg.payload,
1881
- GA: _input.topic,
1882
- dpt: msg.knx.dpt,
1883
- devicename: ''
1884
- })
1926
+ _input.handleSend(msg)
1885
1927
  }
1886
- _input.handleSend(msg)
1887
1928
  }
1888
- }
1929
+ } catch (errDispatch) { node.sysLogger?.error('KNX dispatch client error: ' + (errDispatch && errDispatch.message)) }
1889
1930
  })
1890
1931
  break
1891
1932
 
@@ -2666,6 +2707,9 @@ module.exports = (RED) => {
2666
2707
  } finally {
2667
2708
  try {
2668
2709
  connection.removeAllListeners()
2710
+ // Never leave a (possibly still async-active) client without an 'error'
2711
+ // listener: a late rejection would crash the whole process.
2712
+ connection.on(knx.KNXClientEvents.error, () => { })
2669
2713
  } catch (error) { /* empty */ }
2670
2714
  if (isSerial && serialDriver && typeof serialDriver.close === 'function') {
2671
2715
  try {