node-red-contrib-knx-ultimate 1.4.6 → 1.4.7

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.
@@ -0,0 +1,8 @@
1
+ # Ignore artifacts:
2
+ build
3
+ coverage
4
+ dist
5
+ out
6
+
7
+ # Ignore all JavaScript files:
8
+ *.js
package/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <p>
10
+ <b>Version 1.4.7</b> - November 2022<br/>
11
+ - NEW: added "Griesser Object" Custom Datapoint 6001.001. Thanks @croghostrider<br/>
12
+ </p>
9
13
  <p>
10
14
  <b>Version 1.4.6</b> - November 2022<br/>
11
15
  - NEW: added Airflow Datapoint 9.009.<br/>
@@ -40,7 +40,7 @@ const SocketEvents = {
40
40
  data: 'data',
41
41
  close: 'close'
42
42
  }
43
- var KNXClientEvents;
43
+ let KNXClientEvents;
44
44
  (function (KNXClientEvents) {
45
45
  KNXClientEvents.error = 'error'
46
46
  KNXClientEvents.disconnected = 'disconnected'
@@ -84,7 +84,7 @@ const optionsDefaults = {
84
84
  }
85
85
 
86
86
  class KNXClient extends EventEmitter {
87
- constructor(options) {
87
+ constructor (options) {
88
88
  if (options === undefined) {
89
89
  options = optionsDefaults
90
90
  }
@@ -188,7 +188,7 @@ class KNXClient extends EventEmitter {
188
188
  this._numFailedTelegramACK = 0 // 25/12/2021 Keep count of the failed tunnelig ACK telegrams
189
189
  }
190
190
 
191
- get channelID() {
191
+ get channelID () {
192
192
  return this._channelID
193
193
  }
194
194
 
@@ -215,7 +215,7 @@ class KNXClient extends EventEmitter {
215
215
  // DPT19: null,
216
216
  // DPT20: null
217
217
  // };
218
- getKNXDataBuffer(_data, _dptid) {
218
+ getKNXDataBuffer (_data, _dptid) {
219
219
  const adpu = {}
220
220
  DPTLib.populateAPDU(_data, adpu, _dptid)
221
221
  const iDatapointType = parseInt(_dptid.substr(0, _dptid.indexOf('.')))
@@ -253,7 +253,7 @@ class KNXClient extends EventEmitter {
253
253
  // }
254
254
  // });
255
255
  // }
256
- send(knxPacket) {
256
+ send (knxPacket) {
257
257
  // Logging
258
258
  if (this.sysLogger !== undefined && this.sysLogger !== null) {
259
259
  try {
@@ -320,7 +320,7 @@ class KNXClient extends EventEmitter {
320
320
  * @param {KNXDataBuffer} data
321
321
  */
322
322
  // sendWriteRequest(dstAddress, data) {
323
- write(dstAddress, data, dptid) {
323
+ write (dstAddress, data, dptid) {
324
324
  if (this._connectionState !== STATE.CONNECTED) throw new Error('The socket is not connected. Unable to access the KNX BUS')
325
325
 
326
326
  // Get the Data Buffer from the plain value
@@ -363,7 +363,7 @@ class KNXClient extends EventEmitter {
363
363
  }
364
364
 
365
365
  // sendResponseRequest
366
- respond(dstAddress, data, dptid) {
366
+ respond (dstAddress, data, dptid) {
367
367
  if (this._connectionState !== STATE.CONNECTED) throw new Error('The socket is not connected. Unable to access the KNX BUS')
368
368
 
369
369
  // Get the Data Buffer from the plain value
@@ -405,7 +405,7 @@ class KNXClient extends EventEmitter {
405
405
  }
406
406
 
407
407
  // sendReadRequest
408
- read(dstAddress) {
408
+ read (dstAddress) {
409
409
  if (this._connectionState !== STATE.CONNECTED) throw new Error('The socket is not connected. Unable to access the KNX BUS')
410
410
 
411
411
  if (typeof dstAddress === 'string') dstAddress = KNXAddress.createFromString(dstAddress, KNXAddress.TYPE_GROUP)
@@ -443,7 +443,7 @@ class KNXClient extends EventEmitter {
443
443
  }
444
444
  }
445
445
 
446
- writeRaw(dstAddress, _rawDataBuffer, bitlength) {
446
+ writeRaw (dstAddress, _rawDataBuffer, bitlength) {
447
447
  // bitlength is unused and only for backward compatibility
448
448
 
449
449
  if (this._connectionState !== STATE.CONNECTED) throw new Error('The socket is not connected. Unable to access the KNX BUS')
@@ -500,14 +500,14 @@ class KNXClient extends EventEmitter {
500
500
  }
501
501
  }
502
502
 
503
- startHeartBeat() {
503
+ startHeartBeat () {
504
504
  this.stopHeartBeat()
505
505
  this._heartbeatFailures = 0
506
506
  this._heartbeatRunning = true
507
507
  this._runHeartbeat()
508
508
  }
509
509
 
510
- stopHeartBeat() {
510
+ stopHeartBeat () {
511
511
  if (this._heartbeatTimer !== null) {
512
512
  this._heartbeatRunning = false
513
513
  clearTimeout(this._heartbeatTimer)
@@ -543,7 +543,7 @@ class KNXClient extends EventEmitter {
543
543
  // this._awaitingResponseType = KNXConstants.KNX_CONSTANTS.DESCRIPTION_RESPONSE;
544
544
  // this._sendDescriptionRequestMessage(host, port);
545
545
  // }
546
- Connect(knxLayer = TunnelCRI.TunnelTypes.TUNNEL_LINKLAYER) {
546
+ Connect (knxLayer = TunnelCRI.TunnelTypes.TUNNEL_LINKLAYER) {
547
547
  if (this._clientSocket == null) {
548
548
  throw new Error('No client socket defined')
549
549
  }
@@ -611,7 +611,7 @@ class KNXClient extends EventEmitter {
611
611
  }
612
612
  }
613
613
 
614
- getConnectionStatus() {
614
+ getConnectionStatus () {
615
615
  if (this._clientSocket == null) {
616
616
  throw new Error('No client socket defined')
617
617
  }
@@ -641,7 +641,7 @@ class KNXClient extends EventEmitter {
641
641
  } catch (error) { }
642
642
  }
643
643
 
644
- Disconnect() {
644
+ Disconnect () {
645
645
  if (this._clientSocket === null) {
646
646
  throw new Error('No client socket defined')
647
647
  }
@@ -669,11 +669,11 @@ class KNXClient extends EventEmitter {
669
669
  }, 2000)
670
670
  }
671
671
 
672
- isConnected() {
672
+ isConnected () {
673
673
  return this._connectionState === STATE.CONNECTED
674
674
  }
675
675
 
676
- _setDisconnected(_sReason = '') {
676
+ _setDisconnected (_sReason = '') {
677
677
  try {
678
678
  if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.debug('KNXClient: called _setDisconnected ' + this._options.ipAddr + ':' + this._options.ipPort + ' ' + _sReason)
679
679
  } catch (error) {
@@ -698,7 +698,7 @@ class KNXClient extends EventEmitter {
698
698
  this._clearToSend = true // 26/12/2021 allow to send
699
699
  }
700
700
 
701
- _runHeartbeat() {
701
+ _runHeartbeat () {
702
702
  if (this._heartbeatRunning) {
703
703
  this.getConnectionStatus()
704
704
  const t = setTimeout(() => { // 21/03/2022 fixed possible memory leak. Previously was setTimeout without "let t = ".
@@ -707,16 +707,16 @@ class KNXClient extends EventEmitter {
707
707
  }
708
708
  }
709
709
 
710
- _getSeqNumber() {
710
+ _getSeqNumber () {
711
711
  return this._clientTunnelSeqNumber
712
712
  }
713
713
 
714
714
  // 26/12/2021 Handle the busy state, for example while waiting for ACK
715
- _getClearToSend() {
715
+ _getClearToSend () {
716
716
  return (this._clearToSend !== undefined ? this._clearToSend : true)
717
717
  }
718
718
 
719
- _incSeqNumber() {
719
+ _incSeqNumber () {
720
720
  this._clientTunnelSeqNumber++
721
721
  if (this._clientTunnelSeqNumber > 255) {
722
722
  this._clientTunnelSeqNumber = 0
@@ -727,7 +727,7 @@ class KNXClient extends EventEmitter {
727
727
  // _keyFromCEMIMessage(cEMIMessage) {
728
728
  // return cEMIMessage.dstAddress.toString();
729
729
  // }
730
- _setTimerWaitingForACK(knxTunnelingRequest) {
730
+ _setTimerWaitingForACK (knxTunnelingRequest) {
731
731
  this._clearToSend = false // 26/12/2021 stop sending until ACK received
732
732
  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'}`)
733
733
  if (this._timerWaitingForACK !== null) clearTimeout(this._timerWaitingForACK)
@@ -754,7 +754,7 @@ class KNXClient extends EventEmitter {
754
754
  }, KNXConstants.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000)
755
755
  }
756
756
 
757
- _processInboundMessage(msg, rinfo) {
757
+ _processInboundMessage (msg, rinfo) {
758
758
  try {
759
759
  // Composing debug string
760
760
  try {
@@ -998,31 +998,31 @@ class KNXClient extends EventEmitter {
998
998
  }
999
999
  }
1000
1000
 
1001
- _sendDescriptionRequestMessage() {
1001
+ _sendDescriptionRequestMessage () {
1002
1002
  this.send(KNXProtocol.KNXProtocol.newKNXDescriptionRequest(new HPAI.HPAI(this._options.localIPAddress)))
1003
1003
  }
1004
1004
 
1005
- _sendSearchRequestMessage() {
1005
+ _sendSearchRequestMessage () {
1006
1006
  // this.send(KNXProtocol.KNXProtocol.newKNXSearchRequest(new HPAI.HPAI(this._options.localIPAddress, this._localPort)), KNXConstants.KNX_CONSTANTS.KNX_PORT, KNXConstants.KNX_CONSTANTS.KNX_IP);
1007
1007
  }
1008
1008
 
1009
- _sendConnectRequestMessage(cri) {
1009
+ _sendConnectRequestMessage (cri) {
1010
1010
  this.send(KNXProtocol.KNXProtocol.newKNXConnectRequest(cri))
1011
1011
  }
1012
1012
 
1013
- _sendConnectionStateRequestMessage(channelID) {
1013
+ _sendConnectionStateRequestMessage (channelID) {
1014
1014
  this.send(KNXProtocol.KNXProtocol.newKNXConnectionStateRequest(channelID))
1015
1015
  }
1016
1016
 
1017
- _sendDisconnectRequestMessage(channelID) {
1017
+ _sendDisconnectRequestMessage (channelID) {
1018
1018
  this.send(KNXProtocol.KNXProtocol.newKNXDisconnectRequest(channelID))
1019
1019
  }
1020
1020
 
1021
- _sendDisconnectResponseMessage(channelID, status = KNXConstants.ConnectionStatus.E_NO_ERROR) {
1021
+ _sendDisconnectResponseMessage (channelID, status = KNXConstants.ConnectionStatus.E_NO_ERROR) {
1022
1022
  this.send(KNXProtocol.KNXProtocol.newKNXDisconnectResponse(channelID, status))
1023
1023
  }
1024
1024
 
1025
- _sendSecureSessionRequestMessage(cri) {
1025
+ _sendSecureSessionRequestMessage (cri) {
1026
1026
  const oHPAI = new HPAI.HPAI('0.0.0.0', 0, this._options.hostProtocol === 'TunnelTCP' ? KNXConstants.KNX_CONSTANTS.IPV4_TCP : KNXConstants.KNX_CONSTANTS.IPV4_UDP)
1027
1027
  this.send(KNXProtocol.KNXProtocol.newKNXSecureSessionRequest(cri, oHPAI))
1028
1028
  }
@@ -38,8 +38,8 @@ exports.fromBuffer = function (buf) {
38
38
  knxLog.get().error('DPT21: Buffer should be 8 bit long, got', buf.length)
39
39
  return null
40
40
  }
41
- const sBit =Array.from((parseInt(buf.toString('hex').toUpperCase(), 16).toString(2)).padStart(8, '0')) // Get bit from hex
42
- const ret = { outOfService: sBit[7] ==='1' ? true : false, fault: sBit[6]==='1' ? true : false, overridden: sBit[5]==='1' ? true : false, inAlarm: sBit[4]==='1' ? true : false, alarmUnAck: sBit[3] ==='1' ? true : false }
41
+ const sBit = Array.from((parseInt(buf.toString('hex').toUpperCase(), 16).toString(2)).padStart(8, '0')) // Get bit from hex
42
+ const ret = { outOfService: sBit[7] === '1', fault: sBit[6] === '1', overridden: sBit[5] === '1', inAlarm: sBit[4] === '1', alarmUnAck: sBit[3] === '1' }
43
43
  return ret
44
44
  }
45
45
 
@@ -0,0 +1,276 @@
1
+ const knxLog = require('./../KnxLog')
2
+
3
+ function toRadix (value, radix) {
4
+ if (!Number.isSafeInteger(value)) {
5
+ knxLog.get().error('value must be a safe integer')
6
+ }
7
+
8
+ const digits = Math.ceil(64 / Math.log2(radix))
9
+ const twosComplement = value < 0
10
+ ? BigInt(radix) ** BigInt(digits) + BigInt(value)
11
+ : value
12
+
13
+ return twosComplement.toString(radix).padStart(digits, '0')
14
+ }
15
+
16
+ function griesserSectorCode (Byte0, Byte1) {
17
+ const SectorCode = Byte0 + (Byte1 & 3) * 256
18
+ return SectorCode
19
+ }
20
+
21
+ function griesserCommandCode (Byte1) {
22
+ const command = (Byte1 / 4) >> 0
23
+ return command
24
+ }
25
+
26
+ function griesserParameter (command, Byte2, Byte3, Byte4, Byte5) {
27
+ let commandOperation
28
+ switch (command) {
29
+ case 1: // drive command
30
+ switch (Byte2 & 31) {
31
+ case 0: return 'no driving movement'
32
+ case 1: return 'upper end position'
33
+ case 2: return 'lower end position'
34
+ case 3:
35
+ if ((Byte3 >= 1) & (Byte3 <= 4)) {
36
+ return 'fixed position P' + Byte3 + ' approach'
37
+ } else {
38
+ return 'Unknown value for Pn ' + Byte3
39
+ }
40
+ default:
41
+ return 'Unknown drive command' + Byte2 & 31
42
+ }
43
+ case 4: // set/delete lock
44
+ if (Byte2 === 0) {
45
+ return 'no lock'
46
+ } else {
47
+ switch (Byte2 & 3) {
48
+ case 1: return 'driving command'
49
+ case 2: return 'button lock'
50
+ case 3: return 'driving command- and button lock'
51
+ }
52
+ if (Byte3 === 0) {
53
+ return 'delete lock'
54
+ } else {
55
+ return 'set lock'
56
+ }
57
+ }
58
+ case 5: // operation code
59
+ if (Byte2 <= 6) {
60
+ commandOperation = 'groupoperation'
61
+ } else if (Byte2 >= 128 && Byte2 <= 134) {
62
+ commandOperation = 'localoperation'
63
+ } else {
64
+ commandOperation = 'unknown command Byte3 for ' + Byte2
65
+ }
66
+ if ((Byte2 & 127) === 0) {
67
+ return [commandOperation, 'long up']
68
+ } else if ((Byte2 & 127) === 1) {
69
+ return [commandOperation, 'long down']
70
+ } else if ((Byte2 & 127) === 2) {
71
+ return [commandOperation, 'short up']
72
+ } else if ((Byte2 & 127) === 3) {
73
+ return [commandOperation, 'short down']
74
+ } else if ((Byte2 & 127) === 4) {
75
+ return [commandOperation, 'stop']
76
+ } else if ((Byte2 & 127) === 5) {
77
+ return [commandOperation, 'long-short up']
78
+ } else if ((Byte2 & 127) === 6) {
79
+ return [commandOperation, 'long-short down']
80
+ } else {
81
+ return [commandOperation, 'unknown command Byte3 for ' + Byte2]
82
+ }
83
+ case 22: // driving range limits for automatic button commands
84
+ return ['min. angle: ' + Byte2, 'max. angle: ' + Byte3, 'min. height: ' + Byte4, 'max. height: ' + Byte5]
85
+ default: return 'unknown value for command: ' + command
86
+ }
87
+ }
88
+
89
+ function griesserSectors (SectorCode) {
90
+ let SectorMin, SectorMax, dA, a, SectorCodeMin, SectorCodeMax
91
+ dA = 1
92
+ a = SectorCode
93
+ if (a > 0) {
94
+ while ((a & 1) === 0) {
95
+ a = (a / 2) >> 0
96
+ dA = dA * 2
97
+ }
98
+ dA = dA - 1
99
+ SectorMin = SectorCode - dA
100
+ SectorMax = SectorCode + dA
101
+ } else {
102
+ SectorMin = 0
103
+ SectorMax = 0
104
+ }
105
+ if (SectorMin === 0) {
106
+ SectorCodeMin = 0
107
+ } else {
108
+ SectorCodeMin = (((SectorMin - 1) / 2) >> 0) + 1
109
+ }
110
+ if (SectorMax === 0) {
111
+ SectorCodeMax = 0
112
+ } else {
113
+ SectorCodeMax = (((SectorMax - 1) / 2) >> 0) + 1
114
+ }
115
+ if (SectorCodeMax === SectorCodeMin) {
116
+ return [SectorCodeMin]
117
+ } else {
118
+ const Sectors = []
119
+ for (let i = SectorCodeMin; i <= SectorCodeMax; i++) {
120
+ Sectors.push(i)
121
+ }
122
+ return Sectors
123
+ }
124
+ }
125
+
126
+ function griesserSectorToSectorCode (sectors) {
127
+ if (sectors.length === 1) {
128
+ return sectors[0] + sectors[0] - 1
129
+ } else {
130
+ return Math.min(...sectors) + Math.max(...sectors) - 1
131
+ }
132
+ }
133
+
134
+ function griesserCommandToCommandCode (command) {
135
+ switch (command) {
136
+ case 'operation code': return 5
137
+ default: knxLog.get().error('not implemented yet: ' + command)
138
+ }
139
+ }
140
+
141
+ function griesserCommandToCommandCodeP1 (command) {
142
+ switch (command) {
143
+ case 'long up': return 128
144
+ case 'long down': return 129
145
+ case 'short up': return 130
146
+ case 'short down': return 131
147
+ case 'stop': return 132
148
+ case 'long-short up': return 133
149
+ case 'long-short down': return 134
150
+ default: knxLog.get().error('unknown command: ' + command)
151
+ }
152
+ }
153
+
154
+ function griesserCommand (command) {
155
+ switch (command) {
156
+ case 1: return 'drive command'
157
+ case 2: return 'value correction'
158
+ case 3: return 'automatic state'
159
+ case 4: return 'set/delete lock'
160
+ case 5: return 'operation code'
161
+ case 6: return 'set scene'
162
+ case 7: return 'special command'
163
+ case 8: return 'date'
164
+ case 9: return 'sync time'
165
+ case 10: return 'sensor reading notification'
166
+ case 11: return 'bus monitoring'
167
+ case 16: return 'driving range limits for safety drive commands'
168
+ case 17: return 'driving range limits for safety drive commands'
169
+ case 19: return 'driving range limits for safety drive commands'
170
+ case 20: return 'driving range limits for safety drive commands'
171
+ case 22: return 'driving range limits for automatic drive commands'
172
+ case 23: return 'driving range limits for automatic drive commands'
173
+ case 24: return 'driving range limits for automatic drive commands'
174
+ default: return 'unknown value for function: ' + command
175
+ }
176
+ }
177
+
178
+ function griesserPrio (prio, command) {
179
+ const prioCommand = ((command & 224) / 32) >> 0
180
+ if ((((prio & 252) / 4) >> 0) === 0) {
181
+ switch (prioCommand) {
182
+ case 0: return 'border command'
183
+ case 1: return 'automatic command'
184
+ case 3: return 'priority command'
185
+ case 4: return 'warning command'
186
+ case 5: return 'security command'
187
+ case 6: return 'danger command'
188
+ default: return 'unknown priority' + prioCommand
189
+ }
190
+ } else {
191
+ return '-'
192
+ }
193
+ }
194
+
195
+ // Send to BUS
196
+ exports.formatAPDU = function (value) {
197
+ if (!value) {
198
+ knxLog.get().error('DPT60001: cannot write null value')
199
+ } else {
200
+ if (typeof value === 'object' &&
201
+ Object.prototype.hasOwnProperty.call(value, 'command') &&
202
+ Object.prototype.hasOwnProperty.call(value, 'data') &&
203
+ Object.prototype.hasOwnProperty.call(value, 'sectors') &&
204
+ value.data[0] === 'localoperation'
205
+ ) {
206
+ const sectorCode = griesserSectorToSectorCode(value.sectors)
207
+ const commandCode = griesserCommandToCommandCode(value.command)
208
+ const p1 = griesserCommandToCommandCodeP1(value.data[1])
209
+ const bufferTotal = Buffer.alloc(6)
210
+ bufferTotal[0] = parseInt(toRadix(sectorCode, 2).slice(-8), 2)
211
+ bufferTotal[1] = parseInt(toRadix(commandCode, 2).slice(-6) + toRadix(sectorCode, 2).slice(-10, -8), 2)
212
+ bufferTotal[2] = parseInt(toRadix(p1, 2).slice(-8), 2)
213
+ return bufferTotal
214
+ } else {
215
+ knxLog.get().error('DPT60001: Must supply an value {command:"operation code", data:["localoperation", "long up"], sectors:[159]}')
216
+ }
217
+ }
218
+ }
219
+
220
+ // RX from BUS
221
+ exports.fromBuffer = function (buf) {
222
+ if (buf.length !== 6) {
223
+ knxLog
224
+ .get()
225
+ .warn(
226
+ 'DPTGriesser.fromBuffer: buf should be 6 bytes long (got %d bytes)',
227
+ buf.length
228
+ )
229
+ return null
230
+ } else {
231
+ const hexToDecimal = (hex) => parseInt(hex, 16)
232
+ const bufTotale = buf.toString('hex')
233
+ const Byte0 = hexToDecimal(bufTotale.slice(0, 2))
234
+ const Byte1 = hexToDecimal(bufTotale.slice(2, 4))
235
+ const Byte2 = hexToDecimal(bufTotale.slice(4, 6))
236
+ const Byte3 = hexToDecimal(bufTotale.slice(6, 8))
237
+ const Byte4 = hexToDecimal(bufTotale.slice(8, 10))
238
+ const Byte5 = hexToDecimal(bufTotale.slice(10, 12))
239
+ const sectorCode = griesserSectorCode(Byte0, Byte1)
240
+ const commandCode = griesserCommandCode(Byte1, Byte2)
241
+
242
+ return {
243
+ Byte0,
244
+ Byte1,
245
+ Byte2,
246
+ Byte3,
247
+ Byte4,
248
+ Byte5,
249
+ sectorCode,
250
+ commandCode,
251
+ sectors: griesserSectors(sectorCode),
252
+ prio: griesserPrio(Byte1, Byte2),
253
+ command: griesserCommand(commandCode),
254
+ data: griesserParameter(commandCode, Byte2, Byte3, Byte4, Byte5)
255
+ }
256
+ }
257
+ }
258
+
259
+ // DPT Griesser Object basetype info
260
+ exports.basetype = {
261
+ bitlength: 4 * 8 + 2 * 6 + 1 * 10,
262
+ valuetype: 'composite',
263
+ desc: 'Commands for solar shading actors',
264
+ help: `// Sample of 60001.
265
+ // Now are only the local operation implemented.
266
+ // For example, for 60001, set the sector 42 localy up.
267
+ msg.payload = { command: "operation code", data: ["localoperation", "long up"], sectors: [42] };
268
+ return msg;`
269
+ }
270
+
271
+ exports.subtypes = {
272
+ '001': {
273
+ desc: 'DPT_Griesser_Object',
274
+ name: 'Griesser Object'
275
+ }
276
+ }
@@ -11,7 +11,7 @@ const knxLog = require('./../KnxLog')
11
11
 
12
12
  const util = require('util')
13
13
  // kudos to http://croquetweak.blogspot.gr/2014/08/deconstructing-floats-frexp-and-ldexp.html
14
- function ldexp(mantissa, exponent) {
14
+ function ldexp (mantissa, exponent) {
15
15
  return exponent > 1023 // avoid multiplying by infinity
16
16
  ? mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023)
17
17
  : exponent < -1074 // avoid multiplying by zero
@@ -19,7 +19,7 @@ function ldexp(mantissa, exponent) {
19
19
  : mantissa * Math.pow(2, exponent)
20
20
  }
21
21
 
22
- function frexp(value) {
22
+ function frexp (value) {
23
23
  if (value === 0) return [value, 0]
24
24
  const data = new DataView(new ArrayBuffer(8))
25
25
  data.setFloat64(0, value)
@@ -148,7 +148,7 @@ dpts.fromBuffer = function (buf, dpt) {
148
148
  return value
149
149
  }
150
150
 
151
- function cloneDpt(d) {
151
+ function cloneDpt (d) {
152
152
  let result = {}
153
153
  result = JSON.parse(JSON.stringify(d))
154
154
  result.fromBuffer = d.fromBuffer
@@ -108,7 +108,7 @@ return msg;`,
108
108
  res.json(jRet)
109
109
  })
110
110
 
111
- function knxUltimateConfigNode(config) {
111
+ function knxUltimateConfigNode (config) {
112
112
  RED.nodes.createNode(this, config)
113
113
  const node = this
114
114
  node.host = config.host
@@ -179,7 +179,7 @@ return msg;`,
179
179
  }
180
180
 
181
181
  node.setAllClientsStatus = (_status, _color, _text) => {
182
- function nextStatus(_oClient) {
182
+ function nextStatus (_oClient) {
183
183
  let oClient = RED.nodes.getNode(_oClient.id)
184
184
  oClient.setNodeStatus({ fill: _color, shape: 'dot', text: _status + ' ' + _text, payload: '', GA: oClient.topic, dpt: '', devicename: '' })
185
185
  oClient = null
@@ -225,7 +225,7 @@ return msg;`,
225
225
  // 04/04/2021 Supergiovane, creates the service paths where the persistent files are created.
226
226
  // The values file is stored only upon disconnection/close
227
227
  // ************************
228
- function setupDirectory(_aPath) {
228
+ function setupDirectory (_aPath) {
229
229
  if (!fs.existsSync(_aPath)) {
230
230
  // Create the path
231
231
  try {
@@ -245,7 +245,7 @@ return msg;`,
245
245
  if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info('KNXUltimate-config: payload cache set to ' + path.join(node.userDir, 'knxpersistvalues'))
246
246
  }
247
247
 
248
- function saveExposedGAs() {
248
+ function saveExposedGAs () {
249
249
  const sFile = path.join(node.userDir, 'knxpersistvalues', 'knxpersist' + node.id + '.json')
250
250
  try {
251
251
  if (node.exposedGAs.length > 0) {
@@ -256,7 +256,7 @@ return msg;`,
256
256
  if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error('KNXUltimate-config: unable to write peristent values to the file ' + sFile + ' ' + err.message)
257
257
  }
258
258
  }
259
- function loadExposedGAs() {
259
+ function loadExposedGAs () {
260
260
  const sFile = path.join(node.userDir, 'knxpersistvalues', 'knxpersist' + node.id + '.json')
261
261
  try {
262
262
  node.exposedGAs = JSON.parse(fs.readFileSync(sFile, 'utf8'))
@@ -479,7 +479,7 @@ return msg;`,
479
479
  }
480
480
 
481
481
  // 17/02/2020 Do initial read (called by node.timerDoInitialRead timer)
482
- function DoInitialReadFromKNXBusOrFile() {
482
+ function DoInitialReadFromKNXBusOrFile () {
483
483
  if (node.linkStatus !== 'connected') return // 29/08/2019 If not connected, exit
484
484
  loadExposedGAs() // 04/04/2021 load the current values of GA payload
485
485
  try {
@@ -586,14 +586,14 @@ return msg;`,
586
586
  if (typeof _Protocol !== 'undefined') node.hostProtocol = _Protocol
587
587
  if (typeof _CSV !== 'undefined' && _CSV !== '') {
588
588
  try {
589
- const sTemp = readCSV(_CSV) // 27/09/2022 Set the new CSV
589
+ const sTemp = readCSV(_CSV) // 27/09/2022 Set the new CSV
590
590
  node.csv = sTemp
591
591
  } catch (error) {
592
592
  if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info('Node\'s main config setting error. ' + error.message || '')
593
593
  }
594
594
  }
595
-
596
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("Node's main config setting has been changed. New config: IP " + node.host + ' Port ' + node.port + ' PhysicalAddress ' + node.physAddr + ' BindToInterface ' + node.KNXEthInterface + ((typeof _CSV !== 'undefined' && _CSV !== '') ? '. A new group address CSV has been imported.':''))
595
+
596
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("Node's main config setting has been changed. New config: IP " + node.host + ' Port ' + node.port + ' PhysicalAddress ' + node.physAddr + ' BindToInterface ' + node.KNXEthInterface + ((typeof _CSV !== 'undefined' && _CSV !== '') ? '. A new group address CSV has been imported.' : ''))
597
597
 
598
598
  try {
599
599
  node.Disconnect()
@@ -839,7 +839,7 @@ return msg;`,
839
839
 
840
840
  // Handle BUS events
841
841
  // ---------------------------------------------------------------------------------------
842
- function handleBusEvents(_datagram, _echoed) {
842
+ function handleBusEvents (_datagram, _echoed) {
843
843
  // console.time('handleBusEvents');
844
844
 
845
845
  let _rawValue = null
@@ -1107,7 +1107,7 @@ return msg;`,
1107
1107
  node.telegramsQueue.unshift(_clonedMessage) // Add _clonedMessage as first in the queue pile
1108
1108
  }
1109
1109
 
1110
- function handleTelegramQueue() {
1110
+ function handleTelegramQueue () {
1111
1111
  if (node.knxConnection !== null || node.host.toUpperCase() === 'EMULATE') {
1112
1112
  if (node.lockHandleTelegramQueue === true) return // Exits if the funtion is busy
1113
1113
  node.lockHandleTelegramQueue = true // Lock the function. It cannot be called again until finished.
@@ -1250,14 +1250,14 @@ return msg;`,
1250
1250
  }
1251
1251
 
1252
1252
  // 14/08/2019 If the node has payload same as the received telegram, return false
1253
- function checkRBEInputFromKNXBusAllowSend(_node, _KNXTelegramPayload) {
1253
+ function checkRBEInputFromKNXBusAllowSend (_node, _KNXTelegramPayload) {
1254
1254
  if (_node.inputRBE !== true) return true
1255
1255
 
1256
1256
  return !_.isEqual(_node.currentPayload, _KNXTelegramPayload)
1257
1257
  }
1258
1258
 
1259
1259
  // 26/10/2019 Try to figure out the datapoint type from raw value
1260
- function tryToFigureOutDataPointFromRawValue(_rawValue) {
1260
+ function tryToFigureOutDataPointFromRawValue (_rawValue) {
1261
1261
  // 25/10/2019 Try some Datapoints
1262
1262
  if (_rawValue === null) return '1.001'
1263
1263
  if (_rawValue.length === 1) {
@@ -1303,7 +1303,7 @@ return msg;`,
1303
1303
  }
1304
1304
  }
1305
1305
 
1306
- function buildInputMessage({ _srcGA, _destGA, _event, _Rawvalue, _inputDpt, _devicename, _outputtopic, _oNode }) {
1306
+ function buildInputMessage ({ _srcGA, _destGA, _event, _Rawvalue, _inputDpt, _devicename, _outputtopic, _oNode }) {
1307
1307
  let sPayloadmeasureunit = 'unknown'
1308
1308
  let sDptdesc = 'unknown'
1309
1309
  let sPayloadsubtypevalue = 'unknown'
@@ -1430,7 +1430,7 @@ return msg;`,
1430
1430
  }
1431
1431
  };
1432
1432
 
1433
- function readCSV(_csvText) {
1433
+ function readCSV (_csvText) {
1434
1434
  // 24/02/2020, in the middle of Coronavirus emergency in Italy. Check if it a CSV ETS Export of group addresses, or if it's an EFS
1435
1435
  if (_csvText.split('\n')[0].toUpperCase().indexOf('"') == -1) return readESF(_csvText)
1436
1436
 
@@ -1511,7 +1511,7 @@ return msg;`,
1511
1511
  }
1512
1512
  }
1513
1513
 
1514
- function readESF(_esfText) {
1514
+ function readESF (_esfText) {
1515
1515
  // 24/02/2020 must do an EIS to DPT conversion.
1516
1516
  // https://www.loxone.com/dede/kb/eibknx-datentypen/
1517
1517
  // Format: Attuatori luci.Luci primo piano.0/0/1 Luce camera da letto EIS 1 'Switching' (1 Bit) Low
@@ -1607,7 +1607,7 @@ return msg;`,
1607
1607
  }
1608
1608
 
1609
1609
  // 23/08/2019 Delete unwanted CRLF in the GA description
1610
- function correctCRLFInCSV(_csv) {
1610
+ function correctCRLFInCSV (_csv) {
1611
1611
  let sOut = '' // fixed output text to return
1612
1612
  let sChar = ''
1613
1613
  let bStart = false
@@ -1640,7 +1640,7 @@ return msg;`,
1640
1640
  }
1641
1641
 
1642
1642
  // 26/02/2021 Used to send the messages if the node gateway is in EMULATION mode
1643
- function sendEmulatedTelegram(_msg) {
1643
+ function sendEmulatedTelegram (_msg) {
1644
1644
  // INPUT IS
1645
1645
  // _msg = {
1646
1646
  // grpaddr: '5/0/1',
@@ -1,7 +1,7 @@
1
1
  const KNXAddress = require('./../KNXEngine/protocol/KNXAddress').KNXAddress
2
2
 
3
3
  module.exports = function (RED) {
4
- function knxUltimateViewer(config) {
4
+ function knxUltimateViewer (config) {
5
5
  RED.nodes.createNode(this, config)
6
6
  const node = this
7
7
  node.server = RED.nodes.getNode(config.server)
@@ -96,7 +96,7 @@ module.exports = function (RED) {
96
96
  } else if (typeof element.payload === 'object') {
97
97
  // Is maybe a JSON?
98
98
  try {
99
- //sPayload += '<td>' + JSON.stringify(element.payload) + '</td>'
99
+ // sPayload += '<td>' + JSON.stringify(element.payload) + '</td>'
100
100
  sPayload += '<td><i>' + element.rawPayload + '</i></td>'
101
101
  } catch (error) {
102
102
  sPayload += '<td>' + element.payload + '</td>'
@@ -1,7 +1,7 @@
1
1
  const ping = require('ping')
2
2
 
3
3
  module.exports = function (RED) {
4
- function knxUltimateWatchDog(config) {
4
+ function knxUltimateWatchDog (config) {
5
5
  RED.nodes.createNode(this, config)
6
6
  const node = this
7
7
  node.server = RED.nodes.getNode(config.server)
@@ -26,7 +26,7 @@ module.exports = function (RED) {
26
26
  node.isWatchDog = true
27
27
  node.checkLevel = config.checkLevel !== undefined ? config.checkLevel : 'Ethernet'
28
28
  node.icountMessageInWindow = 0
29
- node.alreadyNotifiedArray = new Array // 20/10/2022 array of already notified errors generated by every single KNX-Ultimate node. This prevents floading with repeated errors, in case of disconnection
29
+ node.alreadyNotifiedArray = new Array() // 20/10/2022 array of already notified errors generated by every single KNX-Ultimate node. This prevents floading with repeated errors, in case of disconnection
30
30
 
31
31
  // Used to call the status update from the config node.
32
32
  node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
@@ -42,7 +42,7 @@ module.exports = function (RED) {
42
42
 
43
43
  if (!node.server) return
44
44
 
45
- function handleTheDog() {
45
+ function handleTheDog () {
46
46
  node.beatNumber += 1
47
47
  if (node.beatNumber > node.maxRetry) {
48
48
  // Confirmed connection error
@@ -110,7 +110,7 @@ module.exports = function (RED) {
110
110
 
111
111
  // 22/10/2022 Find in the array of notified nodes. If found and equal, don't send.
112
112
  // Warning: to be optimized: the watchdog will not emit any "red" message anymore, unless the error's description changes.
113
- let oMsg = node.alreadyNotifiedArray.find(a => a.nodeid === msg.nodeid)
113
+ const oMsg = node.alreadyNotifiedArray.find(a => a.nodeid === msg.nodeid)
114
114
  if (oMsg === undefined) {
115
115
  node.alreadyNotifiedArray.push({ nodeid: msg.nodeid, description: msg.description })
116
116
  node.send(msg)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-knx-ultimate",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
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
  "mkdirp": "1.0.4",