node-red-contrib-huemagic 4.0.5 → 4.2.1

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
@@ -2,7 +2,76 @@
2
2
 
3
3
  # Changelog
4
4
 
5
- ### v3.0.0 (latest)
5
+ ### v4.2.1 (latest)
6
+
7
+ * Commands are now re-executed up to three times if they fail due to a bridge timeout
8
+ * The "image" option on the "Hue Light" node will now set the corresponding gradient colors on supported resources
9
+ * Better handling of broken connections to the bridge ([#309](https://github.com/Foddy/node-red-contrib-huemagic/pull/309)) (thx)
10
+ * Fixed an error with the "Hue Scenes" node on newer bridge firmwares ([#335](https://github.com/Foddy/node-red-contrib-huemagic/issues/335)) ([#339](https://github.com/Foddy/node-red-contrib-huemagic/pull/339)) (thx)
11
+ * Fixed an uncaught exception on newer bridge firmwares ([#302](https://github.com/Foddy/node-red-contrib-huemagic/issues/302)) ([#309](https://github.com/Foddy/node-red-contrib-huemagic/pull/309)) (thx)
12
+ * Updated dependencies to the latest versions
13
+ * Fixed some typos here and there
14
+
15
+ ### v4.1.0
16
+
17
+ * New queue worker throttles the number of parallel requests to the bridge to avoid 503 API limit errors (can be configured in the Bridge configuration)
18
+ * Resources are now alphabetically sorted in the node´s configuration interface ([#282](https://github.com/Foddy/node-red-contrib-huemagic/pull/282)) (thx)
19
+ * "Hue Brightness" node was optimized to output more accurate "dark" and "dayLight" values
20
+ * Several optimizations in the documentation of some nodes
21
+
22
+ ### v4.0.5
23
+
24
+ * The "Hue Group" node now contains the "resources" information with all linked resources behind the group/zone
25
+ * Fixed an issue that caused Node-RED to restart if a command was sent before a node was initialized
26
+
27
+ ### v4.0.4
28
+
29
+ * Fixed an issue with the bridge config node checking for updates too frequently ([#246](https://github.com/Foddy/node-red-contrib-huemagic/issues/246#issuecomment-1009376442))
30
+ * Fixed an issue with multiple bridges configured
31
+
32
+ ### v4.0.3
33
+
34
+ > **Attention!** HueMagic v4+ has been almost completely rewritten under the hood and requires at least the (square-shaped) Philips Hue Bridge firmware 1948086000+ from November 1st, 2021 ([Upgrade instructions](https://www.lighting.philips.com/content/B2C/en_US/microsites/meethue/marketing-catalog/huewireless_ca/support/security-advisory/general/where-and-how-can-i-update-my-hue-system-with-the-latest-software.html)) and Node-RED v1+ ([Upgrade instructions](https://nodered.org/docs/getting-started/local#upgrading-node-red)). If you are upgrading from a previous HueMagic version to the v4, you will have to reconfigure (not completely rebuild) all nodes by clicking them and selecting the appropriate device from the list. This also applies to nodes / functions that are operated in universal mode, as the numeric identifiers of the latest Philips Hue API version have been replaced in UUIDs. The nodes "Hue Switch", "Hue Button" & "Hue Tap" have been replaced in v4 by the universal and uniform node "Hue Buttons", which works with all button / switch devices that are connected to the Hue Bridge (please note here also the new API in the documentation). The request and return objects of the individual nodes are largely compatible with older HueMagic versions - with the exception of the nodes "Hue Bridge", "Hue Buttons", "Hue Scene" & "Hue Group". These need to be adjusted in the v4. Make sure that you meet the minimum technical requirements and have a quiet minute for the migration before upgrading to the v4.
35
+
36
+ * HueMagic speaks now directly with the bridge without any submodules *(huejay dependency removed)*
37
+ * Migrated to the newest CLIP/v2 API version from the Philips Hue bridge
38
+ * Nodes are now updated via push notifications (SSE) from the bridge instead of periodic polling
39
+ * Instant "current status" queries on each node with no loading time
40
+ * "Hue Buttons" node supports all new Philips Hue buttons/switches (e.g. Dimmer Switch v2, Hue Wall Switch…)
41
+ * All nodes provide additional property information in the output (check docs)
42
+ * New universal node "Hue Buttons" replaces the following nodes: "Hue Switch", "Hue Button", "Hue Tap"
43
+ * New "updated" object for all nodes, which only contains the properties that have been updated since the last state
44
+ * New configuration option to suppress first message after node initialization (for all nodes)
45
+ * New gradient color setting for compatible light sources (in "Hue Light" node)
46
+ * New inject button for almost all nodes, which triggers the current status of a node
47
+ * New "universal mode" support for the "Hue Rule" node
48
+ * New SVG-version of each node icon for higher quality rendering in the Node-RED UI
49
+ * New and full featured examples for each node right inside Node-RED
50
+ * New color mix feature in "Hue Light" nodes with the ability to mix the current light color with another
51
+ * New automatic brightness support based on the current time for "Hue Light" & "Hue Group" nodes
52
+ * New "Superhero", "Neon City" & "Christmas" (for next year, sorry) animations in "Hue Magic" node
53
+ * The custom alert effect on "Hue Light" & "Hue Group" nodes can now also be configured in brightness
54
+ * All nodes will now also forward the last command that has been applied ([#249](https://github.com/Foddy/node-red-contrib-huemagic/issues/249))
55
+ * "Hue Temperature" & "Hue Brightness" nodes can now also be activated & deactivated
56
+ * "Hue Light" & "Hue Group" nodes can now also receive XY color settings
57
+ * "Hue Light" & "Hue Group" can now also receive a named color temperature setting
58
+ * "Hue Light" & "Hue Group" nodes can now receive future brightness states in "turned off" mode ([#244](https://github.com/Foddy/node-red-contrib-huemagic/issues/244))
59
+ * Automatic light temperature setting outputs now values from 153 (coldest) to 500 (warmest)
60
+ * Automatic color correction based on the light´s capabilities for more accurate color settings
61
+ * Optimized node editor configuration UI to better match the current Node-RED´s UI
62
+ * The option for "automatic firmware updates" on the bridge moved to the bridge configuration node
63
+ * "Hue Group" node does no longer contain the "msg.info.model" & "msg.info.class" property
64
+ * Fixed timeout connection issues to the bridge
65
+ * Fixed an issue with non stopping custom animations ([#222](https://github.com/Foddy/node-red-contrib-huemagic/issues/222)), ([#224](https://github.com/Foddy/node-red-contrib-huemagic/issues/224)) & ([#226](https://github.com/Foddy/node-red-contrib-huemagic/pull/226)) (thx)
66
+ * Node-RED will no longer crash if there is no active connection to the bridge ([#237](https://github.com/Foddy/node-red-contrib-huemagic/issues/237))
67
+ * Fixed an issue that prevented the light / group from not reporting its own status when queried & node events were deactivated ([#248](https://github.com/Foddy/node-red-contrib-huemagic/issues/248))
68
+ * Fixed an issue with nodes in universal mode ([#245](https://github.com/Foddy/node-red-contrib-huemagic/issues/245))
69
+ * A possible attack target has been fixed ([#217](https://github.com/Foddy/node-red-contrib-huemagic/issues/217))
70
+ * Moved away from "moment.js" to "Day.js" for date/time formatting inside the nodes
71
+ * Updated README and help section on each node
72
+ * Updated dependencies to the latest version
73
+
74
+ ### v3.0.0
6
75
  * Hue Motion, Hue Brightness, Hue Tap, Hue Switch & Hue Button nodes can now receive a status request
7
76
  * New "Universal Mode" for the Hue Motion, Hue Brightness, Hue Tap, Hue Switch & Hue Button nodes
8
77
  * The "colorTemp" property for Hue Light & Hue Group nodes can now also be set to "cold", "normal", "warm" or "auto"
package/README.md CHANGED
@@ -142,7 +142,7 @@ If the "fetch" command has been used on the node, the bridge outputs the corresp
142
142
 
143
143
  #### Global status messages under `msg.updated` (optional)
144
144
 
145
- Unless deactivated, the node outputs an updated status message for each resource on the bridge. The status message under * msg.updated * follows the pattern of the respective resource and varies depending on the type of device that was last updated.
145
+ Unless deactivated, the node outputs an updated status message for each resource on the bridge. The status message under `msg.updated` follows the pattern of the respective resource and varies depending on the type of device that was last updated.
146
146
 
147
147
  #### Last command under `msg.command` (optional)
148
148
 
@@ -172,7 +172,7 @@ To play or stop an animation, pass an object with the following content to the n
172
172
  |--|--|
173
173
  | payload (boolean) | `true`, starts the animation, `false`, stops the animation |
174
174
 
175
- ### Own animations
175
+ ### Custom animations
176
176
 
177
177
  If you pass your own animation to the node, the preselected, pre-defined animation (if set) will be temporarily replaced by yours. Own HueMagic animations are a sequence of commands that have been combined in an array. Each array element forms a step - whereby a step can also consist of several frames (transition effects).
178
178
 
@@ -273,7 +273,7 @@ In addition to simply switching it on and off, there are also many other options
273
273
  | gradient (object {hex […]}) | An object with a supported color object (e.g. `hex`,` rgb`, ...) and several colors to set a gradient to supported lights |
274
274
  | mixColor (object) | A color to be mixed with the current light color. Can accept `color`, `hex`, `rgb` or `xyColor` objects and optionally `amount` (int) to indicate the mixing ratio in percent |
275
275
  | image (string) | Path of an image (local or on the web) to set the current color of the light to the average color of the image |
276
- | saturation (int) | percentage of the saturation of the current color (beta) |
276
+ | saturation (int) | Percentage of the saturation of the current color (beta) |
277
277
  | colorTemp (int / string) | Value between 153 and 500 to set the color temperature of the light or the values `cold`, `normal`, `warm`, `hot` and `auto` - where `auto` is the color temperature based on the current time |
278
278
  | incrementColorTemp (int / boolean) | Value by how much the color temperature should be warmer or `true` to make the color temperature warmer in steps of 50 |
279
279
  | decrementColorTemp (int / boolean) | Value by how much the color temperature should be colder or `true` to make the color temperature colder in steps of 50 |
@@ -796,7 +796,24 @@ If the status of the node has changed via a certain command, the entire command
796
796
 
797
797
  # Changelog
798
798
 
799
- ### v4.0.5 (latest)
799
+ ### v4.2.1 (latest)
800
+
801
+ * Commands are now re-executed up to three times if they fail due to a bridge timeout
802
+ * The "image" option on the "Hue Light" node will now set the corresponding gradient colors on supported resources
803
+ * Better handling of broken connections to the bridge ([#309](https://github.com/Foddy/node-red-contrib-huemagic/pull/309)) (thx)
804
+ * Fixed an error with the "Hue Scenes" node on newer bridge firmwares ([#335](https://github.com/Foddy/node-red-contrib-huemagic/issues/335)) ([#339](https://github.com/Foddy/node-red-contrib-huemagic/pull/339)) (thx)
805
+ * Fixed an uncaught exception on newer bridge firmwares ([#302](https://github.com/Foddy/node-red-contrib-huemagic/issues/302)) ([#309](https://github.com/Foddy/node-red-contrib-huemagic/pull/309)) (thx)
806
+ * Updated dependencies to the latest versions
807
+ * Fixed some typos here and there
808
+
809
+ ### v4.1.0
810
+
811
+ * New queue worker throttles the number of parallel requests to the bridge to avoid 503 API limit errors (can be configured in the Bridge configuration)
812
+ * Resources are now alphabetically sorted in the node´s configuration interface ([#282](https://github.com/Foddy/node-red-contrib-huemagic/pull/282)) (thx)
813
+ * "Hue Brightness" node was optimized to output more accurate "dark" and "dayLight" values
814
+ * Several optimizations in the documentation of some nodes
815
+
816
+ ### v4.0.5
800
817
 
801
818
  * The "Hue Group" node now contains the "resources" information with all linked resources behind the group/zone
802
819
  * Fixed an issue that caused Node-RED to restart if a command was sent before a node was initialized
@@ -861,15 +878,6 @@ If the status of the node has changed via a certain command, the entire command
861
878
  * Fixed an error with future states and the HTTP request node ([#200](https://github.com/Foddy/node-red-contrib-huemagic/pull/200))
862
879
  * Other optimizations, dependency updates and clean up
863
880
 
864
- ### v2.8.6
865
- * Optimized random color mode for Hue Magic, Hue Light & Hue Group nodes ([#190](https://github.com/Foddy/node-red-contrib-huemagic/pull/190))
866
- * New Hue Button node ([#191](https://github.com/Foddy/node-red-contrib-huemagic/pull/191))
867
- * Updated README and Hue Button node docs (+ localized in German)
868
-
869
- ### v2.8.2
870
- * Fixed an issue with Hue Light & Hue Group nodes on extended mode ([#179](https://github.com/Foddy/node-red-contrib-huemagic/issues/179))
871
- * Dependency updates
872
-
873
881
  ### Previous versions
874
882
  A complete overview of the updates can be viewed in the [changelog](https://github.com/Foddy/node-red-contrib-huemagic/blob/master/CHANGELOG.md)…
875
883
 
@@ -25,6 +25,10 @@
25
25
  </button>
26
26
  </div>
27
27
  </div>
28
+ <div class="form-row">
29
+ <label for="node-config-input-worker"><i class="fa fa-cogs"></i> <span>Worker</span></label>
30
+ <input type="text" id="node-config-input-worker" placeholder="10" style="width: calc(100% - 105px)">
31
+ </div>
28
32
  <div class="form-row" style="margin-top: 30px">
29
33
  <div style="display: inline-flex; width: calc(100% - 105px)">
30
34
  <input type="checkbox" id="node-input-autoupdates" style="flex: 15px;">
@@ -47,6 +51,9 @@
47
51
  name: { value:"Hue Bridge", required: true },
48
52
  bridge: { value:"", required: true },
49
53
  key: { value:"", required: true },
54
+ worker: { value: 10, required: true, validate: function(v) {
55
+ return (!isNaN(v) && v > 0);
56
+ }},
50
57
  autoupdates: { value: true },
51
58
  disableupdates: { value: false }
52
59
  },
@@ -9,6 +9,7 @@ module.exports = function(RED)
9
9
  const diff = require("deep-object-diff").diff;
10
10
  const axios = require('axios');
11
11
  const https = require('https');
12
+ const fastq = require('fastq');
12
13
 
13
14
  // READABLE RESOURCE MESSAGES
14
15
  const { HueBridgeMessage,
@@ -32,6 +33,8 @@ module.exports = function(RED)
32
33
  this.resourcesInGroups = {};
33
34
  this.lastStates = {};
34
35
  this.events = new events.EventEmitter();
36
+ this.patchQueue = null;
37
+ this.timerWatchDog = null; // 31/01/2022 Supergiovane: watchdog for the connection with the bridge
35
38
 
36
39
  // RESOURCE ID PATTERN
37
40
  this.validResourceID = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi;
@@ -42,9 +45,35 @@ module.exports = function(RED)
42
45
  // CREATE NODE
43
46
  RED.nodes.createNode(scope, config);
44
47
 
48
+ this.startWatchdog = function() {
49
+ // 31/01/2022 Supergiovane: Check wether the bridge is connected.
50
+ if (this.timerWatchDog !== null) clearTimeout(this.timerWatchDog);
51
+ this.timerWatchDog = setTimeout(() => {
52
+ try {
53
+ API.request({ config: config, resource: "/config", version: 1 })
54
+ .then(function(bridgeInformation)
55
+ {
56
+ // The bridge is still connected
57
+ scope.startWatchdog();
58
+ })
59
+ .catch(function(error)
60
+ {
61
+ // Error connecting to the bridge
62
+ scope.log("Error requesting info from the bridge. Reconnect in some secs. " + error.message);
63
+ scope.start();
64
+ });
65
+ } catch (error) {
66
+ scope.log("Lost connection with the bridge. Reconnect in some secs. " + error.message);
67
+ scope.start();
68
+ }
69
+
70
+ }, 10000);
71
+ }
45
72
  // INITIALIZE
46
73
  this.start = function()
47
74
  {
75
+ this.startWatchdog(); // 31/01/2022 Supergiovane: starts the watchdog.
76
+
48
77
  scope.log("Initializing the bridge ("+config.bridge+")…");
49
78
  API.init({ config: config })
50
79
  .then(function(bridge) {
@@ -301,96 +330,129 @@ module.exports = function(RED)
301
330
 
302
331
  if(type == "bridge")
303
332
  {
304
- const message = new HueBridgeMessage(targetResource, options);
305
-
306
- // GET CURRENT STATE MESSAGE
307
- let currentState = message.msg;
308
- return currentState;
333
+ try {
334
+ const message = new HueBridgeMessage(targetResource, options);
335
+
336
+ // GET CURRENT STATE MESSAGE
337
+ let currentState = message.msg;
338
+ return currentState;
339
+ } catch (error) {
340
+ return false;
341
+ }
342
+
309
343
  }
310
344
  else if(type == "light")
311
345
  {
312
- const message = new HueLightMessage(targetResource, options);
313
-
314
- // GET & SAVE LAST STATE AND DIFFERENCES
315
- let currentState = message.msg;
316
- scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
317
- currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
318
- currentState.lastState = lastState;
319
-
320
- return currentState;
346
+ try {
347
+ const message = new HueLightMessage(targetResource, options);
348
+
349
+ // GET & SAVE LAST STATE AND DIFFERENCES
350
+ let currentState = message.msg;
351
+ scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
352
+ currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
353
+ currentState.lastState = lastState;
354
+
355
+ return currentState;
356
+ } catch (error) {
357
+ return false;
358
+ }
321
359
  }
322
360
  else if(type == "group")
323
361
  {
324
- // GET MESSAGE
325
- const message = new HueGroupMessage(targetResource, { resources: scope.resources, ...options});
326
-
327
- // GET & SAVE LAST STATE AND DIFFERENCES
328
- let currentState = message.msg;
329
- scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
330
- currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
331
- currentState.lastState = lastState;
332
-
333
- return currentState;
362
+ try {
363
+ // GET MESSAGE
364
+ const message = new HueGroupMessage(targetResource, { resources: scope.resources, ...options});
365
+
366
+ // GET & SAVE LAST STATE AND DIFFERENCES
367
+ let currentState = message.msg;
368
+ scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
369
+ currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
370
+ currentState.lastState = lastState;
371
+
372
+ return currentState;
373
+ } catch (error) {
374
+ return false;
375
+ }
334
376
  }
335
377
  else if(type == "button")
336
378
  {
337
- const message = new HueButtonsMessage(targetResource, options);
338
-
339
- // GET & SAVE LAST STATE AND DIFFERENCES
340
- let currentState = message.msg;
341
- scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
342
- currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
343
- currentState.lastState = lastState;
344
-
345
- return currentState;
379
+ try {
380
+ const message = new HueButtonsMessage(targetResource, options);
381
+
382
+ // GET & SAVE LAST STATE AND DIFFERENCES
383
+ let currentState = message.msg;
384
+ scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
385
+ currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
386
+ currentState.lastState = lastState;
387
+
388
+ return currentState;
389
+ } catch (error) {
390
+ return false;
391
+ }
346
392
  }
347
393
  else if(type == "motion")
348
394
  {
349
- const message = new HueMotionMessage(targetResource, options);
350
-
351
- // GET & SAVE LAST STATE AND DIFFERENCES
352
- let currentState = message.msg;
353
- scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
354
- currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
355
- currentState.lastState = lastState;
356
-
357
- return currentState;
395
+ try {
396
+ const message = new HueMotionMessage(targetResource, options);
397
+
398
+ // GET & SAVE LAST STATE AND DIFFERENCES
399
+ let currentState = message.msg;
400
+ scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
401
+ currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
402
+ currentState.lastState = lastState;
403
+
404
+ return currentState;
405
+ } catch (error) {
406
+ return false;
407
+ }
358
408
  }
359
409
  else if(type == "temperature")
360
410
  {
361
- const message = new HueTemperatureMessage(targetResource, options);
362
-
363
- // GET & SAVE LAST STATE AND DIFFERENCES
364
- let currentState = message.msg;
365
- scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
366
- currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
367
- currentState.lastState = lastState;
368
-
369
- return currentState;
411
+ try {
412
+ const message = new HueTemperatureMessage(targetResource, options);
413
+
414
+ // GET & SAVE LAST STATE AND DIFFERENCES
415
+ let currentState = message.msg;
416
+ scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
417
+ currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
418
+ currentState.lastState = lastState;
419
+
420
+ return currentState;
421
+ } catch (error) {
422
+ return false;
423
+ }
370
424
  }
371
425
  else if(type == "light_level")
372
426
  {
373
- const message = new HueBrightnessMessage(targetResource, options);
374
-
375
- // GET & SAVE LAST STATE AND DIFFERENCES
376
- let currentState = message.msg;
377
- scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
378
- currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
379
- currentState.lastState = lastState;
380
-
381
- return currentState;
427
+ try {
428
+ const message = new HueBrightnessMessage(targetResource, options);
429
+
430
+ // GET & SAVE LAST STATE AND DIFFERENCES
431
+ let currentState = message.msg;
432
+ scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
433
+ currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
434
+ currentState.lastState = lastState;
435
+
436
+ return currentState;
437
+ } catch (error) {
438
+ return false;
439
+ }
382
440
  }
383
441
  else if(type == "rule")
384
442
  {
385
- const message = new HueRulesMessage(targetResource, options);
386
-
387
- // GET & SAVE LAST STATE AND DIFFERENCES
388
- let currentState = message.msg;
389
- scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
390
- currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
391
- currentState.lastState = lastState;
392
-
393
- return currentState;
443
+ try {
444
+ const message = new HueRulesMessage(targetResource, options);
445
+
446
+ // GET & SAVE LAST STATE AND DIFFERENCES
447
+ let currentState = message.msg;
448
+ scope.lastStates[type+targetResource.id] = Object.assign({}, currentState);
449
+ currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
450
+ currentState.lastState = lastState;
451
+
452
+ return currentState;
453
+ } catch (error) {
454
+ return false;
455
+ }
394
456
  }
395
457
  else
396
458
  {
@@ -435,24 +497,41 @@ module.exports = function(RED)
435
497
  {
436
498
  return new Promise(function(resolve, reject)
437
499
  {
438
- // GET SERVICE ID
439
- if(version !== 1 && scope.resources[id] && scope.resources[id]["services"] && scope.resources[id]["services"][type])
500
+ if(!scope.patchQueue) { return false; }
501
+ scope.patchQueue.push({ type: type, id: id, patch: patch, version: version }, function (error, response)
440
502
  {
441
- const targetResource = Object.values(scope.resources[id]["services"][type])[0];
442
- id = targetResource.id;
443
- }
444
-
445
- // ACTION!
446
- API.request({ config: config, method: "PUT", resource: (version === 2) ? (type+"/"+id) : id, data: patch, version: version })
447
- .then(function(response) {
448
- resolve(response);
449
- })
450
- .catch(function(error) {
451
- reject(error);
503
+ if(error)
504
+ {
505
+ reject(error);
506
+ }
507
+ else
508
+ {
509
+ resolve(response);
510
+ }
452
511
  });
453
512
  });
454
513
  }
455
514
 
515
+ // PATCH RESOURCE (WORKER) / 7 PROCESSES IN PARALLEL
516
+ this.patchQueue = fastq(function({ type, id, patch, version }, callback)
517
+ {
518
+ // GET SERVICE ID
519
+ if(version !== 1 && scope.resources[id] && scope.resources[id]["services"] && scope.resources[id]["services"][type])
520
+ {
521
+ const targetResource = Object.values(scope.resources[id]["services"][type])[0];
522
+ id = targetResource.id;
523
+ }
524
+
525
+ // ACTION!
526
+ API.request({ config: config, method: "PUT", resource: (version === 2) ? (type+"/"+id) : id, data: patch, version: version })
527
+ .then(function(response) {
528
+ callback(null, response);
529
+ })
530
+ .catch(function(error) {
531
+ callback(error, null);
532
+ });
533
+ }, config.worker ? parseInt(config.worker) : 10);
534
+
456
535
  // RE-FETCH RULE (RECEIVES NO UPDATES VIA SSE)
457
536
  this.refetchRule = function(id)
458
537
  {
@@ -546,8 +625,6 @@ module.exports = function(RED)
546
625
  if((config.autoupdates && config.autoupdates == true) || typeof config.autoupdates == 'undefined')
547
626
  {
548
627
  if(scope.firmwareUpdateTimeout !== null) { clearTimeout(scope.firmwareUpdateTimeout); };
549
-
550
- scope.log("Checking for Hue Bridge firmware updates…");
551
628
  API.request({
552
629
  config: config,
553
630
  method: "PUT",
@@ -564,15 +641,15 @@ module.exports = function(RED)
564
641
  {
565
642
  if(scope.nodeActive == true)
566
643
  {
567
- scope.firmwareUpdateTimeout = setTimeout(function(){ scope.autoUpdateFirmware(); }, 60000 * 180);
644
+ scope.firmwareUpdateTimeout = setTimeout(function(){ scope.autoUpdateFirmware(); }, 60000 * 720);
568
645
  }
569
646
  })
570
647
  .catch(function(error)
571
648
  {
572
- // NO UPDATES AVAILABLE // TRY AGAIN IN 3H
649
+ // NO UPDATES AVAILABLE // TRY AGAIN IN 12H
573
650
  if(scope.nodeActive == true)
574
651
  {
575
- scope.firmwareUpdateTimeout = setTimeout(function(){ scope.autoUpdateFirmware(); }, 60000 * 180);
652
+ scope.firmwareUpdateTimeout = setTimeout(function(){ scope.autoUpdateFirmware(); }, 60000 * 720);
576
653
  }
577
654
  });
578
655
  }
@@ -581,7 +658,7 @@ module.exports = function(RED)
581
658
  //
582
659
  // START THE MAGIC
583
660
  this.start();
584
-
661
+
585
662
  //
586
663
  // CLOSE NODE / REMOVE EVENT LISTENER
587
664
  this.on('close', function()
@@ -597,6 +674,9 @@ module.exports = function(RED)
597
674
 
598
675
  // REMOVE FIRMWARE UPDATE TIMEOUT
599
676
  if(scope.firmwareUpdateTimeout !== null) { clearTimeout(scope.firmwareUpdateTimeout); }
677
+
678
+ // KILL QUEUE
679
+ scope.patchQueue.kill();
600
680
  });
601
681
  }
602
682
 
@@ -81,6 +81,19 @@
81
81
  buttonIcon.removeClass("fa-pencil");
82
82
  buttonIcon.addClass("fa-search");
83
83
  }
84
+
85
+ function sortResourcesBy(prop, resources)
86
+ {
87
+ return resources.sort((a, b) => {
88
+ if ( a[prop] < b[prop] ){
89
+ return -1;
90
+ }
91
+ if ( a[prop] > b[prop] ){
92
+ return 1;
93
+ }
94
+ return 0;
95
+ });
96
+ }
84
97
 
85
98
  function searchAndSelect()
86
99
  {
@@ -98,6 +111,7 @@
98
111
  $.get('hue/resources', { bridge: bridgeConfig.bridge, key: bridgeConfig.key, type: "motion" })
99
112
  .done( function(data) {
100
113
  var allResources = JSON.parse(data);
114
+ allResources = sortResourcesBy('name', allResources);
101
115
  if(allResources.length <= 0)
102
116
  {
103
117
  notification.close();
@@ -194,4 +208,4 @@
194
208
  }
195
209
  }
196
210
  });
197
- </script>
211
+ </script>
@@ -8,6 +8,7 @@ module.exports = function(RED)
8
8
 
9
9
  const scope = this;
10
10
  const bridge = RED.nodes.getNode(config.bridge);
11
+ const async = require('async');
11
12
 
12
13
  // SAVE LAST COMMAND
13
14
  this.lastCommand = null;
@@ -116,7 +117,7 @@ module.exports = function(RED)
116
117
  let currentState = bridge.get("light_level", tempSensorID);
117
118
  if(!currentState)
118
119
  {
119
- scope.error("The sensor in not yet available. Please wait for the bridge to connect before sending any command.");
120
+ scope.error("The sensor in not yet available. Please wait until HueMagic has established a connection with the bridge or check whether the resource ID in the configuration is valid.");
120
121
  return false;
121
122
  }
122
123
 
@@ -157,9 +158,30 @@ module.exports = function(RED)
157
158
  }
158
159
 
159
160
  // PATCH!
160
- bridge.patch("light_level", tempSensorID, patchObject)
161
- .then(function() { if(done) { done(); }})
162
- .catch(function(errors) { scope.error(errors); });
161
+ async.retry({
162
+ times: 3,
163
+ errorFilter: function(err) {
164
+ return (err.status == 503);
165
+ },
166
+ interval: function(retryCount) { return retryCount*2000; }
167
+ },
168
+ function(callback, results)
169
+ {
170
+ bridge.patch("light_level", tempSensorID, patchObject)
171
+ .then(function() { callback(null, true); })
172
+ .catch(function(errors) { callback(errors, null); });
173
+ },
174
+ function(errors, success)
175
+ {
176
+ if(errors)
177
+ {
178
+ scope.error(errors);
179
+ }
180
+ else if(done)
181
+ {
182
+ done();
183
+ }
184
+ });
163
185
  }
164
186
  else
165
187
  {
@@ -81,6 +81,19 @@
81
81
  buttonIcon.removeClass("fa-pencil");
82
82
  buttonIcon.addClass("fa-search");
83
83
  }
84
+
85
+ function sortResourcesBy(prop, resources)
86
+ {
87
+ return resources.sort((a, b) => {
88
+ if ( a[prop] < b[prop] ){
89
+ return -1;
90
+ }
91
+ if ( a[prop] > b[prop] ){
92
+ return 1;
93
+ }
94
+ return 0;
95
+ });
96
+ }
84
97
 
85
98
  function searchAndSelect()
86
99
  {
@@ -98,6 +111,7 @@
98
111
  $.get('hue/resources', { bridge: bridgeConfig.bridge, key: bridgeConfig.key, type: "button" })
99
112
  .done( function(data) {
100
113
  var allResources = JSON.parse(data);
114
+ allResources = sortResourcesBy('name', allResources);
101
115
  if(allResources.length <= 0)
102
116
  {
103
117
  notification.close();
@@ -42,11 +42,6 @@ module.exports = function(RED)
42
42
  bridge.subscribe("button", config.sensorid, function(info)
43
43
  {
44
44
  let currentState = bridge.get("button", info.id);
45
- if(!currentState)
46
- {
47
- scope.error("The button/switch in not yet available. Please wait for the bridge to connect before sending any command.");
48
- return false;
49
- }
50
45
 
51
46
  // RESOURCE FOUND?
52
47
  if(currentState !== false)
@@ -137,6 +132,11 @@ module.exports = function(RED)
137
132
  }
138
133
 
139
134
  let currentState = bridge.get("button", tempSensorID);
135
+ if(!currentState)
136
+ {
137
+ scope.error("The button/switch in not yet available. Please wait until HueMagic has established a connection with the bridge or check whether the resource ID in the configuration is valid.");
138
+ return false;
139
+ }
140
140
 
141
141
  // GET CURRENT STATE
142
142
  if( (typeof msg.payload != 'undefined' && typeof msg.payload.status != 'undefined') || (typeof msg.__user_inject_props__ != 'undefined' && msg.__user_inject_props__ == "status") )