homebridge-deconz 0.0.13 → 0.0.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/README.md +6 -4
- package/cli/deconz.js +2 -2
- package/config.schema.json +2 -2
- package/{homebridge-deconz.png → homebridge-ui/public/homebridge-deconz.png} +0 -0
- package/homebridge-ui/public/index.html +488 -8
- package/homebridge-ui/server.js +40 -25
- package/lib/Deconz/ApiClient.js +1 -1
- package/lib/Deconz/Discovery.js +2 -2
- package/lib/Deconz/Resource.js +3 -3
- package/lib/DeconzAccessory/Gateway.js +79 -28
- package/lib/DeconzAccessory/index.js +1 -1
- package/lib/DeconzPlatform.js +81 -15
- package/lib/DeconzService/Consumption.js +1 -1
- package/lib/DeconzService/Light.js +50 -36
- package/lib/DeconzService/Motion.js +1 -1
- package/lib/DeconzService/Power.js +1 -1
- package/lib/DeconzService/Switch.js +2 -2
- package/lib/DeconzService/Temperature.js +1 -0
- package/lib/DeconzService/Thermostat.js +2 -1
- package/lib/DeconzService/WindowCovering.js +8 -15
- package/lib/DeconzService/index.js +3 -0
- package/package.json +5 -6
package/lib/Deconz/Resource.js
CHANGED
@@ -256,7 +256,7 @@ class Resource {
|
|
256
256
|
get prio () {
|
257
257
|
if (this.rtype === 'groups') return -1
|
258
258
|
if (this.rtype === 'lights') return this.endpoint
|
259
|
-
return sensorsPrios.indexOf(this.
|
259
|
+
return sensorsPrios.indexOf(this.serviceName)
|
260
260
|
}
|
261
261
|
|
262
262
|
/** The resource path of the resource, e.g. `/lights/1`.
|
@@ -270,7 +270,7 @@ class Resource {
|
|
270
270
|
* corresponding HomeKit service, or `null` for unsupported and unknown
|
271
271
|
* resources.
|
272
272
|
*
|
273
|
-
* This is derived from the resource type and`type` in the resource body.
|
273
|
+
* This is derived from the resource type and `type` in the resource body.
|
274
274
|
* @type {string}
|
275
275
|
*/
|
276
276
|
get serviceName () {
|
@@ -656,7 +656,7 @@ class Resource {
|
|
656
656
|
buttons.push([5, 'Next', SINGLE | LONG])
|
657
657
|
break
|
658
658
|
case 'TRADFRI wireless dimmer':
|
659
|
-
if (this.
|
659
|
+
if (this.body.mode === 1) {
|
660
660
|
buttons.push([1, 'Turn Right', SINGLE | LONG])
|
661
661
|
buttons.push([2, 'Turn Left', SINGLE | LONG])
|
662
662
|
} else {
|
@@ -44,6 +44,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
44
44
|
|
45
45
|
this.gateway = this
|
46
46
|
this.id = params.config.bridgeid
|
47
|
+
this.recommendedSoftware = this.platform.packageJson.engines.deCONZ
|
47
48
|
|
48
49
|
/** Persisted properties.
|
49
50
|
* @type {Object}
|
@@ -109,6 +110,9 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
109
110
|
'%s %s gateway v%s', this.values.manufacturer, this.values.model,
|
110
111
|
this.values.software
|
111
112
|
)
|
113
|
+
if (this.values.software !== this.recommendedSoftware) {
|
114
|
+
this.warn('recommended version: deCONZ v%s', this.recommendedSoftware)
|
115
|
+
}
|
112
116
|
|
113
117
|
/** Map of Accessory delegates by id for the gateway.
|
114
118
|
* @type {Object<string, DeconzAccessory.Device>}
|
@@ -157,7 +161,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
157
161
|
this.heartbeatEnabled = true
|
158
162
|
this
|
159
163
|
.on('identify', this.identify)
|
160
|
-
.once('heartbeat', this.
|
164
|
+
.once('heartbeat', (beat) => { this.initialBeat = beat })
|
161
165
|
.on('heartbeat', this.heartbeat)
|
162
166
|
.on('shutdown', this.shutdown)
|
163
167
|
}
|
@@ -182,6 +186,15 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
182
186
|
this.values.manufacturer, this.values.model, this.values.software,
|
183
187
|
this.nAccessories, this.nDevices, this.nResourcesMonitored
|
184
188
|
)
|
189
|
+
if (this.values.software !== this.recommendedSoftware) {
|
190
|
+
this.warn('recommended version: deCONZ v%s', this.recommendedSoftware)
|
191
|
+
}
|
192
|
+
if (this.context.migration != null) {
|
193
|
+
this.log(
|
194
|
+
'migration: %s: %d resources',
|
195
|
+
this.context.migration, this.nResourcesMonitored
|
196
|
+
)
|
197
|
+
}
|
185
198
|
if (this.logLevel > 2) {
|
186
199
|
this.vdebug(
|
187
200
|
'%d gateway resouces: %j', this.nResources,
|
@@ -210,28 +223,24 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
210
223
|
}
|
211
224
|
}
|
212
225
|
|
213
|
-
/** Initialise the gateway delegate.
|
214
|
-
*/
|
215
|
-
async init (beat) {
|
216
|
-
try {
|
217
|
-
this.debug('initialising...')
|
218
|
-
this.initialBeat = beat
|
219
|
-
await this.connect()
|
220
|
-
this.initialised = true
|
221
|
-
this.debug('initialised')
|
222
|
-
this.emit('initialised')
|
223
|
-
} catch (error) { this.error(error) }
|
224
|
-
}
|
225
|
-
|
226
226
|
/** Update properties from gateway announcement.
|
227
227
|
* @param {string} host - The gateway hostname or IP address and port.
|
228
228
|
* @param {Object} config - The response body of an unauthenticated
|
229
229
|
* GET `/config` (from {@link DeconzDiscovery#config config()}.
|
230
230
|
*/
|
231
|
-
found (host, config) {
|
232
|
-
|
233
|
-
|
234
|
-
|
231
|
+
async found (host, config) {
|
232
|
+
try {
|
233
|
+
this.context.host = host
|
234
|
+
this.values.host = host
|
235
|
+
this.context.config = config
|
236
|
+
this.values.software = config.swversion
|
237
|
+
if (!this.initialised) {
|
238
|
+
this.debug('initialising...')
|
239
|
+
await this.connect()
|
240
|
+
}
|
241
|
+
} catch (error) {
|
242
|
+
this.error(error)
|
243
|
+
}
|
235
244
|
}
|
236
245
|
|
237
246
|
async shutdown () {
|
@@ -412,8 +421,8 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
412
421
|
for (const id in this.exposeErrorById) {
|
413
422
|
this.resetExposeError(id)
|
414
423
|
}
|
415
|
-
this.context.fullState = null
|
416
424
|
this.pollNext = true
|
425
|
+
this.pollFullState = true
|
417
426
|
} catch (error) {
|
418
427
|
if (
|
419
428
|
error instanceof Deconz.ApiError && error.type === 101 && retry < 8
|
@@ -627,6 +636,45 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
627
636
|
|
628
637
|
// ===========================================================================
|
629
638
|
|
639
|
+
async onUiGet (a) {
|
640
|
+
this.debug('ui request: GET %s', a.join('/'))
|
641
|
+
if (a.length === 0) {
|
642
|
+
return {
|
643
|
+
status: 200,
|
644
|
+
body: {
|
645
|
+
expose: this.service.values.expose,
|
646
|
+
groups: this.service.values.groups,
|
647
|
+
heartrate: this.service.values.heartrate,
|
648
|
+
lights: this.service.values.lights,
|
649
|
+
logLevel: this.service.values.logLevel,
|
650
|
+
schedules: this.service.values.schedules,
|
651
|
+
sensors: this.service.values.sensors
|
652
|
+
// deviceByRidByRtype: this.deviceByRidByRtype
|
653
|
+
}
|
654
|
+
}
|
655
|
+
}
|
656
|
+
if (a[0] !== 'accessories') {
|
657
|
+
return { status: 403 } // Forbidden
|
658
|
+
}
|
659
|
+
if (a.length === 1) {
|
660
|
+
return { status: 200, body: this.deviceByRidByRtype }
|
661
|
+
}
|
662
|
+
if (this.deviceById[a[1]] == null) {
|
663
|
+
return { status: 404 } // Not Found
|
664
|
+
}
|
665
|
+
if (a.length === 2) {
|
666
|
+
return { status: 200, body: this.deviceById[a[1]] }
|
667
|
+
}
|
668
|
+
return { status: 403 } // Forbidden
|
669
|
+
}
|
670
|
+
|
671
|
+
async onUiPut (a, body) {
|
672
|
+
this.debug('ui request: PUT %s %j', a.join('/'), body)
|
673
|
+
return { status: 501 } // Not Implented
|
674
|
+
}
|
675
|
+
|
676
|
+
// ===========================================================================
|
677
|
+
|
630
678
|
/** Poll the gateway.
|
631
679
|
*
|
632
680
|
* Periodically get the gateway full state and call
|
@@ -639,9 +687,11 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
639
687
|
try {
|
640
688
|
this.polling = true
|
641
689
|
this.vdebug('%spolling...', this.pollNext ? 'priority ' : '')
|
642
|
-
if (this.context.fullState == null) {
|
643
|
-
|
644
|
-
|
690
|
+
if (this.context.fullState == null || this.pollFullState) {
|
691
|
+
const fullState = await this.client.get('/')
|
692
|
+
fullState.groups[0] = await this.client.get('/groups/0')
|
693
|
+
this.context.fullState = fullState
|
694
|
+
this.pollFullState = false
|
645
695
|
} else {
|
646
696
|
const config = await this.client.get('/config')
|
647
697
|
if (config.bridgeid === this.id && config.UTC == null) {
|
@@ -674,6 +724,11 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
674
724
|
this.pollNext = false
|
675
725
|
this.polling = false
|
676
726
|
}
|
727
|
+
if (!this.initialised) {
|
728
|
+
this.initialised = true
|
729
|
+
this.debug('initialised')
|
730
|
+
this.emit('initialised')
|
731
|
+
}
|
677
732
|
}
|
678
733
|
|
679
734
|
/** Analyse the peristed full state of the gateway,
|
@@ -857,6 +912,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
857
912
|
}
|
858
913
|
|
859
914
|
this.nAccessories = Object.keys(this.accessoryById).length
|
915
|
+
this.nResourcesMonitored = Object.keys(this.accessoryByRpath).length
|
860
916
|
this.nExposeErrors = Object.keys(this.exposeErrorById).length
|
861
917
|
if (this.nExposeErrors === 0) {
|
862
918
|
this.vdebug('%d accessories', this.nAccessories)
|
@@ -867,8 +923,6 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
867
923
|
}
|
868
924
|
|
869
925
|
if (changed) {
|
870
|
-
this.nResourcesMonitored = Object.keys(this.accessoryByRpath).length
|
871
|
-
this.identify()
|
872
926
|
if (this.context.migration == null) {
|
873
927
|
const response = await this.client.post('/resourcelinks', {
|
874
928
|
name: 'homebridge-deconz',
|
@@ -882,10 +936,7 @@ class Gateway extends homebridgeLib.AccessoryDelegate {
|
|
882
936
|
links: Object.keys(this.accessoryByRpath).sort()
|
883
937
|
})
|
884
938
|
}
|
885
|
-
this.
|
886
|
-
'migration: %s: %d resources',
|
887
|
-
this.context.migration, this.nResourcesMonitored
|
888
|
-
)
|
939
|
+
this.identify()
|
889
940
|
}
|
890
941
|
}
|
891
942
|
|
package/lib/DeconzPlatform.js
CHANGED
@@ -45,6 +45,7 @@ class DeconzPlatform extends homebridgeLib.Platform {
|
|
45
45
|
.stringKey('name')
|
46
46
|
.stringKey('platform')
|
47
47
|
.boolKey('forceHttp')
|
48
|
+
.stringKey('host')
|
48
49
|
.arrayKey('hosts')
|
49
50
|
.boolKey('noResponse')
|
50
51
|
.intKey('parallelRequests', 1, 30)
|
@@ -60,19 +61,26 @@ class DeconzPlatform extends homebridgeLib.Platform {
|
|
60
61
|
|
61
62
|
try {
|
62
63
|
optionParser.parse(configJson)
|
64
|
+
if (this.config.host != null) {
|
65
|
+
this.config.hosts.push(this.config.host)
|
66
|
+
}
|
63
67
|
this.discovery = new Deconz.Discovery({
|
64
68
|
forceHttp: this.config.forceHttp,
|
65
69
|
timeout: this.config.timeout
|
66
70
|
})
|
67
71
|
this.discovery
|
68
72
|
.on('error', (error) => {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
if (error instanceof homebridgeLib.HttpClient.HttpError) {
|
74
|
+
this.log(
|
75
|
+
'%s: request %d: %s %s', error.request.name,
|
76
|
+
error.request.id, error.request.method, error.request.resource
|
77
|
+
)
|
78
|
+
this.warn(
|
79
|
+
'%s: request %d: %s', error.request.name, error.request.id, error
|
80
|
+
)
|
81
|
+
return
|
82
|
+
}
|
83
|
+
this.warn(error)
|
76
84
|
})
|
77
85
|
.on('request', (request) => {
|
78
86
|
this.debug(
|
@@ -101,12 +109,10 @@ class DeconzPlatform extends homebridgeLib.Platform {
|
|
101
109
|
async foundGateway (host, config) {
|
102
110
|
const id = config.bridgeid
|
103
111
|
if (this.gatewayMap[id] == null) {
|
104
|
-
this.gatewayMap[id] = new DeconzAccessory.Gateway(this, {
|
105
|
-
config: config,
|
106
|
-
host: host
|
107
|
-
})
|
112
|
+
this.gatewayMap[id] = new DeconzAccessory.Gateway(this, { config, host })
|
108
113
|
}
|
109
|
-
this.gatewayMap[id].found(host, config)
|
114
|
+
await this.gatewayMap[id].found(host, config)
|
115
|
+
await events.once(this.gatewayMap[id], 'initialised')
|
110
116
|
this.emit('found')
|
111
117
|
}
|
112
118
|
|
@@ -124,17 +130,25 @@ class DeconzPlatform extends homebridgeLib.Platform {
|
|
124
130
|
async init () {
|
125
131
|
try {
|
126
132
|
const jobs = []
|
127
|
-
this.debug('job %d: find at least one gateway', jobs.length)
|
128
|
-
jobs.push(events.once(this, 'found'))
|
129
133
|
if (this.config.hosts.length > 0) {
|
130
134
|
for (const host of this.config.hosts) {
|
131
135
|
this.debug('job %d: find gateway at %s', jobs.length, host)
|
132
136
|
jobs.push(this.findHost(host))
|
133
137
|
}
|
134
138
|
} else {
|
139
|
+
this.debug('job %d: find at least one gateway', jobs.length)
|
140
|
+
jobs.push(events.once(this, 'found'))
|
135
141
|
for (const id in this.gatewayMap) {
|
142
|
+
const gateway = this.gatewayMap[id]
|
143
|
+
const host = gateway.context.host
|
136
144
|
this.debug('job %d: find gateway %s', jobs.length, id)
|
137
|
-
jobs.push(
|
145
|
+
jobs.push(events.once(gateway, 'initialised'))
|
146
|
+
try {
|
147
|
+
const config = await this.discovery.config(host)
|
148
|
+
await this.foundGateway(host, config)
|
149
|
+
} catch (error) {
|
150
|
+
this.warn('%s: %s', id, error)
|
151
|
+
}
|
138
152
|
}
|
139
153
|
}
|
140
154
|
|
@@ -150,9 +164,61 @@ class DeconzPlatform extends homebridgeLib.Platform {
|
|
150
164
|
|
151
165
|
this.log('%d gateways', Object.keys(this.gatewayMap).length)
|
152
166
|
this.emit('initialised')
|
167
|
+
const dumpInfo = {
|
168
|
+
config: this.config,
|
169
|
+
gatewayMap: {}
|
170
|
+
}
|
171
|
+
for (const id in this.gatewayMap) {
|
172
|
+
const gateway = this.gatewayMap[id]
|
173
|
+
dumpInfo.gatewayMap[id] = gateway.context
|
174
|
+
}
|
175
|
+
await this.createDumpFile(dumpInfo)
|
153
176
|
} catch (error) { this.error(error) }
|
154
177
|
}
|
155
178
|
|
179
|
+
async onUiRequest (method, url, body) {
|
180
|
+
const a = url.split('/').slice(1)
|
181
|
+
if (a.length < 1) {
|
182
|
+
return { status: 403 } // Forbidden
|
183
|
+
}
|
184
|
+
if (a[0] === 'gateways') {
|
185
|
+
if (a.length === 1) {
|
186
|
+
if (method === 'GET') {
|
187
|
+
// const gatewayByHost = await this.discovery.discover()
|
188
|
+
const gatewayByHost = {}
|
189
|
+
for (const id in this.gatewayMap) {
|
190
|
+
const gateway = this.gatewayMap[id]
|
191
|
+
gatewayByHost[gateway.context.host] = {
|
192
|
+
config: gateway.context.config,
|
193
|
+
host: gateway.context.host,
|
194
|
+
id
|
195
|
+
}
|
196
|
+
}
|
197
|
+
return {
|
198
|
+
status: 200,
|
199
|
+
body: Object.keys(gatewayByHost).sort().map((host) => {
|
200
|
+
return gatewayByHost[host]
|
201
|
+
})
|
202
|
+
}
|
203
|
+
}
|
204
|
+
return { status: 405 } // Method Not Allowed
|
205
|
+
}
|
206
|
+
const gateway = this.gatewayMap[a[1]]
|
207
|
+
const path = a.slice(2)
|
208
|
+
if (gateway == null) {
|
209
|
+
return { status: 404 } // Not Found
|
210
|
+
}
|
211
|
+
if (method === 'GET') {
|
212
|
+
return gateway.onUiGet(path)
|
213
|
+
}
|
214
|
+
if (method === 'PUT') {
|
215
|
+
return gateway.onUiPut(path, body)
|
216
|
+
}
|
217
|
+
return { status: 405 } // Method Not Allowed
|
218
|
+
}
|
219
|
+
return { status: 403 } // Forbidden
|
220
|
+
}
|
221
|
+
|
156
222
|
async heartbeat (beat) {
|
157
223
|
try {
|
158
224
|
if (beat % 300 === 5 && this.config.hosts.length === 0) {
|
@@ -51,7 +51,7 @@ class Light extends DeconzService.LightsResource {
|
|
51
51
|
}).on('didSet', (value, fromHomeKit) => {
|
52
52
|
if (fromHomeKit) {
|
53
53
|
const bri = Math.round(value * 2.54)
|
54
|
-
this.put({ bri
|
54
|
+
this.put({ bri })
|
55
55
|
this.updateAdaptiveLighting()
|
56
56
|
}
|
57
57
|
})
|
@@ -97,7 +97,7 @@ class Light extends DeconzService.LightsResource {
|
|
97
97
|
this.capabilities.ctMin, Math.min(value, this.capabilities.ctMax)
|
98
98
|
)
|
99
99
|
if (fromHomeKit) {
|
100
|
-
this.put({ ct
|
100
|
+
this.put({ ct })
|
101
101
|
this.values.colormode = 'ct'
|
102
102
|
}
|
103
103
|
if (this.capabilities.xy && this.values.colormode === 'ct') {
|
@@ -118,7 +118,7 @@ class Light extends DeconzService.LightsResource {
|
|
118
118
|
const xy = hsvToXy(
|
119
119
|
value, this.values.saturation, this.capabilities.gamut
|
120
120
|
)
|
121
|
-
this.put({ xy
|
121
|
+
this.put({ xy })
|
122
122
|
this.values.colormode = 'xy'
|
123
123
|
}
|
124
124
|
})
|
@@ -129,7 +129,7 @@ class Light extends DeconzService.LightsResource {
|
|
129
129
|
}).on('didSet', (value, fromHomeKit) => {
|
130
130
|
if (fromHomeKit) {
|
131
131
|
const xy = hsvToXy(this.values.hue, value, this.capabilities.gamut)
|
132
|
-
this.put({ xy
|
132
|
+
this.put({ xy })
|
133
133
|
this.values.colormode = 'xy'
|
134
134
|
}
|
135
135
|
})
|
@@ -140,8 +140,8 @@ class Light extends DeconzService.LightsResource {
|
|
140
140
|
unit: '°'
|
141
141
|
}).on('didSet', (value, fromHomeKit) => {
|
142
142
|
if (fromHomeKit) {
|
143
|
-
const hue = Math.round(this.
|
144
|
-
this.put({ hue
|
143
|
+
const hue = Math.round(this.values.hue * 65535.0 / 360.0)
|
144
|
+
this.put({ hue })
|
145
145
|
this.values.colormode = 'hs'
|
146
146
|
}
|
147
147
|
})
|
@@ -151,8 +151,8 @@ class Light extends DeconzService.LightsResource {
|
|
151
151
|
unit: '%'
|
152
152
|
}).on('didSet', (value, fromHomeKit) => {
|
153
153
|
if (fromHomeKit) {
|
154
|
-
const sat = Math.round(this.
|
155
|
-
this.put({ sat
|
154
|
+
const sat = Math.round(this.values.saturation * 254.0 / 100.0)
|
155
|
+
this.put({ sat })
|
156
156
|
this.values.colormode = 'hs'
|
157
157
|
}
|
158
158
|
})
|
@@ -165,7 +165,7 @@ class Light extends DeconzService.LightsResource {
|
|
165
165
|
}).on('didSet', (value, fromHomeKit) => {
|
166
166
|
if (fromHomeKit) {
|
167
167
|
const effect = value ? 'colorloop' : 'none'
|
168
|
-
const state = { effect
|
168
|
+
const state = { effect }
|
169
169
|
if (value) {
|
170
170
|
state.colorloopspeed = this.values.colorLoopSpeed
|
171
171
|
}
|
@@ -181,7 +181,7 @@ class Light extends DeconzService.LightsResource {
|
|
181
181
|
}).on('didSet', (value, fromHomeKit) => {
|
182
182
|
if (fromHomeKit) {
|
183
183
|
const effect = 'colorloop'
|
184
|
-
this.put({ effect
|
184
|
+
this.put({ effect, colorloopspeed: value })
|
185
185
|
this.values.colormode = 'hs'
|
186
186
|
}
|
187
187
|
})
|
@@ -236,31 +236,8 @@ class Light extends DeconzService.LightsResource {
|
|
236
236
|
}
|
237
237
|
|
238
238
|
if (this.resource.rtype === 'groups') {
|
239
|
-
this.sceneServices =
|
240
|
-
|
241
|
-
const service = new homebridgeLib.ServiceDelegate(accessory, {
|
242
|
-
name: this.resource.body.name + ' ' + scene.name,
|
243
|
-
Service: this.Services.hap.Switch,
|
244
|
-
subtype: this.subtype + '-S' + scene.id
|
245
|
-
})
|
246
|
-
service.addCharacteristicDelegate({
|
247
|
-
key: 'on',
|
248
|
-
Characteristic: this.Characteristics.hap.On,
|
249
|
-
value: false
|
250
|
-
}).on('didSet', async (value, fromHomeKit) => {
|
251
|
-
this.checkAdaptiveLighting()
|
252
|
-
if (fromHomeKit && value) {
|
253
|
-
try {
|
254
|
-
const path = this.resource.rpath + '/scenes/' + scene.id + '/recall'
|
255
|
-
this.debug('PUT %s', path)
|
256
|
-
await this.client.put(path)
|
257
|
-
} catch (error) { this.error(error) }
|
258
|
-
await timeout(this.platform.config.waitTimeReset)
|
259
|
-
service.values.on = false
|
260
|
-
}
|
261
|
-
})
|
262
|
-
this.sceneServices.push(service)
|
263
|
-
}
|
239
|
+
this.sceneServices = {}
|
240
|
+
this.updateScenes(this.resource.body.scenes)
|
264
241
|
}
|
265
242
|
|
266
243
|
if (this.capabilities.effects != null) {
|
@@ -412,6 +389,43 @@ class Light extends DeconzService.LightsResource {
|
|
412
389
|
super.updateState(state)
|
413
390
|
}
|
414
391
|
|
392
|
+
updateScenes (scenes) {
|
393
|
+
const sceneById = {}
|
394
|
+
for (const scene of scenes) {
|
395
|
+
sceneById[scene.id] = scene
|
396
|
+
if (this.sceneServices[scene.id] == null) {
|
397
|
+
const service = new homebridgeLib.ServiceDelegate(this.accessoryDelegate, {
|
398
|
+
name: this.resource.body.name + ' ' + scene.name,
|
399
|
+
Service: this.Services.hap.Switch,
|
400
|
+
subtype: this.subtype + '-S' + scene.id
|
401
|
+
})
|
402
|
+
service.addCharacteristicDelegate({
|
403
|
+
key: 'on',
|
404
|
+
Characteristic: this.Characteristics.hap.On,
|
405
|
+
value: false
|
406
|
+
}).on('didSet', async (value, fromHomeKit) => {
|
407
|
+
this.checkAdaptiveLighting()
|
408
|
+
if (fromHomeKit && value) {
|
409
|
+
try {
|
410
|
+
const path = this.resource.rpath + '/scenes/' + scene.id + '/recall'
|
411
|
+
this.debug('PUT %s', path)
|
412
|
+
await this.client.put(path)
|
413
|
+
} catch (error) { this.error(error) }
|
414
|
+
await timeout(this.platform.config.waitTimeReset)
|
415
|
+
service.values.on = false
|
416
|
+
}
|
417
|
+
})
|
418
|
+
this.sceneServices[scene.id] = service
|
419
|
+
}
|
420
|
+
}
|
421
|
+
for (const id in this.scenesServices) {
|
422
|
+
if (sceneById[id] == null) {
|
423
|
+
this.scenesSerices[id].destroy()
|
424
|
+
delete this.scenesService[id]
|
425
|
+
}
|
426
|
+
}
|
427
|
+
}
|
428
|
+
|
415
429
|
initAdaptiveLighting () {
|
416
430
|
if (this.adaptiveLighting == null) {
|
417
431
|
this.adaptiveLighting = new homebridgeLib.AdaptiveLighting(
|
@@ -453,7 +467,7 @@ class Light extends DeconzService.LightsResource {
|
|
453
467
|
if (this.values.colormode === 'ct' && ct === this.values.colorTemperature) {
|
454
468
|
return
|
455
469
|
}
|
456
|
-
this.put({ ct
|
470
|
+
this.put({ ct })
|
457
471
|
this.fromAdaptiveLighting = true
|
458
472
|
this.values.colormode = 'ct'
|
459
473
|
if (ct !== this.values.colorTemperature) {
|
@@ -35,7 +35,7 @@ class Motion extends DeconzService.SensorsResource {
|
|
35
35
|
: value === this.Characteristics.eve.Sensitivity.LOW
|
36
36
|
? 0
|
37
37
|
: Math.round(this.sensitivitymax / 2)
|
38
|
-
await this.put('/config', { sensitivity
|
38
|
+
await this.put('/config', { sensitivity })
|
39
39
|
}
|
40
40
|
})
|
41
41
|
|
@@ -42,8 +42,8 @@ class Switch extends DeconzService.SensorsResource {
|
|
42
42
|
this.buttonServices[i] = new DeconzService.Button(this.accessoryDelegate, {
|
43
43
|
name: this.name + ' ' + label,
|
44
44
|
button: Number(i),
|
45
|
-
events
|
46
|
-
hasRepeat
|
45
|
+
events,
|
46
|
+
hasRepeat
|
47
47
|
})
|
48
48
|
}
|
49
49
|
}
|
@@ -27,6 +27,7 @@ class Temperature extends DeconzService.SensorsResource {
|
|
27
27
|
key: 'offset',
|
28
28
|
Characteristic: this.Characteristics.my.Offset,
|
29
29
|
unit: '°C',
|
30
|
+
props: { minValue: -5, maxValue: 5, minStep: 0.1 },
|
30
31
|
value: 0
|
31
32
|
}).on('didSet', async (value, fromHomeKit) => {
|
32
33
|
if (fromHomeKit) {
|
@@ -65,7 +65,7 @@ class Thermostat extends DeconzService.SensorsResource {
|
|
65
65
|
}
|
66
66
|
}).on('didSet', async (value, fromHomeKit) => {
|
67
67
|
if (fromHomeKit) {
|
68
|
-
await this.put('/
|
68
|
+
await this.put('/config', {
|
69
69
|
mode: value === this.Characteristics.hap.TargetHeatingCoolingState.OFF
|
70
70
|
? 'off'
|
71
71
|
: this.capabilities.heatValue
|
@@ -77,6 +77,7 @@ class Thermostat extends DeconzService.SensorsResource {
|
|
77
77
|
key: 'offset',
|
78
78
|
Characteristic: this.Characteristics.my.Offset,
|
79
79
|
unit: '°C',
|
80
|
+
props: { minValue: -5, maxValue: 5, minStep: 0.1 },
|
80
81
|
value: 0
|
81
82
|
}).on('didSet', async (value, fromHomeKit) => {
|
82
83
|
if (fromHomeKit) {
|
@@ -37,10 +37,6 @@ class WindowCovering extends DeconzService.LightsResource {
|
|
37
37
|
key: 'positionState',
|
38
38
|
Characteristic: this.Characteristics.hap.PositionState,
|
39
39
|
value: this.Characteristics.hap.PositionState.STOPPED
|
40
|
-
}).on('didSet', (value) => {
|
41
|
-
if (value === this.Characteristics.hap.PositionState.STOPPED) {
|
42
|
-
this.values.targetPosition = this.values.currentPosition
|
43
|
-
}
|
44
40
|
})
|
45
41
|
|
46
42
|
this.addCharacteristicDelegate({
|
@@ -103,10 +99,6 @@ class WindowCovering extends DeconzService.LightsResource {
|
|
103
99
|
}
|
104
100
|
|
105
101
|
async setPosition () {
|
106
|
-
if (this.timer != null) {
|
107
|
-
clearTimeout(this.timer)
|
108
|
-
delete this.timer
|
109
|
-
}
|
110
102
|
let lift = 100 - this.values.targetPosition // % closed --> % open
|
111
103
|
if (this.venetianBlind) {
|
112
104
|
if (this.values.closeUpwards) {
|
@@ -121,11 +113,8 @@ class WindowCovering extends DeconzService.LightsResource {
|
|
121
113
|
this.values.targetPosition > this.values.currentPosition
|
122
114
|
? this.Characteristics.hap.PositionState.INCREASING
|
123
115
|
: this.Characteristics.hap.PositionState.DECREASING
|
124
|
-
|
125
|
-
this.
|
126
|
-
this.values.positionState =
|
127
|
-
this.Characteristics.hap.PositionState.STOPPED
|
128
|
-
}, 15000)
|
116
|
+
this.moving = new Date()
|
117
|
+
await this.put({ lift })
|
129
118
|
}
|
130
119
|
|
131
120
|
updateState (state) {
|
@@ -146,9 +135,13 @@ class WindowCovering extends DeconzService.LightsResource {
|
|
146
135
|
this.values.closeUpwards = closeUpwards
|
147
136
|
}
|
148
137
|
if (
|
149
|
-
|
150
|
-
|
138
|
+
this.moving == null || new Date() - this.moving >= 30000 || (
|
139
|
+
position === this.values.targetPosition &&
|
140
|
+
(closeUpwards == null || closeUpwards === this.targetCloseUpwards)
|
141
|
+
)
|
151
142
|
) {
|
143
|
+
this.moving = null
|
144
|
+
this.values.targetPosition = position
|
152
145
|
this.values.positionState = this.Characteristics.hap.PositionState.STOPPED
|
153
146
|
}
|
154
147
|
}
|