homey-api 1.5.13 → 1.5.16
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/specifications/HomeyAPIV2.json +505 -425
- package/assets/types/homey-api.d.ts +545 -300
- package/assets/types/homey-api.private.d.ts +572 -276
- package/lib/HomeyAPI/HomeyAPIV2/Device.js +93 -20
- package/lib/HomeyAPI/HomeyAPIV2/DeviceCapability.js +1 -0
- package/lib/HomeyAPI/HomeyAPIV2/Item.js +2 -1
- package/lib/HomeyAPI/HomeyAPIV2/Manager.js +117 -67
- package/lib/HomeyAPI/HomeyAPIV2.js +16 -5
- package/package.json +1 -1
|
@@ -113,32 +113,105 @@ class Device extends Item {
|
|
|
113
113
|
async connect() {
|
|
114
114
|
this.__debug('connect');
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
this.
|
|
130
|
-
|
|
131
|
-
|
|
116
|
+
// If disconnecting, await that first
|
|
117
|
+
try {
|
|
118
|
+
await this.__disconnectPromise;
|
|
119
|
+
} catch (err) { }
|
|
120
|
+
|
|
121
|
+
this.__connectPromise = Promise.resolve().then(async () => {
|
|
122
|
+
if (!this.io) {
|
|
123
|
+
this.io = this.homey.subscribe(this.uri, {
|
|
124
|
+
onConnect: () => {
|
|
125
|
+
this.__debug('onConnect');
|
|
126
|
+
this.__connected = true;
|
|
127
|
+
},
|
|
128
|
+
onDisconnect: () => {
|
|
129
|
+
this.__debug('onDisconnect');
|
|
130
|
+
this.__connected = false;
|
|
131
|
+
},
|
|
132
|
+
onReconnect: () => {
|
|
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
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
await this.io;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Delete the connecting Promise
|
|
173
|
+
this.__connectPromise
|
|
174
|
+
.catch(() => { })
|
|
175
|
+
.finally(() => {
|
|
176
|
+
delete this.__connectPromise;
|
|
132
177
|
});
|
|
133
|
-
|
|
178
|
+
|
|
179
|
+
await this.__connectPromise;
|
|
134
180
|
}
|
|
135
181
|
|
|
136
182
|
async disconnect() {
|
|
137
183
|
this.__debug('disconnect');
|
|
138
184
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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(() => { });
|
|
142
215
|
}
|
|
143
216
|
|
|
144
217
|
}
|
|
@@ -253,6 +253,7 @@ class Manager extends EventEmitter {
|
|
|
253
253
|
} else {
|
|
254
254
|
// Get from HTTP
|
|
255
255
|
result = await this.homey.call({
|
|
256
|
+
$timeout,
|
|
256
257
|
headers,
|
|
257
258
|
body,
|
|
258
259
|
path: `/api/manager/${this.id}${path}`,
|
|
@@ -383,80 +384,102 @@ class Manager extends EventEmitter {
|
|
|
383
384
|
async connect() {
|
|
384
385
|
this.__debug('connect');
|
|
385
386
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
this.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
387
|
+
// If disconnecting, await that first
|
|
388
|
+
try {
|
|
389
|
+
await this.__disconnectPromise;
|
|
390
|
+
} catch (err) { }
|
|
391
|
+
|
|
392
|
+
this.__connectPromise = Promise.resolve().then(async () => {
|
|
393
|
+
if (!this.io) {
|
|
394
|
+
this.io = this.homey.subscribe(this.uri, {
|
|
395
|
+
onConnect: () => {
|
|
396
|
+
this.__debug('onConnect');
|
|
397
|
+
this.__connected = true;
|
|
398
|
+
},
|
|
399
|
+
onDisconnect: () => {
|
|
400
|
+
this.__debug('onDisconnect');
|
|
401
|
+
this.__connected = false;
|
|
402
|
+
|
|
403
|
+
// Clear CRUD Item cache
|
|
404
|
+
for (const itemId of Object.keys(this.__cache)) {
|
|
405
|
+
this.__cache[itemId] = {};
|
|
406
|
+
this.__cacheAllComplete[itemId] = false;
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
onEvent: (event, data) => {
|
|
410
|
+
this.__debug('onEvent', event);
|
|
411
|
+
|
|
412
|
+
// Transform & add to cache if this is a CRUD event
|
|
413
|
+
if (event.endsWith('.create')
|
|
414
|
+
|| event.endsWith('.update')
|
|
415
|
+
|| event.endsWith('.delete')) {
|
|
416
|
+
const [itemId, operation] = event.split('.');
|
|
417
|
+
const ItemClass = this.constructor.ITEMS[itemId] || Item;
|
|
418
|
+
const itemType = this.__itemsById[itemId].type;
|
|
419
|
+
const key = itemType === 'filter'
|
|
420
|
+
? `${data.uri}:${data.id}`
|
|
421
|
+
: `${data.id}`;
|
|
422
|
+
|
|
423
|
+
switch (operation) {
|
|
424
|
+
case 'create': {
|
|
425
|
+
this.__cache[itemId][key] = new ItemClass({
|
|
426
|
+
key,
|
|
427
|
+
manager: this,
|
|
428
|
+
properties: { ...data },
|
|
429
|
+
});
|
|
430
|
+
this.__cache[itemId][key].emit('create', data);
|
|
424
431
|
|
|
425
|
-
|
|
432
|
+
data = this.__cache[itemId][key];
|
|
426
433
|
|
|
427
|
-
|
|
428
|
-
}
|
|
429
|
-
case 'update': {
|
|
430
|
-
if (this.__cache[itemId][key]) {
|
|
431
|
-
this.__cache[itemId][key].__update(data);
|
|
432
|
-
this.__cache[itemId][key].emit('update', data);
|
|
434
|
+
break;
|
|
433
435
|
}
|
|
436
|
+
case 'update': {
|
|
437
|
+
if (this.__cache[itemId][key]) {
|
|
438
|
+
this.__cache[itemId][key].__update(data);
|
|
439
|
+
this.__cache[itemId][key].emit('update', data);
|
|
440
|
+
} else {
|
|
441
|
+
this.__cache[itemId][key] = new ItemClass({
|
|
442
|
+
key,
|
|
443
|
+
manager: this,
|
|
444
|
+
properties: { ...data },
|
|
445
|
+
});
|
|
446
|
+
}
|
|
434
447
|
|
|
435
|
-
|
|
448
|
+
data = this.__cache[itemId][key];
|
|
436
449
|
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
case 'delete': {
|
|
440
|
-
if (this.__cache[itemId][key]) {
|
|
441
|
-
this.__cache[itemId][key].emit('delete');
|
|
442
|
-
this.__cache[itemId][key].destroy();
|
|
443
|
-
delete this.__cache[itemId][key];
|
|
444
|
-
data = null;
|
|
450
|
+
break;
|
|
445
451
|
}
|
|
446
|
-
|
|
452
|
+
case 'delete': {
|
|
453
|
+
if (this.__cache[itemId][key]) {
|
|
454
|
+
this.__cache[itemId][key].emit('delete');
|
|
455
|
+
this.__cache[itemId][key].destroy();
|
|
456
|
+
delete this.__cache[itemId][key];
|
|
457
|
+
data = null;
|
|
458
|
+
}
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
default:
|
|
462
|
+
break;
|
|
447
463
|
}
|
|
448
|
-
default:
|
|
449
|
-
break;
|
|
450
464
|
}
|
|
451
|
-
}
|
|
452
465
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
466
|
+
// Fire event listeners
|
|
467
|
+
this.emit(event, data);
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
await this.io;
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Delete the connecting Promise
|
|
476
|
+
this.__connectPromise
|
|
477
|
+
.catch(() => { })
|
|
478
|
+
.finally(() => {
|
|
479
|
+
delete this.__connectPromise;
|
|
458
480
|
});
|
|
459
|
-
|
|
481
|
+
|
|
482
|
+
await this.__connectPromise;
|
|
460
483
|
}
|
|
461
484
|
|
|
462
485
|
/**
|
|
@@ -466,12 +489,35 @@ class Manager extends EventEmitter {
|
|
|
466
489
|
async disconnect() {
|
|
467
490
|
this.__debug('disconnect');
|
|
468
491
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
492
|
+
// If connecting, await that first
|
|
493
|
+
try {
|
|
494
|
+
await this.__connectPromise;
|
|
495
|
+
} catch (err) { }
|
|
496
|
+
|
|
497
|
+
this.__disconnectPromise = Promise.resolve().then(async () => {
|
|
498
|
+
this.__connected = false;
|
|
499
|
+
|
|
500
|
+
if (this.io) {
|
|
501
|
+
await this.io
|
|
502
|
+
.then(io => io.unsubscribe())
|
|
503
|
+
.catch(err => this.__debug('Error Disconnecting:', err));
|
|
504
|
+
|
|
505
|
+
delete this.io;
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// Delete the disconnecting Promise
|
|
510
|
+
this.__disconnectPromise
|
|
511
|
+
.catch(() => { })
|
|
512
|
+
.finally(() => {
|
|
513
|
+
delete this.__disconnectPromise;
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
await this.__disconnectPromise;
|
|
472
517
|
}
|
|
473
518
|
|
|
474
519
|
destroy() {
|
|
520
|
+
// Clear cache
|
|
475
521
|
for (const id of Object.keys(this.__cache)) {
|
|
476
522
|
this.__cache[id] = {};
|
|
477
523
|
}
|
|
@@ -480,6 +526,10 @@ class Manager extends EventEmitter {
|
|
|
480
526
|
this.__cacheAllComplete[id] = false;
|
|
481
527
|
}
|
|
482
528
|
|
|
529
|
+
// Remove all event listeners
|
|
530
|
+
this.removeAllListeners();
|
|
531
|
+
|
|
532
|
+
// Disconnect from Socket.io
|
|
483
533
|
this.disconnect().catch(() => { });
|
|
484
534
|
}
|
|
485
535
|
|
|
@@ -184,9 +184,12 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
184
184
|
|
|
185
185
|
// Don't discover, just set the only strategy
|
|
186
186
|
if (Object.keys(urls).length === 1) {
|
|
187
|
+
this.__baseUrl = Object.values(urls)[0];
|
|
188
|
+
this.__strategyId = Object.keys(urls)[0];
|
|
189
|
+
|
|
187
190
|
return {
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
baseUrl: this.__baseUrl,
|
|
192
|
+
strategyId: this.__strategyId,
|
|
190
193
|
};
|
|
191
194
|
}
|
|
192
195
|
|
|
@@ -285,8 +288,6 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
285
288
|
promises.push(pings[HomeyAPI.DISCOVERY_STRATEGIES.MDNS]);
|
|
286
289
|
}
|
|
287
290
|
|
|
288
|
-
// TODO: Move this to the catch handler to always fallback on cloud
|
|
289
|
-
// Now mdns or local will error first and cloud won't have a chance!!
|
|
290
291
|
if (pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]) {
|
|
291
292
|
promises.push(pings[HomeyAPI.DISCOVERY_STRATEGIES.CLOUD]);
|
|
292
293
|
}
|
|
@@ -298,7 +299,16 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
298
299
|
return Promise.race(promises);
|
|
299
300
|
})
|
|
300
301
|
.then(result => resolve(result))
|
|
301
|
-
.catch(
|
|
302
|
+
.catch(err => {
|
|
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
|
+
});
|
|
302
312
|
} else if (pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL]) {
|
|
303
313
|
pings[HomeyAPI.DISCOVERY_STRATEGIES.LOCAL]
|
|
304
314
|
.then(result => resolve(result))
|
|
@@ -526,6 +536,7 @@ class HomeyAPIV2 extends HomeyAPI {
|
|
|
526
536
|
|
|
527
537
|
return {
|
|
528
538
|
unsubscribe: () => {
|
|
539
|
+
this.__ioNamespace.emit('unsubscribe', uri);
|
|
529
540
|
this.__ioNamespace.removeListener(uri, __onEvent);
|
|
530
541
|
this.__io.removeListener('disconnect', __onDisconnect);
|
|
531
542
|
this.__io.removeListener('reconnect', __onReconnect);
|