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

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/.eslintrc.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
- "env": {
3
- "browser": true,
4
- "commonjs": true,
5
- "es2021": true
6
- },
7
- "extends": "airbnb-base",
8
- "parserOptions": {
9
- "ecmaVersion": "latest"
10
- },
11
- "rules": {
12
- "quotes": "off"
13
- }
2
+ "env": {
3
+ "browser": true,
4
+ "commonjs": true,
5
+ "es2021": true
6
+ },
7
+ "extends": "airbnb-base",
8
+ "parserOptions": {
9
+ "ecmaVersion": "latest"
10
+ },
11
+ "rules": {
12
+ "quotes": "off"
13
+ }
14
14
  }
package/CHANGELOG.md CHANGED
@@ -1,10 +1,26 @@
1
1
  ![Sample Node](img/logo.png)
2
2
 
3
- [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
3
+ [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
4
4
 
5
5
  <br/>
6
6
 
7
7
  # CHANGELOG
8
+
9
+ <p>
10
+ <b>Version 2.2.2</b> - October 2023<br/>
11
+ - NEW: HUE Motion: support HUE Camera motion events via the HUE Motion node.<br/>
12
+ - HUE Light: some tweaking to the GUI.<br/>
13
+ - HUE Grouped Light: Fixed relative dimming function.<br/>
14
+ - KNK Alerter node: <a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/SampleAlerter">fixed the wiki sample page</a>.<br/>
15
+ - KNX Viewer: added the help pane in Node-Red.<br/>
16
+ </p>
17
+ <p>
18
+ <b>Version 2.2.1</b> - October 2023<br/>
19
+ - Massive rewrite of the DIM function, for brightness and for color temperature.<br/>
20
+ - NEW: HUE Light: Added dimming speed, minimum dim value and maximum dim value.<br/>
21
+ - NEW: HUE Light: Read of the lamp status at node-red start and after deploy of a new node.<br/>
22
+ - Security fix: patched a vulnerability in crypto.js.<br/>
23
+ </p>
8
24
  <p>
9
25
  <b>Version 2.1.63</b> - October 2023<br/>
10
26
  - HUE Light: optimized the GUI in select color TAB.<br/>
@@ -44,7 +44,7 @@
44
44
  "log-driver": "1.2.7",
45
45
  "lodash": "4.17.21",
46
46
  "path": "0.12.7",
47
- "crypto-js": "4.1.1",
47
+ "crypto-js": "4.2.0",
48
48
  "xml2js": "0.6.0"
49
49
  },
50
50
  "devDependencies": {}
@@ -129,8 +129,8 @@
129
129
 
130
130
 
131
131
  </script>
132
- <script type="text/markdown" data-help-name="hue-config">
133
- <p> This node registers to the Hue Bridge.<br/>
132
+ <script type="text/markdown" data-help-name="hue-config"
133
+ This node registers to the Hue Bridge.
134
134
 
135
135
  Just set the Bridge's IP and click **CONNECT** button.
136
136
 
@@ -35,12 +35,13 @@ const toConcattedSubtypes = (acc, baseType) => {
35
35
  return acc.concat(subtypes);
36
36
  };
37
37
 
38
- module.exports = (RED) => {
39
- RED.httpAdmin.get("/knxUltimateDpts", RED.auth.needsPermission("hue-config.read"), (req, res) => {
40
- const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy("base")).reduce(toConcattedSubtypes, []);
41
- res.json(dpts);
42
- });
38
+ function getRandomInt(min, max) {
39
+ min = Math.ceil(min);
40
+ max = Math.floor(max);
41
+ return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
42
+ }
43
43
 
44
+ module.exports = (RED) => {
44
45
  function hueConfig(config) {
45
46
  RED.nodes.createNode(this, config);
46
47
  const node = this;
@@ -48,6 +49,7 @@ module.exports = (RED) => {
48
49
  node.nodeClients = []; // Stores the registered clients
49
50
  node.loglevel = config.loglevel !== undefined ? config.loglevel : "error"; // 18/02/2020 Loglevel default error
50
51
  node.sysLogger = null;
52
+ node.hueAllResources = null;
51
53
  try {
52
54
  node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }); // New logger to adhere to the loglevel selected in the config-window
53
55
  } catch (error) {
@@ -80,20 +82,72 @@ module.exports = (RED) => {
80
82
  });
81
83
  // Connected
82
84
  node.hueManager.on("connected", () => {
85
+ (async () => {
86
+ try {
87
+ await node.loadResourcesFromHUEBridge(); // Then, you can use node.getResources, that works locally and doesn't query the HUE Bridge.
88
+ } catch (error) { /* empty */ }
89
+ })();
83
90
  if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info("node.hueManager connected event");
84
91
  });
85
- // Initialize the http wrapper, to use the provided key.
86
- // This http wrapper is used to get the data from HUE brigde
87
- await node.refreshResources();
88
92
  };
89
93
 
90
- (async () => {
91
- await node.ConnectToHueBridge();
92
- })();
93
-
94
- // Get all devices and join it with relative rooms, by adding the room name to the device name
94
+ // Query the HUE Bridge to return the resources
95
+ node.loadResourcesFromHUEBridge = (_callerNode = undefined) => new Promise((resolve, reject) => {
96
+ (async () => {
97
+ // °°°°°° Load ALL resources
98
+ 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];
122
+ 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);
135
+ }
136
+ }
137
+ }
138
+ resolve(true);
139
+ } catch (error) {
140
+ if (this.sysLogger !== undefined && this.sysLogger !== null) {
141
+ this.sysLogger.error(`KNXUltimatehueEngine: loadResourcesFromHUEBridge: ${error.message}`);
142
+ reject(error.message);
143
+ }
144
+ }
145
+ })();
146
+ });
147
+ // Returns the cached devices (node.hueAllResources) by type.
95
148
  node.getResources = (_rtype) => {
96
149
  try {
150
+ if (node.hueAllResources === undefined) return;
97
151
  // Returns capitalized string
98
152
  function capStr(s) {
99
153
  if (typeof s !== "string") return "";
@@ -121,14 +175,14 @@ module.exports = (RED) => {
121
175
  resourceName += "ALL GROUPS and ";
122
176
  } else {
123
177
  resourceName += `${owner.metadata.name} and `;
124
- //const room = node.hueAllRooms.find((child) => child.children.find((a) => a.rid === owner.id));
125
- //sRoom += room !== undefined ? `${room.metadata.name} + ` : " + ";
126
- sType += capStr(owner.type) + ' + ';
178
+ // const room = node.hueAllRooms.find((child) => child.children.find((a) => a.rid === owner.id));
179
+ // sRoom += room !== undefined ? `${room.metadata.name} + ` : " + ";
180
+ sType += `${capStr(owner.type)} + `;
127
181
  }
128
182
  }
129
183
  sType = sType.slice(0, -" + ".length);
130
184
  resourceName = resourceName.slice(0, -" and ".length);
131
- resourceName += sType !== "" ? ' (' + sType + ')' : "";
185
+ resourceName += sType !== "" ? ` (${sType})` : "";
132
186
  retArray.push({
133
187
  name: `${capStr(resource.type)}: ${resourceName}`,
134
188
  id: resource.id,
@@ -153,7 +207,7 @@ module.exports = (RED) => {
153
207
  id: resource.id,
154
208
  });
155
209
  }
156
- if (_rtype === "motion") {
210
+ if (_rtype === "motion" || _rtype === "camera_motion") {
157
211
  const linkedDevName = node.hueAllResources.find((dev) => dev.type === "device" && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || "";
158
212
  retArray.push({
159
213
  name: `${capStr(_rtype)}: ${linkedDevName}`,
@@ -200,32 +254,16 @@ module.exports = (RED) => {
200
254
  }
201
255
  return { devices: retArray };
202
256
  } catch (error) {
203
- if (node.sysLogger !== undefined && node.sysLogger !== null)
204
- node.sysLogger.error(`KNXUltimateHue: hueEngine: classHUE: getDevices: error ${error.message}`);
257
+ if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: classHUE: getResources: error ${error.message}`);
205
258
  return { devices: error.message };
206
259
  }
207
260
  };
208
- node.refreshResources = () => new Promise((resolve, reject) => {
209
- (async () => {
210
- // Reload all resources
211
- try {
212
- node.hueAllResources = await node.hueManager.hueApiV2.get("/resource");
213
- node.hueAllRooms = node.hueAllResources.filter((a) => a.type === "room");
214
- resolve(true);
215
- } catch (error) {
216
- if (this.sysLogger !== undefined && this.sysLogger !== null) {
217
- this.sysLogger.error(`KNXUltimatehueEngine: getting resources: ${error.message}`);
218
- reject(error.message);
219
- }
220
- }
221
- })();
222
- });
223
261
 
224
- // Get all devices and join it with relative rooms, by adding the room name to the device name
262
+ // Query HUE Bridge and get the updated color.
225
263
  node.getColorFromHueLight = (_lightId) => new Promise((resolve, reject) => {
226
264
  (async () => {
227
265
  try {
228
- await node.refreshResources();
266
+ // await node.loadResourcesFromHUEBridge();
229
267
  const oLight = node.hueAllResources.filter((a) => a.id === _lightId)[0];
230
268
  const ret = hueColorConverter.ColorConverter.xyBriToRgb(
231
269
  oLight.color.xy.x,
@@ -307,9 +345,13 @@ module.exports = (RED) => {
307
345
  })();
308
346
  }
309
347
  });
310
- }
311
348
 
312
- // RED.nodes.registerType("hue-config", hue-config);
349
+ 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, []);
352
+ res.json(dpts);
353
+ });
354
+ }
313
355
  RED.nodes.registerType("hue-config", hueConfig, {
314
356
  credentials: {
315
357
  username: { type: "password" },