node-red-contrib-huemagic 4.0.2 → 4.1.0

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.
Files changed (36) hide show
  1. package/README.md +31 -14
  2. package/examples/Hue Bridge.json +1 -1
  3. package/huemagic/hue-bridge-config.html +11 -4
  4. package/huemagic/hue-bridge-config.js +180 -154
  5. package/huemagic/hue-bridge.js +5 -5
  6. package/huemagic/hue-brightness.html +22 -8
  7. package/huemagic/hue-brightness.js +16 -5
  8. package/huemagic/hue-buttons.html +21 -7
  9. package/huemagic/hue-buttons.js +16 -5
  10. package/huemagic/hue-group.html +26 -12
  11. package/huemagic/hue-group.js +8 -3
  12. package/huemagic/hue-light.html +22 -8
  13. package/huemagic/hue-light.js +8 -3
  14. package/huemagic/hue-motion.html +22 -8
  15. package/huemagic/hue-motion.js +16 -5
  16. package/huemagic/hue-rules.html +20 -6
  17. package/huemagic/hue-rules.js +15 -4
  18. package/huemagic/hue-scene.html +20 -6
  19. package/huemagic/hue-scene.js +1 -1
  20. package/huemagic/hue-temperature.html +22 -8
  21. package/huemagic/hue-temperature.js +16 -5
  22. package/huemagic/locales/de/hue-bridge-config.html +8 -0
  23. package/huemagic/locales/de/hue-bridge.json +1 -1
  24. package/huemagic/locales/de/hue-group.html +1 -0
  25. package/huemagic/locales/en-US/hue-bridge-config.html +8 -0
  26. package/huemagic/locales/en-US/hue-bridge.html +1 -1
  27. package/huemagic/locales/en-US/hue-bridge.json +1 -1
  28. package/huemagic/locales/en-US/hue-buttons.html +1 -1
  29. package/huemagic/locales/en-US/hue-group.html +1 -0
  30. package/huemagic/locales/en-US/hue-light.html +1 -1
  31. package/huemagic/locales/en-US/hue-magic.html +3 -3
  32. package/huemagic/locales/en-US/hue-motion.html +1 -1
  33. package/huemagic/locales/en-US/hue-temperature.html +1 -1
  34. package/huemagic/utils/api.js +88 -94
  35. package/huemagic/utils/messages.js +134 -126
  36. package/package.json +2 -1
@@ -5,83 +5,77 @@ const EventSource = require('eventsource');
5
5
 
6
6
  function API()
7
7
  {
8
- // STORAGE
9
- this.bridge = null;
10
- this.ipAddress = null;
11
- this.accessKey = null;
12
- this.events = null;
8
+ // EVENTS
9
+ this.events = {};
13
10
 
14
11
  //
15
12
  // INITIALIZE
16
- this.init = function({ ip, key = 'default' })
13
+ this.init = function({ config = null })
17
14
  {
18
15
  const scope = this;
19
16
 
20
- // FORCE RELOAD?
21
- const forceReload = (this.ipAddress != ip.toString().trim());
22
-
23
- // STORE ACCESS INFORMATION
24
- this.ipAddress = ip.toString().trim();
25
- this.accessKey = key.toString().trim();
26
-
27
17
  // GET BRIDGE
28
18
  return new Promise(function(resolve, reject)
29
19
  {
30
- if(scope.bridge !== null && forceReload === false)
20
+ if(!config)
31
21
  {
32
- resolve(scope.bridge);
22
+ reject("Bridge is not configured!");
23
+ return false;
33
24
  }
34
- else
25
+
26
+ // GET BRIDGE INFORMATION
27
+ axios({
28
+ "method": "GET",
29
+ "url": "https://" + config.bridge + "/api/config",
30
+ "headers": { "Content-Type": "application/json; charset=utf-8" },
31
+ "httpsAgent": new https.Agent({ rejectUnauthorized: false }),
32
+ })
33
+ .then(function(response)
35
34
  {
36
- // GET BRIDGE INFORMATION
37
- axios({
38
- "method": "GET",
39
- "url": "https://" + ip + "/api/config",
40
- "headers": { "Content-Type": "application/json; charset=utf-8" },
41
- "httpsAgent": new https.Agent({ rejectUnauthorized: false }),
42
- })
43
- .then(function(response)
44
- {
45
- scope.bridge = response.data;
46
- resolve(scope.bridge);
47
- })
48
- .catch(function(error)
49
- {
50
- reject(error);
51
- });
52
- }
35
+ resolve(response.data);
36
+ })
37
+ .catch(function(error)
38
+ {
39
+ reject(error);
40
+ });
53
41
  });
54
42
  }
55
43
 
56
44
  //
57
45
  // MAKE A REQUEST
58
- this.request = function({ method = 'GET', ressource = null, data = null, version = 2 })
46
+ this.request = function({ config = null, method = 'GET', resource = null, data = null, version = 2 })
59
47
  {
60
48
  const scope = this;
61
49
  return new Promise(function(resolve, reject)
62
50
  {
51
+ if(!config)
52
+ {
53
+ reject("Bridge is not configured!");
54
+ return false;
55
+ }
56
+
63
57
  // BUILD REQUEST OBJECT
64
58
  var request = {
65
59
  "method": method,
66
- "url": "https://" + scope.ipAddress,
60
+ "url": "https://" + config.bridge,
67
61
  "headers": {
68
62
  "Content-Type": "application/json; charset=utf-8",
69
- "hue-application-key": scope.accessKey
63
+ "hue-application-key": config.key
70
64
  },
71
65
  "httpsAgent": new https.Agent({ rejectUnauthorized: false }), // Node is somehow not able to parse the official Philips Hue PEM
72
66
  };
73
67
 
74
- // HAS RESSOURCE? -> APPEND
75
- if(ressource !== null)
68
+ // HAS RESOURCE? -> APPEND
69
+ if(resource !== null)
76
70
  {
77
71
  if(version === 2)
78
72
  {
79
- ressource = (ressource !== "all") ? "/"+ressource : "";
80
- request['url'] += "/clip/v2/resource" + ressource;
73
+ resource = (resource !== "all") ? "/"+resource : "";
74
+ request['url'] += "/clip/v2/resource" + resource;
81
75
  }
82
76
  else if(version === 1)
83
77
  {
84
- request['url'] += "/api/" + scope.accessKey + ressource;
78
+ request['url'] += "/api/" + config.key + resource;
85
79
  }
86
80
  }
87
81
 
@@ -119,23 +113,23 @@ function API()
119
113
 
120
114
  //
121
115
  // SUBSCRIBE TO BRIDGE EVENTS
122
- this.subscribe = function(callback)
116
+ this.subscribe = function(config, callback)
123
117
  {
124
118
  const scope = this;
125
119
  return new Promise(function(resolve, reject)
126
120
  {
127
- if(scope.events === null)
121
+ if(!scope.events[config.id])
128
122
  {
129
- var sseURL = "https://" + scope.ipAddress + "/eventstream/clip/v2";
123
+ var sseURL = "https://" + config.bridge + "/eventstream/clip/v2";
130
124
 
131
125
  // INITIALIZE EVENT SOURCE
132
- scope.events = new EventSource(sseURL, {
133
- headers: { 'hue-application-key': scope.accessKey },
126
+ scope.events[config.id] = new EventSource(sseURL, {
127
+ headers: { 'hue-application-key': config.key },
134
128
  https: { rejectUnauthorized: false },
135
129
  });
136
130
 
137
131
  // PIPE MESSAGE TO TARGET FUNCTION
138
- scope.events.onmessage = function(event)
132
+ scope.events[config.id].onmessage = function(event)
139
133
  {
140
134
  if(event && event.type === 'message' && event.data)
141
135
  {
@@ -152,71 +146,71 @@ function API()
152
146
  };
153
147
 
154
148
  // CONNECTED?
155
- scope.events.onopen = function()
149
+ scope.events[config.id].onopen = function()
156
150
  {
157
151
  resolve(true);
158
152
  }
159
153
 
160
154
  // ERROR? -> RETRY?
161
- scope.events.onerror = function(error)
155
+ scope.events[config.id].onerror = function(error)
162
156
  {
163
157
  console.log("HueMagic:", "Connection to bridge lost. Trying to reconnect again in 30 seconds…", err);
164
- setTimeout(function(){ scope.subscribe(callback); }, 30000);
158
+ setTimeout(function(){ scope.subscribe(config, callback); }, 30000);
165
159
  resolve(true);
166
160
  }
167
161
  }
168
162
  else
169
163
  {
170
- scope.unsubscribe();
171
- scope.subscribe(callback);
164
+ scope.unsubscribe(config);
165
+ scope.subscribe(config, callback);
172
166
  }
173
167
  });
174
168
  }
175
169
 
176
170
  //
177
171
  // UNSUBSCRIBE
178
- this.unsubscribe = function()
172
+ this.unsubscribe = function(config)
179
173
  {
180
- if(this.events !== null) { this.events.close(); }
181
- this.events = null;
174
+ if(this.events[config.id] !== null) { this.events[config.id].close(); }
175
+ this.events[config.id] = null;
182
176
  }
183
177
 
184
178
  //
185
- // GET FULL/ROOT RESSOURCE
186
- this.fullRessource = function(ressource, allRessources = {})
179
+ // GET FULL/ROOT RESOURCE
180
+ this.fullResource = function(resource, allResources = {})
187
181
  {
188
182
  const scope = this;
189
- var fullRessource = Object.assign({}, ressource);
183
+ var fullResource = Object.assign({}, resource);
190
184
 
191
- if(ressource["owner"])
185
+ if(resource["owner"])
192
186
  {
193
- fullRessource = scope.fullRessource(allRessources[fullRessource["owner"]["rid"]], allRessources);
187
+ fullResource = scope.fullResource(allResources[fullResource["owner"]["rid"]], allResources);
194
188
  }
195
- else if(ressource["type"] === "device" || ressource["type"] === "room" || ressource["type"] === "zone" || ressource["type"] === "bridge_home")
189
+ else if(resource["type"] === "device" || resource["type"] === "room" || resource["type"] === "zone" || resource["type"] === "bridge_home")
196
190
  {
197
191
  // RESOLVE SERVICES
198
192
  var allServices = {};
199
193
 
200
- for (var i = ressource["services"].length - 1; i >= 0; i--)
194
+ for (var i = resource["services"].length - 1; i >= 0; i--)
201
195
  {
202
- const targetService = ressource["services"][i];
196
+ const targetService = resource["services"][i];
203
197
  const targetType = targetService["rtype"];
204
198
  const targetID = targetService["rid"];
205
199
 
206
200
  if(!allServices[targetType]) { allServices[targetType] = {}; }
207
- allServices[targetType][targetID] = Object.assign({}, allRessources[targetID]);
201
+ allServices[targetType][targetID] = Object.assign({}, allResources[targetID]);
208
202
  }
209
203
 
210
204
  // REPLACE SERVICES
211
- fullRessource["services"] = allServices;
205
+ fullResource["services"] = allServices;
212
206
  }
213
207
 
214
- return fullRessource;
208
+ return fullResource;
215
209
  }
216
210
 
217
211
  //
218
- // PROCESS RESSOURCES
219
- this.processRessources = function(ressources)
212
+ // PROCESS RESOURCES
213
+ this.processResources = function(resources)
220
214
  {
221
215
  const scope = this;
222
216
 
@@ -226,62 +220,62 @@ function API()
226
220
  // ACTION!
227
221
  return new Promise(function(resolve, reject)
228
222
  {
229
- let ressourceList = {};
230
- let processedRessources = {
223
+ let resourceList = {};
224
+ let processedResources = {
231
225
  _groupsOf: {}
232
226
  };
233
227
 
234
- // CREATE ID BASED OBJECT OF ALL RESSOURCES
235
- ressources.forEach(function(ressource, index)
228
+ // CREATE ID BASED OBJECT OF ALL RESOURCES
229
+ resources.forEach(function(resource, index)
236
230
  {
237
231
  // IS BUTTON? -> REMOVE PREVIOUS STATE
238
- if(ressource.type === "button")
232
+ if(resource.type === "button")
239
233
  {
240
- delete ressource["button"];
234
+ delete resource["button"];
241
235
  }
242
236
 
243
- ressourceList[ressource.id] = ressource;
237
+ resourceList[resource.id] = resource;
244
238
  });
245
239
 
246
- // GET FULL RESSOURCES OF EACH OBJECT
247
- ressources.forEach(function(ressource, index)
240
+ // GET FULL RESOURCES OF EACH OBJECT
241
+ resources.forEach(function(resource, index)
248
242
  {
249
- // GET FULL RESSOURCE
250
- let fullRessource = scope.fullRessource(ressource, ressourceList);
243
+ // GET FULL RESOURCE
244
+ let fullResource = scope.fullResource(resource, resourceList);
251
245
 
252
246
  // ADD CURRENT DATE/TIME
253
- fullRessource["updated"] = currentDateTime;
247
+ fullResource["updated"] = currentDateTime;
254
248
 
255
- // ALL ALL TYPES BEHIND RESSOURCE
256
- fullRessource["types"] = [ fullRessource["type"] ];
249
+ // ALL ALL TYPES BEHIND RESOURCE
250
+ fullResource["types"] = [ fullResource["type"] ];
257
251
 
258
- // RESSOURCE HAS SERVICES?
259
- if(fullRessource["services"])
252
+ // RESOURCE HAS SERVICES?
253
+ if(fullResource["services"])
260
254
  {
261
- let additionalServiceTypes = Object.keys(fullRessource["services"]);
255
+ let additionalServiceTypes = Object.keys(fullResource["services"]);
262
256
 
263
257
  // SET ADDITIONAL TYPES BEHIND RECCOURCE
264
- fullRessource["types"] = fullRessource["types"].concat(additionalServiceTypes);
258
+ fullResource["types"] = fullResource["types"].concat(additionalServiceTypes);
265
259
  }
266
260
 
267
261
  // RESOURCE HAS GROUPED SERVICES?
268
- if(fullRessource["grouped_services"])
262
+ if(fullResource["grouped_services"])
269
263
  {
270
- for (var g = fullRessource["grouped_services"].length - 1; g >= 0; g--)
264
+ for (var g = fullResource["grouped_services"].length - 1; g >= 0; g--)
271
265
  {
272
- const groupedService = fullRessource["grouped_services"][g];
266
+ const groupedService = fullResource["grouped_services"][g];
273
267
  const groupedServiceID = groupedService["rid"];
274
268
 
275
- if(!processedRessources["_groupsOf"][groupedServiceID]) { processedRessources["_groupsOf"][groupedServiceID] = []; }
276
- processedRessources["_groupsOf"][groupedServiceID].push(fullRessource.id);
269
+ if(!processedResources["_groupsOf"][groupedServiceID]) { processedResources["_groupsOf"][groupedServiceID] = []; }
270
+ processedResources["_groupsOf"][groupedServiceID].push(fullResource.id);
277
271
  }
278
272
  }
279
273
 
280
- // GIVE FULL RESSOURCE BACK TO COLLECTION
281
- processedRessources[fullRessource.id] = fullRessource;
274
+ // GIVE FULL RESOURCE BACK TO COLLECTION
275
+ processedResources[fullResource.id] = fullResource;
282
276
  });
283
277
 
284
- resolve(processedRessources);
278
+ resolve(processedResources);
285
279
  });
286
280
  }
287
281
  }