node-red-contrib-knx-ultimate 2.1.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <p>
10
+ <b>Version 2.1.4</b> - June 2023<br/>
11
+ - NEW: Hue light node: added random color cycle effect group address.<br/>
12
+ - Fixed destroying KNX nodes.<br/>
13
+ - Fixed destroying HUE nodes.<br/>
14
+ - Several HUE bugfixes.<br/>
15
+ </p>
16
+ <p>
17
+ <b>Version 2.1.3</b> - June 2023<br/>
18
+ - Bugfix.<br/>
19
+ </p>
9
20
  <p>
10
21
  <b>Version 2.1.2</b> - June 2023<br/>
11
22
  - NEW: Hue Hue Light node, added BLINK option.<br/>
@@ -1,7 +1,7 @@
1
1
 
2
2
  const dptlib = require('./../KNXEngine/dptlib')
3
3
  const hueClass = require('./utils/hueUtils').classHUE
4
-
4
+ const loggerEngine = require('./utils/sysLogger.js')
5
5
  // Helpers
6
6
  const sortBy = (field) => (a, b) => {
7
7
  if (a[field] > b[field]) { return 1 } else { return -1 }
@@ -57,7 +57,7 @@ module.exports = (RED) => {
57
57
  .sort(sortBy('base'))
58
58
  .reduce(toConcattedSubtypes, [])
59
59
 
60
- res.json(dpts)
60
+ res.json(dpts)
61
61
  })
62
62
 
63
63
  function hueConfig(config) {
@@ -68,7 +68,7 @@ module.exports = (RED) => {
68
68
  node.loglevel = config.loglevel !== undefined ? config.loglevel : 'error' // 18/02/2020 Loglevel default error
69
69
  node.sysLogger = null // 20/03/2022 Default
70
70
  try {
71
- node.sysLogger = require('./utils/sysLogger.js').get({ loglevel: node.loglevel }) // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
71
+ node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }) // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
72
72
  } catch (error) { }
73
73
  node.name = (config.name === undefined || config.name === '') ? node.host : config.name // 12/08/2021
74
74
 
@@ -80,7 +80,7 @@ module.exports = (RED) => {
80
80
  node.nodeClients.forEach(_oClient => {
81
81
  const oClient = RED.nodes.getNode(_oClient.id)
82
82
  try {
83
- oClient.handleSendHUE(_event)
83
+ if (oClient.handleSendHUE !== undefined) oClient.handleSendHUE(_event)
84
84
  } catch (error) {
85
85
  if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error('Errore node.hueManager.on(event): ' + error.message)
86
86
  }
@@ -112,7 +112,7 @@ module.exports = (RED) => {
112
112
  // Check if node already exists
113
113
  if (node.nodeClients.filter(x => x.id === _Node.id).length === 0) {
114
114
  // Add _Node to the clients array
115
- _Node.setNodeStatus({ fill: 'grey', shape: 'ring', text: 'Hue initialized.', payload: '', GA: '', dpt: '', devicename: '' })
115
+ _Node.setNodeStatusHue({ fill: 'grey', shape: 'ring', text: 'Hue initialized.' })
116
116
  // 01/06/2023 Add node to the array
117
117
  const jNode = {}
118
118
  jNode.id = _Node.id
@@ -130,10 +130,19 @@ module.exports = (RED) => {
130
130
 
131
131
  node.on('close', function (done) {
132
132
  try {
133
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.destroy()
133
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger = null; loggerEngine.destroy()
134
134
  node.nodeClients = []
135
- } catch (error) { }
136
- done()
135
+ node.hueManager.removeAllListeners();
136
+ (async () => {
137
+ await node.hueManager.close()
138
+ node.hueManager = null;
139
+ delete node.hueManager;
140
+ done()
141
+ })()
142
+ } catch (error) {
143
+ done()
144
+ console.log(error.message)
145
+ }
137
146
  })
138
147
  }
139
148
 
@@ -11,6 +11,7 @@ const path = require('path')
11
11
  const fs = require('fs')
12
12
  // const { Server } = require('http')
13
13
  const payloadRounder = require('./utils/payloadManipulation')
14
+ const loggerEngine = require('./utils/sysLogger.js')
14
15
 
15
16
  // Helpers
16
17
  const sortBy = (field) => (a, b) => {
@@ -127,7 +128,7 @@ return msg;`,
127
128
  node.loglevel = config.loglevel !== undefined ? config.loglevel : 'error' // 18/02/2020 Loglevel default error
128
129
  node.sysLogger = null // 20/03/2022 Default
129
130
  try {
130
- node.sysLogger = require('./utils/sysLogger.js').get({ loglevel: node.loglevel }) // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
131
+ node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }) // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
131
132
  } catch (error) { }
132
133
  // 12/11/2021 Connect at start delay
133
134
  node.autoReconnect = true // 20/03/2022 Default
@@ -1840,7 +1841,7 @@ return msg;`,
1840
1841
  node.telegramsQueue = []
1841
1842
  node.nodeClients = [] // 05/04/2022 Nullify
1842
1843
  try {
1843
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.destroy()
1844
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger = null; loggerEngine.destroy()
1844
1845
  } catch (error) { }
1845
1846
  done()
1846
1847
  })
@@ -31,7 +31,12 @@ module.exports = function (RED) {
31
31
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
32
32
 
33
33
  }
34
-
34
+ // Used to call the status update from the HUE config node.
35
+ node.setNodeStatusHue = ({ fill, shape, text }) => {
36
+ const dDate = new Date()
37
+ node.status({ fill: fill, shape: shape, text: text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
38
+ }
39
+
35
40
  // This function is called by the knx-ultimate config node, to output a msg.payload.
36
41
  node.handleSend = msg => {
37
42
  const state = {}
@@ -159,6 +164,9 @@ module.exports = function (RED) {
159
164
  if (node.server) {
160
165
  node.server.removeClient(node)
161
166
  }
167
+ if (node.serverHue) {
168
+ node.serverHue.removeClient(node)
169
+ }
162
170
  done()
163
171
  })
164
172
  }
@@ -40,6 +40,10 @@
40
40
  GALightBlink: { value: "" },
41
41
  dptLightBlink: { value: "" },
42
42
 
43
+ nameLightColorCycle: { value: "" },
44
+ GALightColorCycle: { value: "" },
45
+ dptLightColorCycle: { value: "" },
46
+
43
47
  hueDevice: { value: "" }
44
48
  },
45
49
  inputs: 0,
@@ -447,6 +451,54 @@
447
451
 
448
452
 
449
453
 
454
+ // DPT Cycle Colors
455
+ // ########################
456
+ $.getJSON('knxUltimateDpts', (data) => {
457
+ data.forEach(dpt => {
458
+ if (dpt.value.startsWith("1.")) {
459
+ $("#node-input-dptLightColorCycle").append($("<option></option>")
460
+ .attr("value", dpt.value)
461
+ .text(dpt.text))
462
+ }
463
+ });
464
+ $("#node-input-dptLightColorCycle").val(this.dptLightColorCycle)
465
+ })
466
+
467
+ // Autocomplete suggestion with ETS csv File
468
+ $("#node-input-GALightColorCycle").autocomplete({
469
+ minLength: 1,
470
+ source: function (request, response) {
471
+ //$.getJSON("csv", request, function( data, status, xhr ) {
472
+ $.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
473
+ response($.map(data, function (value, key) {
474
+ var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
475
+ if (fullSearch(sSearch, request.term + " 1.")) {
476
+ return {
477
+ label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
478
+ value: value.ga // Value
479
+ }
480
+ } else {
481
+ return null;
482
+ }
483
+ }));
484
+ });
485
+ }, select: function (event, ui) {
486
+ // Sets Datapoint and device name automatically
487
+ var sDevName = ui.item.label.split("#")[1].trim();
488
+ try {
489
+ sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
490
+ } catch (error) {
491
+ }
492
+ $('#node-input-nameLightColorCycle').val(sDevName);
493
+ var optVal = $("#node-input-dptLightColorCycle option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
494
+ // Select the option value
495
+ $("#node-input-dptLightColorCycle").val(optVal);
496
+ }
497
+ });
498
+ // ########################
499
+
500
+
501
+
450
502
  // Autocomplete suggestion with HUE Lights
451
503
  $("#node-input-name").autocomplete({
452
504
  minLength: 1,
@@ -643,7 +695,19 @@
643
695
  <label for="node-input-nameLightBlink" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
644
696
  <input type="text" id="node-input-nameLightBlink" style="width:200px;margin-left: 5px; text-align: left;">
645
697
  </div>
698
+ </div>
699
+ <div class="form-row">
700
+ <label for="node-input-nameLightColorCycle" style="width:100px;"><i class="fa fa-play-circle-o"></i> Color Cycle</label>
646
701
 
702
+ <label for="node-input-GALightColorCycle" style="width:20px;">GA</label>
703
+ <input type="text" id="node-input-GALightColorCycle" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
704
+
705
+ <label for="node-input-dptLightColorCycle" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
706
+ <select id="node-input-dptLightColorCycle" style="width:140px;"></select>
707
+
708
+ <label for="node-input-nameLightColorCycle" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
709
+ <input type="text" id="node-input-nameLightColorCycle" style="width:200px;margin-left: 5px; text-align: left;">
710
+ </div>
647
711
  <br/>
648
712
  <br/>
649
713
  <br/>
@@ -676,7 +740,8 @@ Start typing in the GA field, the name or group address of your KNX device, the
676
740
  | Color Status | Link this to the light's color status group address. Accepted datapoint is RGB triplet (r,g,b)|
677
741
  | Brightness | This command is used to change the absolute HUE light's brightness |
678
742
  | Brightness Status| Link this to the light's brightness status group address |
679
- | Blink| *true* Blink the light, *false* Stop blinking. Blinks the light on and off. Useful for signalling. It is compatible with all HUE lights. |
743
+ | Blink| *true* Blink the light, *false* Stop blinking. Blinks the light on and off. Useful for signalling. Works with all HUE lights. |
744
+ | Color Cycle| *true* start cycle, *false* Stop cycle. Randomly changes the HUE light's color at regular interval. Works with all HUE lights having color capabilities. |
680
745
 
681
746
  <br/>
682
747
 
@@ -33,6 +33,11 @@ module.exports = function (RED) {
33
33
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
34
34
 
35
35
  }
36
+ // Used to call the status update from the HUE config node.
37
+ node.setNodeStatusHue = ({ fill, shape, text }) => {
38
+ const dDate = new Date()
39
+ node.status({ fill: fill, shape: shape, text: text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
40
+ }
36
41
 
37
42
  // This function is called by the knx-ultimate config node, to output a msg.payload.
38
43
  node.handleSend = msg => {
@@ -81,13 +86,37 @@ module.exports = function (RED) {
81
86
  //state = msg.payload === true ? { on: { on: true } } : { on: { on: false } }
82
87
  state = msg.payload === true ? { on: { on: true }, dimming: { brightness: 100 } } : { on: { on: false } }
83
88
  node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state)
84
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state)
89
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state) // It's ok twice, so the light turns off immeridaley
85
90
  }, 600);
86
91
  } else {
87
92
  if (node.timerBlink !== undefined) clearInterval(node.timerBlink)
88
93
  node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } })
89
94
  }
90
95
  break
96
+ case config.GALightColorCycle:
97
+ const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightSwitch))
98
+ if (gaValColorCycle) {
99
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } })
100
+ node.timerColorCycle = setInterval(() => {
101
+ function getRandomIntInclusive(min, max) {
102
+ min = Math.ceil(min);
103
+ max = Math.floor(max);
104
+ return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
105
+ }
106
+ const red = getRandomIntInclusive(0, 255)
107
+ const green = getRandomIntInclusive(0, 255)
108
+ const blue = getRandomIntInclusive(0, 255)
109
+ const gamut = node.currentHUEDevice.color.gamut_type || null
110
+ const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut)
111
+ const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue)
112
+ state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } }
113
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state)
114
+ }, 10000);
115
+ } else {
116
+ if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle)
117
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } })
118
+ }
119
+ break
91
120
  default:
92
121
  break
93
122
  }
@@ -189,7 +218,7 @@ module.exports = function (RED) {
189
218
  } catch (error) {
190
219
  console.log('Error: knxUltimateHueLight: node.serverHue.hueManager.getLight: ' + error.message)
191
220
  }
192
- }, 1000);
221
+ }, 5000);
193
222
 
194
223
  }
195
224
 
@@ -202,6 +231,9 @@ module.exports = function (RED) {
202
231
  if (node.server) {
203
232
  node.server.removeClient(node)
204
233
  }
234
+ if (node.serverHue) {
235
+ node.serverHue.removeClient(node)
236
+ }
205
237
  done()
206
238
  })
207
239
  }
@@ -31,7 +31,12 @@ module.exports = function (RED) {
31
31
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
32
32
 
33
33
  }
34
-
34
+ // Used to call the status update from the HUE config node.
35
+ node.setNodeStatusHue = ({ fill, shape, text }) => {
36
+ const dDate = new Date()
37
+ node.status({ fill: fill, shape: shape, text: text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
38
+ }
39
+
35
40
  // This function is called by the knx-ultimate config node, to output a msg.payload.
36
41
  node.handleSend = msg => {
37
42
  }
@@ -81,6 +86,9 @@ module.exports = function (RED) {
81
86
  if (node.server) {
82
87
  node.server.removeClient(node)
83
88
  }
89
+ if (node.serverHue) {
90
+ node.serverHue.removeClient(node)
91
+ }
84
92
  done()
85
93
  })
86
94
  }
@@ -31,7 +31,12 @@ module.exports = function (RED) {
31
31
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
32
32
 
33
33
  }
34
-
34
+ // Used to call the status update from the HUE config node.
35
+ node.setNodeStatusHue = ({ fill, shape, text }) => {
36
+ const dDate = new Date()
37
+ node.status({ fill: fill, shape: shape, text: text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
38
+ }
39
+
35
40
  // This function is called by the knx-ultimate config node, to output a msg.payload.
36
41
  node.handleSend = msg => {
37
42
  }
@@ -81,6 +86,9 @@ module.exports = function (RED) {
81
86
  if (node.server) {
82
87
  node.server.removeClient(node)
83
88
  }
89
+ if (node.serverHue) {
90
+ node.serverHue.removeClient(node)
91
+ }
84
92
  done()
85
93
  })
86
94
  }
@@ -53,7 +53,12 @@ module.exports = function (RED) {
53
53
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
54
54
 
55
55
  }
56
-
56
+ // Used to call the status update from the HUE config node.
57
+ node.setNodeStatusHue = ({ fill, shape, text }) => {
58
+ const dDate = new Date()
59
+ node.status({ fill: fill, shape: shape, text: text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
60
+ }
61
+
57
62
  // This function is called by the knx-ultimate config node, to output a msg.payload.
58
63
  node.handleSend = msg => {
59
64
  }
@@ -88,7 +93,7 @@ module.exports = function (RED) {
88
93
  }
89
94
  // Send to KNX bus
90
95
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
91
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX start Dim' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
96
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX Change color clockwise' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
92
97
  }
93
98
  } else if (_event.relative_rotary.last_event.rotation.direction === 'counter_clock_wise') {
94
99
  if (knxMsgPayload.dpt.startsWith('3.007')) {
@@ -112,7 +117,7 @@ module.exports = function (RED) {
112
117
  }
113
118
  // Send to KNX bus
114
119
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
115
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX start Dim' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
120
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX Change color counterclockwise' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
116
121
  }
117
122
  }
118
123
 
@@ -159,6 +164,9 @@ module.exports = function (RED) {
159
164
  if (node.server) {
160
165
  node.server.removeClient(node)
161
166
  }
167
+ if (node.serverHue) {
168
+ node.serverHue.removeClient(node)
169
+ }
162
170
  done()
163
171
  })
164
172
  }
@@ -31,6 +31,12 @@ module.exports = function (RED) {
31
31
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
32
32
 
33
33
  }
34
+ // Used to call the status update from the HUE config node.
35
+ node.setNodeStatusHue = ({ fill, shape, text }) => {
36
+ const dDate = new Date()
37
+ node.status({ fill: fill, shape: shape, text: text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
38
+ }
39
+
34
40
 
35
41
  // This function is called by the knx-ultimate config node, to output a msg.payload.
36
42
  node.handleSend = msg => {
@@ -81,6 +87,9 @@ module.exports = function (RED) {
81
87
  if (node.server) {
82
88
  node.server.removeClient(node)
83
89
  }
90
+ if (node.serverHue) {
91
+ node.serverHue.removeClient(node)
92
+ }
84
93
  done()
85
94
  })
86
95
  }
@@ -23,6 +23,7 @@ class classHUE extends EventEmitter {
23
23
  this.clientkey = _clientkey
24
24
  this.bridgeid = _bridgeid
25
25
  this.commandQueue = []
26
+ this.closePushEventStream = false
26
27
  this.timerwriteQueueAdd = setTimeout(this.handleQueue, 3000) // First start
27
28
 
28
29
  // start the SSE Stream Receiver
@@ -54,8 +55,8 @@ class classHUE extends EventEmitter {
54
55
  for (let index = 0; index < events.length; index++) {
55
56
  const oEvento = events[index]
56
57
  if (oEvento.type === 'update') {
57
- for (let index = 0; index < oEvento.data.length; index++) {
58
- const element = oEvento.data[index]
58
+ for (let i = 0; i < oEvento.data.length; i++) {
59
+ const element = oEvento.data[i]
59
60
  this.emit('event', element)
60
61
  }
61
62
  }
@@ -68,6 +69,7 @@ class classHUE extends EventEmitter {
68
69
  };
69
70
  // Funzione per richiedere gli eventi
70
71
  const req = () => {
72
+ if (this.closePushEventStream) return // I'm destroying the class
71
73
  const request = https.request(options, handleResponse);
72
74
  request.on('error', (error) => {
73
75
  console.log('KNXUltimateHUEConfig: classHUE: request.on(error): ' + error.message)
@@ -160,6 +162,20 @@ class classHUE extends EventEmitter {
160
162
  console.log('KNXUltimateHUEConfig: classHUE: getLight: ' + error.message)
161
163
  }
162
164
  }
163
-
165
+
166
+ close = async () => {
167
+ return new Promise((resolve, reject) => {
168
+ try {
169
+ this.closePushEventStream = true
170
+ setTimeout(() => {
171
+ resolve(true)
172
+ }, 1000);
173
+ } catch (error) {
174
+ reject(error)
175
+ }
176
+ })
177
+
178
+
179
+ }
164
180
  }
165
181
  module.exports.classHUE = classHUE
@@ -57,7 +57,7 @@ module.exports = {
57
57
  }
58
58
  }
59
59
  })
60
- }
60
+ }
61
61
  return (logger)
62
62
  },
63
63
  destroy: function () {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=14.0.0"
5
5
  },
6
- "version": "2.1.2",
6
+ "version": "2.1.4",
7
7
  "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. With integrated Philips HUE devices handling.",
8
8
  "dependencies": {
9
9
  "mkdirp": "1.0.4",