homey-api 3.14.17 → 3.14.19

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.
@@ -2289,7 +2289,11 @@ export class APIError extends Error {
2289
2289
 
2290
2290
  message: string;
2291
2291
 
2292
+ message: string;
2293
+
2292
2294
  statusCode: number;
2295
+
2296
+ message: string;
2293
2297
  }
2294
2298
 
2295
2299
  export class APIErrorHomeyOffline extends APIError {
@@ -2298,6 +2302,14 @@ export class APIErrorHomeyOffline extends APIError {
2298
2302
  statusCode: number;
2299
2303
  }
2300
2304
 
2305
+ export class APIErrorHomeySubscriptionInactive extends APIError {
2306
+ statusCode: number;
2307
+
2308
+ statusCode: number;
2309
+
2310
+ message: string;
2311
+ }
2312
+
2301
2313
  export class APIErrorNotFound extends APIError {
2302
2314
  message: string;
2303
2315
 
@@ -2537,7 +2549,11 @@ export class APIError extends Error {
2537
2549
 
2538
2550
  message: string;
2539
2551
 
2552
+ message: string;
2553
+
2540
2554
  statusCode: number;
2555
+
2556
+ message: string;
2541
2557
  }
2542
2558
 
2543
2559
  export class APIErrorHomeyOffline extends APIError {
@@ -2546,6 +2562,14 @@ export class APIErrorHomeyOffline extends APIError {
2546
2562
  statusCode: number;
2547
2563
  }
2548
2564
 
2565
+ export class APIErrorHomeySubscriptionInactive extends APIError {
2566
+ statusCode: number;
2567
+
2568
+ statusCode: number;
2569
+
2570
+ message: string;
2571
+ }
2572
+
2549
2573
  export class APIErrorNotFound extends APIError {
2550
2574
  message: string;
2551
2575
 
@@ -2354,7 +2354,11 @@ export class APIError extends Error {
2354
2354
 
2355
2355
  message: string;
2356
2356
 
2357
+ message: string;
2358
+
2357
2359
  statusCode: number;
2360
+
2361
+ message: string;
2358
2362
  }
2359
2363
 
2360
2364
  export class APIErrorHomeyOffline extends APIError {
@@ -2363,6 +2367,14 @@ export class APIErrorHomeyOffline extends APIError {
2363
2367
  statusCode: number;
2364
2368
  }
2365
2369
 
2370
+ export class APIErrorHomeySubscriptionInactive extends APIError {
2371
+ statusCode: number;
2372
+
2373
+ statusCode: number;
2374
+
2375
+ message: string;
2376
+ }
2377
+
2366
2378
  export class APIErrorNotFound extends APIError {
2367
2379
  message: string;
2368
2380
 
@@ -5950,8 +5962,6 @@ export class Util {
5950
5962
 
5951
5963
  static env(): string;
5952
5964
 
5953
- static promiseAny(promises: Array<Promise>): Promise<any>;
5954
-
5955
5965
  static serializeQueryObject(queryObject: object): string;
5956
5966
 
5957
5967
  static encodeUrlSearchParams(params: object): string;
@@ -5994,8 +6004,6 @@ export class Util {
5994
6004
 
5995
6005
  static env(): string;
5996
6006
 
5997
- static promiseAny(promises: Array<Promise>): Promise<any>;
5998
-
5999
6007
  static serializeQueryObject(queryObject: object): string;
6000
6008
 
6001
6009
  static encodeUrlSearchParams(params: object): string;
@@ -6016,7 +6024,11 @@ export class APIError extends Error {
6016
6024
 
6017
6025
  message: string;
6018
6026
 
6027
+ message: string;
6028
+
6019
6029
  statusCode: number;
6030
+
6031
+ message: string;
6020
6032
  }
6021
6033
 
6022
6034
  export class APIErrorHomeyOffline extends APIError {
@@ -6025,6 +6037,14 @@ export class APIErrorHomeyOffline extends APIError {
6025
6037
  statusCode: number;
6026
6038
  }
6027
6039
 
6040
+ export class APIErrorHomeySubscriptionInactive extends APIError {
6041
+ statusCode: number;
6042
+
6043
+ statusCode: number;
6044
+
6045
+ message: string;
6046
+ }
6047
+
6028
6048
  export class APIErrorNotFound extends APIError {
6029
6049
  message: string;
6030
6050
 
@@ -9548,8 +9568,6 @@ export class Util {
9548
9568
 
9549
9569
  static env(): string;
9550
9570
 
9551
- static promiseAny(promises: Array<Promise>): Promise<any>;
9552
-
9553
9571
  static serializeQueryObject(queryObject: object): string;
9554
9572
 
9555
9573
  static encodeUrlSearchParams(params: object): string;
@@ -9592,8 +9610,6 @@ export class Util {
9592
9610
 
9593
9611
  static env(): string;
9594
9612
 
9595
- static promiseAny(promises: Array<Promise>): Promise<any>;
9596
-
9597
9613
  static serializeQueryObject(queryObject: object): string;
9598
9614
 
9599
9615
  static encodeUrlSearchParams(params: object): string;
@@ -9,7 +9,6 @@ const APIError = require('./APIError');
9
9
  * @extends APIError
10
10
  */
11
11
  class APIErrorHomeyOffline extends APIError {
12
-
13
12
  constructor(err) {
14
13
  if (err instanceof Error) {
15
14
  super(`Homey Offline (${err.message || err.toString()})`, 500);
@@ -17,7 +16,6 @@ class APIErrorHomeyOffline extends APIError {
17
16
  super('Homey Offline', 500);
18
17
  }
19
18
  }
20
-
21
19
  }
22
20
 
23
21
  module.exports = APIErrorHomeyOffline;
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const APIError = require('./APIError');
4
+
5
+ /**
6
+ * Thrown when Homey subscription is inactive.
7
+ * @class
8
+ * @hideconstructor
9
+ * @extends APIError
10
+ */
11
+ class APIErrorHomeySubscriptionInactive extends APIError {
12
+ /**
13
+ *
14
+ * @param {string} message
15
+ * @param {number} statusCode
16
+ */
17
+ constructor(message, statusCode) {
18
+ super(message);
19
+
20
+ /**
21
+ * Error message
22
+ * @var {string} APIError#message
23
+ */
24
+
25
+ /**
26
+ * HTTP Status Code
27
+ * @type {number}
28
+ */
29
+ this.statusCode = statusCode;
30
+ }
31
+ }
32
+
33
+ module.exports = APIErrorHomeySubscriptionInactive;
@@ -2,6 +2,7 @@
2
2
 
3
3
  const SocketIOClient = require('socket.io-client');
4
4
  const APIErrorHomeyOffline = require('../APIErrorHomeyOffline');
5
+ const APIErrorHomeySubscriptionInactive = require('../APIErrorHomeySubscriptionInactive');
5
6
  const Util = require('../Util');
6
7
  const HomeyAPI = require('./HomeyAPI');
7
8
  const HomeyAPIError = require('./HomeyAPIError');
@@ -226,6 +227,9 @@ class HomeyAPIV3 extends HomeyAPI {
226
227
  throw new Error('No Discovery Strategies Available');
227
228
  }
228
229
 
230
+ // TODO
231
+ // maybe we should always ping
232
+
229
233
  // Don't discover, just set the only strategy
230
234
  if (Object.keys(urls).length === 1) {
231
235
  this.__baseUrl = Object.values(urls)[0];
@@ -272,6 +276,26 @@ class HomeyAPIV3 extends HomeyAPI {
272
276
  const text = await response.text();
273
277
 
274
278
  if (!response.ok) {
279
+ const responseContentType = response.headers.get('Content-Type');
280
+ let parsed;
281
+
282
+ if (responseContentType && responseContentType.toLowerCase().includes('application/json')) {
283
+ try {
284
+ parsed = JSON.parse(text);
285
+ // eslint-disable-next-line no-empty
286
+ } catch (err) {}
287
+ }
288
+
289
+ if (typeof parsed === 'object' && parsed !== null) {
290
+ if (
291
+ parsed.error &&
292
+ parsed.error === 'Subscription Inactive' &&
293
+ parsed.statusCode === 403
294
+ ) {
295
+ throw new APIErrorHomeySubscriptionInactive(parsed.error, parsed.statusCode);
296
+ }
297
+ }
298
+
275
299
  throw new Error(text || response.statusText);
276
300
  }
277
301
 
@@ -358,7 +382,7 @@ class HomeyAPIV3 extends HomeyAPI {
358
382
  if (pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL_SECURE]) {
359
383
  pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL_SECURE]
360
384
  .then((result) => resolve(result))
361
- .catch(() => {
385
+ .catch((error) => {
362
386
  const promises = [];
363
387
 
364
388
  if (pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL]) {
@@ -377,14 +401,34 @@ class HomeyAPIV3 extends HomeyAPI {
377
401
  promises.push(pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]);
378
402
  }
379
403
 
404
+ if (error instanceof APIErrorHomeySubscriptionInactive) {
405
+ throw error;
406
+ }
407
+
380
408
  if (!promises.length) {
381
409
  throw new APIErrorHomeyOffline();
382
410
  }
383
411
 
384
- return Util.promiseAny(promises);
412
+ return Promise.any(promises);
385
413
  })
386
414
  .then((result) => resolve(result))
387
- .catch(() => reject(new APIErrorHomeyOffline()));
415
+ .catch((error) => {
416
+ if (error instanceof AggregateError) {
417
+ for (const err of error.errors) {
418
+ if (err instanceof APIErrorHomeySubscriptionInactive) {
419
+ reject(err);
420
+ return;
421
+ }
422
+ }
423
+ }
424
+
425
+ if (error instanceof APIErrorHomeySubscriptionInactive) {
426
+ reject(error);
427
+ return;
428
+ }
429
+
430
+ reject(new APIErrorHomeyOffline());
431
+ });
388
432
  } else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL]) {
389
433
  pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL]
390
434
  .then((result) => resolve(result))
@@ -392,7 +436,14 @@ class HomeyAPIV3 extends HomeyAPI {
392
436
  if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
393
437
  pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]
394
438
  .then((result) => resolve(result))
395
- .catch((err) => reject(new APIErrorHomeyOffline(err)));
439
+ .catch((err) => {
440
+ if (err instanceof APIErrorHomeySubscriptionInactive) {
441
+ reject(err);
442
+ return;
443
+ }
444
+
445
+ reject(new APIErrorHomeyOffline(err));
446
+ });
396
447
  }
397
448
  });
398
449
  } else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.MDNS]) {
@@ -402,7 +453,14 @@ class HomeyAPIV3 extends HomeyAPI {
402
453
  if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
403
454
  pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]
404
455
  .then((result) => resolve(result))
405
- .catch((err) => reject(new APIErrorHomeyOffline(err)));
456
+ .catch((err) => {
457
+ if (err instanceof APIErrorHomeySubscriptionInactive) {
458
+ reject(err);
459
+ return;
460
+ }
461
+
462
+ reject(new APIErrorHomeyOffline(err));
463
+ });
406
464
  }
407
465
  });
408
466
  } else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.REMOTE_FORWARDED]) {
@@ -412,13 +470,27 @@ class HomeyAPIV3 extends HomeyAPI {
412
470
  if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
413
471
  pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]
414
472
  .then((result) => resolve(result))
415
- .catch((err) => reject(new APIErrorHomeyOffline(err)));
473
+ .catch((err) => {
474
+ if (err instanceof APIErrorHomeySubscriptionInactive) {
475
+ reject(err);
476
+ return;
477
+ }
478
+
479
+ reject(new APIErrorHomeyOffline(err));
480
+ });
416
481
  }
417
482
  });
418
483
  } else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
419
484
  pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]
420
485
  .then((result) => resolve(result))
421
- .catch((err) => reject(new APIErrorHomeyOffline(err)));
486
+ .catch((err) => {
487
+ if (err instanceof APIErrorHomeySubscriptionInactive) {
488
+ reject(err);
489
+ return;
490
+ }
491
+
492
+ reject(new APIErrorHomeyOffline(err));
493
+ });
422
494
  } else {
423
495
  reject(new APIErrorHomeyOffline());
424
496
  }
package/lib/Util.js CHANGED
@@ -9,7 +9,6 @@ const APIErrorTimeout = require('./APIErrorTimeout');
9
9
  * @hideconstructor
10
10
  */
11
11
  class Util {
12
-
13
12
  /**
14
13
  * Makes a call using `window.fetch` or `node-fetch`.
15
14
  * @param {string} args
@@ -24,7 +23,7 @@ class Util {
24
23
 
25
24
  if (timeoutDuration != null) {
26
25
  const abortController = new AbortController();
27
-
26
+
28
27
  abortTimeout = setTimeout(() => {
29
28
  abortController.abort('Timeout');
30
29
  }, timeoutDuration);
@@ -38,7 +37,7 @@ class Util {
38
37
  responsePromise = fetch(url, options);
39
38
  } else if (this.isBrowser()) {
40
39
  responsePromise = window.fetch(url, options);
41
- } else if (this.isNodeJS()) {
40
+ } else if (this.isNodeJS()) {
42
41
  const fetch = require('node-fetch');
43
42
  responsePromise = fetch(url, options);
44
43
  } else if (typeof fetch !== 'undefined') {
@@ -74,7 +73,7 @@ class Util {
74
73
  * @returns {Promise<void>}
75
74
  */
76
75
  static async wait(ms) {
77
- return new Promise(resolve => {
76
+ return new Promise((resolve) => {
78
77
  setTimeout(resolve, ms);
79
78
  });
80
79
  }
@@ -86,10 +85,14 @@ class Util {
86
85
  * @param {string} [message="Timeout after 5000ms"]
87
86
  * @returns {Promise}
88
87
  */
89
- static async timeout(promise, timeoutMillis = 5000, message = `Timeout after ${timeoutMillis}ms`) {
88
+ static async timeout(
89
+ promise,
90
+ timeoutMillis = 5000,
91
+ message = `Timeout after ${timeoutMillis}ms`
92
+ ) {
90
93
  const timeoutError = new APIErrorTimeout(message);
91
94
  let timeoutRef;
92
-
95
+
93
96
  const returnPromise = Promise.race([
94
97
  promise,
95
98
  new Promise((_, reject) => {
@@ -98,10 +101,10 @@ class Util {
98
101
  }, timeoutMillis);
99
102
  }),
100
103
  ]);
101
-
104
+
102
105
  returnPromise
103
106
  // eslint-disable-next-line no-unused-vars
104
- .catch(err => { })
107
+ .catch((err) => {})
105
108
  .finally(() => {
106
109
  clearTimeout(timeoutRef);
107
110
  });
@@ -136,7 +139,7 @@ class Util {
136
139
  * @returns {boolean}
137
140
  */
138
141
  static isReactNative() {
139
- return (typeof navigator !== 'undefined' && navigator.product === 'ReactNative');
142
+ return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
140
143
  }
141
144
 
142
145
  /**
@@ -144,7 +147,7 @@ class Util {
144
147
  */
145
148
  static isBrowser() {
146
149
  if (this.isReactNative()) return false;
147
- return (typeof document !== 'undefined' && typeof window.document !== 'undefined');
150
+ return typeof document !== 'undefined' && typeof window.document !== 'undefined';
148
151
  }
149
152
 
150
153
  /**
@@ -152,7 +155,7 @@ class Util {
152
155
  */
153
156
  static isNodeJS() {
154
157
  if (this.isReactNative()) return false;
155
- return (typeof process !== 'undefined');
158
+ return typeof process !== 'undefined';
156
159
  }
157
160
 
158
161
  /**
@@ -225,9 +228,9 @@ class Util {
225
228
  * @returns {string} - UUID v4 string
226
229
  */
227
230
  static uuid() {
228
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
229
- const r = Math.random() * 16 | 0; const
230
- v = c === 'x' ? r : ((r & 0x3) | 0x8);
231
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
232
+ const r = (Math.random() * 16) | 0;
233
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
231
234
  return v.toString(16);
232
235
  });
233
236
  }
@@ -262,35 +265,6 @@ class Util {
262
265
  .toUpperCase();
263
266
  }
264
267
 
265
- /**
266
- * Polyfill for Promise.any, which is only supported on Node.js >=15
267
- * @param {Array<Promise>} promises
268
- */
269
- static async promiseAny(promises) {
270
- if (promises.length === 0) return;
271
- const rejections = [];
272
- let rejectionCount = 0;
273
-
274
- return new Promise((resolve, reject) => {
275
- promises.forEach((promise, i) => {
276
- promise
277
- .then(result => resolve(result))
278
- .catch(err => {
279
- rejectionCount = rejectionCount + 1;
280
- rejections[i] = err;
281
-
282
- // Check if all promises have been rejected
283
- if (rejectionCount === promises.length) {
284
- const error = new Error('All Promises rejected');
285
- error.error = rejections;
286
- error.name = 'AggregateError';
287
- reject(rejections);
288
- }
289
- });
290
- });
291
- });
292
- }
293
-
294
268
  /**
295
269
  * Converts an object to a query string
296
270
  * @param {object} queryObject - Query parameter object
@@ -303,13 +277,8 @@ class Util {
303
277
 
304
278
  function add(key, value) {
305
279
  // If value is a function, invoke it and return its value.
306
- value = (typeof value === 'function') ?
307
- value() :
308
- value === null ?
309
- '' :
310
- value;
311
- querystring[querystring.length] = encodeURIComponent(key) +
312
- '=' + encodeURIComponent(value);
280
+ value = typeof value === 'function' ? value() : value === null ? '' : value;
281
+ querystring[querystring.length] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
313
282
  }
314
283
 
315
284
  function buildParams(prefix, obj, add) {
@@ -322,10 +291,11 @@ class Util {
322
291
  add(prefix, obj[index]);
323
292
  } else {
324
293
  // Item is non-scalar (array or object), encode its numeric index.
325
- buildParams(prefix + '[' + (typeof (obj[index]) === 'object' ?
326
- index :
327
- ''
328
- ) + ']', obj[index], add);
294
+ buildParams(
295
+ prefix + '[' + (typeof obj[index] === 'object' ? index : '') + ']',
296
+ obj[index],
297
+ add
298
+ );
329
299
  }
330
300
  }
331
301
  } else if (typeof obj === 'object') {
@@ -352,7 +322,7 @@ class Util {
352
322
  /**
353
323
  * We use this instead of URLSearchParams because in react-native URLSearchParams are not encoded
354
324
  * for some reason.
355
- *
325
+ *
356
326
  * @param {object} params
357
327
  * @returns {string} encoded params
358
328
  */
@@ -362,12 +332,11 @@ class Util {
362
332
  for (const [key, value] of Object.entries(params)) {
363
333
  const encodedKey = encodeURIComponent(key);
364
334
  const encodedValue = encodeURIComponent(value);
365
- encodedPairs.push(encodedKey + "=" + encodedValue);
335
+ encodedPairs.push(encodedKey + '=' + encodedValue);
366
336
  }
367
337
 
368
- return encodedPairs.join("&");
338
+ return encodedPairs.join('&');
369
339
  }
370
-
371
340
  }
372
341
 
373
342
  module.exports = Util;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homey-api",
3
- "version": "3.14.17",
3
+ "version": "3.14.19",
4
4
  "description": "Homey API",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -51,7 +51,7 @@
51
51
  "form-data": "^4.0.0",
52
52
  "node-fetch": "^2.6.7",
53
53
  "socket.io-client": "^2.5.0",
54
- "engine.io-client": "github:athombv/engine.io-client#backport/infinite-loop-fix-node-native-websocket"
54
+ "engine.io-client": "https://github.com/athombv/engine.io-client/tarball/backport/infinite-loop-fix-node-native-websocket"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@athombv/jsdoc-template": "^1.6.2",