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.
@@ -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: "group" })
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();
@@ -205,4 +219,4 @@
205
219
  }
206
220
  }
207
221
  });
208
- </script>
222
+ </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
  // EXPORT CONFIG
13
14
  this.exportedConfig = config;
@@ -112,7 +113,7 @@ module.exports = function(RED)
112
113
  let currentState = bridge.get("group", tempGroupID, { colornames: config.colornamer ? true : false });
113
114
  if(!currentState)
114
115
  {
115
- scope.error("The group in not yet available. Please wait for the bridge to connect before sending any command.");
116
+ scope.error("The group 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.");
116
117
  return false;
117
118
  }
118
119
 
@@ -158,18 +159,41 @@ module.exports = function(RED)
158
159
  "bri": msg.payload.brightness ? Math.round((254/100)*msg.payload.brightness) : 254
159
160
  };
160
161
 
161
- bridge.patch("group", currentState.info.idV1 + "/action", patchObject, 1)
162
- .then(function(status) {
163
- // RESET COLORLOOP ANIMATION AFTER X SECONDS
164
- setTimeout(function()
165
- {
166
- bridge.patch("group", currentState.info.idV1 + "/action", { "effect": "none" }, 1)
167
- .then(function() { if(done) { done(); }});
168
- }, parseInt(msg.payload.colorloop) * 1000);
169
- })
170
- .catch(function(errors) {
171
- scope.error(errors);
172
- scope.status({fill: "red", shape: "ring", text: "hue-group.node.error-input"});
162
+ // PATCH!
163
+ async.retry({
164
+ times: 3,
165
+ errorFilter: function(err) {
166
+ return (err.status == 503);
167
+ },
168
+ interval: function(retryCount) { return retryCount*2000; }
169
+ },
170
+ function(callback, results)
171
+ {
172
+ bridge.patch("group", currentState.info.idV1 + "/action", patchObject, 1)
173
+ .then(function(status) {
174
+ // RESET COLORLOOP ANIMATION AFTER X SECONDS
175
+ setTimeout(function()
176
+ {
177
+ bridge.patch("group", currentState.info.idV1 + "/action", { "effect": "none" }, 1)
178
+ .then(function() { if(done) { done(); }});
179
+ }, parseInt(msg.payload.colorloop) * 1000);
180
+ callback(null, true);
181
+ })
182
+ .catch(function(errors) {
183
+ callback(errors, null);
184
+ });
185
+ },
186
+ function(errors, success)
187
+ {
188
+ if(errors)
189
+ {
190
+ scope.error(errors);
191
+ scope.status({fill: "red", shape: "ring", text: "hue-group.node.error-input"});
192
+ }
193
+ else if(done)
194
+ {
195
+ done();
196
+ }
173
197
  });
174
198
 
175
199
  return false;
@@ -240,42 +264,65 @@ module.exports = function(RED)
240
264
  // SET ALERT EFFECT
241
265
  patchObject["alert"] = "lselect";
242
266
 
243
- // 1. TURN ON THE LIGHT BULB
244
- bridge.patch("group", currentState.info.idV1 + "/action", patchObject, 1)
245
- .then(function(status) {
246
- setTimeout(function()
267
+ // PATCH!
268
+ async.retry({
269
+ times: 3,
270
+ errorFilter: function(err) {
271
+ return (err.status == 503);
272
+ },
273
+ interval: function(retryCount) { return retryCount*2000; }
274
+ },
275
+ function(callback, results)
276
+ {
277
+ // 1. TURN ON THE LIGHT BULB
278
+ bridge.patch("group", currentState.info.idV1 + "/action", patchObject, 1)
279
+ .then(function(status)
247
280
  {
248
- const tempPreviousState = scope.context().get('groupPreviousState');
249
- var tempPreviousStatePatch = {};
250
-
251
- tempPreviousStatePatch.dimming = { brightness: tempPreviousState.payload.brightness };
252
- if(tempPreviousState.payload.xyColor)
253
- {
254
- tempPreviousStatePatch.xy = [tempPreviousState.payload.xyColor.x, tempPreviousState.payload.xyColor.y];
255
- }
256
- else if(tempPreviousState.payload.colorTemp)
281
+ setTimeout(function()
257
282
  {
258
- tempPreviousStatePatch.ct = tempPreviousState.payload.colorTemp;
259
- }
283
+ const tempPreviousState = scope.context().get('groupPreviousState');
284
+ var tempPreviousStatePatch = {};
260
285
 
261
- bridge.patch("group", currentState.info.idV1 + "/action", tempPreviousStatePatch, 1)
262
- .then(function(status)
263
- {
264
- return bridge.patch("group", currentState.info.idV1 + "/action", { on: false }, 1);
265
- })
266
- .then(function(status) {
267
- if(done) { done(); }
268
- if(tempPreviousState.payload.on === true)
286
+ tempPreviousStatePatch.dimming = { brightness: tempPreviousState.payload.brightness };
287
+ if(tempPreviousState.payload.xyColor)
269
288
  {
270
- bridge.patch("group", currentState.info.idV1, { on: true }, 1);
289
+ tempPreviousStatePatch.xy = [tempPreviousState.payload.xyColor.x, tempPreviousState.payload.xyColor.y];
271
290
  }
272
- });
273
- }, parseInt(msg.payload.alert) * 1000);
274
- })
275
- .catch(function(errors) {
276
- scope.error(errors);
277
- scope.status({fill: "red", shape: "ring", text: "hue-group.node.error-input"});
278
- if(done) { done(error); }
291
+ else if(tempPreviousState.payload.colorTemp)
292
+ {
293
+ tempPreviousStatePatch.ct = tempPreviousState.payload.colorTemp;
294
+ }
295
+
296
+ bridge.patch("group", currentState.info.idV1 + "/action", tempPreviousStatePatch, 1)
297
+ .then(function(status)
298
+ {
299
+ return bridge.patch("group", currentState.info.idV1 + "/action", { on: false }, 1);
300
+ })
301
+ .then(function(status) {
302
+ if(tempPreviousState.payload.on === true)
303
+ {
304
+ bridge.patch("group", currentState.info.idV1, { on: true }, 1);
305
+ }
306
+ });
307
+ }, parseInt(msg.payload.alert) * 1000);
308
+
309
+ callback(null, true);
310
+ })
311
+ .catch(function(errors) {
312
+ callback(errors, null);
313
+ });
314
+ },
315
+ function(errors, success)
316
+ {
317
+ if(errors)
318
+ {
319
+ scope.error(errors);
320
+ scope.status({fill: "red", shape: "ring", text: "hue-group.node.error-input"});
321
+ }
322
+ else if(done)
323
+ {
324
+ done();
325
+ }
279
326
  });
280
327
  }
281
328
  // ANIMATION STARTED?
@@ -300,19 +347,44 @@ module.exports = function(RED)
300
347
  tempPreviousStatePatch.ct = tempPreviousState.payload.colorTemp;
301
348
  }
302
349
 
303
- bridge.patch("light", currentState.info.lightIds[l], tempPreviousStatePatch).
304
- then(function(status)
350
+ // PATCH!
351
+ async.retry({
352
+ times: 3,
353
+ errorFilter: function(err) {
354
+ return (err.status == 503);
355
+ },
356
+ interval: function(retryCount) { return retryCount*2000; }
357
+ },
358
+ function(callback, results)
305
359
  {
306
- if(tempPreviousState.payload.on === false)
360
+ bridge.patch("light", currentState.info.lightIds[l], tempPreviousStatePatch).
361
+ then(function(status)
307
362
  {
308
- bridge.patch("light", currentState.info.lightIds[l], { on: { on: false } })
363
+ if(tempPreviousState.payload.on === false)
364
+ {
365
+ bridge.patch("light", currentState.info.lightIds[l], { on: { on: false } })
366
+ .then(function() { callback(null, true); });
367
+ }
368
+ else
369
+ {
370
+ bridge.patch("light", currentState.info.lightIds[l], { on: { on: false } })
371
+ .then(function(status) {
372
+ callback(null, true);
373
+ return bridge.patch("light", currentState.info.lightIds[l], { on: { on: true } });
374
+ });
375
+ }
376
+ })
377
+ .catch(function(errors) { callback(errors, null); });
378
+ },
379
+ function(errors, success)
380
+ {
381
+ if(errors)
382
+ {
383
+ scope.error(errors);
309
384
  }
310
- else
385
+ else if(done)
311
386
  {
312
- bridge.patch("light", currentState.info.lightIds[l], { on: { on: false } })
313
- .then(function(status) {
314
- return bridge.patch("light", currentState.info.lightIds[l], { on: { on: true } })
315
- });
387
+ done();
316
388
  }
317
389
  });
318
390
  }
@@ -516,9 +588,30 @@ module.exports = function(RED)
516
588
  }
517
589
 
518
590
  // PATCH!
519
- bridge.patch("group", currentState.info.idV1 + "/action", patchObject, 1)
520
- .then(function() { if(done) { done(); }})
521
- .catch(function(errors) { scope.error(errors); });
591
+ async.retry({
592
+ times: 3,
593
+ errorFilter: function(err) {
594
+ return (err.status == 503);
595
+ },
596
+ interval: function(retryCount) { return retryCount*2000; }
597
+ },
598
+ function(callback, results)
599
+ {
600
+ bridge.patch("group", currentState.info.idV1 + "/action", patchObject, 1)
601
+ .then(function() { callback(null, true); })
602
+ .catch(function(errors) { callback(errors, null); });
603
+ },
604
+ function(errors, success)
605
+ {
606
+ if(errors)
607
+ {
608
+ scope.error(errors);
609
+ }
610
+ else if(done)
611
+ {
612
+ done();
613
+ }
614
+ });
522
615
  }
523
616
  else
524
617
  {
@@ -88,6 +88,19 @@
88
88
  buttonIcon.removeClass("fa-pencil");
89
89
  buttonIcon.addClass("fa-search");
90
90
  }
91
+
92
+ function sortResourcesBy(prop, resources)
93
+ {
94
+ return resources.sort((a, b) => {
95
+ if ( a[prop] < b[prop] ){
96
+ return -1;
97
+ }
98
+ if ( a[prop] > b[prop] ){
99
+ return 1;
100
+ }
101
+ return 0;
102
+ });
103
+ }
91
104
 
92
105
  function searchAndSelect()
93
106
  {
@@ -105,6 +118,7 @@
105
118
  $.get('hue/resources', { bridge: bridgeConfig.bridge, key: bridgeConfig.key, type: "light" })
106
119
  .done( function(data) {
107
120
  var allResources = JSON.parse(data);
121
+ allResources = sortResourcesBy('name', allResources);
108
122
  if(allResources.length <= 0)
109
123
  {
110
124
  notification.close();
@@ -201,4 +215,4 @@
201
215
  }
202
216
  }
203
217
  });
204
- </script>
218
+ </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 FUTURE PATCH
13
14
  this.futurePatchState = {};
@@ -125,7 +126,7 @@ module.exports = function(RED)
125
126
  let currentState = bridge.get("light", tempLightID, { colornames: config.colornamer ? true : false });
126
127
  if(!currentState)
127
128
  {
128
- scope.error("The light in not yet available. Please wait for the bridge to connect before sending any command.");
129
+ scope.error("The light 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.");
129
130
  return false;
130
131
  }
131
132
 
@@ -171,18 +172,43 @@ module.exports = function(RED)
171
172
  "bri": msg.payload.brightness ? Math.round((254/100)*msg.payload.brightness) : currentState.brightnessLevel
172
173
  };
173
174
 
174
- bridge.patch("light", currentState.info.idV1 + "/state", patchObject, 1)
175
- .then(function(status) {
176
- // RESET COLORLOOP ANIMATION AFTER X SECONDS
177
- setTimeout(function()
178
- {
179
- bridge.patch("light", currentState.info.idV1 + "/state", { "effect": "none" }, 1);
180
- if(done) { done(); }
181
- }, parseInt(msg.payload.colorloop) * 1000);
182
- })
183
- .catch(function(errors) {
184
- scope.error(errors);
185
- scope.status({fill: "red", shape: "ring", text: "hue-light.node.error-input"});
175
+ // PATCH!
176
+ async.retry({
177
+ times: 3,
178
+ errorFilter: function(err) {
179
+ return (err.status == 503);
180
+ },
181
+ interval: function(retryCount) { return retryCount*2000; }
182
+ },
183
+ function(callback, results)
184
+ {
185
+ bridge.patch("light", currentState.info.idV1 + "/state", patchObject, 1)
186
+ .then(function(status)
187
+ {
188
+ // RESET COLORLOOP ANIMATION AFTER X SECONDS
189
+ setTimeout(function()
190
+ {
191
+ bridge.patch("light", currentState.info.idV1 + "/state", { "effect": "none" }, 1);
192
+ }, parseInt(msg.payload.colorloop) * 1000);
193
+
194
+ callback(null, true);
195
+ })
196
+ .catch(function(errors)
197
+ {
198
+ callback(errors, null);
199
+ });
200
+ },
201
+ function(errors, success)
202
+ {
203
+ if(errors)
204
+ {
205
+ scope.error(errors);
206
+ scope.status({fill: "red", shape: "ring", text: "hue-light.node.error-input"});
207
+ }
208
+ else if(done)
209
+ {
210
+ done();
211
+ }
186
212
  });
187
213
 
188
214
  return false;
@@ -261,48 +287,72 @@ module.exports = function(RED)
261
287
  scope.status({fill: "grey", shape: "ring", text: "hue-light.node.command"});
262
288
  }
263
289
 
264
- // 1. TURN ON THE LIGHT BULB
265
- bridge.patch("light", tempLightID, patchObject)
266
- .then(function(status) {
267
- // 2. APPLY ALERT EFFECT
268
- const alertEffect = { alert: { action: "breathe" }};
269
- return bridge.patch("light", tempLightID, alertEffect);
270
- })
271
- .then(function(status) {
272
- // 3. RESET PREVIOUS STATE (AFTER X SECONDS)
273
- setTimeout(function()
274
- {
275
- const tempPreviousState = scope.context().get('lightPreviousState');
276
- var tempPreviousStatePatch = {};
277
-
278
- tempPreviousStatePatch.dimming = { brightness: tempPreviousState.payload.brightness };
279
- if(tempPreviousState.payload.xyColor)
280
- {
281
- tempPreviousStatePatch.color = { xy: tempPreviousState.payload.xyColor };
282
- }
283
- else if(tempPreviousState.payload.colorTemp)
284
- {
285
- tempPreviousStatePatch.color_temperature = { mirek: tempPreviousState.payload.colorTemp };
286
- }
287
290
 
288
- bridge.patch("light", tempLightID, tempPreviousStatePatch).
289
- then(function(status)
291
+ // APPLY THE EFFECT
292
+ async.retry({
293
+ times: 3,
294
+ errorFilter: function(err) {
295
+ return (err.status == 503);
296
+ },
297
+ interval: function(retryCount) { return retryCount*2000; }
298
+ },
299
+ function(callback, results)
300
+ {
301
+ // 1. TURN ON THE LIGHT BULB
302
+ bridge.patch("light", tempLightID, patchObject)
303
+ .then(function(status) {
304
+ // 2. APPLY ALERT EFFECT
305
+ const alertEffect = { alert: { action: "breathe" }};
306
+ return bridge.patch("light", tempLightID, alertEffect);
307
+ })
308
+ .then(function(status) {
309
+ // 3. RESET PREVIOUS STATE (AFTER X SECONDS)
310
+ setTimeout(function()
290
311
  {
291
- return bridge.patch("light", tempLightID, { on: { on: false } })
292
- .then(function() { if(done) { done(); }});
293
- })
294
- .then(function(status) {
295
- if(tempPreviousState.payload.on === true)
312
+ const tempPreviousState = scope.context().get('lightPreviousState');
313
+ var tempPreviousStatePatch = {};
314
+
315
+ tempPreviousStatePatch.dimming = { brightness: tempPreviousState.payload.brightness };
316
+ if(tempPreviousState.payload.xyColor)
296
317
  {
297
- bridge.patch("light", tempLightID, { on: { on: true } })
298
- .then(function() { if(done) { done(); }});
318
+ tempPreviousStatePatch.color = { xy: tempPreviousState.payload.xyColor };
319
+ }
320
+ else if(tempPreviousState.payload.colorTemp)
321
+ {
322
+ tempPreviousStatePatch.color_temperature = { mirek: tempPreviousState.payload.colorTemp };
299
323
  }
300
- });
301
- }, parseInt(msg.payload.alert) * 1000);
302
- })
303
- .catch(function(errors) {
304
- scope.error(errors);
305
- scope.status({fill: "red", shape: "ring", text: "hue-light.node.error-input"});
324
+
325
+ bridge.patch("light", tempLightID, tempPreviousStatePatch).
326
+ then(function(status)
327
+ {
328
+ return bridge.patch("light", tempLightID, { on: { on: false } })
329
+ .then(function() { if(done) { done(); }});
330
+ })
331
+ .then(function(status) {
332
+ if(tempPreviousState.payload.on === true)
333
+ {
334
+ bridge.patch("light", tempLightID, { on: { on: true } })
335
+ .then(function() { if(done) { done(); }});
336
+ }
337
+ });
338
+ }, parseInt(msg.payload.alert) * 1000);
339
+ callback(null, true);
340
+ })
341
+ .catch(function(errors) {
342
+ callback(errors, null);
343
+ });
344
+ },
345
+ function(errors, success)
346
+ {
347
+ if(errors)
348
+ {
349
+ scope.error(errors);
350
+ scope.status({fill: "red", shape: "ring", text: "hue-light.node.error-input"});
351
+ }
352
+ else if(done)
353
+ {
354
+ done();
355
+ }
306
356
  });
307
357
  }
308
358
  // ANIMATION STARTED?
@@ -327,21 +377,44 @@ module.exports = function(RED)
327
377
  tempPreviousStatePatch.color_temperature = { mirek: tempPreviousState.payload.colorTemp };
328
378
  }
329
379
 
330
- bridge.patch("light", tempLightID, tempPreviousStatePatch).
331
- then(function(status)
380
+ // PATCH!
381
+ async.retry({
382
+ times: 3,
383
+ errorFilter: function(err) {
384
+ return (err.status == 503);
385
+ },
386
+ interval: function(retryCount) { return retryCount*2000; }
387
+ },
388
+ function(callback, results)
332
389
  {
333
- if(tempPreviousState.payload.on === false)
390
+ bridge.patch("light", tempLightID, tempPreviousStatePatch)
391
+ .then(function(status)
334
392
  {
335
- bridge.patch("light", tempLightID, { on: { on: false } })
336
- .then(function() { if(done) { done(); }});
393
+ if(tempPreviousState.payload.on === false)
394
+ {
395
+ bridge.patch("light", tempLightID, { on: { on: false } })
396
+ .then(function() { callback(null, true); });
397
+ }
398
+ else
399
+ {
400
+ bridge.patch("light", tempLightID, { on: { on: false } })
401
+ .then(function(status) {
402
+ callback(null, true);
403
+ return bridge.patch("light", tempLightID, { on: { on: true } });
404
+ });
405
+ }
406
+ })
407
+ .catch(function(errors) { callback(errors, null); });
408
+ },
409
+ function(errors, success)
410
+ {
411
+ if(errors)
412
+ {
413
+ scope.error(errors);
337
414
  }
338
- else
415
+ else if(done)
339
416
  {
340
- bridge.patch("light", tempLightID, { on: { on: false } })
341
- .then(function(status) {
342
- if(done) { done(); }
343
- return bridge.patch("light", tempLightID, { on: { on: true } })
344
- });
417
+ done();
345
418
  }
346
419
  });
347
420
  }
@@ -613,16 +686,42 @@ module.exports = function(RED)
613
686
  }
614
687
 
615
688
  // SET DOMINANT COLORS FROM IMAGE
616
- if(typeof msg.payload != 'undefined' && typeof msg.payload.image != 'undefined' && typeof currentState.payload.xyColor != 'undefined')
689
+ if(typeof msg.payload != 'undefined' && typeof msg.payload.image != 'undefined' && (typeof currentState.payload.xyColor != 'undefined' || typeof currentState.payload.gradient != 'undefined'))
617
690
  {
618
- var colors = await colorUtils.getColors(msg.payload.image);
691
+ let colors = await colorUtils.getColors(msg.payload.image);
619
692
  if(colors.length > 0)
620
693
  {
621
- var colorsHEX = colors.map(color => color.hex());
622
- let rgbFromHex = colorUtils.hexRgb(colorsHEX[0]);
623
- patchObject["color"] = {
624
- xy: colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut)
625
- };
694
+ let colorsHEX = colors.map(color => color.hex());
695
+
696
+ // SET MULTIPLE COLORS ON SUPPORTED LIGHTS
697
+ if(typeof currentState.payload.gradient != 'undefined')
698
+ {
699
+ let XYColorSet = [];
700
+
701
+ for (var i = 0; i < currentState.payload.gradient.totalColors; i++)
702
+ {
703
+ if(typeof colorsHEX[i] != 'undefined')
704
+ {
705
+ let rgbFromHex = colorUtils.hexRgb(colorsHEX[i]);
706
+ XYColorSet.push({
707
+ color: { xy: colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut) }
708
+ });
709
+ }
710
+ }
711
+
712
+ if(XYColorSet.length > 0)
713
+ {
714
+ patchObject["gradient"] = { points: XYColorSet };
715
+ }
716
+ }
717
+ // SET SINGLE COLOR
718
+ else
719
+ {
720
+ let rgbFromHex = colorUtils.hexRgb(colorsHEX[0]);
721
+ patchObject["color"] = {
722
+ xy: colorUtils.rgbToXy(rgbFromHex[0], rgbFromHex[1], rgbFromHex[2], currentState.info.model.colorGamut)
723
+ };
724
+ }
626
725
  }
627
726
  }
628
727
 
@@ -718,9 +817,30 @@ module.exports = function(RED)
718
817
  }
719
818
 
720
819
  // PATCH!
721
- bridge.patch("light", tempLightID, patchObject)
722
- .then(function() { if(done) { done(); }})
723
- .catch(function(errors) { scope.error(errors); });
820
+ async.retry({
821
+ times: 3,
822
+ errorFilter: function(err) {
823
+ return (err.status == 503);
824
+ },
825
+ interval: function(retryCount) { return retryCount*2000; }
826
+ },
827
+ function(callback, results)
828
+ {
829
+ bridge.patch("light", tempLightID, patchObject)
830
+ .then(function() { callback(null, true); })
831
+ .catch(function(errors) { callback(errors, null); });
832
+ },
833
+ function(errors, success)
834
+ {
835
+ if(errors)
836
+ {
837
+ scope.error(errors);
838
+ }
839
+ else if(done)
840
+ {
841
+ done();
842
+ }
843
+ });
724
844
  }
725
845
  else
726
846
  {
@@ -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>