node-red-contrib-knx-ultimate 2.1.5 → 2.1.6

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
@@ -7,7 +7,11 @@
7
7
  # CHANGELOG
8
8
 
9
9
  <p>
10
- <b>Version 2.1.5</b> - June 2023<br/>
10
+ <b>Version 2.1.6</b> - June 2023<br/>
11
+ - Several fixes for reading the correct GAMUT color.<br/>
12
+ </p>
13
+ <p>
14
+ <b>Version 2.1.4</b> - June 2023<br/>
11
15
  - NEW: Hue light node: added random color cycle effect group address.<br/>
12
16
  - Fixed destroying KNX nodes.<br/>
13
17
  - Fixed destroying HUE nodes.<br/>
@@ -47,7 +47,7 @@ module.exports = function (RED) {
47
47
  case config.GALightSwitch:
48
48
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightSwitch))
49
49
  state = msg.payload === true ? { on: { on: true } } : { on: { on: false } }
50
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state)
50
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
51
51
  break
52
52
  case config.GALightDIM:
53
53
  // { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
@@ -64,17 +64,16 @@ module.exports = function (RED) {
64
64
  case config.GALightBrightness:
65
65
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBrightness))
66
66
  state = { dimming: { brightness: msg.payload } }
67
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state)
67
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
68
68
  break
69
69
  case config.GALightColor:
70
70
  // Behavior like ISE HUE CONNECT, by setting the brightness and on/off as well
71
- if (node.currentHUEDevice === undefined) return
72
71
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightColor))
73
- const gamut = node.currentHUEDevice.color.gamut_type || null
72
+ const gamut = node.currentHUEDevice !== undefined ? node.currentHUEDevice.color.gamut_type : null
74
73
  const retXY = hueColorConverter.ColorConverter.rgbToXy(msg.payload.red, msg.payload.green, msg.payload.blue, gamut)
75
74
  const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(msg.payload.red, msg.payload.green, msg.payload.blue)
76
75
  state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } }
77
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state)
76
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
78
77
  break
79
78
  case config.GALightBlink:
80
79
  const gaVal = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightSwitch))
@@ -85,19 +84,19 @@ module.exports = function (RED) {
85
84
  msg.payload = node.blinkValue
86
85
  //state = msg.payload === true ? { on: { on: true } } : { on: { on: false } }
87
86
  state = msg.payload === true ? { on: { on: true }, dimming: { brightness: 100 } } : { on: { on: false } }
88
- 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
87
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
88
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight') // It's ok twice, so the light turns off immeridaley
90
89
  }, 600);
91
90
  } else {
92
91
  if (node.timerBlink !== undefined) clearInterval(node.timerBlink)
93
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } })
92
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } }, 'setLight')
94
93
  }
95
94
  break
96
95
  case config.GALightColorCycle:
97
96
  const gaValColorCycle = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightSwitch))
98
97
  if (gaValColorCycle) {
99
98
 
100
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } })
99
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } }, 'setLight')
101
100
  node.timerColorCycle = setInterval(() => {
102
101
  try {
103
102
  function getRandomIntInclusive(min, max) {
@@ -108,27 +107,19 @@ module.exports = function (RED) {
108
107
  const red = getRandomIntInclusive(0, 255)
109
108
  const green = getRandomIntInclusive(0, 255)
110
109
  const blue = getRandomIntInclusive(0, 255)
111
- const gamut = node.currentHUEDevice.color.gamut_type || null
110
+ const gamut = node.currentHUEDevice !== undefined ? node.currentHUEDevice.color.gamut_type : null
112
111
  const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut)
113
112
  const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue)
114
113
  state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } }
115
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state)
114
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
116
115
  } catch (error) {
117
- (async () => {
118
- try {
119
- const retLight = await node.serverHue.hueManager.getLight(config.hueDevice)
120
- node.currentHUEDevice = retLight[0]
121
- RED.log.error('Errore knxUltimateHueLight node.currentHUEDevice Samba ' + error.message)
122
- } catch (err) {
123
- RED.log.error('Errore knxUltimateHueLight node.currentHUEDevice Banana ' + err.message)
124
- }
125
- })()
116
+
126
117
  }
127
118
  }, 10000);
128
119
 
129
- } else {
120
+ } else {
130
121
  if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle)
131
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } })
122
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } }, 'setLight')
132
123
  }
133
124
  break
134
125
  default:
@@ -158,7 +149,7 @@ module.exports = function (RED) {
158
149
  node.timerDim = setInterval(() => {
159
150
  node.timeoutDim += 1
160
151
  if (node.timeoutDim > 100) { node.timeoutDim = 0; clearInterval(node.timerDim) }
161
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, node.dimDirection)
152
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, node.dimDirection, 'setLight')
162
153
  }, 300);
163
154
  }
164
155
 
@@ -182,10 +173,9 @@ module.exports = function (RED) {
182
173
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
183
174
  }
184
175
  if (_event.hasOwnProperty('color')) {
185
- if (node.currentHUEDevice === undefined) return
186
176
  knxMsgPayload.topic = config.GALightColorState
187
177
  knxMsgPayload.dpt = config.dptLightColorState
188
- knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(_event.color.xy.x, _event.color.xy.y, node.currentHUEDevice.dimming.brightness)
178
+ knxMsgPayload.payload = hueColorConverter.ColorConverter.xyBriToRgb(_event.color.xy.x, _event.color.xy.y, (node.currentHUEDevice !== undefined ? node.currentHUEDevice.dimming.brightness : 100))
189
179
  // Send to KNX bus
190
180
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
191
181
  }
@@ -217,12 +207,15 @@ module.exports = function (RED) {
217
207
  }
218
208
  if (node.serverHue) {
219
209
  node.serverHue.removeClient(node)
210
+ // I must get the light object, to store it in the node.currentHUEDevice variable
211
+ // I queue the state request, by passing the callback to call whenever the HUE bridge send me the light status async
220
212
  if (node !== null && node.serverHue !== null && node.serverHue.hueManager !== null) {
221
213
  (async () => {
222
214
  try {
223
- const retLight = await node.serverHue.hueManager.getLight(config.hueDevice)
224
- node.currentHUEDevice = retLight[0]
225
- node.serverHue.addClient(node)
215
+ await node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getLight', (jLight) => {
216
+ node.currentHUEDevice = jLight
217
+ node.serverHue.addClient(node)
218
+ })
226
219
  } catch (err) {
227
220
  RED.log.error('Errore knxUltimateHueLight node.currentHUEDevice ' + err.message)
228
221
  }
@@ -230,7 +223,6 @@ module.exports = function (RED) {
230
223
  }
231
224
  }
232
225
 
233
-
234
226
  node.on('input', function (msg) {
235
227
 
236
228
  })
@@ -249,7 +249,7 @@ Start typing in the GA field, the name or group address of your KNX device, the
249
249
 
250
250
  |Property|Description|
251
251
  |--|--|
252
- | Rotate | This command is used either to send DIM (increase/decrease), aboslute brightness, or a random color, depending on the selected datapoint. |
252
+ | Rotate | This command is used either to send DIM (increase/decrease), aboslute brightness, or a random color, depending on the selected datapoint. If the random color (datapoint 232.600) is selected, **clockwise rotation** changes random colors and **counterclockwise rotation** set the light to **white** |
253
253
 
254
254
  ### Outputs
255
255
 
@@ -1,19 +1,5 @@
1
1
  module.exports = function (RED) {
2
2
 
3
- async function getLightState(node, _lightID) {
4
- return new Promise((resolve, reject) => {
5
- try {
6
- if (node !== null && node.serverHue !== null && node.serverHue.hueManager !== null) {
7
- node.serverHue.hueManager.getLight(_lightID).then(ret => {
8
- node.currentHUEDevice = ret[0]
9
- resolve(ret)
10
- })
11
- }
12
- } catch (error) {
13
- reject(error)
14
- }
15
- })
16
- }
17
3
 
18
4
  function knxUltimateHueTapDial(config) {
19
5
  RED.nodes.createNode(this, config)
@@ -41,14 +27,6 @@ module.exports = function (RED) {
41
27
  node.brightnessState = 0
42
28
  node.isTimerDimStopRunning = false
43
29
 
44
- // Read the state of the light and store it in the holding object
45
- try {
46
- if (config.hueLight !== undefined && config.hueLight !== '') getLightState(node, config.hueLight)
47
- } catch (error) {
48
- }
49
-
50
-
51
-
52
30
  // Used to call the status update from the config node.
53
31
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
54
32
 
@@ -58,7 +36,7 @@ module.exports = function (RED) {
58
36
  const dDate = new Date()
59
37
  node.status({ fill: fill, shape: shape, text: text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
60
38
  }
61
-
39
+
62
40
  // This function is called by the knx-ultimate config node, to output a msg.payload.
63
41
  node.handleSend = msg => {
64
42
  }
@@ -84,16 +62,20 @@ module.exports = function (RED) {
84
62
  node.brightnessState < 100 ? node.brightnessState += 20 : node.brightnessState = 100
85
63
  knxMsgPayload.payload = node.brightnessState
86
64
  } else if (knxMsgPayload.dpt.startsWith('232.600')) {
87
- // Random color
88
- knxMsgPayload.payload = { red: getRandomIntInclusive(0, 255), green: getRandomIntInclusive(0, 255), blue: getRandomIntInclusive(0, 255) }
89
- function getRandomIntInclusive(min, max) {
90
- min = Math.ceil(min);
91
- max = Math.floor(max);
92
- return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
65
+
66
+ if (_event.relative_rotary.last_event.action === 'start') {
67
+ // Random color
68
+ knxMsgPayload.payload = { red: getRandomIntInclusive(0, 255), green: getRandomIntInclusive(0, 255), blue: getRandomIntInclusive(0, 255) }
69
+ function getRandomIntInclusive(min, max) {
70
+ min = Math.ceil(min);
71
+ max = Math.floor(max);
72
+ return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
73
+ }
74
+ // Send to KNX bus
75
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
76
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX Change color clockwise' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
93
77
  }
94
- // Send to KNX bus
95
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
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() + ')' })
78
+
97
79
  }
98
80
  } else if (_event.relative_rotary.last_event.rotation.direction === 'counter_clock_wise') {
99
81
  if (knxMsgPayload.dpt.startsWith('3.007')) {
@@ -108,16 +90,15 @@ module.exports = function (RED) {
108
90
  node.brightnessState > 0 ? node.brightnessState -= 20 : node.brightnessState = 0
109
91
  knxMsgPayload.payload = node.brightnessState
110
92
  } else if (knxMsgPayload.dpt.startsWith('232.600')) {
111
- // Random color
112
- knxMsgPayload.payload = { red: getRandomIntInclusive(0, 255), green: getRandomIntInclusive(0, 255), blue: getRandomIntInclusive(0, 255) }
113
- function getRandomIntInclusive(min, max) {
114
- min = Math.ceil(min);
115
- max = Math.floor(max);
116
- return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
93
+
94
+ if (_event.relative_rotary.last_event.action === 'start') {
95
+ // Set white color
96
+ knxMsgPayload.payload = { red: 255, green: 255, blue: 255 }
97
+ // Send to KNX bus
98
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
99
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX Change color counterclockwise' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
117
100
  }
118
- // Send to KNX bus
119
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
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() + ')' })
101
+
121
102
  }
122
103
  }
123
104
 
@@ -75,6 +75,7 @@ class classHUE extends EventEmitter {
75
75
  console.log('KNXUltimateHUEConfig: classHUE: request.on(error): ' + error.message)
76
76
  // Restart the connection
77
77
  setTimeout(() => {
78
+ this.commandQueue = []
78
79
  req();
79
80
  }, 2000);
80
81
  });
@@ -89,19 +90,33 @@ class classHUE extends EventEmitter {
89
90
  handleQueue = async () => {
90
91
  if (this.commandQueue.length > 0) {
91
92
  const jRet = this.commandQueue.shift()
92
- try {
93
- const hue = hueApiV2.connect({ host: this.hueBridgeIP, key: this.username })
94
- const ok = await hue.setLight(jRet._lightID, jRet._state)
95
- } catch (error) {
96
- console.log('KNXUltimateHUEConfig: classHUE: handleQueue: ' + error.message)
97
- return ({ error: error.message })
93
+ switch (jRet._operation) {
94
+ case 'setLight':
95
+ try {
96
+ const hue = hueApiV2.connect({ host: this.hueBridgeIP, key: this.username })
97
+ const ok = await hue.setLight(jRet._lightID, jRet._state)
98
+ } catch (error) {
99
+ console.log('KNXUltimateHUEConfig: classHUE: handleQueue: setLight: ' + error.message)
100
+ }
101
+ break;
102
+ case 'getLight':
103
+ try {
104
+ const hue = hueApiV2.connect({ host: this.hueBridgeIP, key: this.username })
105
+ const jReturn = await hue.getLight(jRet._lightID)
106
+ jRet._callback(jReturn[0]) // Need to call the callback, because the event is absolutely async
107
+ } catch (error) {
108
+ console.log('KNXUltimateHUEConfig: classHUE: handleQueue: getLight: ' + error.message)
109
+ }
110
+ break
111
+ default:
112
+ break;
98
113
  }
99
114
  }
100
115
  // The Hue bridge allows about 10 telegram per second, so i need to make a queue manager
101
116
  setTimeout(this.handleQueue, 100)
102
117
  }
103
- writeHueQueueAdd = async (_lightID, _state) => {
104
- this.commandQueue.push({ _lightID, _state })
118
+ writeHueQueueAdd = async (_lightID, _state, _operation = 'setLight', _callback) => {
119
+ this.commandQueue.push({ _lightID, _state, _operation, _callback })
105
120
  }
106
121
 
107
122
 
@@ -153,15 +168,7 @@ class classHUE extends EventEmitter {
153
168
  }
154
169
 
155
170
 
156
- // Get light state
157
- getLight = async (_LightID) => {
158
- try {
159
- const hue = hueApiV2.connect({ host: this.hueBridgeIP, key: this.username })
160
- return await hue.getLight(_LightID)
161
- } catch (error) {
162
- console.log('KNXUltimateHUEConfig: classHUE: getLight: ' + error.message)
163
- }
164
- }
171
+
165
172
 
166
173
  close = async () => {
167
174
  return new Promise((resolve, reject) => {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=14.0.0"
5
5
  },
6
- "version": "2.1.5",
6
+ "version": "2.1.6",
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",