node-red-contrib-knx-ultimate 2.1.37 → 2.1.39

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,18 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <p>
10
+ <b>Version 2.1.39</b> - August 2023<br/>
11
+ - KNX-Ultimate Node: fidex an issue with the topic, in case of update the value without writing on the KNX bus.<br/>
12
+ - NEW: HUE Light: now you can set the color temperature, using datapoint 5.001 as well.<br/>
13
+ - NEW: HUE Light: the node will now disable parts of the UI, based on the capabilities of the HUE lamp.<br/>
14
+ - HUE nodes now wait 15 seconds before getting status and updating KNX devices, after node-red restart. <br/>
15
+ </p>
16
+ <p>
17
+ <b>Version 2.1.38</b> - August 2023<br/>
18
+ - Strenghten HUE eventsource resiliency.<br/>
19
+ - Implemented standard logging on all HUE nodes (there was temporary console.log statements).<br/>
20
+ </p>
9
21
  <p>
10
22
  <b>Version 2.1.37</b> - July 2023<br/>
11
23
  - Load control: added msg.shedding to force shed/unshed.<br/>
@@ -64,7 +64,7 @@ module.exports = (RED) => {
64
64
  node.name = (config.name === undefined || config.name === '') ? node.host : config.name // 12/08/2021
65
65
 
66
66
  // Init HUE Utility
67
- node.hueManager = new hueClass(node.host, node.credentials.username, node.credentials.clientkey, config.bridgeid)
67
+ node.hueManager = new hueClass(node.host, node.credentials.username, node.credentials.clientkey, config.bridgeid, node.sysLogger)
68
68
 
69
69
  // Event clip V2
70
70
  node.hueManager.on('event', _event => {
@@ -1154,7 +1154,7 @@ return msg;`,
1154
1154
 
1155
1155
  } else {
1156
1156
  const msg = {
1157
- topic: input.topic,
1157
+ topic: input.outputtopic,
1158
1158
  payload: oKNXMessage.payload,
1159
1159
  devicename: input.name ? input.name : '',
1160
1160
  event: 'Update_NoWrite',
@@ -13,7 +13,7 @@ module.exports = function (RED) {
13
13
  return
14
14
  }
15
15
  node.topic = config.topic
16
- node.outputtopic = (typeof config.outputtopic === 'undefined' || config.outputtopic == '') ? config.topic : config.outputtopic // 07/02/2020 Importante, per retrocompatibilità
16
+ node.outputtopic = (config.outputtopic === undefined || config.outputtopic === '') ? config.topic : config.outputtopic // 07/02/2020 Importante, per retrocompatibilità
17
17
  node.dpt = config.dpt || '1.001'
18
18
  node.notifyreadrequest = config.notifyreadrequest || false
19
19
  node.notifyreadrequestalsorespondtobus = config.notifyreadrequestalsorespondtobus || 'false' // Auto respond if notifireadrequest is true
@@ -78,12 +78,12 @@ module.exports = function (RED) {
78
78
  if (node.serverHue !== null && node.serverHue.hueManager !== null) {
79
79
  (async () => {
80
80
  try {
81
+ node.serverHue.addClient(node)
81
82
  await node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getBattery', (jLight) => {
82
- node.serverHue.addClient(node)
83
83
  node.handleSendHUE(jLight)
84
84
  })
85
85
  } catch (err) {
86
- RED.log.error('Errore knxUltimateHueLight node.currentHUEDevice ' + err.message)
86
+ RED.log.error('Errore knxUltimateHueBattery subscribe: ' + err.message)
87
87
  }
88
88
  })()
89
89
  }
@@ -348,7 +348,7 @@
348
348
  // ########################
349
349
  $.getJSON('knxUltimateDpts', (data) => {
350
350
  data.forEach(dpt => {
351
- if (dpt.value === "3.007") {
351
+ if (dpt.value === "3.007" || dpt.value === "5.001") {
352
352
  $("#node-input-dptLightHSV").append($("<option></option>")
353
353
  .attr("value", dpt.value)
354
354
  .text(dpt.text))
@@ -376,7 +376,7 @@
376
376
  source: function (request, response) {
377
377
  $.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
378
378
  response($.map(data, function (value, key) {
379
- if (value.dpt === "3.007") {
379
+ if (value.dpt === "3.007" || value.dpt === "5.001") {
380
380
  var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
381
381
  if (fullSearch(sSearch, request.term)) {
382
382
  return {
@@ -688,8 +688,8 @@
688
688
  var sSearch = (value.name);
689
689
  if (fullSearch(sSearch, request.term)) {
690
690
  return {
691
- hueDevice: value.id, // Label for Display
692
- value: value.name // Value
691
+ hueDevice: value.id,
692
+ value: value.name
693
693
  }
694
694
  } else {
695
695
  return null;
@@ -706,6 +706,27 @@
706
706
  }
707
707
  });
708
708
 
709
+ // Get the HUE capabilities to enable/disable UI parts
710
+ $.getJSON("KNXUltimateGetResourcesHUE?rtype=light&nodeID=" + oNodeServerHue.id, (data) => {
711
+ data.devices.forEach(element => {
712
+ if (element.id === this.hueDevice.split('#')[0] && element.deviceObject !== undefined) {
713
+ // Check dimming
714
+ if (element.deviceObject.dimming === undefined){
715
+ $('#tabs').tabs("disable", "#tabs-2");
716
+ $('#divColorsAtSwitchOn').hide()
717
+ $('#divColorCycle').hide()
718
+ }
719
+ // Check color
720
+ if (element.deviceObject.color_temperature === undefined) $('#tabs').tabs("disable", "#tabs-3");
721
+ if (element.deviceObject.color === undefined){
722
+ $('#tabs').tabs("disable", "#tabs-4");
723
+ $('#divColorsAtSwitchOn').hide()
724
+ $('#divColorCycle').hide()
725
+ }
726
+ return
727
+ }
728
+ });
729
+ });
709
730
 
710
731
  // ########################
711
732
 
@@ -802,45 +823,47 @@
802
823
  <label for="node-input-nameLightState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
803
824
  <input type="text" id="node-input-nameLightState" style="width:190px;margin-left: 5px; text-align: left;">
804
825
  </div>
805
- <div class="form-row">
806
- <label for="node-input-colorAtSwitchOnDayTime" style="width:260px">
807
- <i class="fa fa-sun-o"></i> Switch On - color/brightness
808
- </label>
809
- <input type="text" id="node-input-colorAtSwitchOnDayTime" placeholder='Leave blank or, for example, {"red":255, "green":255, "blue":255}'
810
- style="width:260px">
811
- </div>
812
- <div class="form-row">
813
- <label style="width:170px" for="node-input-enableDayNightLighting">
814
- <i class="fa fa-clone"></i> Night Lighting
815
- </label>
816
- <input type="checkbox" id="node-input-enableDayNightLighting" style="display:inline-block; width:auto; vertical-align:top;" />
817
- </div>
818
- <div id="DivEnableDayNightLighting">
826
+ <div id="divColorsAtSwitchOn">
819
827
  <div class="form-row">
820
- <label for="node-input-colorAtSwitchOnNightTime" style="width:260px">
821
- <i class="fa fa-moon-o"></i> Switch On - color/brightness at Night
828
+ <label for="node-input-colorAtSwitchOnDayTime" style="width:260px">
829
+ <i class="fa fa-sun-o"></i> Switch On - color/brightness
822
830
  </label>
823
- <input type="text" id="node-input-colorAtSwitchOnNightTime" placeholder='Example {"red":100, "green":0, "blue":50}'
831
+ <input type="text" id="node-input-colorAtSwitchOnDayTime" placeholder='Leave blank or, for example, {"red":255, "green":255, "blue":255}'
824
832
  style="width:260px">
825
833
  </div>
826
834
  <div class="form-row">
827
- <label for="node-input-nameDaylightSensor" style="width:110px;"><i class="fa fa-clock-o"></i> Day/Night</label>
828
-
829
- <label for="node-input-GADaylightSensor" style="width:20px;">GA</label>
830
- <input type="text" id="node-input-GADaylightSensor" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
831
-
832
- <label for="node-input-dptDaylightSensor" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
833
- <select id="node-input-dptDaylightSensor" style="width:140px;"></select>
834
-
835
- <label for="node-input-nameDaylightSensor" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
836
- <input type="text" id="node-input-nameDaylightSensor" style="width:190px;margin-left: 5px; text-align: left;">
837
- </div>
838
- <div class="form-row">
839
- <label style="width:170px" for="node-input-invertDayNight">
840
- <i class="fa fa-shuffle"></i> Invert day/night values
835
+ <label style="width:170px" for="node-input-enableDayNightLighting">
836
+ <i class="fa fa-clone"></i> Night Lighting
841
837
  </label>
842
- <input type="checkbox" id="node-input-invertDayNight" style="display:inline-block; width:auto; vertical-align:top;" />
843
-
838
+ <input type="checkbox" id="node-input-enableDayNightLighting" style="display:inline-block; width:auto; vertical-align:top;" />
839
+ </div>
840
+ <div id="DivEnableDayNightLighting">
841
+ <div class="form-row">
842
+ <label for="node-input-colorAtSwitchOnNightTime" style="width:260px">
843
+ <i class="fa fa-moon-o"></i> Switch On - color/brightness at Night
844
+ </label>
845
+ <input type="text" id="node-input-colorAtSwitchOnNightTime" placeholder='Example {"red":100, "green":0, "blue":50}'
846
+ style="width:260px">
847
+ </div>
848
+ <div class="form-row">
849
+ <label for="node-input-nameDaylightSensor" style="width:110px;"><i class="fa fa-clock-o"></i> Day/Night</label>
850
+
851
+ <label for="node-input-GADaylightSensor" style="width:20px;">GA</label>
852
+ <input type="text" id="node-input-GADaylightSensor" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
853
+
854
+ <label for="node-input-dptDaylightSensor" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
855
+ <select id="node-input-dptDaylightSensor" style="width:140px;"></select>
856
+
857
+ <label for="node-input-nameDaylightSensor" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
858
+ <input type="text" id="node-input-nameDaylightSensor" style="width:190px;margin-left: 5px; text-align: left;">
859
+ </div>
860
+ <div class="form-row">
861
+ <label style="width:170px" for="node-input-invertDayNight">
862
+ <i class="fa fa-shuffle"></i> Invert day/night values
863
+ </label>
864
+ <input type="checkbox" id="node-input-invertDayNight" style="display:inline-block; width:auto; vertical-align:top;" />
865
+
866
+ </div>
844
867
  </div>
845
868
  </div>
846
869
  </p>
@@ -958,17 +981,19 @@
958
981
  <input type="text" id="node-input-nameLightBlink" style="width:190px;margin-left: 5px; text-align: left;">
959
982
  </div>
960
983
 
961
- <div class="form-row">
962
- <label for="node-input-nameLightColorCycle" style="width:110px;"><i class="fa fa-play-circle-o"></i> Color Cycle</label>
963
-
964
- <label for="node-input-GALightColorCycle" style="width:20px;">GA</label>
965
- <input type="text" id="node-input-GALightColorCycle" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
966
-
967
- <label for="node-input-dptLightColorCycle" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
968
- <select id="node-input-dptLightColorCycle" style="width:140px;"></select>
969
-
970
- <label for="node-input-nameLightColorCycle" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
971
- <input type="text" id="node-input-nameLightColorCycle" style="width:190px;margin-left: 5px; text-align: left;">
984
+ <div id="divColorCycle">
985
+ <div class="form-row">
986
+ <label for="node-input-nameLightColorCycle" style="width:110px;"><i class="fa fa-play-circle-o"></i> Color Cycle</label>
987
+
988
+ <label for="node-input-GALightColorCycle" style="width:20px;">GA</label>
989
+ <input type="text" id="node-input-GALightColorCycle" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
990
+
991
+ <label for="node-input-dptLightColorCycle" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
992
+ <select id="node-input-dptLightColorCycle" style="width:140px;"></select>
993
+
994
+ <label for="node-input-nameLightColorCycle" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="knxUltimateHueLight.node-input-name"></span></label>
995
+ <input type="text" id="node-input-nameLightColorCycle" style="width:190px;margin-left: 5px; text-align: left;">
996
+ </div>
972
997
  </div>
973
998
  </p>
974
999
  </div>
@@ -1030,8 +1055,8 @@ Start typing in the GA field, the name or group address of your KNX device, the
1030
1055
  **Tunable white**
1031
1056
  |Property|Description|
1032
1057
  |--|--|
1033
- | Tunable white | This command is used to change the HUE light's white temperature. Datapoint is 3.007 dimming. |
1034
- | Tunable white Status | Link this to the light temperature status group address. Datapoint is 5.001 absolute value|
1058
+ | Tunable white | This command is used to change the HUE light's white temperature. Datapoint is 3.007 dimming or 5.001 percentage. When set to 5.001, a value of 0 is full warm, a value of 100 is full cold .|
1059
+ | Tunable white Status | Link this to the light temperature status group address. Datapoint is 5.001 absolute value. 0 is full warm, 100 is full cold.|
1035
1060
 
1036
1061
  <br/>
1037
1062
 
@@ -90,7 +90,7 @@ module.exports = function (RED) {
90
90
  if (config.linkBrightnessToSwitchStatus === undefined || config.linkBrightnessToSwitchStatus === 'yes') {
91
91
  state = { on: { on: false }, dimming: { brightness: 0 }, dynamics: { duration: 2000 } }
92
92
  } else {
93
- state = { on: { on: false } }
93
+ state = { on: { on: false }, dynamics: { duration: 2000 } }
94
94
  }
95
95
  }
96
96
  node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, (node.isGrouped_light === false ? 'setLight' : 'setGroupedLight'))
@@ -141,6 +141,15 @@ module.exports = function (RED) {
141
141
  node.startDimStopperTunableWhite('stop')
142
142
  }
143
143
  }
144
+ if (config.dptLightHSV === '5.001') {
145
+ // 0-100% tunable white
146
+ msg.payload = 100 - dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightHSV))
147
+ const retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 100], [153, 500])
148
+ msg.payload = retMirek
149
+ state = { color_temperature: { mirek: msg.payload } }
150
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, (node.isGrouped_light === false ? 'setLight' : 'setGroupedLight'))
151
+ node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: state })
152
+ }
144
153
  node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: msg.payload })
145
154
  break
146
155
  case config.GALightBrightness:
@@ -401,9 +410,10 @@ module.exports = function (RED) {
401
410
  if (node.serverHue !== null && node.serverHue.hueManager !== null) {
402
411
  (async () => {
403
412
  try {
413
+ node.serverHue.addClient(node)
404
414
  await node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, (node.isGrouped_light === false ? 'getLight' : 'getGroupedLight'), (jLight) => {
405
415
  node.currentHUEDevice = jLight
406
- node.serverHue.addClient(node)
416
+ node.setNodeStatusHue({ fill: 'blue', shape: 'ring', text: 'Connected. Is on: ', payload: node.currentHUEDevice.on.on === true })
407
417
  })
408
418
  } catch (err) {
409
419
  RED.log.error('Errore knxUltimateHueLight node.currentHUEDevice ' + err.message)
@@ -74,17 +74,17 @@ module.exports = function (RED) {
74
74
  }
75
75
  if (node.serverHue) {
76
76
  node.serverHue.removeClient(node)
77
- // I must get the object, to store read the battery status
77
+ // I must get the object, to store read the status
78
78
  // I queue the state request, by passing the callback to call whenever the HUE bridge send me the light status async
79
79
  if (node.serverHue !== null && node.serverHue.hueManager !== null) {
80
80
  (async () => {
81
+ node.serverHue.addClient(node)
81
82
  try {
82
- await node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getLightLevel', (jLight) => {
83
- node.serverHue.addClient(node)
83
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getLightLevel', (jLight) => {
84
84
  node.handleSendHUE(jLight)
85
85
  })
86
- } catch (err) {
87
- RED.log.error('Errore knxUltimateHueLight node.currentHUEDevice ' + err.message)
86
+ } catch (error) {
87
+ RED.log.error('Errore knxUltimateHueLightSensor subscribing: ' + error.message)
88
88
  }
89
89
  })()
90
90
  }
@@ -79,8 +79,8 @@ module.exports = function (RED) {
79
79
  if (node.serverHue !== null && node.serverHue.hueManager !== null) {
80
80
  (async () => {
81
81
  try {
82
+ node.serverHue.addClient(node)
82
83
  await node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getTemperature', (jLight) => {
83
- node.serverHue.addClient(node)
84
84
  node.handleSendHUE(jLight)
85
85
  })
86
86
  } catch (err) {
@@ -169,7 +169,7 @@ module.exports = function (RED) {
169
169
  }
170
170
  }
171
171
  }
172
- }, 10000)
172
+ }, 20000)
173
173
  }
174
174
 
175
175
  // Start the timer
@@ -5,19 +5,20 @@ const http = require('./http.js')
5
5
  const EventSource = require('eventsource');
6
6
 
7
7
  class classHUE extends EventEmitter {
8
- constructor(_hueBridgeIP, _username, _clientkey, _bridgeid) {
8
+ constructor(_hueBridgeIP, _username, _clientkey, _bridgeid, _sysLogger) {
9
9
  super()
10
- this.setup(_hueBridgeIP, _username, _clientkey, _bridgeid)
10
+ this.setup(_hueBridgeIP, _username, _clientkey, _bridgeid, _sysLogger)
11
11
  }
12
12
 
13
- setup = async (_hueBridgeIP, _username, _clientkey, _bridgeid) => {
13
+ setup = async (_hueBridgeIP, _username, _clientkey, _bridgeid, _sysLogger) => {
14
14
  this.hueBridgeIP = _hueBridgeIP
15
15
  this.username = _username
16
16
  this.clientkey = _clientkey
17
17
  this.bridgeid = _bridgeid
18
18
  this.commandQueue = []
19
19
  this.closePushEventStream = false
20
- this.timerwriteQueueAdd = setTimeout(this.handleQueue, 3000) // First start
20
+ this.timerwriteQueueAdd = setTimeout(this.handleQueue, 15000) // First start. Allow the KNX to connect
21
+ this.sysLogger = _sysLogger
21
22
 
22
23
  // #############################################
23
24
  const options = {
@@ -25,8 +26,8 @@ class classHUE extends EventEmitter {
25
26
  'hue-application-key': this.username,
26
27
  },
27
28
  https: {
28
- rejectUnauthorized: false,
29
- },
29
+ rejectUnauthorized: false
30
+ }
30
31
  };
31
32
 
32
33
  // Connect
@@ -47,25 +48,21 @@ class classHUE extends EventEmitter {
47
48
  })
48
49
  }
49
50
  } catch (error) {
50
- console.log('KNXUltimatehueEngine: classHUE: this.es.onmessage: ' + error.message)
51
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: this.es.onmessage: ' + error.message)
51
52
  }
52
53
 
53
54
  };
54
55
 
55
56
  this.es.onopen = () => {
56
- //console.log('KNXUltimatehueEngine: classHUE: SSE-Connected')
57
+ //if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: SSE-Connected')
57
58
  //this.emit('connected');
58
59
  }
59
60
 
60
61
  this.es.onerror = (error) => {
61
62
  try {
62
- if (this.timerReconnect !== undefined) clearTimeout(this.timerReconnect)
63
- this.timerReconnect = setTimeout(() => {
64
- this.connect()
65
- }, 5000)
66
63
  this.es.close()
67
64
  this.es = null
68
- console.log('KNXUltimatehueEngine: classHUE: request.on(error): ' + error.message)
65
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: request.on(error): ' + error.message)
69
66
  } catch (error) {
70
67
  }
71
68
  //this.emit('error', err)
@@ -83,12 +80,20 @@ class classHUE extends EventEmitter {
83
80
  this.hueAllRooms = await this.hueApiV2.get('/resource/room')
84
81
  this.hueAllDevices = await this.hueApiV2.get('/resource/device')
85
82
  } catch (error) {
86
- if (this.timerReconnect !== undefined) clearTimeout(this.timerReconnect)
87
- this.timerReconnect = setTimeout(() => {
88
- this.connect()
89
- }, 5000)
90
- console.log('KNXUltimatehueEngine: classHUE: this.hueApiV2 = await http.use: ' + error.message)
83
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: this.hueApiV2 = await http.use: ' + error.message)
91
84
  }
85
+
86
+ // 31/07/2023 Every now and then, restart the connection to the eventsource, because it can goes down without knowing that
87
+ if (this.timerReconnect !== undefined) clearInterval(this.timerReconnect)
88
+ this.timerReconnect = setInterval(() => {
89
+ try {
90
+ this.es.close()
91
+ this.es = null
92
+ } catch (error) {
93
+ }
94
+ this.connect()
95
+ }, 300000)
96
+
92
97
  }
93
98
  // First connection
94
99
  this.connect()
@@ -101,6 +106,7 @@ class classHUE extends EventEmitter {
101
106
  // ######################################
102
107
  handleQueue = async () => {
103
108
  if (this.commandQueue.length > 0) {
109
+ //const jRet = { ...this.commandQueue.shift() } //Clone the object by value
104
110
  const jRet = this.commandQueue.shift()
105
111
  switch (jRet._operation) {
106
112
  case 'setLight':
@@ -108,14 +114,14 @@ class classHUE extends EventEmitter {
108
114
  try {
109
115
  const ok = await this.hueApiV2.put('/resource/light/' + jRet._lightID, jRet._state)
110
116
  } catch (error) {
111
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: setLight light: ' + error.message)
117
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info('KNXUltimatehueEngine: classHUE: handleQueue: setLight light: ' + error.message)
112
118
  }
113
119
  break
114
120
  case 'setGroupedLight':
115
121
  try {
116
122
  const ok = await this.hueApiV2.put('/resource/grouped_light/' + jRet._lightID, jRet._state)
117
123
  } catch (error) {
118
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: setLight grouped_light: ' + error.message)
124
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info('KNXUltimatehueEngine: classHUE: handleQueue: setLight grouped_light: ' + error.message)
119
125
  }
120
126
  break
121
127
  case 'getLight':
@@ -123,7 +129,7 @@ class classHUE extends EventEmitter {
123
129
  const jReturn = await this.hueApiV2.get('/resource/light/' + jRet._lightID)
124
130
  jRet._callback(jReturn[0]) // Need to call the callback, because the event is absolutely async
125
131
  } catch (error) {
126
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: getLight light: ' + error.message)
132
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info('KNXUltimatehueEngine: classHUE: handleQueue: getLight light: ' + error.message)
127
133
  }
128
134
  break
129
135
  case 'getGroupedLight':
@@ -131,7 +137,7 @@ class classHUE extends EventEmitter {
131
137
  const jReturn = await this.hueApiV2.get('/resource/grouped_light/' + jRet._lightID)
132
138
  jRet._callback(jReturn[0]) // Need to call the callback, because the event is absolutely async
133
139
  } catch (error) {
134
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: getLight grouped_light: ' + error.message)
140
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info('KNXUltimatehueEngine: classHUE: handleQueue: getLight grouped_light: ' + error.message)
135
141
  }
136
142
  break
137
143
  case 'setScene':
@@ -139,7 +145,7 @@ class classHUE extends EventEmitter {
139
145
  const sceneID = jRet._lightID
140
146
  const ok = await this.hueApiV2.put('/resource/scene/' + sceneID, jRet._state)
141
147
  } catch (error) {
142
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: setScene: ' + error.message)
148
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.info('KNXUltimatehueEngine: classHUE: handleQueue: setScene: ' + error.message)
143
149
  }
144
150
  break
145
151
  case 'stopScene':
@@ -152,7 +158,7 @@ class classHUE extends EventEmitter {
152
158
  this.writeHueQueueAdd(light.rid, jRet._state, 'setLight')
153
159
  });
154
160
  } catch (error) {
155
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: stopScene: ' + error.message)
161
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: handleQueue: stopScene: ' + error.message)
156
162
  }
157
163
  break
158
164
  case 'getBattery':
@@ -160,14 +166,14 @@ class classHUE extends EventEmitter {
160
166
  const jReturn = await this.hueApiV2.get('/resource/device_power/' + jRet._lightID)
161
167
  jRet._callback(jReturn[0]) // Need to call the callback, because the event is absolutely async
162
168
  } catch (error) {
163
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: getBattery: ' + error.message)
169
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: handleQueue: getBattery: ' + error.message)
164
170
  }
165
171
  case 'getLightLevel':
166
172
  try {
167
173
  const jReturn = await this.hueApiV2.get('/resource/light_level/' + jRet._lightID)
168
174
  jRet._callback(jReturn[0]) // Need to call the callback, because the event is absolutely async
169
175
  } catch (error) {
170
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: getLightLevel: ' + error.message)
176
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: handleQueue: getLightLevel: ' + error.message)
171
177
  }
172
178
  break
173
179
  case 'getTemperature':
@@ -175,123 +181,127 @@ class classHUE extends EventEmitter {
175
181
  const jReturn = await this.hueApiV2.get('/resource/temperature/' + jRet._lightID)
176
182
  jRet._callback(jReturn[0]) // Need to call the callback, because the event is absolutely async
177
183
  } catch (error) {
178
- console.log('KNXUltimatehueEngine: classHUE: handleQueue: getTemperature: ' + error.message)
184
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimatehueEngine: classHUE: handleQueue: getTemperature: ' + error.message)
179
185
  }
180
186
  break
181
187
  default:
182
188
  break
183
189
  }
184
190
  }
185
- // The Hue bridge allows about 10 telegram per second, so i need to make a queue manager
186
- setTimeout(this.handleQueue, 100)
191
+ // The Hue bridge allows about 10 telegram per second, so i need to make a queue manager
192
+ this.timerwriteQueueAdd = setTimeout(this.handleQueue, 100)
187
193
  }
188
194
 
189
- writeHueQueueAdd = async (_lightID, _state, _operation, _callback) => {
190
- // Add the new item
191
- this.commandQueue.push({ _lightID, _state, _operation, _callback })
192
- }
193
- // ######################################
194
195
 
195
196
 
196
197
 
197
- // Get all devices and join it with relative rooms, by adding the room name to the device name
198
- getResources = async (_rtype, _host, _username) => {
199
- try {
200
- // Api V2
198
+ writeHueQueueAdd = async (_lightID, _state, _operation, _callback) => {
199
+ // Add the new item
200
+ this.commandQueue.push({ _lightID, _state, _operation, _callback })
201
+ }
202
+ // ######################################
201
203
 
202
- // Returns capitalized string
203
- function capStr(s) {
204
- if (typeof s !== 'string') return ''
205
- return s.charAt(0).toUpperCase() + s.slice(1)
206
- }
207
204
 
208
- const retArray = []
209
- let allResources = undefined
210
- if (_rtype === 'light' || _rtype === 'grouped_light')
211
- allResources = await this.hueAllResources.filter(a => a.type === 'light' || a.type === 'grouped_light')
212
- else {
213
- allResources = await this.hueAllResources.filter(a => a.type === _rtype)
214
- }
215
- for (let index = 0; index < allResources.length; index++) {
216
- const resource = allResources[index];
217
- // Get the owner
218
- try {
219
- let resourceName = ''
220
- let sRoom = ''
221
- if (_rtype === 'light' || _rtype === 'grouped_light') {
222
- // It's a service, having a owner
223
- const owners = await this.hueAllResources.filter(a => a.id === resource.owner.rid)
224
- for (let index = 0; index < owners.length; index++) {
225
- const owner = owners[index];
226
- //resourceName += (owner.metadata !== undefined && owner.metadata.name !== undefined) ? owner.metadata.name + ' and ' : ' and '
227
- resourceName += owner.metadata.name + ' and '
228
- const room = await this.hueAllRooms.find(child => child.children.find(a => a.rid === owner.id))
229
- sRoom += room !== undefined ? room.metadata.name + ' + ' : ' + '
230
- }
231
- sRoom = sRoom.slice(0, -(' + '.length))
232
- resourceName = resourceName.slice(0, -(' and '.length))
233
- resourceName += sRoom !== '' ? ' - Room: ' + sRoom : ''
234
- retArray.push({ name: capStr(resource.type) + ': ' + resourceName, id: resource.id })
235
- }
236
- if (_rtype === 'scene') {
237
- resourceName = resource.metadata.name || '**Name Not Found**'
238
- // Get the linked zone
239
- const zone = await this.hueAllResources.find(res => res.id === resource.group.rid)
240
- resourceName += ' - ' + capStr(resource.group.rtype) + ': ' + zone.metadata.name
241
- retArray.push({ name: capStr(_rtype) + ': ' + resourceName, id: resource.id })
242
- }
243
- if (_rtype === 'button') {
244
- let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
245
- const controlID = resource.metadata !== undefined ? (resource.metadata.control_id || '') : ''
246
- retArray.push({ name: capStr(_rtype) + ': ' + linkedDevName + ', button ' + controlID, id: resource.id })
247
- }
248
- if (_rtype === 'motion') {
249
- let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
250
- retArray.push({ name: capStr(_rtype) + ': ' + linkedDevName, id: resource.id })
251
- }
252
- if (_rtype === 'relative_rotary') {
253
- let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
254
- retArray.push({ name: 'Rotary: ' + linkedDevName, id: resource.id })
255
- }
256
- if (_rtype === 'light_level') {
257
- let Room = await this.hueAllRooms.find(room => room.children.find(child => child.rid === resource.owner.rid))
258
- let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
259
- retArray.push({ name: 'Light Level: ' + linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: resource.id })
260
- }
261
- if (_rtype === 'temperature') {
262
- let Room = await this.hueAllRooms.find(room => room.children.find(child => child.rid === resource.owner.rid))
263
- let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
264
- retArray.push({ name: 'Temperature: ' + linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: resource.id })
265
- }
266
- if (_rtype === 'device_power') {
267
- let Room = await this.hueAllRooms.find(room => room.children.find(child => child.rid === resource.owner.rid))
268
- let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
269
- retArray.push({ name: 'Battery: ' + linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: resource.id })
270
- }
271
- } catch (error) {
272
- //retArray.push({ name: _rtype + ': ERROR ' + error.message, id: resource.id })
273
- }
274
- }
275
- return { devices: retArray }
276
- } catch (error) {
277
- console.log('KNXUltimateHue: hueEngine: classHUE: getDevices: error ' + error.message)
278
- return ({ devices: error.message })
205
+
206
+ // Get all devices and join it with relative rooms, by adding the room name to the device name
207
+ getResources = async (_rtype, _host, _username) => {
208
+ try {
209
+ // Api V2
210
+
211
+ // Returns capitalized string
212
+ function capStr(s) {
213
+ if (typeof s !== 'string') return ''
214
+ return s.charAt(0).toUpperCase() + s.slice(1)
279
215
  }
280
- }
281
216
 
282
- close = async () => {
283
- return new Promise((resolve, reject) => {
217
+ const retArray = []
218
+ let allResources = undefined
219
+ if (_rtype === 'light' || _rtype === 'grouped_light')
220
+ allResources = await this.hueAllResources.filter(a => a.type === 'light' || a.type === 'grouped_light')
221
+ else {
222
+ allResources = await this.hueAllResources.filter(a => a.type === _rtype)
223
+ }
224
+ for (let index = 0; index < allResources.length; index++) {
225
+ const resource = allResources[index];
226
+ // Get the owner
284
227
  try {
285
- this.closePushEventStream = true
286
- if (this.es !== null) this.es.close();
287
- this.es = null;
288
- setTimeout(() => {
289
- resolve(true)
290
- }, 500)
228
+ let resourceName = ''
229
+ let sRoom = ''
230
+ if (_rtype === 'light' || _rtype === 'grouped_light') {
231
+ // It's a service, having a owner
232
+ const owners = await this.hueAllResources.filter(a => a.id === resource.owner.rid)
233
+ for (let index = 0; index < owners.length; index++) {
234
+ const owner = owners[index];
235
+ //resourceName += (owner.metadata !== undefined && owner.metadata.name !== undefined) ? owner.metadata.name + ' and ' : ' and '
236
+ resourceName += owner.metadata.name + ' and '
237
+ const room = await this.hueAllRooms.find(child => child.children.find(a => a.rid === owner.id))
238
+ sRoom += room !== undefined ? room.metadata.name + ' + ' : ' + '
239
+ }
240
+ sRoom = sRoom.slice(0, -(' + '.length))
241
+ resourceName = resourceName.slice(0, -(' and '.length))
242
+ resourceName += sRoom !== '' ? ' - Room: ' + sRoom : ''
243
+ retArray.push({ name: capStr(resource.type) + ': ' + resourceName, id: resource.id, deviceObject:resource })
244
+ }
245
+ if (_rtype === 'scene') {
246
+ resourceName = resource.metadata.name || '**Name Not Found**'
247
+ // Get the linked zone
248
+ const zone = await this.hueAllResources.find(res => res.id === resource.group.rid)
249
+ resourceName += ' - ' + capStr(resource.group.rtype) + ': ' + zone.metadata.name
250
+ retArray.push({ name: capStr(_rtype) + ': ' + resourceName, id: resource.id })
251
+ }
252
+ if (_rtype === 'button') {
253
+ let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
254
+ const controlID = resource.metadata !== undefined ? (resource.metadata.control_id || '') : ''
255
+ retArray.push({ name: capStr(_rtype) + ': ' + linkedDevName + ', button ' + controlID, id: resource.id })
256
+ }
257
+ if (_rtype === 'motion') {
258
+ let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
259
+ retArray.push({ name: capStr(_rtype) + ': ' + linkedDevName, id: resource.id })
260
+ }
261
+ if (_rtype === 'relative_rotary') {
262
+ let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
263
+ retArray.push({ name: 'Rotary: ' + linkedDevName, id: resource.id })
264
+ }
265
+ if (_rtype === 'light_level') {
266
+ let Room = await this.hueAllRooms.find(room => room.children.find(child => child.rid === resource.owner.rid))
267
+ let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
268
+ retArray.push({ name: 'Light Level: ' + linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: resource.id })
269
+ }
270
+ if (_rtype === 'temperature') {
271
+ let Room = await this.hueAllRooms.find(room => room.children.find(child => child.rid === resource.owner.rid))
272
+ let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
273
+ retArray.push({ name: 'Temperature: ' + linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: resource.id })
274
+ }
275
+ if (_rtype === 'device_power') {
276
+ let Room = await this.hueAllRooms.find(room => room.children.find(child => child.rid === resource.owner.rid))
277
+ let linkedDevName = await this.hueAllResources.find(dev => dev.type === 'device' && dev.services.find(serv => serv.rid === resource.id)).metadata.name || ''
278
+ retArray.push({ name: 'Battery: ' + linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: resource.id })
279
+ }
291
280
  } catch (error) {
292
- reject(error)
281
+ //retArray.push({ name: _rtype + ': ERROR ' + error.message, id: resource.id })
293
282
  }
294
- })
283
+ }
284
+ return { devices: retArray }
285
+ } catch (error) {
286
+ if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error('KNXUltimateHue: hueEngine: classHUE: getDevices: error ' + error.message)
287
+ return ({ devices: error.message })
295
288
  }
296
289
  }
290
+
291
+ close = async () => {
292
+ return new Promise((resolve, reject) => {
293
+ try {
294
+ if (this.timerReconnect !== undefined) clearInterval(this.timerReconnect)
295
+ this.closePushEventStream = true
296
+ if (this.es !== null) this.es.close();
297
+ this.es = null;
298
+ setTimeout(() => {
299
+ resolve(true)
300
+ }, 500)
301
+ } catch (error) {
302
+ reject(error)
303
+ }
304
+ })
305
+ }
306
+ }
297
307
  module.exports.classHUE = classHUE
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.1.37",
6
+ "version": "2.1.39",
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": "3.0.1",