homebridge-deconz 0.0.26 → 0.1.0
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/config.schema.json +0 -7
- package/lib/Deconz/ApiError.js +12 -5
- package/lib/Deconz/ApiResponse.js +10 -7
- package/lib/Deconz/Resource.js +27 -4
- package/lib/DeconzAccessory/AirPurifier.js +0 -2
- package/lib/DeconzAccessory/Gateway.js +297 -147
- package/lib/DeconzAccessory/Light.js +32 -61
- package/lib/DeconzAccessory/Sensor.js +89 -1
- package/lib/DeconzAccessory/Thermostat.js +5 -8
- package/lib/DeconzAccessory/WarningDevice.js +2 -13
- package/lib/DeconzAccessory/WindowCovering.js +0 -2
- package/lib/DeconzAccessory/index.js +123 -13
- package/lib/DeconzPlatform.js +12 -20
- package/lib/DeconzService/AirPressure.js +8 -1
- package/lib/DeconzService/AirPurifier.js +2 -2
- package/lib/DeconzService/AirQuality.js +1 -1
- package/lib/DeconzService/Alarm.js +1 -1
- package/lib/DeconzService/Battery.js +21 -10
- package/lib/DeconzService/Button.js +1 -0
- package/lib/DeconzService/CarbonMonoxide.js +1 -1
- package/lib/DeconzService/Consumption.js +3 -1
- package/lib/DeconzService/Contact.js +1 -25
- package/lib/DeconzService/Daylight.js +22 -19
- package/lib/DeconzService/Flag.js +1 -1
- package/lib/DeconzService/Gateway.js +48 -0
- package/lib/DeconzService/Humidity.js +1 -1
- package/lib/DeconzService/Leak.js +1 -1
- package/lib/DeconzService/Light.js +121 -74
- package/lib/DeconzService/LightLevel.js +3 -3
- package/lib/DeconzService/LightsResource.js +24 -18
- package/lib/DeconzService/Motion.js +3 -9
- package/lib/DeconzService/Power.js +2 -1
- package/lib/DeconzService/Status.js +1 -1
- package/lib/DeconzService/WindowCovering.js +14 -4
- package/lib/DeconzService/index.js +14 -3
- package/package.json +6 -6
- package/lib/DeconzAccessory/Contact.js +0 -54
- package/lib/DeconzAccessory/Motion.js +0 -54
- package/lib/DeconzAccessory/Temperature.js +0 -63
- package/lib/DeconzService/DeviceSettings.js +0 -56
- package/lib/DeconzService/GatewaySettings.js +0 -175
@@ -50,24 +50,15 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
50
50
|
* @type {Object}
|
51
51
|
* @property {Object} config - Response body of unauthenticated
|
52
52
|
* GET `/config` (from {@link DeconzDiscovery#config config()}.
|
53
|
-
* @property {Object.<string, boolean>} blacklist - Map of blacklisted
|
54
|
-
* devices.
|
55
53
|
* @property {Object} fullState - The gateway's full state, from the
|
56
54
|
* last time the gateway was polled.
|
57
|
-
* @property {Object}
|
55
|
+
* @property {Object.<String, Object>} settingsById - The persisted settings, maintained through
|
58
56
|
* the Homebridge UI.
|
59
57
|
*/
|
60
58
|
this.context // eslint-disable-line no-unused-expressions
|
61
|
-
this.context.host = params.host
|
62
59
|
this.context.config = params.config
|
63
60
|
if (this.context.settingsById == null) {
|
64
61
|
this.context.settingsById = {}
|
65
|
-
// migration
|
66
|
-
for (const id in this.context.blacklist) {
|
67
|
-
this.context.settingsById[id] = { expose: false }
|
68
|
-
}
|
69
|
-
delete this.context.blacklist
|
70
|
-
// end migration
|
71
62
|
}
|
72
63
|
if (this.context.fullState != null) {
|
73
64
|
this.analyseFullState(this.context.fullState, {
|
@@ -83,6 +74,41 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
83
74
|
this.client.apiKey = value
|
84
75
|
})
|
85
76
|
|
77
|
+
this.addPropertyDelegate({
|
78
|
+
key: 'autoExpose',
|
79
|
+
value: { groups: false, lights: true, schedules: false, sensors: true },
|
80
|
+
silent: true
|
81
|
+
}).on('didSet', async () => {
|
82
|
+
this.pollNext = true
|
83
|
+
})
|
84
|
+
|
85
|
+
this.addPropertyDelegate({
|
86
|
+
key: 'brightnessAdjustment',
|
87
|
+
value: 1,
|
88
|
+
silent: true
|
89
|
+
})
|
90
|
+
|
91
|
+
this.addPropertyDelegate({
|
92
|
+
key: 'expose',
|
93
|
+
value: true,
|
94
|
+
silent: true
|
95
|
+
}).on('didSet', async (value) => {
|
96
|
+
try {
|
97
|
+
this.service.values.statusActive = value
|
98
|
+
if (value) {
|
99
|
+
await this.connect()
|
100
|
+
} else {
|
101
|
+
await this.reset()
|
102
|
+
}
|
103
|
+
} catch (error) { this.error(error) }
|
104
|
+
})
|
105
|
+
|
106
|
+
this.addPropertyDelegate({
|
107
|
+
key: 'heartrate',
|
108
|
+
value: 30,
|
109
|
+
silent: true
|
110
|
+
})
|
111
|
+
|
86
112
|
this.addPropertyDelegate({
|
87
113
|
key: 'host',
|
88
114
|
value: params.host,
|
@@ -99,11 +125,54 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
99
125
|
this.values.host = params.host
|
100
126
|
|
101
127
|
this.addPropertyDelegate({
|
102
|
-
key: '
|
103
|
-
value:
|
128
|
+
key: 'periodicEvents',
|
129
|
+
value: true,
|
104
130
|
silent: true
|
105
|
-
})
|
106
|
-
|
131
|
+
})
|
132
|
+
|
133
|
+
this.addPropertyDelegate({
|
134
|
+
key: 'restart',
|
135
|
+
value: false,
|
136
|
+
silent: true
|
137
|
+
}).on('didSet', async (value) => {
|
138
|
+
if (value) {
|
139
|
+
try {
|
140
|
+
await this.client.restart()
|
141
|
+
this.values.search = false
|
142
|
+
this.values.unlock = false
|
143
|
+
return
|
144
|
+
} catch (error) { this.warn(error) }
|
145
|
+
}
|
146
|
+
})
|
147
|
+
|
148
|
+
this.addPropertyDelegate({
|
149
|
+
key: 'search',
|
150
|
+
value: false,
|
151
|
+
silent: true
|
152
|
+
}).on('didSet', async (value) => {
|
153
|
+
if (value) {
|
154
|
+
try {
|
155
|
+
await this.client.search()
|
156
|
+
await homebridgeLib.timeout(120000)
|
157
|
+
this.values.search = false
|
158
|
+
return
|
159
|
+
} catch (error) { this.warn(error) }
|
160
|
+
}
|
161
|
+
})
|
162
|
+
|
163
|
+
this.addPropertyDelegate({
|
164
|
+
key: 'unlock',
|
165
|
+
value: false,
|
166
|
+
silent: true
|
167
|
+
}).on('didSet', async (value) => {
|
168
|
+
if (value) {
|
169
|
+
try {
|
170
|
+
await this.client.unlock()
|
171
|
+
await homebridgeLib.timeout(60000)
|
172
|
+
this.values.unlock = false
|
173
|
+
return
|
174
|
+
} catch (error) { this.warn(error) }
|
175
|
+
}
|
107
176
|
})
|
108
177
|
|
109
178
|
this.addPropertyDelegate({
|
@@ -142,20 +211,14 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
142
211
|
*/
|
143
212
|
this.exposeErrorById = {}
|
144
213
|
|
145
|
-
/** Map of services to un-blacklist a gateway device.
|
146
|
-
* @type {Object<string, DeconzService.DeviceSettings>}
|
147
|
-
*/
|
148
|
-
this.serviceById = {}
|
149
|
-
|
150
214
|
/** The service delegate for the Gateway Settings settings.
|
151
215
|
* @type {DeconzService.GatewaySettings}
|
152
216
|
*/
|
153
|
-
this.service = new DeconzService.
|
154
|
-
name: this.name + ' Gateway
|
217
|
+
this.service = new DeconzService.Gateway(this, {
|
218
|
+
name: this.name + ' Gateway',
|
155
219
|
primaryService: true,
|
156
220
|
host: params.host
|
157
221
|
})
|
158
|
-
this.manageLogLevel(this.service.characteristicDelegate('logLevel'))
|
159
222
|
|
160
223
|
/** The service delegate for the Stateless Programmable Switch service.
|
161
224
|
* @type {DeconzService.Button}
|
@@ -241,7 +304,6 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
241
304
|
*/
|
242
305
|
async found (host, config) {
|
243
306
|
try {
|
244
|
-
this.context.host = host
|
245
307
|
this.values.host = host
|
246
308
|
this.context.config = config
|
247
309
|
this.values.software = config.swversion
|
@@ -255,6 +317,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
255
317
|
}
|
256
318
|
|
257
319
|
async shutdown () {
|
320
|
+
this.service.values.statusActive = false
|
258
321
|
return this.wsClient.close()
|
259
322
|
}
|
260
323
|
|
@@ -264,14 +327,14 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
264
327
|
async heartbeat (beat) {
|
265
328
|
beat -= this.initialBeat
|
266
329
|
try {
|
267
|
-
if (beat > 0) {
|
330
|
+
if (this.values.periodicEvents && beat > 0) {
|
268
331
|
for (const { rate, event } of periodicEvents) {
|
269
332
|
if (beat % rate === 0) {
|
270
333
|
this.buttonService.update(event)
|
271
334
|
}
|
272
335
|
}
|
273
336
|
}
|
274
|
-
if (beat - this.pollBeat >= this.
|
337
|
+
if (beat - this.pollBeat >= this.values.heartrate || this.pollNext) {
|
275
338
|
this.pollBeat = beat
|
276
339
|
await this.poll()
|
277
340
|
}
|
@@ -390,13 +453,13 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
390
453
|
})
|
391
454
|
.on('added', (rtype, rid, body) => {
|
392
455
|
this.vdebug('/%s/%d: added: %j', rtype, rid, body)
|
393
|
-
if (this.
|
456
|
+
if (this.values.autoExpose[rtype]) {
|
394
457
|
this.pollNext = true
|
395
458
|
}
|
396
459
|
})
|
397
460
|
.on('deleted', (rtype, rid) => {
|
398
461
|
this.vdebug('/%s/%d: deleted', rtype, rid)
|
399
|
-
if (this.
|
462
|
+
if (this.values.autoExpose[rtype]) {
|
400
463
|
this.pollNext = true
|
401
464
|
}
|
402
465
|
})
|
@@ -418,7 +481,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
418
481
|
* gateway, and analyse the full state.
|
419
482
|
*/
|
420
483
|
async connect (retry = 0) {
|
421
|
-
if (!this.
|
484
|
+
if (!this.values.expose) {
|
422
485
|
this.warn('unlock gateway and set Expose to obtain an API key')
|
423
486
|
return
|
424
487
|
}
|
@@ -428,6 +491,8 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
428
491
|
await this.client.getApiKey('homebridge-deconz')
|
429
492
|
}
|
430
493
|
this.wsClient.listen()
|
494
|
+
this.service.values.restart = false
|
495
|
+
this.service.values.statusActive = true
|
431
496
|
this.checkApiKeys = true
|
432
497
|
for (const id in this.exposeErrorById) {
|
433
498
|
this.resetExposeError(id)
|
@@ -443,7 +508,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
443
508
|
return this.connect(retry + 1)
|
444
509
|
}
|
445
510
|
this.error(error)
|
446
|
-
this.
|
511
|
+
this.values.expose = false
|
447
512
|
}
|
448
513
|
}
|
449
514
|
|
@@ -472,26 +537,14 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
472
537
|
this.deleteAccessory(id)
|
473
538
|
}
|
474
539
|
}
|
475
|
-
for (const id in this.serviceById) {
|
476
|
-
this.deleteService(id)
|
477
|
-
}
|
478
540
|
this.exposeErrors = {}
|
479
541
|
this.context.settingsById = {}
|
480
|
-
this.context.settingsById[this.id] = {
|
481
|
-
autoExposeGroups: false,
|
482
|
-
autoExposeLights: false,
|
483
|
-
autoExposeSensors: false,
|
484
|
-
autoExposeSchedules: false,
|
485
|
-
logLevel: 2
|
486
|
-
}
|
487
542
|
this.context.fullState = null
|
488
543
|
this.context.migration = null
|
489
|
-
this.
|
490
|
-
this.
|
491
|
-
|
492
|
-
|
493
|
-
this.service.values.logLevel = 2
|
494
|
-
this.values.rtypes = []
|
544
|
+
this.values.logLevel = 2
|
545
|
+
this.values.autoExpose = {
|
546
|
+
groups: false, lights: true, schedules: false, sensors: true
|
547
|
+
}
|
495
548
|
} catch (error) { this.error(error) }
|
496
549
|
}
|
497
550
|
|
@@ -516,6 +569,25 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
516
569
|
this.pollNext = true
|
517
570
|
}
|
518
571
|
|
572
|
+
/** Re-expose an accessory.
|
573
|
+
*
|
574
|
+
* Delete the accessory delegate, but keep the HAP accessory, including
|
575
|
+
* the persisted context.
|
576
|
+
* The delegate will be re-created when the gateway is next polled.
|
577
|
+
* @params {string} id - The device ID.
|
578
|
+
* @params {boolean} expose - Set to `false` to blacklist the device.
|
579
|
+
*/
|
580
|
+
reExposeAccessory (id) {
|
581
|
+
if (id === this.id) {
|
582
|
+
throw new RangeError(`${id}: gateway ID`)
|
583
|
+
}
|
584
|
+
if (this.accessoryById[id] == null) {
|
585
|
+
throw new RangeError(`${id}: unknown accessory ID`)
|
586
|
+
}
|
587
|
+
this.deleteAccessory(id, true)
|
588
|
+
this.pollNext = true
|
589
|
+
}
|
590
|
+
|
519
591
|
/** Add the accessory for the device.
|
520
592
|
* @params {string} id - The device ID.
|
521
593
|
* @return {?DeconzAccessory} - The accessory delegate.
|
@@ -531,7 +603,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
531
603
|
const device = this.deviceById[id]
|
532
604
|
delete this.exposeErrorById[id]
|
533
605
|
const { body } = device.resource
|
534
|
-
this.
|
606
|
+
this.debug('%s: add accessory', body.name)
|
535
607
|
let { serviceName } = device.resource
|
536
608
|
if (DeconzAccessory[serviceName] == null) {
|
537
609
|
// this.warn('%s: %s: not yet supported %s type', body.name, body.type, rtype)
|
@@ -548,19 +620,23 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
548
620
|
return this.accessoryById[id]
|
549
621
|
}
|
550
622
|
|
551
|
-
/** Delete the accessory
|
623
|
+
/** Delete the accessory delegate and associated HomeKit accessory.
|
552
624
|
* @params {string} id - The device ID.
|
625
|
+
* @params {boolean} [delegateOnly=false] - Delete the delegate, but keep the
|
626
|
+
* associated HomeKit accessory (including context).
|
553
627
|
*/
|
554
|
-
deleteAccessory (id) {
|
628
|
+
deleteAccessory (id, delegateOnly = false) {
|
555
629
|
if (id === this.id) {
|
556
630
|
throw new RangeError(`${id}: gateway ID`)
|
557
631
|
}
|
558
632
|
if (this.accessoryById[id] != null) {
|
559
|
-
this.log('%s: delete accessory', this.accessoryById[id].name)
|
560
633
|
this.monitorResources(this.accessoryById[id], false)
|
561
|
-
this.accessoryById[id].
|
634
|
+
this.log('%s: delete accessory', this.accessoryById[id].name)
|
635
|
+
this.accessoryById[id].destroy(delegateOnly)
|
562
636
|
delete this.accessoryById[id]
|
563
|
-
if (this.exposeErrorById[id]
|
637
|
+
if (this.exposeErrorById[id] != null) {
|
638
|
+
delete this.exposeErrorById[id]
|
639
|
+
} else if (!delegateOnly) {
|
564
640
|
const id = Object.keys(this.exposeErrorById)[0]
|
565
641
|
if (id != null) {
|
566
642
|
this.log(
|
@@ -569,8 +645,6 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
569
645
|
this.deleteAccessory(id)
|
570
646
|
this.deleteService(id)
|
571
647
|
}
|
572
|
-
} else {
|
573
|
-
delete this.exposeErrorById[id]
|
574
648
|
}
|
575
649
|
}
|
576
650
|
}
|
@@ -607,86 +681,182 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
607
681
|
this.deleteService(id)
|
608
682
|
}
|
609
683
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
throw new RangeError(`${id}: gateway ID`)
|
617
|
-
}
|
618
|
-
if (this.deviceById[id] == null) {
|
619
|
-
throw new RangeError(`${id}: unknown device ID`)
|
684
|
+
// ===========================================================================
|
685
|
+
|
686
|
+
_deviceToMap (id, details = false) {
|
687
|
+
const device = this.deviceById[id]
|
688
|
+
if (device == null) {
|
689
|
+
return { status: 404 } // Not Found
|
620
690
|
}
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
691
|
+
const body = {
|
692
|
+
expose: details ? undefined : this.accessoryById[device.id] != null,
|
693
|
+
id: details ? device.id : undefined,
|
694
|
+
manufacturer: device.resource.manufacturer,
|
695
|
+
model: device.resource.model,
|
696
|
+
name: device.resource.body.name,
|
697
|
+
resources: device.rpaths,
|
698
|
+
settings: details
|
699
|
+
? {
|
700
|
+
expose: this.accessoryById[device.id] != null,
|
701
|
+
outlet: undefined, // expose as _Outlet_
|
702
|
+
switch: undefined, // expose as _Switch
|
703
|
+
valve: undefined // expose as _Valve_
|
704
|
+
}
|
705
|
+
: undefined,
|
706
|
+
type: device.resource.rtype,
|
707
|
+
zigbee: device.zigbee
|
634
708
|
}
|
635
|
-
return
|
709
|
+
return { status: 200, body }
|
636
710
|
}
|
637
711
|
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
712
|
+
async onUiGet (path) {
|
713
|
+
this.debug('ui request: GET %s', path.join('/'))
|
714
|
+
if (path.length === 0) {
|
715
|
+
const body = {
|
716
|
+
host: this.values.host,
|
717
|
+
id: this.id,
|
718
|
+
manufacturer: this.values.manufacturer,
|
719
|
+
model: this.values.model,
|
720
|
+
name: this.name,
|
721
|
+
settings: {
|
722
|
+
autoExpose: this.values.autoExpose,
|
723
|
+
brightnessAdjustment: this.values.brightnessAdjustment * 100,
|
724
|
+
expose: this.values.expose,
|
725
|
+
heartrate: this.values.heartrate,
|
726
|
+
logLevel: this.values.logLevel,
|
727
|
+
periodicEvents: this.values.periodicEvents,
|
728
|
+
restart: this.values.restart,
|
729
|
+
search: this.values.search,
|
730
|
+
unlock: this.values.unlock
|
731
|
+
}
|
732
|
+
}
|
733
|
+
return { status: 200, body }
|
644
734
|
}
|
645
|
-
if (
|
646
|
-
|
647
|
-
|
735
|
+
if (path[0] === 'accessories') {
|
736
|
+
if (path.length === 1) {
|
737
|
+
const body = {}
|
738
|
+
for (const id of Object.keys(this.accessoryById).sort()) {
|
739
|
+
body[id] = this.accessoryById[id].onUiGet().body
|
740
|
+
}
|
741
|
+
return { status: 200, body }
|
742
|
+
}
|
743
|
+
if (path.length === 2) {
|
744
|
+
const id = path[1]
|
745
|
+
if (this.accessoryById[id] == null) {
|
746
|
+
return { status: 404 } // Not Found
|
747
|
+
}
|
748
|
+
return this.accessoryById[id].onUiGet(true)
|
749
|
+
}
|
648
750
|
}
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
this.debug('ui request: GET %s', a.join('/'))
|
655
|
-
if (a.length === 0) {
|
656
|
-
return {
|
657
|
-
status: 200,
|
658
|
-
body: {
|
659
|
-
expose: this.service.values.expose,
|
660
|
-
groups: this.service.values.groups,
|
661
|
-
heartrate: this.service.values.heartrate,
|
662
|
-
lights: this.service.values.lights,
|
663
|
-
logLevel: this.service.values.logLevel,
|
664
|
-
schedules: this.service.values.schedules,
|
665
|
-
sensors: this.service.values.sensors
|
666
|
-
// deviceByRidByRtype: this.deviceByRidByRtype
|
751
|
+
if (path[0] === 'devices') {
|
752
|
+
if (path.length === 1) {
|
753
|
+
const body = {}
|
754
|
+
for (const id of Object.keys(this.deviceById).sort()) {
|
755
|
+
body[id] = this._deviceToMap(id).body
|
667
756
|
}
|
757
|
+
return { status: 200, body }
|
758
|
+
}
|
759
|
+
if (path.length === 2) {
|
760
|
+
return this._deviceToMap(path[1], true)
|
668
761
|
}
|
669
762
|
}
|
670
|
-
|
671
|
-
|
763
|
+
return { status: 403 } // Forbidden
|
764
|
+
}
|
765
|
+
|
766
|
+
async onUiPut (path, body) {
|
767
|
+
this.debug('ui request: PUT %s %j', path.join('/'), body)
|
768
|
+
if (path.length === 0) {
|
769
|
+
return { status: 405 } // Method Not Allowed
|
672
770
|
}
|
673
|
-
if (
|
674
|
-
|
771
|
+
if (path[0] === 'settings') {
|
772
|
+
const settings = {}
|
773
|
+
const optionParser = new homebridgeLib.OptionParser(settings, true)
|
774
|
+
optionParser
|
775
|
+
.on('userInputError', (error) => {
|
776
|
+
this.warn(error)
|
777
|
+
})
|
778
|
+
.objectKey('autoExpose')
|
779
|
+
.intKey('brightnessAdjustment', 10, 100)
|
780
|
+
.boolKey('expose')
|
781
|
+
.intKey('heartrate', 1, 60)
|
782
|
+
.intKey('logLevel', 0, 3)
|
783
|
+
.boolKey('periodicEvents')
|
784
|
+
.boolKey('restart')
|
785
|
+
.boolKey('search')
|
786
|
+
.boolKey('unlock')
|
787
|
+
await optionParser.parse(body)
|
788
|
+
|
789
|
+
const responseBody = {}
|
790
|
+
for (const key in settings) {
|
791
|
+
switch (key) {
|
792
|
+
case 'autoExpose':
|
793
|
+
for (const rtype in settings[key]) {
|
794
|
+
if (this.values[key][rtype] == null) {
|
795
|
+
this.warn('ui error: %s.%s: invalid key', key, rtype)
|
796
|
+
continue
|
797
|
+
}
|
798
|
+
try {
|
799
|
+
settings[key][rtype] = homebridgeLib.OptionParser.toBool(
|
800
|
+
key + '.' + rtype, settings[key][rtype], true
|
801
|
+
)
|
802
|
+
} catch (error) {
|
803
|
+
this.warn(error)
|
804
|
+
continue
|
805
|
+
}
|
806
|
+
this.values[key][rtype] = settings[key][rtype]
|
807
|
+
if (responseBody[key] == null) {
|
808
|
+
responseBody[key] = {}
|
809
|
+
}
|
810
|
+
responseBody[key][rtype] = this.values[key][rtype]
|
811
|
+
}
|
812
|
+
break
|
813
|
+
case 'brightnessAdjustment':
|
814
|
+
this.values[key] = settings[key] / 100
|
815
|
+
responseBody[key] = this.values[key]
|
816
|
+
break
|
817
|
+
case 'expose':
|
818
|
+
case 'heartrate':
|
819
|
+
case 'logLevel':
|
820
|
+
case 'periodicEvents':
|
821
|
+
case 'restart':
|
822
|
+
case 'search':
|
823
|
+
case 'unlock':
|
824
|
+
this.values[key] = settings[key]
|
825
|
+
responseBody[key] = this.values[key]
|
826
|
+
break
|
827
|
+
default:
|
828
|
+
break
|
829
|
+
}
|
830
|
+
}
|
831
|
+
return { status: 200, body: responseBody }
|
675
832
|
}
|
676
|
-
if (
|
677
|
-
|
833
|
+
if (path[0] === 'accessories') {
|
834
|
+
if (path.length === 3 && path[2] === 'settings') {
|
835
|
+
const id = path[1]
|
836
|
+
if (this.accessoryById[id] == null) {
|
837
|
+
return { status: 404 } // Not Found
|
838
|
+
}
|
839
|
+
return this.accessoryById[id].onUiPut(body)
|
840
|
+
}
|
841
|
+
return { status: 405 } // Method Not Allowed
|
678
842
|
}
|
679
|
-
if (
|
680
|
-
|
843
|
+
if (path[0] === 'devices') {
|
844
|
+
if (path.length === 3 && path[2] === 'settings') {
|
845
|
+
const id = path[1]
|
846
|
+
if (this.deviceById[id] == null) {
|
847
|
+
return { status: 404 } // Not Found
|
848
|
+
}
|
849
|
+
if (body.expose != null) {
|
850
|
+
this.exposeDevice(id, body.expose)
|
851
|
+
return { status: 200, body: { expose: body.expose } }
|
852
|
+
}
|
853
|
+
return { status: 200 }
|
854
|
+
}
|
855
|
+
return { status: 405 } // Method Not Allowed
|
681
856
|
}
|
682
857
|
return { status: 403 } // Forbidden
|
683
858
|
}
|
684
859
|
|
685
|
-
async onUiPut (a, body) {
|
686
|
-
this.debug('ui request: PUT %s %j', a.join('/'), body)
|
687
|
-
return { status: 501 } // Not Implented
|
688
|
-
}
|
689
|
-
|
690
860
|
// ===========================================================================
|
691
861
|
|
692
862
|
/** Poll the gateway.
|
@@ -709,24 +879,24 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
709
879
|
} else {
|
710
880
|
const config = await this.client.get('/config')
|
711
881
|
if (config.bridgeid === this.id && config.UTC == null) {
|
712
|
-
this.
|
882
|
+
this.values.expose = false
|
713
883
|
this.values.apiKey = null
|
714
884
|
await this.wsClient.close()
|
715
885
|
return
|
716
886
|
}
|
717
887
|
this.context.fullState.config = config
|
718
888
|
if (
|
719
|
-
this.
|
720
|
-
this.
|
889
|
+
this.values.autoExpose.lights || this.nDevicesByRtype.lights > 0 ||
|
890
|
+
this.values.autoExpose.sensors || this.nDevicesByRtype.sensors > 0
|
721
891
|
) {
|
722
892
|
this.context.fullState.lights = await this.client.get('/lights')
|
723
893
|
this.context.fullState.sensors = await this.client.get('/sensors')
|
724
894
|
}
|
725
|
-
if (this.
|
895
|
+
if (this.values.autoExpose.groups || this.nDevicesByRtype.groups > 0) {
|
726
896
|
this.context.fullState.groups = await this.client.get('/groups')
|
727
897
|
this.context.fullState.groups[0] = await this.client.get('/groups/0')
|
728
898
|
}
|
729
|
-
if (this.
|
899
|
+
if (this.values.autoExpose.schedules) {
|
730
900
|
this.context.fullState.schedules = await this.client.get('/schedules')
|
731
901
|
}
|
732
902
|
}
|
@@ -870,7 +1040,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
870
1040
|
) {
|
871
1041
|
delete this.context.settingsById[id]
|
872
1042
|
this.deleteAccessory(id)
|
873
|
-
this.deleteService(id)
|
1043
|
+
// this.deleteService(id)
|
874
1044
|
changed = true
|
875
1045
|
} else {
|
876
1046
|
/** Emitted when the gateway has been polled.
|
@@ -882,43 +1052,23 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
882
1052
|
} catch (error) { this.error(error) }
|
883
1053
|
}
|
884
1054
|
|
885
|
-
|
886
|
-
for (const id in this.serviceById) {
|
887
|
-
try {
|
888
|
-
if (
|
889
|
-
this.deviceById[id] == null ||
|
890
|
-
!this.values.rtypes.includes(this.deviceById[id].resource.rtype)
|
891
|
-
) {
|
892
|
-
delete this.exposeErrorById[id]
|
893
|
-
this.deleteService(id)
|
894
|
-
changed = true
|
895
|
-
}
|
896
|
-
} catch (error) { this.error(error) }
|
897
|
-
}
|
898
|
-
|
899
|
-
for (const rtype of this.values.rtypes) {
|
1055
|
+
for (const rtype of rtypes) {
|
900
1056
|
this.vdebug('analysing %s devices...', rtype)
|
901
1057
|
const rids = Object.keys(this.deviceByRidByRtype[rtype]).sort()
|
902
1058
|
for (const rid of rids) {
|
903
1059
|
try {
|
904
1060
|
const { id } = this.deviceByRidByRtype[rtype][rid]
|
905
1061
|
if (this.context.settingsById[id] == null) {
|
906
|
-
this.context.settingsById[id] = {
|
1062
|
+
this.context.settingsById[id] = {
|
1063
|
+
expose: this.values.autoExpose[rtype]
|
1064
|
+
}
|
907
1065
|
}
|
908
1066
|
if (this.context.settingsById[id].expose) {
|
909
1067
|
if (this.accessoryById[id] == null) {
|
910
1068
|
this.addAccessory(id)
|
911
1069
|
changed = true
|
912
1070
|
}
|
913
|
-
if (this.serviceById[id] != null) {
|
914
|
-
this.deleteService(id)
|
915
|
-
changed = true
|
916
|
-
}
|
917
1071
|
} else {
|
918
|
-
if (this.serviceById[id] == null) {
|
919
|
-
this.addService(id)
|
920
|
-
changed = true
|
921
|
-
}
|
922
1072
|
if (this.accessoryById[id] != null) {
|
923
1073
|
this.deleteAccessory(id)
|
924
1074
|
changed = true
|