homebridge-deconz 0.0.15 → 0.0.18
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 -4
- package/cli/deconz.js +2 -2
- package/homebridge-ui/public/index.html +249 -460
- package/homebridge-ui/public/index.old.html +506 -0
- package/lib/Deconz/ApiClient.js +3 -4
- package/lib/Deconz/Discovery.js +7 -4
- package/lib/Deconz/Resource.js +20 -3
- package/lib/DeconzAccessory/AirPurifier.js +38 -0
- package/lib/DeconzAccessory/Gateway.js +44 -20
- package/lib/DeconzAccessory/Motion.js +3 -0
- package/lib/DeconzAccessory/index.js +3 -3
- package/lib/DeconzPlatform.js +13 -12
- package/lib/DeconzService/AirPurifier.js +216 -0
- package/lib/DeconzService/AirQuality.js +23 -7
- package/lib/DeconzService/Button.js +4 -0
- package/lib/DeconzService/Consumption.js +1 -1
- package/lib/DeconzService/Contact.js +2 -0
- package/lib/DeconzService/Light.js +9 -9
- package/lib/DeconzService/LightLevel.js +2 -0
- package/lib/DeconzService/Motion.js +1 -1
- package/lib/DeconzService/Outlet.js +1 -1
- package/lib/DeconzService/Power.js +1 -1
- package/lib/DeconzService/Switch.js +35 -23
- package/lib/DeconzService/Temperature.js +1 -0
- package/lib/DeconzService/Thermostat.js +1 -0
- package/lib/DeconzService/WindowCovering.js +1 -1
- package/lib/DeconzService/index.js +1 -0
- package/package.json +6 -6
@@ -6,501 +6,290 @@ Copyright © 2022 Erik Baauw. All rights reserved.
|
|
6
6
|
-->
|
7
7
|
|
8
8
|
<link rel="stylesheet" href="style.css">
|
9
|
+
<script src="https://unpkg.com/vue@3"></script>
|
10
|
+
|
9
11
|
<p align="center">
|
10
12
|
<a href="https://github.com/ebaauw/homebridge-deconz/wiki/Configuration" target="_blank">
|
11
13
|
<img src="homebridge-deconz.png" height="200px">
|
12
14
|
</a>
|
13
15
|
</p>
|
14
16
|
|
15
|
-
<
|
17
|
+
<div id="app">
|
18
|
+
<!-- v-if can be used on any element, it will only render the element if the condition is true -->
|
19
|
+
<div v-if="view === 'gateways'">
|
20
|
+
<div class="w-100 d-flex justify-content-between align-items-center mb-1">
|
21
|
+
<h4 class="mb-0">Gateways</h4>
|
22
|
+
<!-- @click will call the addGateway method -->
|
23
|
+
<button class="btn btn-primary mr-0" @click="addGateway">Add <i class="fas fa-plus" ></i></button>
|
24
|
+
</div>
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
continue
|
42
|
-
}
|
43
|
-
const pong = await homebridge.request(
|
44
|
-
'get', { uiPort: gateway.context.uiPort, path: '/ping' }
|
45
|
-
)
|
46
|
-
if (pong === 'pong') {
|
47
|
-
result[gateway.context.host] = gateway.context
|
48
|
-
}
|
49
|
-
}
|
50
|
-
const gateways = Object.keys(result).sort()
|
51
|
-
console.log('%s: gateways: %j',config.name, gateways)
|
52
|
-
}
|
53
|
-
}
|
54
|
-
homebridge.hideSpinner()
|
26
|
+
<ul class="list-group">
|
27
|
+
<!-- here we are looping over data.pluginConfig.gateways using v-for -->
|
28
|
+
<li class="list-group-item d-flex justify-content-between align-items-center" v-for="(gateway, $index) in pluginConfig.gateways" :key="$index">
|
29
|
+
<div>
|
30
|
+
{{ gateway.name }}
|
31
|
+
<div class="grey-text">
|
32
|
+
{{ gateway.host }}
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
<div>
|
36
|
+
<div class="btn-group" role="group" aria-label="Basic example">
|
37
|
+
<!-- @click will call the editGateway(gateway) method with the selected gateway as the first argument -->
|
38
|
+
<button class="btn btn-primary" @click="editGateway(gateway)">
|
39
|
+
<i class="fas fa-cog"></i>
|
40
|
+
</button>
|
41
|
+
<!-- @click will call the deleteGateway($index) method with its position in the array / index as the first argument -->
|
42
|
+
<button class="btn btn-danger" @click="deleteGateway($index)">
|
43
|
+
<i class="fas fa-trash"></i>
|
44
|
+
</button>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</li>
|
48
|
+
</ul>
|
49
|
+
</div>
|
55
50
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
description: 'Configure a child bridge per deCONZ gateway. See <a href="https://github.com/ebaauw/homebridge-deconz/wiki/Configuration" target="_blank">wiki</a> for details.',
|
64
|
-
type: 'array',
|
65
|
-
disabled: true,
|
66
|
-
items: {
|
67
|
-
type: 'object',
|
68
|
-
properties: {
|
69
|
-
host: {
|
70
|
-
description: 'Gateway hostname and port.',
|
71
|
-
default: 'localhost:80',
|
72
|
-
type: 'string',
|
73
|
-
required: true
|
74
|
-
},
|
75
|
-
name: {
|
76
|
-
description: 'Homebridge log plugin name.',
|
77
|
-
default: 'deCONZ',
|
78
|
-
type: 'string'
|
79
|
-
},
|
80
|
-
_bridge: {
|
81
|
-
type: 'object',
|
82
|
-
required: true,
|
83
|
-
properties: {
|
84
|
-
name: {
|
85
|
-
type: 'string',
|
86
|
-
required: true
|
87
|
-
},
|
88
|
-
username: {
|
89
|
-
type: 'string',
|
90
|
-
pattern: '^([A-F0-9]{2}:){5}[A-F0-9]{2}$',
|
91
|
-
placeholder: 'AA:BB:CC:DD:EE:FF',
|
92
|
-
required: true
|
93
|
-
},
|
94
|
-
port: {
|
95
|
-
type: 'integer',
|
96
|
-
minimum: 1025,
|
97
|
-
// maximum: 65535,
|
98
|
-
required: true
|
99
|
-
},
|
100
|
-
manufacturer: {
|
101
|
-
type: 'string',
|
102
|
-
enabled: false,
|
103
|
-
required: true
|
104
|
-
},
|
105
|
-
model: {
|
106
|
-
type: 'string',
|
107
|
-
required: true
|
108
|
-
}
|
109
|
-
}
|
110
|
-
}
|
111
|
-
}
|
112
|
-
}
|
113
|
-
}
|
114
|
-
}
|
115
|
-
},
|
116
|
-
// layout: null
|
117
|
-
layout: [
|
118
|
-
{
|
119
|
-
type: 'tabarray',
|
120
|
-
title: '{{ value.name }}',
|
121
|
-
items: [
|
122
|
-
{
|
123
|
-
type: 'fieldset',
|
124
|
-
title: 'Gateway Settings',
|
125
|
-
key: 'config[]',
|
126
|
-
items: [
|
127
|
-
{
|
128
|
-
type: 'flex',
|
129
|
-
'flex-flow': 'row',
|
130
|
-
items: [
|
131
|
-
'config[].host',
|
132
|
-
'config[].name',
|
133
|
-
]
|
134
|
-
}
|
135
|
-
]
|
136
|
-
},
|
137
|
-
{
|
138
|
-
type: 'flex',
|
139
|
-
'flex-flow': 'row',
|
140
|
-
key: 'config[]',
|
141
|
-
items: [
|
142
|
-
{
|
143
|
-
type: 'button',
|
144
|
-
title: 'Connect',
|
145
|
-
key: 'config[].connect'
|
146
|
-
},
|
147
|
-
{
|
148
|
-
type: 'button',
|
149
|
-
title: 'Get API Key',
|
150
|
-
key: 'config[].getApiKey'
|
151
|
-
},
|
152
|
-
{
|
153
|
-
type: 'submit',
|
154
|
-
title: 'Configure',
|
155
|
-
key: 'config[].configure'
|
156
|
-
}
|
157
|
-
]
|
158
|
-
},
|
159
|
-
{
|
160
|
-
type: 'fieldset',
|
161
|
-
key: 'config[]._bridge',
|
162
|
-
// expandable: true,
|
163
|
-
title: 'Child Bridge Accessory Settings',
|
164
|
-
items: [
|
165
|
-
{
|
166
|
-
type: 'flex',
|
167
|
-
'flex-flow': 'row',
|
168
|
-
items: [
|
169
|
-
'config[]._bridge.username',
|
170
|
-
'config[]._bridge.port'
|
171
|
-
]
|
172
|
-
},
|
173
|
-
'config[]._bridge.name',
|
174
|
-
{
|
175
|
-
type: 'flex',
|
176
|
-
'flex-flow': 'row',
|
177
|
-
items: [
|
178
|
-
'config[]._bridge.manufacturer',
|
179
|
-
'config[]._bridge.model'
|
180
|
-
]
|
181
|
-
}
|
182
|
-
]
|
183
|
-
}
|
184
|
-
]
|
185
|
-
}
|
186
|
-
]
|
187
|
-
}, {
|
188
|
-
config: pluginConfig,
|
189
|
-
},
|
190
|
-
'Gateway Settings',
|
191
|
-
'Homebridge Settings'
|
192
|
-
)
|
193
|
-
form.onChange(async (form) => {
|
194
|
-
console.log('change: %o', form)
|
195
|
-
})
|
196
|
-
form.onSubmit(async (form) => {
|
197
|
-
console.log('submit: %o', form)
|
198
|
-
})
|
199
|
-
form.onCancel(async (form) => {
|
200
|
-
console.log('cancel: %o', form)
|
201
|
-
})
|
51
|
+
<div v-if="view === 'edit-gateway'">
|
52
|
+
<h4 class="mb-2">
|
53
|
+
Configure Gateway
|
54
|
+
<span v-if="selectedGateway">
|
55
|
+
- {{ selectedGateway.name }}
|
56
|
+
</span>
|
57
|
+
</h4>
|
202
58
|
|
203
|
-
|
59
|
+
<!-- @click will call the connect method -->
|
60
|
+
<button class="btn btn-primary ml-0" @click="connect">Connect</button>
|
204
61
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
return accessory.plugin === 'homebridge-deconz' &&
|
210
|
-
accessory.context != null &&
|
211
|
-
accessory.context.className === 'Gateway'
|
212
|
-
})
|
213
|
-
const result = {}
|
214
|
-
for (const gateway of cachedGateways) {
|
215
|
-
if (gateway.context.uiPort == null) {
|
216
|
-
continue
|
217
|
-
}
|
218
|
-
const pong = await homebridge.request(
|
219
|
-
'get', { uiPort: gateway.context.uiPort, path: '/ping' }
|
220
|
-
)
|
221
|
-
if (pong === 'pong') {
|
222
|
-
result[gateway.context.host] = gateway.context
|
223
|
-
}
|
224
|
-
}
|
225
|
-
const gateways = Object.keys(result).sort()
|
226
|
-
homebridge.hideSpinner()
|
227
|
-
if (gateways.length === 0) {
|
228
|
-
homebridge.showSchemaForm()
|
229
|
-
return
|
230
|
-
}
|
231
|
-
// const form = homebridge.createForm({
|
232
|
-
// schema: {
|
233
|
-
// type: 'object',
|
234
|
-
// properties: {
|
235
|
-
// gateway: {
|
236
|
-
// title: 'Connected Gateways',
|
237
|
-
// type: 'string',
|
238
|
-
// oneOf: gateways.map((name) => {
|
239
|
-
// const config = result[name].context.config
|
240
|
-
// return {
|
241
|
-
// title: `${name}: dresden elektronik ${config.modelid} gateway v${config.swversion} / ${config.devicename} ${config.bridgeid}`,
|
242
|
-
// enum: [name]
|
243
|
-
// }
|
244
|
-
// }),
|
245
|
-
// required: true
|
246
|
-
// }
|
247
|
-
// }
|
248
|
-
// },
|
249
|
-
// layout: null,
|
250
|
-
// form: null
|
251
|
-
// }, {
|
252
|
-
// gateway: gateway != null ? gateway : gateways[0]
|
253
|
-
// }, 'Gateway Settings', 'Homebridge Settings')
|
254
|
-
const form = homebridge.createForm({
|
255
|
-
footerDisplay: 'For a detailed description, see the [wiki](https://github.com/ebaauw/homebridge-deconz/wiki/Configuration).',
|
256
|
-
schema: {
|
257
|
-
type: 'object',
|
258
|
-
properties: {
|
259
|
-
name: {
|
260
|
-
description: 'Plugin name as displayed in the Homebridge log.',
|
261
|
-
type: 'string',
|
262
|
-
required: true,
|
263
|
-
default: 'deCONZ'
|
264
|
-
},
|
265
|
-
gateways: {
|
266
|
-
title: 'Gateways',
|
267
|
-
type: 'array',
|
268
|
-
disabled: true,
|
269
|
-
items: {
|
270
|
-
type: 'object',
|
271
|
-
properties: {
|
272
|
-
host: {
|
273
|
-
description: 'Hostname and port of the deCONZ gateway.',
|
274
|
-
type: 'string'
|
275
|
-
},
|
276
|
-
expose: {
|
277
|
-
description: 'Expose gateway to HomeKit.',
|
278
|
-
type: 'boolean'
|
279
|
-
}
|
280
|
-
}
|
281
|
-
}
|
62
|
+
<!-- @click will call the getApiKey method -->
|
63
|
+
<button class="btn btn-primary" @click="getApiKey">Get API Key</button>
|
64
|
+
</div>
|
65
|
+
</div>
|
282
66
|
|
283
|
-
|
284
|
-
|
285
|
-
} //,
|
286
|
-
// layout: [
|
287
|
-
// 'name',
|
288
|
-
// {
|
289
|
-
// key: 'gateways',
|
290
|
-
// type: 'array',
|
291
|
-
// buttonText: 'Add Gateway',
|
292
|
-
// items: [
|
293
|
-
// {
|
294
|
-
// type: 'section',
|
295
|
-
// htmlClass: 'row',
|
296
|
-
// items: [
|
297
|
-
// {
|
298
|
-
// type: 'section',
|
299
|
-
// htmlClass: 'col',
|
300
|
-
// items: [
|
301
|
-
// 'gateways[].host'
|
302
|
-
// ]
|
303
|
-
// },
|
304
|
-
// {
|
305
|
-
// type: 'section',
|
306
|
-
// htmlClass: 'col',
|
307
|
-
// items: [
|
308
|
-
// {
|
309
|
-
// key: 'gateways[].expose',
|
310
|
-
// disabled: true
|
311
|
-
// }
|
312
|
-
// ]
|
313
|
-
// }
|
314
|
-
// ]
|
315
|
-
// }
|
316
|
-
// ]
|
317
|
-
// }
|
318
|
-
// ]
|
319
|
-
}, {
|
320
|
-
|
321
|
-
}, 'Gateway Settings', 'Homebridge Settings')
|
322
|
-
form.onChange(async (form) => {
|
323
|
-
// showFormGatewaySettings(result[form.gateway])
|
324
|
-
})
|
325
|
-
form.onSubmit(async (form) => {
|
326
|
-
await showFormGatewaySettings(result[form.gateways])
|
327
|
-
})
|
328
|
-
form.onCancel(() => { homebridge.showSchemaForm() })
|
329
|
-
}
|
67
|
+
<script>
|
68
|
+
const { createApp } = Vue;
|
330
69
|
|
331
|
-
|
332
|
-
homebridge.showSpinner()
|
333
|
-
const data = await homebridge.request(
|
334
|
-
'get', {
|
335
|
-
uiPort: gateway.uiPort,
|
336
|
-
path: '/gateways/' + gateway.id
|
337
|
-
}
|
338
|
-
)
|
339
|
-
const values = {}
|
340
|
-
for (const rtype in data.deviceByRidByRtype) {
|
341
|
-
values[rtype] = []
|
342
|
-
for (const rid in data.deviceByRidByRtype[rtype]) {
|
343
|
-
const device = data.deviceByRidByRtype[rtype][rid]
|
344
|
-
values[rtype].push({
|
345
|
-
title: ['', rtype, rid].join('/') + ': ' +
|
346
|
-
device.resourceBySubtype[device.primary].body.name,
|
347
|
-
enum: [device.id]
|
348
|
-
})
|
349
|
-
}
|
350
|
-
}
|
351
|
-
data.lightsDevice = values.lights[0].enum[0]
|
352
|
-
data.sensorsDevice = values.sensors[0].enum[0]
|
353
|
-
data.groupsDevice = values.groups[0].enum[0]
|
354
|
-
homebridge.hideSpinner()
|
355
|
-
const form = homebridge.createForm({
|
70
|
+
const gatewaySchema = {
|
356
71
|
schema: {
|
357
72
|
type: 'object',
|
358
73
|
properties: {
|
359
|
-
|
360
|
-
|
361
|
-
|
74
|
+
host: {
|
75
|
+
description: 'Gateway hostname and port.',
|
76
|
+
default: 'localhost:80',
|
77
|
+
type: 'string',
|
78
|
+
required: true
|
79
|
+
},
|
80
|
+
name: {
|
81
|
+
description: 'Homebridge log plugin name.',
|
82
|
+
default: 'deCONZ',
|
83
|
+
type: 'string'
|
362
84
|
},
|
363
|
-
|
364
|
-
|
365
|
-
type:
|
85
|
+
forceHttp: {
|
86
|
+
description: "Use plain http instead of https.",
|
87
|
+
type: "boolean"
|
366
88
|
},
|
367
|
-
|
368
|
-
|
369
|
-
type:
|
89
|
+
noResponse: {
|
90
|
+
description: "Report unreachable lights as <i>No Response</i> in HomeKit.",
|
91
|
+
type: "boolean"
|
370
92
|
},
|
371
|
-
|
372
|
-
|
373
|
-
type:
|
93
|
+
parallelRequests: {
|
94
|
+
description:" The number of ansynchronous requests Homebridge deCONZ sends in parallel to a deCONZ gateway. Default: 10.",
|
95
|
+
type: "integer",
|
96
|
+
minimum: 1,
|
97
|
+
maximum: 30
|
374
98
|
},
|
375
|
-
|
376
|
-
|
377
|
-
type:
|
99
|
+
stealth: {
|
100
|
+
description: "Stealth mode: don't make any calls to the Internet. Default: false.",
|
101
|
+
type: "boolean"
|
378
102
|
},
|
379
|
-
|
380
|
-
|
381
|
-
type:
|
382
|
-
|
383
|
-
|
384
|
-
condition: {
|
385
|
-
functionBody: 'return model.expose'
|
386
|
-
}
|
103
|
+
timeout: {
|
104
|
+
description: "The timeout in seconds to wait for a response from a deCONZ gateway. Default: 5.",
|
105
|
+
type: "integer",
|
106
|
+
minimum: 1,
|
107
|
+
maximum: 30
|
387
108
|
},
|
388
|
-
|
389
|
-
|
390
|
-
type:
|
391
|
-
|
392
|
-
|
109
|
+
waitTimePut: {
|
110
|
+
description: "The time, in milliseconds, to wait after sending a PUT request, before sending the next PUT request. Default: 50.",
|
111
|
+
type: "integer",
|
112
|
+
minimum: 0,
|
113
|
+
maximum: 50
|
393
114
|
},
|
394
|
-
|
395
|
-
|
396
|
-
type:
|
397
|
-
|
398
|
-
|
115
|
+
waitTimePutGroup: {
|
116
|
+
description: "The time, in milliseconds, to wait after sending a PUT request to a group, before sending the next PUT request. Default: 1000.",
|
117
|
+
type: "integer",
|
118
|
+
minimum: 0,
|
119
|
+
maximum: 1000
|
399
120
|
},
|
400
|
-
|
401
|
-
|
402
|
-
type:
|
403
|
-
|
404
|
-
|
121
|
+
waitTimeResend: {
|
122
|
+
description: "The time, in milliseconds, to wait before resending a request after an ECONNRESET or http status 503 error. Default: 300.",
|
123
|
+
type: "integer",
|
124
|
+
minimum: 100,
|
125
|
+
maximum: 1000
|
126
|
+
},
|
127
|
+
waitTimeReset: {
|
128
|
+
description: "The timeout in milliseconds, to wait before resetting a characteristic value. Default: 500.",
|
129
|
+
type: "integer",
|
130
|
+
minimum: 10,
|
131
|
+
maximum: 2000
|
132
|
+
},
|
133
|
+
waitTimeUpdate: {
|
134
|
+
description: "The time, in milliseconds, to wait for a change from HomeKit to another characteristic for the same light or group, before updating the deCONZ gateway. Default: 100.",
|
135
|
+
type: "integer",
|
136
|
+
minimum: 0,
|
137
|
+
maximum: 500
|
405
138
|
}
|
406
139
|
}
|
407
140
|
},
|
408
141
|
layout: [
|
142
|
+
"host",
|
143
|
+
"name",
|
409
144
|
{
|
410
|
-
type:
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
'logLevel',
|
415
|
-
{
|
416
|
-
type: 'flex',
|
417
|
-
'flex-flow': 'row',
|
418
|
-
title: 'Automatically Expose New',
|
419
|
-
items: [
|
420
|
-
'lights',
|
421
|
-
'sensors',
|
422
|
-
'groups',
|
423
|
-
'schedules'
|
424
|
-
],
|
425
|
-
condition: {
|
426
|
-
functionBody: 'return model.expose'
|
427
|
-
}
|
428
|
-
},
|
429
|
-
{
|
430
|
-
type: 'fieldset',
|
145
|
+
type: "fieldset",
|
146
|
+
expandable: true,
|
147
|
+
title: "Advanced Settings",
|
148
|
+
description: "Don't change these, unless you understand what you're doing.",
|
431
149
|
items: [
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
title: 'Sensors',
|
443
|
-
items: [
|
444
|
-
'sensorsDevice'
|
445
|
-
]
|
446
|
-
},
|
447
|
-
{
|
448
|
-
title: 'Groups',
|
449
|
-
items: [
|
450
|
-
'groupsDevice'
|
451
|
-
]
|
452
|
-
}
|
453
|
-
]
|
454
|
-
}
|
455
|
-
],
|
456
|
-
condition: {
|
457
|
-
functionBody: 'return model.expose'
|
458
|
-
}
|
150
|
+
"forceHttp",
|
151
|
+
"parallelRequests",
|
152
|
+
"stealth",
|
153
|
+
"timeout",
|
154
|
+
"waitTimePut",
|
155
|
+
"waitTimePutGroup",
|
156
|
+
"waitTimeResend",
|
157
|
+
"waitTimeReset",
|
158
|
+
"waitTimeUpdate"
|
159
|
+
]
|
459
160
|
}
|
460
161
|
]
|
461
|
-
}
|
462
|
-
form.onChange((form) => {})
|
463
|
-
form.onSubmit((form) => {
|
464
|
-
showFormDeviceSettings(gateway, form.lightsDevice)
|
465
|
-
})
|
466
|
-
form.onCancel((form) => {
|
467
|
-
showFormGateways(gateway.context.host)
|
468
|
-
})
|
469
|
-
}
|
162
|
+
};
|
470
163
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
164
|
+
const myApp = createApp({
|
165
|
+
/**
|
166
|
+
* This is called when the app is loaded. It's the entry point.
|
167
|
+
*/
|
168
|
+
async created() {
|
169
|
+
const config = await homebridge.getPluginConfig();
|
170
|
+
|
171
|
+
if (!config.length) {
|
172
|
+
// if no config yet, create the basic config required
|
173
|
+
this.pluginConfig = {
|
174
|
+
gateways: [],
|
175
|
+
};
|
176
|
+
} else {
|
177
|
+
// if config does exist, we pretty safely assume only one config block and take this out
|
178
|
+
this.pluginConfig = config[0];
|
179
|
+
if (!Array.isArray(this.pluginConfig.gateways)) {
|
180
|
+
this.pluginConfig.gateways = [];
|
181
|
+
}
|
182
|
+
}
|
183
|
+
},
|
184
|
+
data() {
|
185
|
+
/**
|
186
|
+
* This is reactive data - it can be accessed in any of the methods via this.key
|
187
|
+
* It can be used in the HTML directly via {{ key }}
|
188
|
+
*/
|
189
|
+
return {
|
190
|
+
view: 'gateways',
|
191
|
+
selectedGateway: null,
|
192
|
+
pluginConfig: {
|
193
|
+
gateways: [],
|
480
194
|
},
|
481
|
-
|
482
|
-
|
195
|
+
}
|
196
|
+
},
|
197
|
+
watch: {
|
198
|
+
/**
|
199
|
+
* This handler will be called whenever the object of data.pluginConfig changes
|
200
|
+
* Doing it likes this means we don't need to worry about keeping the UI in sync with plugin changes
|
201
|
+
* This will take care of everything as long as we keep data.pluginConfig correct
|
202
|
+
*/
|
203
|
+
pluginConfig: {
|
204
|
+
deep: true,
|
205
|
+
handler(newValue, oldValue) {
|
206
|
+
// need to do a deep copy to clean the object before sending it to the Homebridge UI
|
207
|
+
const config = JSON.parse(JSON.stringify(newValue))
|
208
|
+
homebridge.updatePluginConfig([config]);
|
483
209
|
}
|
210
|
+
},
|
211
|
+
},
|
212
|
+
methods: {
|
213
|
+
/**
|
214
|
+
* These are methods that can be called from the HTML.
|
215
|
+
* eg. <button @click="methodName"> or <button @click="methodName(someArg)">
|
216
|
+
*/
|
217
|
+
addGateway() {
|
218
|
+
// create an empty selected gateway
|
219
|
+
this.selectedGateway = {};
|
220
|
+
|
221
|
+
// set the view to edit-gateway
|
222
|
+
this.view = "edit-gateway";
|
223
|
+
|
224
|
+
// start the form
|
225
|
+
const gatewayForm = homebridge.createForm(gatewaySchema, {}, 'OK', 'Cancel');
|
226
|
+
|
227
|
+
gatewayForm.onChange((form) => {
|
228
|
+
// push changes as they happen into the selectedGateway object
|
229
|
+
this.selectedGateway = form;
|
230
|
+
});
|
231
|
+
|
232
|
+
gatewayForm.onSubmit((form) => {
|
233
|
+
// on save, push the new gateway object into the pluginConfig.gateways array
|
234
|
+
this.pluginConfig.gateways.push(form);
|
235
|
+
this.view = "gateways";
|
236
|
+
gatewayForm.end();
|
237
|
+
});
|
238
|
+
|
239
|
+
gatewayForm.onCancel((form) => {
|
240
|
+
this.view = "gateways";
|
241
|
+
gatewayForm.end();
|
242
|
+
});
|
243
|
+
},
|
244
|
+
editGateway(gateway) {
|
245
|
+
this.view = "edit-gateway";
|
246
|
+
|
247
|
+
// create a copy of the current gateway object
|
248
|
+
const source = JSON.parse(JSON.stringify(gateway))
|
249
|
+
|
250
|
+
// set the selectedGateway so we can access it in the template easily
|
251
|
+
this.selectedGateway = source;
|
252
|
+
|
253
|
+
// load the form
|
254
|
+
const gatewayForm = homebridge.createForm(gatewaySchema, source, 'OK', 'Cancel');
|
255
|
+
|
256
|
+
// on changes, update the selectedGateway
|
257
|
+
gatewayForm.onChange((form) => {
|
258
|
+
this.selectedGateway = form;
|
259
|
+
});
|
260
|
+
|
261
|
+
// on save, update the gateway object in the pluginConfig.gateways array
|
262
|
+
gatewayForm.onSubmit((form) => {
|
263
|
+
Object.assign(gateway, form);
|
264
|
+
this.view = "gateways";
|
265
|
+
gatewayForm.end();
|
266
|
+
});
|
267
|
+
|
268
|
+
// on cancel, just go back to the gateways view, not updating the pluginConfig.gateways array with any changes
|
269
|
+
gatewayForm.onCancel((form) => {
|
270
|
+
this.view = "gateways";
|
271
|
+
gatewayForm.end();
|
272
|
+
});
|
273
|
+
},
|
274
|
+
deleteGateway(index) {
|
275
|
+
// on delete, just remove it from the gateways array
|
276
|
+
this.pluginConfig.gateways.splice(index, 1);
|
277
|
+
},
|
278
|
+
connect() {
|
279
|
+
console.log('connect clicked for ', this.selectedGateway);
|
280
|
+
},
|
281
|
+
getApiKey() {
|
282
|
+
console.log('get api key clicked for ', this.selectedGateway);
|
484
283
|
}
|
284
|
+
|
485
285
|
}
|
486
|
-
}
|
487
|
-
gateway: gateway.context.host,
|
488
|
-
device: device
|
489
|
-
}, 'OK', 'Cancel')
|
490
|
-
form.onChange((form) => {})
|
491
|
-
form.onSubmit((form) => {
|
492
|
-
showFormGatewaySettings(gateway)
|
493
|
-
})
|
494
|
-
form.onCancel((form) => {
|
495
|
-
showFormGatewaySettings(gateway)
|
496
|
-
})
|
497
|
-
}
|
286
|
+
});
|
498
287
|
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
console.
|
504
|
-
|
505
|
-
})
|
288
|
+
/**
|
289
|
+
* Watch for the ready event, then start the vue app
|
290
|
+
*/
|
291
|
+
homebridge.addEventListener('ready', async () => {
|
292
|
+
console.log('ready')
|
293
|
+
myApp.mount('#app')
|
294
|
+
});
|
506
295
|
</script>
|