node-red-contrib-knx-ultimate 2.1.4 → 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
@@ -6,6 +6,10 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <p>
10
+ <b>Version 2.1.6</b> - June 2023<br/>
11
+ - Several fixes for reading the correct GAMUT color.<br/>
12
+ </p>
9
13
  <p>
10
14
  <b>Version 2.1.4</b> - June 2023<br/>
11
15
  - NEW: Hue light node: added random color cycle effect group address.<br/>
@@ -28,14 +28,6 @@ const convertSubtype = (baseType) => (kv) => {
28
28
  }
29
29
  }
30
30
 
31
- // 06/02/2020 To be tested
32
- // convertSubtype = (baseType) => (kv) => {
33
- // let value = `${baseType.base}.${kv[0]}`
34
- // return {
35
- // value: value
36
- // , text: value + ` (${kv[1].name}${kv[1].unit !== undefined?" - " + kv[1].unit:""})`
37
- // }
38
- // }
39
31
 
40
32
  const toConcattedSubtypes = (acc, baseType) => {
41
33
  const subtypes =
@@ -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,36 +84,42 @@ 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
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } })
98
+
99
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: true } }, 'setLight')
100
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
101
+ try {
102
+ function getRandomIntInclusive(min, max) {
103
+ min = Math.ceil(min);
104
+ max = Math.floor(max);
105
+ return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
106
+ }
107
+ const red = getRandomIntInclusive(0, 255)
108
+ const green = getRandomIntInclusive(0, 255)
109
+ const blue = getRandomIntInclusive(0, 255)
110
+ const gamut = node.currentHUEDevice !== undefined ? node.currentHUEDevice.color.gamut_type : null
111
+ const retXY = hueColorConverter.ColorConverter.rgbToXy(red, green, blue, gamut)
112
+ const bright = hueColorConverter.ColorConverter.getBrightnessFromRGB(red, green, blue)
113
+ state = bright > 0 ? { on: { on: true }, dimming: { brightness: bright }, color: { xy: retXY } } : { on: { on: false } }
114
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setLight')
115
+ } catch (error) {
116
+
105
117
  }
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
118
  }, 10000);
119
+
115
120
  } else {
116
121
  if (node.timerColorCycle !== undefined) clearInterval(node.timerColorCycle)
117
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } })
122
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } }, 'setLight')
118
123
  }
119
124
  break
120
125
  default:
@@ -144,7 +149,7 @@ module.exports = function (RED) {
144
149
  node.timerDim = setInterval(() => {
145
150
  node.timeoutDim += 1
146
151
  if (node.timeoutDim > 100) { node.timeoutDim = 0; clearInterval(node.timerDim) }
147
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, node.dimDirection)
152
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, node.dimDirection, 'setLight')
148
153
  }, 300);
149
154
  }
150
155
 
@@ -168,10 +173,9 @@ module.exports = function (RED) {
168
173
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
169
174
  }
170
175
  if (_event.hasOwnProperty('color')) {
171
- if (node.currentHUEDevice === undefined) return
172
176
  knxMsgPayload.topic = config.GALightColorState
173
177
  knxMsgPayload.dpt = config.dptLightColorState
174
- 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))
175
179
  // Send to KNX bus
176
180
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
177
181
  }
@@ -203,26 +207,22 @@ module.exports = function (RED) {
203
207
  }
204
208
  if (node.serverHue) {
205
209
  node.serverHue.removeClient(node)
206
- node.serverHue.addClient(node)
207
- setTimeout(() => {
208
- try {
209
- if (node !== null && node.serverHue !== null && node.serverHue.hueManager !== null) {
210
- node.serverHue.hueManager.getLight(config.hueDevice).then(ret => {
211
- try {
212
- if (ret !== undefined && ret.length > 0) node.currentHUEDevice = ret[0]
213
- } catch (error) {
214
- }
215
- //console.log("retrieving node.currentHUEDevice" + node.currentHUEDevice.metadata.name)
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
212
+ if (node !== null && node.serverHue !== null && node.serverHue.hueManager !== null) {
213
+ (async () => {
214
+ try {
215
+ await node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getLight', (jLight) => {
216
+ node.currentHUEDevice = jLight
217
+ node.serverHue.addClient(node)
216
218
  })
219
+ } catch (err) {
220
+ RED.log.error('Errore knxUltimateHueLight node.currentHUEDevice ' + err.message)
217
221
  }
218
- } catch (error) {
219
- console.log('Error: knxUltimateHueLight: node.serverHue.hueManager.getLight: ' + error.message)
220
- }
221
- }, 5000);
222
-
222
+ })()
223
+ }
223
224
  }
224
225
 
225
-
226
226
  node.on('input', function (msg) {
227
227
 
228
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.4",
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",