homey-api 1.10.17 → 3.0.0-rc.2
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/README.md +1 -1
- package/assets/types/homey-api.d.ts +47 -588
- package/assets/types/homey-api.private.d.ts +47 -648
- package/index.js +1 -1
- package/lib/APIErrorNotFound.js +20 -0
- package/lib/AthomCloudAPI/Homey.js +3 -1
- package/lib/EventEmitter.js +0 -6
- package/lib/HomeyAPI/HomeyAPI.js +49 -5
- package/lib/HomeyAPI/HomeyAPIErrorNotFound.js +21 -0
- package/lib/HomeyAPI/HomeyAPIV2/Manager.js +2 -575
- package/lib/HomeyAPI/HomeyAPIV2/ManagerDevices/Capability.js +20 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerDevices/Device.js +18 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerDevices.js +20 -3
- package/lib/HomeyAPI/HomeyAPIV2/ManagerDrivers/Driver.js +25 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerDrivers.js +29 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlow/AdvancedFlow.js +17 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlow/Flow.js +34 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlow/FlowCardAction.js +25 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlow/FlowCardCondition.js +25 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlow/FlowCardTrigger.js +25 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlow.js +104 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlowToken/FlowToken.js +24 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerFlowToken.js +29 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerInsights/Log.js +23 -0
- package/lib/HomeyAPI/HomeyAPIV2/ManagerInsights.js +29 -0
- package/lib/HomeyAPI/HomeyAPIV2.js +12 -716
- package/lib/HomeyAPI/HomeyAPIV3/Item.js +173 -2
- package/lib/HomeyAPI/HomeyAPIV3/Manager.js +531 -3
- package/lib/HomeyAPI/{HomeyAPIV2 → HomeyAPIV3/ManagerApps}/App.js +1 -1
- package/lib/HomeyAPI/{HomeyAPIV2 → HomeyAPIV3}/ManagerApps.js +4 -3
- package/lib/HomeyAPI/HomeyAPIV3/ManagerDevices/Capability.js +9 -0
- package/lib/HomeyAPI/{HomeyAPIV2 → HomeyAPIV3/ManagerDevices}/Device.js +78 -3
- package/lib/HomeyAPI/{HomeyAPIV2 → HomeyAPIV3/ManagerDevices}/DeviceCapability.js +3 -3
- package/lib/HomeyAPI/HomeyAPIV3/ManagerDevices.js +17 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerDrivers/Driver.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerDrivers.js +15 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlow/AdvancedFlow.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlow/Flow.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlow/FlowCard.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlow/FlowCardAction.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlow/FlowCardCondition.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlow/FlowCardTrigger.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlow.js +12 -23
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlowToken/FlowToken.js +16 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerFlowToken.js +15 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerInsights/Log.js +9 -0
- package/lib/HomeyAPI/HomeyAPIV3/ManagerInsights.js +15 -0
- package/lib/HomeyAPI/HomeyAPIV3.js +728 -4
- package/lib/HomeyAPI/HomeyAPIV3Cloud.js +1 -1
- package/lib/HomeyAPI/HomeyAPIV3Local.js +1 -1
- package/package.json +1 -1
- package/lib/HomeyAPI/HomeyAPIApp.js +0 -127
- package/lib/HomeyAPI/HomeyAPIV2/Item.js +0 -177
|
@@ -1,14 +1,542 @@
|
|
|
1
|
+
/* eslint-disable no-multi-assign */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
|
-
const
|
|
5
|
+
const EventEmitter = require('../../EventEmitter');
|
|
6
|
+
const Util = require('../../Util');
|
|
7
|
+
const HomeyAPIError = require('../HomeyAPIError');
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line no-unused-vars
|
|
10
|
+
const Item = require('./Item');
|
|
4
11
|
|
|
5
12
|
/**
|
|
6
13
|
* @class
|
|
7
|
-
* @extends HomeyAPIV2.Manager
|
|
8
14
|
* @hideconstructor
|
|
9
15
|
* @memberof HomeyAPIV3
|
|
10
16
|
*/
|
|
11
|
-
class Manager extends
|
|
17
|
+
class Manager extends EventEmitter {
|
|
18
|
+
|
|
19
|
+
static ID = null; // Set by HomeyAPIV3.js
|
|
20
|
+
static CRUD = {};
|
|
21
|
+
|
|
22
|
+
constructor({
|
|
23
|
+
homey,
|
|
24
|
+
items,
|
|
25
|
+
operations,
|
|
26
|
+
}) {
|
|
27
|
+
super();
|
|
28
|
+
|
|
29
|
+
// Set Homey
|
|
30
|
+
Object.defineProperty(this, 'homey', {
|
|
31
|
+
value: homey,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
writable: false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Set Items
|
|
37
|
+
Object.defineProperty(this, 'items', {
|
|
38
|
+
value: Object.entries(items).reduce((obj, [itemName, item]) => {
|
|
39
|
+
const ItemClass = this.constructor.CRUD[itemName]
|
|
40
|
+
? this.constructor.CRUD[itemName]
|
|
41
|
+
// eslint-disable-next-line no-eval
|
|
42
|
+
: eval(`(class ${itemName} extends Item {})`);
|
|
43
|
+
ItemClass.ID = item.id;
|
|
44
|
+
obj[item.id] = ItemClass;
|
|
45
|
+
|
|
46
|
+
return obj;
|
|
47
|
+
}, {}),
|
|
48
|
+
enumerable: false,
|
|
49
|
+
writable: false,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Set Connected
|
|
53
|
+
Object.defineProperty(this, '__connected', {
|
|
54
|
+
value: false,
|
|
55
|
+
enumerable: false,
|
|
56
|
+
writable: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Set Cache
|
|
60
|
+
Object.defineProperty(this, '__cache', {
|
|
61
|
+
value: Object.values(items).reduce((obj, item) => ({
|
|
62
|
+
...obj,
|
|
63
|
+
[item.id]: {},
|
|
64
|
+
}), {}),
|
|
65
|
+
enumerable: false,
|
|
66
|
+
writable: false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
Object.defineProperty(this, '__cacheAllComplete', {
|
|
70
|
+
value: Object.values(items).reduce((obj, item) => ({
|
|
71
|
+
...obj,
|
|
72
|
+
[item.id]: false,
|
|
73
|
+
}), {}),
|
|
74
|
+
enumerable: false,
|
|
75
|
+
writable: false,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Create methods
|
|
79
|
+
for (const [operationId, operation] of Object.entries(operations)) {
|
|
80
|
+
Object.defineProperty(this,
|
|
81
|
+
// Name method __super__foo if there's an override method
|
|
82
|
+
this[operationId]
|
|
83
|
+
? `__super__${operationId}`
|
|
84
|
+
: operationId,
|
|
85
|
+
{
|
|
86
|
+
value: async ({
|
|
87
|
+
$validate = true,
|
|
88
|
+
$cache = true,
|
|
89
|
+
$timeout = operation.timeout ?? 5000,
|
|
90
|
+
$socket = operation.socket ?? true,
|
|
91
|
+
$body = {},
|
|
92
|
+
$query = {},
|
|
93
|
+
$headers = {},
|
|
94
|
+
...args
|
|
95
|
+
} = {}) => {
|
|
96
|
+
let { path } = operation;
|
|
97
|
+
let body = { ...$body };
|
|
98
|
+
const query = { ...$query };
|
|
99
|
+
const headers = { ...$headers };
|
|
100
|
+
|
|
101
|
+
// Verify & Transform parameters
|
|
102
|
+
if (operation.parameters) {
|
|
103
|
+
// Parse Parameters
|
|
104
|
+
for (const [parameterId, parameter] of Object.entries(operation.parameters)) {
|
|
105
|
+
const value = args[parameterId];
|
|
106
|
+
|
|
107
|
+
// Validate the parameter
|
|
108
|
+
if ($validate) {
|
|
109
|
+
if (parameter.required === true && typeof value === 'undefined') {
|
|
110
|
+
throw new Error(`Missing Parameter: ${parameterId}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (typeof value !== 'undefined') {
|
|
114
|
+
if (parameter.type === 'string' && typeof value !== 'string') {
|
|
115
|
+
throw new Error(`Invalid Parameter Type: ${parameterId}. Got: ${typeof value}. Expected: string`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (parameter.type === 'number' && typeof value !== 'number') {
|
|
119
|
+
throw new Error(`Invalid Parameter Type: ${parameterId}. Got: ${typeof value}. Expected: number`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (parameter.type === 'boolean' && typeof value !== 'boolean') {
|
|
123
|
+
throw new Error(`Invalid Parameter Type: ${parameterId}. Got: ${typeof value}. Expected: boolean`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (parameter.type === 'object' && typeof value !== 'object') {
|
|
127
|
+
throw new Error(`Invalid Parameter Type: ${parameterId}. Got: ${typeof value}. Expected: object`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (parameter.type === 'array' && !Array.isArray(value)) {
|
|
131
|
+
throw new Error(`Invalid Parameter Type: ${parameterId}. Got: ${typeof value}. Expected: array`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (Array.isArray(parameter.type)) {
|
|
135
|
+
// TODO
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Set the parameter
|
|
141
|
+
if (typeof value !== 'undefined') {
|
|
142
|
+
switch (parameter.in) {
|
|
143
|
+
case 'path': {
|
|
144
|
+
if (typeof value !== 'string') {
|
|
145
|
+
throw new Error(`Invalid Parameter Type: ${parameterId}. Got: ${typeof value}. Expected: string`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
path = path.replace(`:${parameterId}`, value);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case 'body': {
|
|
152
|
+
if (parameter.root) {
|
|
153
|
+
body = value;
|
|
154
|
+
} else {
|
|
155
|
+
body[parameterId] = value;
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case 'query': {
|
|
160
|
+
if (typeof value !== 'string') {
|
|
161
|
+
throw new Error(`Invalid Parameter Type: ${parameterId}. Got: ${typeof value}. Expected: string`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
query[parameterId] = value;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
default: {
|
|
168
|
+
throw new Error(`Invalid 'in': ${parameter.in}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Append query to path
|
|
176
|
+
if (Object.keys(query).length > 0) {
|
|
177
|
+
const queryString = Object.entries(query).map(([key, value]) => {
|
|
178
|
+
return `${key}=${encodeURIComponent(value)}`;
|
|
179
|
+
}).join('&');
|
|
180
|
+
path = `${path}?${queryString}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let result;
|
|
184
|
+
const benchmark = Util.benchmark();
|
|
185
|
+
|
|
186
|
+
// If connected to Socket.io,
|
|
187
|
+
// try to get the CRUD Item from Cache.
|
|
188
|
+
if (this.isConnected() && operation.crud && $cache === true) {
|
|
189
|
+
const itemId = items[operation.crud.item].id;
|
|
190
|
+
|
|
191
|
+
switch (operation.crud.type) {
|
|
192
|
+
case 'getOne': {
|
|
193
|
+
if (this.__cache[itemId][args.id]) {
|
|
194
|
+
return this.__cache[itemId][args.id];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case 'getAll': {
|
|
200
|
+
if (this.__cache[itemId]
|
|
201
|
+
&& this.__cacheAllComplete[itemId]) {
|
|
202
|
+
return this.__cache[itemId];
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
default:
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// If Homey is connected to Socket.io,
|
|
212
|
+
// send the API request to socket.io.
|
|
213
|
+
// This is about ~2x faster than HTTP
|
|
214
|
+
if (this.homey.isConnected() && $socket === true) {
|
|
215
|
+
result = await Util.timeout(new Promise((resolve, reject) => {
|
|
216
|
+
this.homey.__ioNamespace.emit('api', {
|
|
217
|
+
args,
|
|
218
|
+
operation: operationId,
|
|
219
|
+
uri: this.uri,
|
|
220
|
+
}, (err, result) => {
|
|
221
|
+
// String Error
|
|
222
|
+
if (typeof err === 'string') {
|
|
223
|
+
err = new HomeyAPIError({
|
|
224
|
+
error: err,
|
|
225
|
+
}, 500);
|
|
226
|
+
return reject(err);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Object Error
|
|
230
|
+
if (typeof err === 'object' && err !== null) {
|
|
231
|
+
err = new HomeyAPIError({
|
|
232
|
+
stack: err.stack,
|
|
233
|
+
error: err.error,
|
|
234
|
+
error_description: err.error_description,
|
|
235
|
+
}, err.statusCode || 500);
|
|
236
|
+
return reject(err);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return resolve(result);
|
|
240
|
+
});
|
|
241
|
+
}), $timeout);
|
|
242
|
+
} else {
|
|
243
|
+
// Get from HTTP
|
|
244
|
+
result = await this.homey.call({
|
|
245
|
+
$timeout,
|
|
246
|
+
headers,
|
|
247
|
+
body,
|
|
248
|
+
path: `/api/manager/${this.constructor.ID}${path}`,
|
|
249
|
+
method: operation.method,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Transform and cache output if this is a CRUD call
|
|
254
|
+
if (operation.crud) {
|
|
255
|
+
const itemId = items[operation.crud.item].id;
|
|
256
|
+
const Item = this.items[itemId];
|
|
257
|
+
|
|
258
|
+
switch (operation.crud.type) {
|
|
259
|
+
case 'getOne': {
|
|
260
|
+
let item = { ...result };
|
|
261
|
+
item = Item.transform(item);
|
|
262
|
+
item = new Item({
|
|
263
|
+
itemId,
|
|
264
|
+
id: item.id,
|
|
265
|
+
homey: this.homey,
|
|
266
|
+
manager: this,
|
|
267
|
+
properties: { ...item },
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (this.isConnected()) {
|
|
271
|
+
this.__cache[itemId][item.id] = item;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return item;
|
|
275
|
+
}
|
|
276
|
+
case 'getAll': {
|
|
277
|
+
const items = {};
|
|
278
|
+
|
|
279
|
+
// Add all to cache
|
|
280
|
+
for (let item of Object.values(result)) {
|
|
281
|
+
item = Item.transform(item);
|
|
282
|
+
if (this.__cache[itemId][item.id]) {
|
|
283
|
+
this.__cache[itemId][item.id].__update(item);
|
|
284
|
+
items[item.id] = this.__cache[itemId][item.id];
|
|
285
|
+
} else {
|
|
286
|
+
items[item.id] = new Item({
|
|
287
|
+
itemId,
|
|
288
|
+
id: item.id,
|
|
289
|
+
homey: this.homey,
|
|
290
|
+
manager: this,
|
|
291
|
+
properties: { ...item },
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
if (this.isConnected()) {
|
|
295
|
+
this.__cache[itemId][item.id] = items[item.id];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Find and delete deleted items from cache
|
|
301
|
+
if (this.__cache[itemId]) {
|
|
302
|
+
for (const cachedItem of Object.values(this.__cache[itemId])) {
|
|
303
|
+
if (!items[cachedItem.id]) {
|
|
304
|
+
delete this.__cache[itemId][cachedItem.id];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Mark cache as complete
|
|
310
|
+
if (this.isConnected()) {
|
|
311
|
+
this.__cacheAllComplete[itemId] = true;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return items;
|
|
315
|
+
}
|
|
316
|
+
case 'createOne':
|
|
317
|
+
case 'updateOne': {
|
|
318
|
+
let item = { ...result };
|
|
319
|
+
item = Item.transform(item);
|
|
320
|
+
if (this.__cache[itemId][item.id]) {
|
|
321
|
+
item = this.__cache[itemId][item.id].__update(item);
|
|
322
|
+
} else {
|
|
323
|
+
item = Item.transform(item);
|
|
324
|
+
item = new Item({
|
|
325
|
+
itemId,
|
|
326
|
+
id: item.id,
|
|
327
|
+
homey: this.homey,
|
|
328
|
+
manager: this,
|
|
329
|
+
properties: { ...item },
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (this.isConnected()) {
|
|
333
|
+
this.__cache[itemId][item.id] = item;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return item;
|
|
338
|
+
}
|
|
339
|
+
case 'deleteOne': {
|
|
340
|
+
if (this.__cache[itemId][args.id]) {
|
|
341
|
+
this.__cache[itemId][args.id].destroy();
|
|
342
|
+
delete this.__cache[itemId][args.id];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
default:
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this.__debug(`${operationId} took ${benchmark()}ms`);
|
|
353
|
+
return result;
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
get uri() {
|
|
360
|
+
return `homey:manager:${this.constructor.ID}`;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
__debug(...props) {
|
|
364
|
+
this.homey.__debug(`[${this.constructor.name}]`, ...props);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* If this manager's namespace is connected to Socket.io.
|
|
369
|
+
* @returns {Boolean}
|
|
370
|
+
*/
|
|
371
|
+
isConnected() {
|
|
372
|
+
return this.__connected === true;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Connect to the realtime namespace.
|
|
377
|
+
* @returns {Promise<void>}
|
|
378
|
+
*/
|
|
379
|
+
async connect() {
|
|
380
|
+
this.__debug('connect');
|
|
381
|
+
|
|
382
|
+
// If disconnecting, await that first
|
|
383
|
+
try {
|
|
384
|
+
await this.__disconnectPromise;
|
|
385
|
+
} catch (err) { }
|
|
386
|
+
|
|
387
|
+
this.__connectPromise = Promise.resolve().then(async () => {
|
|
388
|
+
if (!this.io) {
|
|
389
|
+
this.io = this.homey.subscribe(this.uri, {
|
|
390
|
+
onConnect: () => {
|
|
391
|
+
this.__debug('onConnect');
|
|
392
|
+
this.__connected = true;
|
|
393
|
+
},
|
|
394
|
+
onDisconnect: reason => {
|
|
395
|
+
this.__debug(`onDisconnect Reason:${reason}`);
|
|
396
|
+
this.__connected = false;
|
|
397
|
+
|
|
398
|
+
// Clear CRUD Item cache
|
|
399
|
+
for (const itemId of Object.keys(this.__cache)) {
|
|
400
|
+
this.__cache[itemId] = {};
|
|
401
|
+
this.__cacheAllComplete[itemId] = false;
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
onEvent: (event, data) => {
|
|
405
|
+
this.__debug('onEvent', event);
|
|
406
|
+
|
|
407
|
+
// Transform & add to cache if this is a CRUD event
|
|
408
|
+
if (event.endsWith('.create')
|
|
409
|
+
|| event.endsWith('.update')
|
|
410
|
+
|| event.endsWith('.delete')) {
|
|
411
|
+
const [itemId, operation] = event.split('.');
|
|
412
|
+
const Item = this.items[itemId];
|
|
413
|
+
|
|
414
|
+
switch (operation) {
|
|
415
|
+
case 'create': {
|
|
416
|
+
let item = { ...data };
|
|
417
|
+
item = Item.transform(item);
|
|
418
|
+
item = new Item({
|
|
419
|
+
itemId,
|
|
420
|
+
id: item.id,
|
|
421
|
+
homey: this.homey,
|
|
422
|
+
manager: this,
|
|
423
|
+
properties: { ...item },
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
this.__cache[itemId][item.id] = item;
|
|
427
|
+
this.__cache[itemId][item.id].emit('create');
|
|
428
|
+
|
|
429
|
+
return this.emit(`${itemId}.create`, item);
|
|
430
|
+
}
|
|
431
|
+
case 'update': {
|
|
432
|
+
let item = { ...data };
|
|
433
|
+
item = Item.transform(item);
|
|
434
|
+
|
|
435
|
+
if (this.__cache[itemId][item.id]) {
|
|
436
|
+
item = this.__cache[itemId][item.id];
|
|
437
|
+
item.__update(item);
|
|
438
|
+
item.emit('update');
|
|
439
|
+
} else {
|
|
440
|
+
item = new Item({
|
|
441
|
+
itemId,
|
|
442
|
+
id: item.id,
|
|
443
|
+
homey: this.homey,
|
|
444
|
+
manager: this,
|
|
445
|
+
properties: { ...item },
|
|
446
|
+
});
|
|
447
|
+
this.__cache[itemId][item.id] = item;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return this.emit(`${itemId}.update`, item);
|
|
451
|
+
}
|
|
452
|
+
case 'delete': {
|
|
453
|
+
let item = Item.transform({ ...data });
|
|
454
|
+
item = Item.transform(item);
|
|
455
|
+
|
|
456
|
+
if (this.__cache[itemId][item.id]) {
|
|
457
|
+
this.__cache[itemId][item.id].emit('delete');
|
|
458
|
+
this.__cache[itemId][item.id].destroy();
|
|
459
|
+
delete this.__cache[itemId][item.id];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return this.emit(`${itemId}.delete`, {
|
|
463
|
+
id: item.id,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
default:
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Fire event listeners
|
|
472
|
+
this.emit(event, data);
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
await this.io;
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Delete the connecting Promise
|
|
481
|
+
this.__connectPromise
|
|
482
|
+
.catch(() => { })
|
|
483
|
+
.finally(() => {
|
|
484
|
+
delete this.__connectPromise;
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
await this.__connectPromise;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Disconnect from the realtime namespace.
|
|
492
|
+
* @returns {Promise<void>}
|
|
493
|
+
*/
|
|
494
|
+
async disconnect() {
|
|
495
|
+
this.__debug('disconnect');
|
|
496
|
+
|
|
497
|
+
// If connecting, await that first
|
|
498
|
+
try {
|
|
499
|
+
await this.__connectPromise;
|
|
500
|
+
} catch (err) { }
|
|
501
|
+
|
|
502
|
+
this.__disconnectPromise = Promise.resolve().then(async () => {
|
|
503
|
+
this.__connected = false;
|
|
504
|
+
|
|
505
|
+
if (this.io) {
|
|
506
|
+
await this.io
|
|
507
|
+
.then(io => io.unsubscribe())
|
|
508
|
+
.catch(err => this.__debug('Error Disconnecting:', err));
|
|
509
|
+
|
|
510
|
+
delete this.io;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// Delete the disconnecting Promise
|
|
515
|
+
this.__disconnectPromise
|
|
516
|
+
.catch(() => { })
|
|
517
|
+
.finally(() => {
|
|
518
|
+
delete this.__disconnectPromise;
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
await this.__disconnectPromise;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
destroy() {
|
|
525
|
+
// Clear cache
|
|
526
|
+
for (const id of Object.keys(this.__cache)) {
|
|
527
|
+
this.__cache[id] = {};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
for (const id of Object.keys(this.__cacheAllComplete)) {
|
|
531
|
+
this.__cacheAllComplete[id] = false;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Remove all event listeners
|
|
535
|
+
this.removeAllListeners();
|
|
536
|
+
|
|
537
|
+
// Disconnect from Socket.io
|
|
538
|
+
this.disconnect().catch(() => { });
|
|
539
|
+
}
|
|
12
540
|
|
|
13
541
|
}
|
|
14
542
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const Manager = require('./Manager');
|
|
4
|
-
const App = require('./App');
|
|
4
|
+
const App = require('./ManagerApps/App');
|
|
5
5
|
|
|
6
6
|
class ManagerApps extends Manager {
|
|
7
7
|
|
|
8
|
-
static
|
|
9
|
-
|
|
8
|
+
static CRUD = {
|
|
9
|
+
...super.CRUD,
|
|
10
|
+
App,
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Util = require('
|
|
4
|
-
const Item = require('
|
|
3
|
+
const Util = require('../../../Util');
|
|
4
|
+
const Item = require('../Item');
|
|
5
5
|
const DeviceCapability = require('./DeviceCapability');
|
|
6
6
|
|
|
7
7
|
class Device extends Item {
|
|
@@ -17,6 +17,10 @@ class Device extends Item {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
get uri() {
|
|
21
|
+
return `homey:device:${this.id}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* Creates an {@link HomeyAPIV2.DeviceCapability} for realtime capability updates.
|
|
22
26
|
* @param {string} capabilityId
|
|
@@ -130,8 +134,79 @@ class Device extends Item {
|
|
|
130
134
|
});
|
|
131
135
|
})
|
|
132
136
|
// eslint-disable-next-line no-console
|
|
133
|
-
.catch(err =>
|
|
137
|
+
.catch(err => this.__debug(`Device[${this.id}].onReconnectError:`, err));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async getZone() {
|
|
142
|
+
return this.homey.zones.getZone({
|
|
143
|
+
id: this.zone,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async getDriver() {
|
|
148
|
+
return this.homey.drivers.getDriver({
|
|
149
|
+
id: this.driverId,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async getLogs() {
|
|
154
|
+
const logs = await this.homey.insights.getLogs();
|
|
155
|
+
return Object.values(logs)
|
|
156
|
+
.filter(log => log.ownerUri === this.uri)
|
|
157
|
+
.reduce((result, log) => ({
|
|
158
|
+
...result,
|
|
159
|
+
[log.id]: log,
|
|
160
|
+
}), {});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async getFlows() {
|
|
164
|
+
const flows = await this.homey.flow.getFlows();
|
|
165
|
+
return Object.values(flows)
|
|
166
|
+
.filter(flow => {
|
|
167
|
+
if (flow.trigger && flow.trigger.id.startsWith(this.uri)) return true;
|
|
168
|
+
if (Array.isArray(flow.conditions) && flow.conditions.some(card => card.id.startsWith(this.uri))) return true;
|
|
169
|
+
if (Array.isArray(flow.actions) && flow.actions.some(card => card.id.startsWith(this.uri))) return true;
|
|
170
|
+
|
|
171
|
+
// TODO: Zone cards
|
|
172
|
+
// TODO: Subzone cards
|
|
173
|
+
|
|
174
|
+
return false;
|
|
175
|
+
})
|
|
176
|
+
.reduce((result, flow) => ({
|
|
177
|
+
...result,
|
|
178
|
+
[flow.id]: flow,
|
|
179
|
+
}), {});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async getAdvancedFlows() {
|
|
183
|
+
const advancedFlows = await this.homey.flow.getAdvancedFlows();
|
|
184
|
+
return Object.values(advancedFlows)
|
|
185
|
+
.filter(advancedFlow => {
|
|
186
|
+
return Object.values(advancedFlow.cards)
|
|
187
|
+
.filter(card => ['trigger', 'condition', 'action'].includes(card.type))
|
|
188
|
+
.some(card => {
|
|
189
|
+
if (card.id.startsWith(this.uri)) return true;
|
|
190
|
+
|
|
191
|
+
// TODO: Zone cards
|
|
192
|
+
// TODO: Subzone cards
|
|
193
|
+
return false;
|
|
194
|
+
});
|
|
195
|
+
})
|
|
196
|
+
.reduce((result, advancedFlow) => ({
|
|
197
|
+
...result,
|
|
198
|
+
[advancedFlow.id]: advancedFlow,
|
|
199
|
+
}), {});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
static transform(item) {
|
|
203
|
+
for (const capabilityObj of Object.values(item.capabilitiesObj)) {
|
|
204
|
+
if (capabilityObj.lastUpdated) {
|
|
205
|
+
capabilityObj.lastUpdated = new Date(capabilityObj.lastUpdated);
|
|
206
|
+
}
|
|
134
207
|
}
|
|
208
|
+
|
|
209
|
+
return item;
|
|
135
210
|
}
|
|
136
211
|
|
|
137
212
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Util = require('
|
|
4
|
-
const EventEmitter = require('
|
|
3
|
+
const Util = require('../../../Util');
|
|
4
|
+
const EventEmitter = require('../../../EventEmitter');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @class
|
|
@@ -75,7 +75,7 @@ class DeviceCapability extends EventEmitter {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
__debug(...props) {
|
|
78
|
-
this.device.__debug(`[
|
|
78
|
+
this.device.__debug(`[DeviceCapability:${this.id}]`, ...props);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|