node-red-contrib-knx-ultimate 2.2.2 → 2.2.3

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,9 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <b>Version 2.2.3</b> - October 2023<br/>
10
+ - HUE Light: Again, rewrite of the DIM function to get rid of the dimming_delta.<br/>
11
+ </p>
9
12
  <p>
10
13
  <b>Version 2.2.2</b> - October 2023<br/>
11
14
  - NEW: HUE Motion: support HUE Camera motion events via the HUE Motion node.<br/>
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-param-reassign */
1
2
  /* eslint-disable no-inner-declarations */
2
3
  /* eslint-disable max-len */
3
4
  const dptlib = require("../KNXEngine/src/dptlib");
@@ -5,6 +6,12 @@ const HueClass = require("./utils/hueEngine").classHUE;
5
6
  const loggerEngine = require("./utils/sysLogger");
6
7
  const hueColorConverter = require("./utils/hueColorConverter");
7
8
 
9
+ function getRandomIntInclusive(min, max) {
10
+ min = Math.ceil(min);
11
+ max = Math.floor(max);
12
+ return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
13
+ }
14
+
8
15
  // Helpers
9
16
  const sortBy = (field) => (a, b) => {
10
17
  if (a[field] > b[field]) {
@@ -85,67 +92,49 @@ module.exports = (RED) => {
85
92
  (async () => {
86
93
  try {
87
94
  await node.loadResourcesFromHUEBridge(); // Then, you can use node.getResources, that works locally and doesn't query the HUE Bridge.
88
- } catch (error) { /* empty */ }
95
+ } catch (error) {
96
+ /* empty */
97
+ }
89
98
  })();
90
99
  if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("node.hueManager connected event");
91
100
  });
92
101
  };
93
102
 
94
103
  // Query the HUE Bridge to return the resources
95
- node.loadResourcesFromHUEBridge = (_callerNode = undefined) => new Promise((resolve, reject) => {
104
+ node.loadResourcesFromHUEBridge = () => {
96
105
  (async () => {
97
106
  // °°°°°° Load ALL resources
98
107
  try {
99
- if (_callerNode === undefined) {
100
- node.hueAllResources = await node.hueManager.hueApiV2.get("/resource");
101
- node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
102
- // Update all KNX State of the nodes with the new hue device values
103
- node.nodeClients.forEach((nodeClient) => {
104
- if (nodeClient.hueDevice !== undefined) {
105
- const oHUEDevice = node.hueAllResources.filter((a) => a.id === nodeClient.hueDevice)[0];
106
- if (oHUEDevice !== undefined) {
107
- nodeClient.currentHUEDevice = oHUEDevice;
108
- for (const [key, value] of Object.entries(oHUEDevice)) {
109
- // Update KNX State
110
- const oProperty = { id: oHUEDevice.id };
111
- oProperty[key] = value;
112
- nodeClient.handleSendHUE(oProperty);
113
- }
114
- }
115
- }
116
- });
117
- } else {
118
- // °°°°°° Read ONE resource and update only one node. The node is requesting an update because it has been edited in Node-Red's window by the user
119
- try {
120
- // Please KEEP THE AWAIT, otherwise al posto dell'oggetto, a Promisse will be returned.
121
- const oHUEDevice = await node.hueAllResources.filter((a) => a.id === _callerNode.hueDevice)[0];
108
+ node.hueAllResources = await node.hueManager.hueApiV2.get("/resource");
109
+ node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
110
+ // Update all KNX State of the nodes with the new hue device values
111
+ node.nodeClients.forEach((nodeClient) => {
112
+ if (nodeClient.hueDevice !== undefined) {
113
+ const oHUEDevice = node.hueAllResources.filter((a) => a.id === nodeClient.hueDevice)[0];
122
114
  if (oHUEDevice !== undefined) {
123
- _callerNode.currentHUEDevice = oHUEDevice;
124
- for (const [key, value] of Object.entries(oHUEDevice)) {
125
- // Update KNX State
126
- const oProperty = { id: oHUEDevice.id };
127
- oProperty[key] = value;
128
- _callerNode.handleSendHUE(oProperty);
129
- }
130
- }
131
- } catch (error) {
132
- if (this.sysLogger !== undefined && this.sysLogger !== null) {
133
- this.sysLogger.error(`KNXUltimatehueEngine: loadResourcesFromHUEBridge, from a single node that has been edited: ${error.message}`);
134
- reject(error.message);
115
+ nodeClient.currentHUEDevice = oHUEDevice;
116
+ nodeClient.handleSendHUE(oHUEDevice);
117
+ // for (const [key, value] of Object.entries(oHUEDevice)) {
118
+ // // Update KNX State
119
+ // const oProperty = { id: oHUEDevice.id };
120
+ // oProperty[key] = value;
121
+ // nodeClient.handleSendHUE(oProperty);
122
+ // }
135
123
  }
136
124
  }
137
- }
138
- resolve(true);
125
+ });
139
126
  } catch (error) {
140
127
  if (this.sysLogger !== undefined && this.sysLogger !== null) {
141
128
  this.sysLogger.error(`KNXUltimatehueEngine: loadResourcesFromHUEBridge: ${error.message}`);
142
- reject(error.message);
129
+ return (error.message);
143
130
  }
144
131
  }
132
+ return true;
145
133
  })();
146
- });
134
+ };
135
+
147
136
  // Returns the cached devices (node.hueAllResources) by type.
148
- node.getResources = (_rtype) => {
137
+ node.getResources = function getResources(_rtype) {
149
138
  try {
150
139
  if (node.hueAllResources === undefined) return;
151
140
  // Returns capitalized string
@@ -260,23 +249,17 @@ module.exports = (RED) => {
260
249
  };
261
250
 
262
251
  // Query HUE Bridge and get the updated color.
263
- node.getColorFromHueLight = (_lightId) => new Promise((resolve, reject) => {
264
- (async () => {
265
- try {
266
- // await node.loadResourcesFromHUEBridge();
267
- const oLight = node.hueAllResources.filter((a) => a.id === _lightId)[0];
268
- const ret = hueColorConverter.ColorConverter.xyBriToRgb(
269
- oLight.color.xy.x,
270
- oLight.color.xy.y,
271
- oLight.dimming.brightness,
272
- );
273
- resolve(JSON.stringify(ret));
274
- } catch (error) {
275
- if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: getColorFromHueLight: error ${error.message}`);
276
- reject(error.message);
277
- }
278
- })();
279
- });
252
+ node.getColorFromHueLight = (_lightId) => {
253
+ try {
254
+ // await node.loadResourcesFromHUEBridge();
255
+ const oLight = node.hueAllResources.filter((a) => a.id === _lightId)[0];
256
+ const ret = hueColorConverter.ColorConverter.xyBriToRgb(oLight.color.xy.x, oLight.color.xy.y, oLight.dimming.brightness);
257
+ return JSON.stringify(ret);
258
+ } catch (error) {
259
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: getColorFromHueLight: error ${error.message}`);
260
+ return {};
261
+ }
262
+ };
280
263
 
281
264
  node.addClient = (_Node) => {
282
265
  // Check if node already exists
@@ -288,6 +271,12 @@ module.exports = (RED) => {
288
271
  text: "Hue initialized.",
289
272
  });
290
273
  node.nodeClients.push(_Node);
274
+ // Update the node hue device, as soon as a node register itself to hue-config nodeClients
275
+ if (node.hueAllResources !== null) { // At first start, due to the async method for retrieving hueAllResources, hueAllResources is still null. The first start is handled in node.hueManager.on("connected")
276
+ const oHUEDevice = node.hueAllResources.filter((a) => a.id === _Node.hueDevice)[0];
277
+ _Node.currentHUEDevice = oHUEDevice;
278
+ if (oHUEDevice !== undefined) _Node.handleSendHUE(oHUEDevice);
279
+ }
291
280
  }
292
281
  };
293
282
 
@@ -295,7 +284,9 @@ module.exports = (RED) => {
295
284
  // Remove the client node from the clients array
296
285
  try {
297
286
  node.nodeClients = node.nodeClients.filter((x) => x.id !== _Node.id);
298
- } catch (error) { /* empty */ }
287
+ } catch (error) {
288
+ /* empty */
289
+ }
299
290
  };
300
291
 
301
292
  node.on("close", (done) => {
@@ -320,14 +311,12 @@ module.exports = (RED) => {
320
311
  });
321
312
 
322
313
  RED.httpAdmin.get("/knxUltimateGetHueColor", RED.auth.needsPermission("hue-config.read"), (req, res) => {
323
- (async () => {
324
- try {
325
- const rgbColor = await node.getColorFromHueLight(req.query.id);
326
- res.json(rgbColor);
327
- } catch (error) {
328
- res.json("Select the device first!");
329
- }
330
- })();
314
+ try {
315
+ const rgbColor = node.getColorFromHueLight(req.query.id);
316
+ res.json(rgbColor);
317
+ } catch (error) {
318
+ res.json("Select the device first!");
319
+ }
331
320
  });
332
321
 
333
322
  RED.httpAdmin.get("/KNXUltimateGetResourcesHUE", RED.auth.needsPermission("hue-config.read"), (req, res) => {
@@ -347,8 +336,7 @@ module.exports = (RED) => {
347
336
  });
348
337
 
349
338
  RED.httpAdmin.get("/knxUltimateDpts", RED.auth.needsPermission("hue-config.read"), (req, res) => {
350
- const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy("base"))
351
- .reduce(toConcattedSubtypes, []);
339
+ const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy("base")).reduce(toConcattedSubtypes, []);
352
340
  res.json(dpts);
353
341
  });
354
342
  }
@@ -940,14 +940,16 @@ return msg;`,
940
940
  }
941
941
  // 12/11/2021 Starts the telegram out queue handler
942
942
  if (node.timerSendTelegramFromQueue !== null) clearInterval(node.timerSendTelegramFromQueue);
943
- node.timerSendTelegramFromQueue = setInterval(handleTelegramQueue, config.delaybetweentelegrams === undefined || Number(config.delaybetweentelegrams) < 20 ? 20 : Number(config.delaybetweentelegrams),
944
- ); // 02/01/2020 Start the timer that handles the queue of telegrams
943
+ node.timerSendTelegramFromQueue = setInterval(handleTelegramQueue, config.delaybetweentelegrams === undefined || Number(config.delaybetweentelegrams) < 20 ? 20 : Number(config.delaybetweentelegrams)); // 02/01/2020 Start the timer that handles the queue of telegrams
945
944
  node.linkStatus = "connected";
946
945
 
947
946
  // Start the timer to do initial read.
948
947
  if (node.timerDoInitialRead !== null) clearTimeout(node.timerDoInitialRead);
949
- node.timerDoInitialRead = setTimeout(DoInitialReadFromKNXBusOrFile, 6000); // 17/02/2020 Do initial read of all nodes requesting initial read
950
- node.timerCallConnectToHueBridgeOfAllHUEServers = setTimeout(callConnectToHueBridgeOfAllHUEServers, 10000); // connects all hue-config nodes to the HUE Bridge.
948
+ node.timerDoInitialRead = setTimeout(() => {
949
+ DoInitialReadFromKNXBusOrFile();
950
+ callConnectToHueBridgeOfAllHUEServers();
951
+ }, 6000); // 17/02/2020 Do initial read of all nodes requesting initial read
952
+ //node.timerCallConnectToHueBridgeOfAllHUEServers = setTimeout(callConnectToHueBridgeOfAllHUEServers, 6000); // connects all hue-config nodes to the HUE Bridge.
951
953
  const t = setTimeout(() => {
952
954
  // 21/03/2022 fixed possible memory leak. Previously was setTimeout without "let t = ".
953
955
  node.setAllClientsStatus("Connected.", "green", "On duty.");
@@ -1,107 +1,108 @@
1
1
  module.exports = function (RED) {
2
2
  function knxUltimateHueBattery(config) {
3
- RED.nodes.createNode(this, config)
4
- const node = this
5
- node.server = RED.nodes.getNode(config.server)
6
- node.serverHue = RED.nodes.getNode(config.serverHue)
7
- node.topic = node.name
8
- node.name = config.name === undefined ? 'Hue' : config.name
9
- node.dpt = ''
10
- node.notifyreadrequest = false
11
- node.notifyreadrequestalsorespondtobus = 'false'
12
- node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
13
- node.notifyresponse = false
14
- node.notifywrite = true
15
- node.initialread = true
16
- node.listenallga = true // Don't remove
17
- node.outputtype = 'write'
18
- node.outputRBE = false // Apply or not RBE to the output (Messages coming from flow)
19
- node.inputRBE = false // Apply or not RBE to the input (Messages coming from BUS)
20
- node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg
21
- node.passthrough = 'no'
22
- node.formatmultiplyvalue = 1
23
- node.formatnegativevalue = 'leave'
24
- node.formatdecimalsvalue = 2
3
+ RED.nodes.createNode(this, config);
4
+ const node = this;
5
+ node.server = RED.nodes.getNode(config.server);
6
+ node.serverHue = RED.nodes.getNode(config.serverHue);
7
+ node.topic = node.name;
8
+ node.name = config.name === undefined ? 'Hue' : config.name;
9
+ node.dpt = '';
10
+ node.notifyreadrequest = false;
11
+ node.notifyreadrequestalsorespondtobus = 'false';
12
+ node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
13
+ node.notifyresponse = false;
14
+ node.notifywrite = true;
15
+ node.initialread = true;
16
+ node.listenallga = true; // Don't remove
17
+ node.outputtype = 'write';
18
+ node.outputRBE = false; // Apply or not RBE to the output (Messages coming from flow)
19
+ node.inputRBE = false; // Apply or not RBE to the input (Messages coming from BUS)
20
+ node.currentPayload = ''; // Current value for the RBE input and for the .previouspayload msg
21
+ node.passthrough = 'no';
22
+ node.formatmultiplyvalue = 1;
23
+ node.formatnegativevalue = 'leave';
24
+ node.formatdecimalsvalue = 2;
25
25
 
26
26
  // Used to call the status update from the config node.
27
- node.setNodeStatus = ({ fill, shape, text, payload }) => {
27
+ node.setNodeStatus = ({
28
+ fill, shape, text, payload,
29
+ }) => {
28
30
 
29
- }
31
+ };
30
32
  // Used to call the status update from the HUE config node.
31
- node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
32
- if (payload === undefined) return
33
- const dDate = new Date()
34
- payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
35
- node.status({ fill, shape, text: text + ' ' + payload + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
36
- }
33
+ node.setNodeStatusHue = ({
34
+ fill, shape, text, payload,
35
+ }) => {
36
+ if (payload === undefined) return;
37
+ const dDate = new Date();
38
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
39
+ node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
40
+ };
37
41
 
38
42
  // This function is called by the knx-ultimate config node, to output a msg.payload.
39
- node.handleSend = msg => {
40
- }
43
+ node.handleSend = (msg) => {
44
+ };
41
45
 
42
- node.handleSendHUE = _event => {
46
+ node.handleSendHUE = (_event) => {
43
47
  try {
44
48
  if (_event.id === config.hueDevice) {
45
- const knxMsgPayload = {}
46
- knxMsgPayload.topic = config.GAbatterysensor
47
- knxMsgPayload.dpt = config.dptbatterysensor
49
+ const knxMsgPayload = {};
50
+ knxMsgPayload.topic = config.GAbatterysensor;
51
+ knxMsgPayload.dpt = config.dptbatterysensor;
48
52
 
49
53
  if (_event.hasOwnProperty('power_state') && _event.power_state.hasOwnProperty('battery_level')) {
50
- knxMsgPayload.payload = _event.power_state.battery_level
54
+ knxMsgPayload.payload = _event.power_state.battery_level;
51
55
  // Send to KNX bus
52
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
53
-
56
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
57
+ node.server.writeQueueAdd({
58
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
59
+ });
60
+ }
61
+
54
62
  // Setup the output msg
55
- knxMsgPayload.name = node.name
56
- knxMsgPayload.event = 'power_state'
63
+ knxMsgPayload.name = node.name;
64
+ knxMsgPayload.event = 'power_state';
57
65
 
58
66
  // Send payload
59
- knxMsgPayload.rawEvent = _event
60
- node.send(knxMsgPayload)
61
- node.setNodeStatusHue({ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload })
67
+ knxMsgPayload.rawEvent = _event;
68
+ node.send(knxMsgPayload);
69
+ node.setNodeStatusHue({
70
+ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload,
71
+ });
62
72
  }
63
73
  }
64
74
  } catch (error) {
65
- node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
75
+ node.status({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
66
76
  }
67
- }
77
+ };
68
78
 
69
79
  // On each deploy, unsubscribe+resubscribe
70
80
  if (node.server) {
71
- node.server.removeClient(node)
72
- node.server.addClient(node)
81
+ node.server.removeClient(node);
82
+ node.server.addClient(node);
73
83
  }
74
84
  if (node.serverHue) {
75
- node.serverHue.removeClient(node)
85
+ node.serverHue.removeClient(node);
76
86
  // I must get the object, to store read the battery status
77
87
  // I queue the state request, by passing the callback to call whenever the HUE bridge send me the light status async
78
88
  if (node.serverHue !== null && node.serverHue.hueManager !== null) {
79
- (async () => {
80
- try {
81
- node.serverHue.addClient(node)
82
- await node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, null, 'getBattery', (jLight) => {
83
- node.handleSendHUE(jLight)
84
- })
85
- } catch (err) {
86
- RED.log.error('Errore knxUltimateHueBattery subscribe: ' + err.message)
87
- }
88
- })()
89
+ node.serverHue.addClient(node);
89
90
  }
90
91
  }
91
92
 
92
- node.on('input', function (msg) {
93
+ node.on('input', (msg) => {
93
94
 
94
- })
95
+ });
95
96
 
96
- node.on('close', function (done) {
97
+ node.on('close', (done) => {
97
98
  if (node.server) {
98
- node.server.removeClient(node)
99
+ node.server.removeClient(node);
99
100
  }
100
101
  if (node.serverHue) {
101
- node.serverHue.removeClient(node)
102
+ node.serverHue.removeClient(node);
102
103
  }
103
- done()
104
- })
104
+ done();
105
+ });
105
106
  }
106
- RED.nodes.registerType('knxUltimateHueBattery', knxUltimateHueBattery)
107
- }
107
+ RED.nodes.registerType('knxUltimateHueBattery', knxUltimateHueBattery);
108
+ };
@@ -66,7 +66,7 @@
66
66
  dptDaylightSensor: { value: "" },
67
67
 
68
68
  specifySwitchOnBrightness: { value: "yes" },
69
- updateKNXBrightnessStatusOnHUEOnOff: { value: "onhueoff" },
69
+ updateKNXBrightnessStatusOnHUEOnOff: { value: "no" },
70
70
  dimSpeed: { value: 5000, required: false },
71
71
  minDimLevelLight: { value: 10, required: false },
72
72
  maxDimLevelLight: { value: 100, required: false },