homey-api 1.5.20 → 1.5.23
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/assets/types/homey-api.d.ts +87 -85
- package/assets/types/homey-api.private.d.ts +94 -92
- package/lib/{HomeyAPI/HomeyOfflineError.js → APIErrorHomeyOffline.js} +3 -4
- package/lib/APIErrorTimeout.js +0 -1
- package/lib/HomeyAPI/HomeyAPIV2/Device.js +24 -115
- package/lib/HomeyAPI/HomeyAPIV2/Item.js +108 -0
- package/lib/HomeyAPI/HomeyAPIV2/Manager.js +28 -1
- package/lib/HomeyAPI/HomeyAPIV2.js +33 -43
- package/lib/Util.js +23 -0
- package/package.json +1 -1
|
@@ -9,20 +9,6 @@ class Device extends Item {
|
|
|
9
9
|
constructor(...props) {
|
|
10
10
|
super(...props);
|
|
11
11
|
|
|
12
|
-
// Set URI
|
|
13
|
-
Object.defineProperty(this, 'uri', {
|
|
14
|
-
value: `homey:device:${this.id}`,
|
|
15
|
-
enumerable: false,
|
|
16
|
-
writable: true,
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
// Set Connected
|
|
20
|
-
Object.defineProperty(this, '__connected', {
|
|
21
|
-
value: false,
|
|
22
|
-
enumerable: false,
|
|
23
|
-
writable: true,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
12
|
// Set Capability Instances
|
|
27
13
|
Object.defineProperty(this, '__capabilityInstances', {
|
|
28
14
|
value: {},
|
|
@@ -110,108 +96,31 @@ class Device extends Item {
|
|
|
110
96
|
});
|
|
111
97
|
}
|
|
112
98
|
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.__debug('onDisconnect');
|
|
134
|
-
|
|
135
|
-
const capabilityInstances = this.__capabilityInstances;
|
|
136
|
-
if (Object.keys(capabilityInstances).length > 0) {
|
|
137
|
-
// Get the device's latest values
|
|
138
|
-
// TODO: Optimize this with `getDevices()` when >1 device has >0 capability instances.
|
|
139
|
-
this.manager.getDevice({
|
|
140
|
-
id: this.id,
|
|
141
|
-
}).then(async device => {
|
|
142
|
-
Object.entries(capabilityInstances).forEach(([capabilityId, capabilityInstance]) => {
|
|
143
|
-
const value = device.capabilitiesObj
|
|
144
|
-
? typeof device.capabilitiesObj[capabilityId] !== 'undefined'
|
|
145
|
-
? device.capabilitiesObj[capabilityId].value
|
|
146
|
-
: null
|
|
147
|
-
: null;
|
|
148
|
-
|
|
149
|
-
capabilityInstance.__onCapabilityValue({
|
|
150
|
-
capabilityId,
|
|
151
|
-
value,
|
|
152
|
-
transactionId: Util.uuid(),
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
})
|
|
156
|
-
// eslint-disable-next-line no-console
|
|
157
|
-
.catch(err => console.error(`Device[${this.id}].onReconnectError:`, err));
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
onEvent: (event, data) => {
|
|
161
|
-
// // Fire event listeners
|
|
162
|
-
if (Array.isArray(this.__listeners[event])) {
|
|
163
|
-
this.__listeners[event].forEach(listener => listener(data));
|
|
164
|
-
}
|
|
165
|
-
},
|
|
99
|
+
onReconnect() {
|
|
100
|
+
const capabilityInstances = this.__capabilityInstances;
|
|
101
|
+
if (Object.keys(capabilityInstances).length > 0) {
|
|
102
|
+
// Get the device's latest values
|
|
103
|
+
// TODO: Optimize this with `getDevices()` when >1 device has >0 capability instances.
|
|
104
|
+
this.manager.getDevice({
|
|
105
|
+
id: this.id,
|
|
106
|
+
}).then(async device => {
|
|
107
|
+
Object.entries(capabilityInstances).forEach(([capabilityId, capabilityInstance]) => {
|
|
108
|
+
const value = device.capabilitiesObj
|
|
109
|
+
? typeof device.capabilitiesObj[capabilityId] !== 'undefined'
|
|
110
|
+
? device.capabilitiesObj[capabilityId].value
|
|
111
|
+
: null
|
|
112
|
+
: null;
|
|
113
|
+
|
|
114
|
+
capabilityInstance.__onCapabilityValue({
|
|
115
|
+
capabilityId,
|
|
116
|
+
value,
|
|
117
|
+
transactionId: Util.uuid(),
|
|
118
|
+
});
|
|
166
119
|
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Delete the connecting Promise
|
|
173
|
-
this.__connectPromise
|
|
174
|
-
.catch(() => { })
|
|
175
|
-
.finally(() => {
|
|
176
|
-
delete this.__connectPromise;
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
await this.__connectPromise;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async disconnect() {
|
|
183
|
-
this.__debug('disconnect');
|
|
184
|
-
|
|
185
|
-
// If connecting, await that first
|
|
186
|
-
try {
|
|
187
|
-
await this.__connectPromise;
|
|
188
|
-
} catch (err) { }
|
|
189
|
-
|
|
190
|
-
this.__disconnectPromise = Promise.resolve().then(async () => {
|
|
191
|
-
this.__connected = false;
|
|
192
|
-
|
|
193
|
-
if (this.io) {
|
|
194
|
-
this.io
|
|
195
|
-
.then(io => io.unsubscribe())
|
|
196
|
-
.catch(err => this.__debug('Error Disconnecting:', err));
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// Delete the disconnecting Promise
|
|
201
|
-
this.__disconnectPromise
|
|
202
|
-
.catch(() => { })
|
|
203
|
-
.finally(() => {
|
|
204
|
-
delete this.__disconnectPromise;
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
await this.__disconnectPromise;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
destroy() {
|
|
211
|
-
super.destroy();
|
|
212
|
-
|
|
213
|
-
// Disconnect from Socket.io
|
|
214
|
-
this.disconnect().catch(() => { });
|
|
120
|
+
})
|
|
121
|
+
// eslint-disable-next-line no-console
|
|
122
|
+
.catch(err => console.error(`Device[${this.id}].onReconnectError:`, err));
|
|
123
|
+
}
|
|
215
124
|
}
|
|
216
125
|
|
|
217
126
|
}
|
|
@@ -5,6 +5,7 @@ const EventEmitter = require('../../EventEmitter');
|
|
|
5
5
|
class Item extends EventEmitter {
|
|
6
6
|
|
|
7
7
|
constructor({
|
|
8
|
+
uri,
|
|
8
9
|
key,
|
|
9
10
|
homey,
|
|
10
11
|
manager,
|
|
@@ -33,6 +34,20 @@ class Item extends EventEmitter {
|
|
|
33
34
|
writable: false,
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
// Set URI
|
|
38
|
+
Object.defineProperty(this, '__uri', {
|
|
39
|
+
value: uri,
|
|
40
|
+
enumerable: false,
|
|
41
|
+
writable: true,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Set Connected
|
|
45
|
+
Object.defineProperty(this, '__connected', {
|
|
46
|
+
value: false,
|
|
47
|
+
enumerable: false,
|
|
48
|
+
writable: true,
|
|
49
|
+
});
|
|
50
|
+
|
|
36
51
|
// Set Properties
|
|
37
52
|
for (const [key, value] of Object.entries(properties)) {
|
|
38
53
|
Object.defineProperty(this, key, {
|
|
@@ -54,9 +69,102 @@ class Item extends EventEmitter {
|
|
|
54
69
|
return this;
|
|
55
70
|
}
|
|
56
71
|
|
|
72
|
+
async connect() {
|
|
73
|
+
this.__debug('connect');
|
|
74
|
+
|
|
75
|
+
// If disconnecting, await that first
|
|
76
|
+
try {
|
|
77
|
+
await this.__disconnectPromise;
|
|
78
|
+
} catch (err) { }
|
|
79
|
+
|
|
80
|
+
this.__connectPromise = Promise.resolve().then(async () => {
|
|
81
|
+
if (!this.io) {
|
|
82
|
+
this.io = this.homey.subscribe(this.__uri, {
|
|
83
|
+
onConnect: () => {
|
|
84
|
+
this.__debug('onConnect');
|
|
85
|
+
this.__connected = true;
|
|
86
|
+
|
|
87
|
+
this.onConnect();
|
|
88
|
+
},
|
|
89
|
+
onDisconnect: () => {
|
|
90
|
+
this.__debug('onDisconnect');
|
|
91
|
+
this.__connected = false;
|
|
92
|
+
|
|
93
|
+
this.onDisconnect();
|
|
94
|
+
},
|
|
95
|
+
onReconnect: () => {
|
|
96
|
+
this.__debug('onDisconnect');
|
|
97
|
+
|
|
98
|
+
this.onReconnect();
|
|
99
|
+
},
|
|
100
|
+
onEvent: (event, data) => {
|
|
101
|
+
// // Fire event listeners
|
|
102
|
+
if (Array.isArray(this.__listeners[event])) {
|
|
103
|
+
this.__listeners[event].forEach(listener => listener(data));
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await this.io;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Delete the connecting Promise
|
|
113
|
+
this.__connectPromise
|
|
114
|
+
.catch(() => { })
|
|
115
|
+
.finally(() => {
|
|
116
|
+
delete this.__connectPromise;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
await this.__connectPromise;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async disconnect() {
|
|
123
|
+
this.__debug('disconnect');
|
|
124
|
+
|
|
125
|
+
// If connecting, await that first
|
|
126
|
+
try {
|
|
127
|
+
await this.__connectPromise;
|
|
128
|
+
} catch (err) { }
|
|
129
|
+
|
|
130
|
+
this.__disconnectPromise = Promise.resolve().then(async () => {
|
|
131
|
+
this.__connected = false;
|
|
132
|
+
|
|
133
|
+
if (this.io) {
|
|
134
|
+
this.io
|
|
135
|
+
.then(io => io.unsubscribe())
|
|
136
|
+
.catch(err => this.__debug('Error Disconnecting:', err));
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Delete the disconnecting Promise
|
|
141
|
+
this.__disconnectPromise
|
|
142
|
+
.catch(() => { })
|
|
143
|
+
.finally(() => {
|
|
144
|
+
delete this.__disconnectPromise;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
await this.__disconnectPromise;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
onConnect() {
|
|
151
|
+
// Overload Me
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
onReconnect() {
|
|
155
|
+
// Overload Me
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
onDisconnect() {
|
|
159
|
+
// Overload Me
|
|
160
|
+
}
|
|
161
|
+
|
|
57
162
|
destroy() {
|
|
58
163
|
// Remove all event listeners
|
|
59
164
|
this.removeAllListeners();
|
|
165
|
+
|
|
166
|
+
// Disconnect from Socket.io
|
|
167
|
+
this.disconnect().catch(() => { });
|
|
60
168
|
}
|
|
61
169
|
|
|
62
170
|
}
|
|
@@ -109,6 +109,7 @@ class Manager extends EventEmitter {
|
|
|
109
109
|
$validate = true,
|
|
110
110
|
$cache = true,
|
|
111
111
|
$timeout = 5000,
|
|
112
|
+
$socket = true,
|
|
112
113
|
$body = {},
|
|
113
114
|
$query = {},
|
|
114
115
|
$headers = {},
|
|
@@ -189,6 +190,14 @@ class Manager extends EventEmitter {
|
|
|
189
190
|
}
|
|
190
191
|
}
|
|
191
192
|
|
|
193
|
+
// Append query to path
|
|
194
|
+
if (Object.keys(query).length > 0) {
|
|
195
|
+
const queryString = Object.entries(query).map(([key, value]) => {
|
|
196
|
+
return `${key}=${encodeURIComponent(value)}`;
|
|
197
|
+
}).join('&');
|
|
198
|
+
path = `${path}?${queryString}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
192
201
|
let result;
|
|
193
202
|
const benchmark = Util.benchmark();
|
|
194
203
|
|
|
@@ -236,7 +245,7 @@ class Manager extends EventEmitter {
|
|
|
236
245
|
// If Homey is connected to Socket.io,
|
|
237
246
|
// send the API request to socket.io.
|
|
238
247
|
// This is about ~2x faster than HTTP
|
|
239
|
-
if (this.homey.isConnected()) {
|
|
248
|
+
if (this.homey.isConnected() && $socket === true) {
|
|
240
249
|
result = await Util.timeout(new Promise((resolve, reject) => {
|
|
241
250
|
this.homey.__ioNamespace.emit('api', {
|
|
242
251
|
args,
|
|
@@ -268,13 +277,20 @@ class Manager extends EventEmitter {
|
|
|
268
277
|
if (itemType === 'id') return props.id;
|
|
269
278
|
throw new Error('Invalid Item Type');
|
|
270
279
|
};
|
|
280
|
+
const getItemUri = props => {
|
|
281
|
+
if (itemType === 'filter') return null;
|
|
282
|
+
if (itemType === 'id') return `homey:${itemId}:${props.id}`;
|
|
283
|
+
throw new Error('Invalid Item Type');
|
|
284
|
+
};
|
|
271
285
|
|
|
272
286
|
switch (operation.crud.type) {
|
|
273
287
|
case 'getOne': {
|
|
274
288
|
const key = getItemKey(result);
|
|
289
|
+
const uri = getItemUri(result);
|
|
275
290
|
|
|
276
291
|
result = new ItemClass({
|
|
277
292
|
key,
|
|
293
|
+
uri,
|
|
278
294
|
homey: this.homey,
|
|
279
295
|
manager: this,
|
|
280
296
|
properties: { ...result },
|
|
@@ -290,12 +306,14 @@ class Manager extends EventEmitter {
|
|
|
290
306
|
// Add all to cache
|
|
291
307
|
for (const [resultKey, item] of Object.entries(result)) {
|
|
292
308
|
const key = getItemKey(item);
|
|
309
|
+
const uri = getItemUri(item);
|
|
293
310
|
|
|
294
311
|
if (this.__cache[itemId][key]) {
|
|
295
312
|
result[resultKey] = this.__cache[itemId][key].__update(item);
|
|
296
313
|
} else {
|
|
297
314
|
result[resultKey] = new ItemClass({
|
|
298
315
|
key,
|
|
316
|
+
uri,
|
|
299
317
|
homey: this.homey,
|
|
300
318
|
manager: this,
|
|
301
319
|
properties: { ...item },
|
|
@@ -326,11 +344,14 @@ class Manager extends EventEmitter {
|
|
|
326
344
|
case 'createOne':
|
|
327
345
|
case 'updateOne': {
|
|
328
346
|
const key = getItemKey(result);
|
|
347
|
+
const uri = getItemUri(result);
|
|
348
|
+
|
|
329
349
|
if (this.__cache[itemId][key]) {
|
|
330
350
|
result = this.__cache[itemId][key].__update(result);
|
|
331
351
|
} else {
|
|
332
352
|
result = new ItemClass({
|
|
333
353
|
key,
|
|
354
|
+
uri,
|
|
334
355
|
manager: this,
|
|
335
356
|
properties: result,
|
|
336
357
|
});
|
|
@@ -343,6 +364,7 @@ class Manager extends EventEmitter {
|
|
|
343
364
|
}
|
|
344
365
|
case 'deleteOne': {
|
|
345
366
|
const key = getItemKey(args);
|
|
367
|
+
|
|
346
368
|
if (this.__cache[itemId][key]) {
|
|
347
369
|
this.__cache[itemId][key].destroy();
|
|
348
370
|
delete this.__cache[itemId][key];
|
|
@@ -416,11 +438,15 @@ class Manager extends EventEmitter {
|
|
|
416
438
|
const key = itemType === 'filter'
|
|
417
439
|
? `${data.uri}:${data.id}`
|
|
418
440
|
: `${data.id}`;
|
|
441
|
+
const uri = itemType === 'filter'
|
|
442
|
+
? null
|
|
443
|
+
: `homey:${itemId}:${data.id}`;
|
|
419
444
|
|
|
420
445
|
switch (operation) {
|
|
421
446
|
case 'create': {
|
|
422
447
|
this.__cache[itemId][key] = new ItemClass({
|
|
423
448
|
key,
|
|
449
|
+
uri,
|
|
424
450
|
manager: this,
|
|
425
451
|
properties: { ...data },
|
|
426
452
|
});
|
|
@@ -437,6 +463,7 @@ class Manager extends EventEmitter {
|
|
|
437
463
|
} else {
|
|
438
464
|
this.__cache[itemId][key] = new ItemClass({
|
|
439
465
|
key,
|
|
466
|
+
uri,
|
|
440
467
|
manager: this,
|
|
441
468
|
properties: { ...data },
|
|
442
469
|
});
|
|
@@ -7,7 +7,7 @@ const ManagerApps = require('./HomeyAPIV2/ManagerApps');
|
|
|
7
7
|
const ManagerDevices = require('./HomeyAPIV2/ManagerDevices');
|
|
8
8
|
const HomeyAPI = require('./HomeyAPI');
|
|
9
9
|
const HomeyAPIError = require('./HomeyAPIError');
|
|
10
|
-
const
|
|
10
|
+
const APIErrorHomeyOffline = require('../APIErrorHomeyOffline');
|
|
11
11
|
const Util = require('../Util');
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -211,40 +211,39 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
211
211
|
|
|
212
212
|
// Ping method
|
|
213
213
|
const ping = async (strategyId, timeout) => {
|
|
214
|
+
let pingTimeout;
|
|
214
215
|
const baseUrl = urls[strategyId];
|
|
215
|
-
|
|
216
|
+
return Promise.race([
|
|
216
217
|
Util.fetch(`${baseUrl}/api/manager/system/ping?id=${this.id}`, {
|
|
217
218
|
headers: {
|
|
218
219
|
'X-Homey-ID': this.id,
|
|
219
220
|
},
|
|
221
|
+
}).then(async res => {
|
|
222
|
+
const text = await res.text();
|
|
223
|
+
if (!res.ok) throw new Error(text || res.statusText);
|
|
224
|
+
if (text === 'false') throw new Error('Invalid Homey ID');
|
|
225
|
+
|
|
226
|
+
const homeyId = res.headers.get('X-Homey-ID');
|
|
227
|
+
if (homeyId) {
|
|
228
|
+
if (homeyId !== this.id) throw new Error('Invalid Homey ID'); // TODO: Add to Homey Connect
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Set the version that Homey told us.
|
|
232
|
+
// It's the absolute truth, because the Cloud API may be behind.
|
|
233
|
+
const homeyVersion = res.headers.get('X-Homey-Version');
|
|
234
|
+
if (homeyVersion !== this.version) {
|
|
235
|
+
this.version = homeyVersion;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
baseUrl,
|
|
240
|
+
strategyId,
|
|
241
|
+
};
|
|
220
242
|
}),
|
|
221
243
|
new Promise((_, reject) => {
|
|
222
|
-
|
|
223
|
-
promise
|
|
224
|
-
.catch(() => { })
|
|
225
|
-
.finally(() => clearTimeout(pingTimeout));
|
|
244
|
+
pingTimeout = setTimeout(() => reject(new Error('PingTimeout')), timeout);
|
|
226
245
|
}),
|
|
227
|
-
]);
|
|
228
|
-
const text = await res.text();
|
|
229
|
-
if (!res.ok) throw new Error(text || res.statusText);
|
|
230
|
-
if (text === 'false') throw new Error('Invalid Homey ID');
|
|
231
|
-
|
|
232
|
-
const homeyId = res.headers.get('X-Homey-ID');
|
|
233
|
-
if (homeyId) {
|
|
234
|
-
if (homeyId !== this.id) throw new Error('Invalid Homey ID'); // TODO: Add to Homey Connect
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Set the version that Homey told us.
|
|
238
|
-
// It's the absolute truth, because the Cloud API may be behind.
|
|
239
|
-
const homeyVersion = res.headers.get('X-Homey-Version');
|
|
240
|
-
if (homeyVersion !== this.version) {
|
|
241
|
-
this.version = homeyVersion;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
baseUrl,
|
|
246
|
-
strategyId,
|
|
247
|
-
};
|
|
246
|
+
]).finally(() => clearTimeout(pingTimeout));
|
|
248
247
|
};
|
|
249
248
|
|
|
250
249
|
const pings = {};
|
|
@@ -293,22 +292,13 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
293
292
|
}
|
|
294
293
|
|
|
295
294
|
if (!promises.length) {
|
|
296
|
-
throw new
|
|
295
|
+
throw new APIErrorHomeyOffline();
|
|
297
296
|
}
|
|
298
297
|
|
|
299
|
-
return
|
|
298
|
+
return Util.promiseAny(promises);
|
|
300
299
|
})
|
|
301
300
|
.then(result => resolve(result))
|
|
302
|
-
.catch(
|
|
303
|
-
// Last resort: try cloud
|
|
304
|
-
if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
|
|
305
|
-
return pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD].catch(err => {
|
|
306
|
-
throw new HomeyOfflineError(err);
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
reject(new HomeyOfflineError(err));
|
|
311
|
-
});
|
|
301
|
+
.catch(() => reject(new APIErrorHomeyOffline()));
|
|
312
302
|
} else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL]) {
|
|
313
303
|
pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL]
|
|
314
304
|
.then(result => resolve(result))
|
|
@@ -316,7 +306,7 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
316
306
|
if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
|
|
317
307
|
pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]
|
|
318
308
|
.then(result => resolve(result))
|
|
319
|
-
.catch(err => reject(new
|
|
309
|
+
.catch(err => reject(new APIErrorHomeyOffline(err)));
|
|
320
310
|
}
|
|
321
311
|
});
|
|
322
312
|
} else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.MDNS]) {
|
|
@@ -326,15 +316,15 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
326
316
|
if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
|
|
327
317
|
pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]
|
|
328
318
|
.then(result => resolve(result))
|
|
329
|
-
.catch(err => reject(new
|
|
319
|
+
.catch(err => reject(new APIErrorHomeyOffline(err)));
|
|
330
320
|
}
|
|
331
321
|
});
|
|
332
322
|
} else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
|
|
333
323
|
pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]
|
|
334
324
|
.then(result => resolve(result))
|
|
335
|
-
.catch(err => reject(new
|
|
325
|
+
.catch(err => reject(new APIErrorHomeyOffline(err)));
|
|
336
326
|
} else {
|
|
337
|
-
reject(new
|
|
327
|
+
reject(new APIErrorHomeyOffline());
|
|
338
328
|
}
|
|
339
329
|
|
|
340
330
|
return promise;
|
package/lib/Util.js
CHANGED
|
@@ -172,6 +172,29 @@ class Util {
|
|
|
172
172
|
.toUpperCase();
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Polyfill for Promise.any, which is only supported on Node.js >=15
|
|
177
|
+
* @param {Array<Promise>} promises
|
|
178
|
+
*/
|
|
179
|
+
static async promiseAny(promises) {
|
|
180
|
+
if (promises.length === 0) return;
|
|
181
|
+
const rejections = [];
|
|
182
|
+
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
promises.forEach((promise, i) => {
|
|
185
|
+
promise
|
|
186
|
+
.then(result => resolve(result))
|
|
187
|
+
.catch(err => {
|
|
188
|
+
rejections[i] = err;
|
|
189
|
+
|
|
190
|
+
if (rejections.length === promises.length) {
|
|
191
|
+
reject(rejections);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
175
198
|
}
|
|
176
199
|
|
|
177
200
|
module.exports = Util;
|