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
@@ -0,0 +1,506 @@
|
|
1
|
+
<!--
|
2
|
+
homebridge-deconz/homebridge-ui/public/index.html
|
3
|
+
|
4
|
+
Homebridge plug-in for deCONZ.
|
5
|
+
Copyright © 2022 Erik Baauw. All rights reserved.
|
6
|
+
-->
|
7
|
+
|
8
|
+
<link rel="stylesheet" href="style.css">
|
9
|
+
<p align="center">
|
10
|
+
<a href="https://github.com/ebaauw/homebridge-deconz/wiki/Configuration" target="_blank">
|
11
|
+
<img src="homebridge-deconz.png" height="200px">
|
12
|
+
</a>
|
13
|
+
</p>
|
14
|
+
|
15
|
+
<script>
|
16
|
+
|
17
|
+
async function showFormPluginConfig () {
|
18
|
+
homebridge.showSpinner()
|
19
|
+
const pluginConfig = await homebridge.getPluginConfig()
|
20
|
+
console.log('pluginConfig: %o', pluginConfig)
|
21
|
+
// const pluginConfigSchema = await homebridge.getPluginConfigSchema()
|
22
|
+
// console.log('pluginConfigSchema: %o', pluginConfigSchema)
|
23
|
+
// const cachedAccessories = await homebridge.getCachedAccessories()
|
24
|
+
// console.log('cachedAccessories: %o', cachedAccessories)
|
25
|
+
// const discoveredGateways = await homebridge.request('discover')
|
26
|
+
// console.log('discovered gateways: %o', discoveredGateways)
|
27
|
+
for (const config of pluginConfig) {
|
28
|
+
if (config._bridge != null) {
|
29
|
+
const cachedAccessories = await homebridge.request('cachedAccessories', {
|
30
|
+
username: config._bridge.username
|
31
|
+
})
|
32
|
+
console.log('%s: cachedAccessories: %o', config.name, cachedAccessories)
|
33
|
+
const cachedGateways = cachedAccessories.filter((accessory) => {
|
34
|
+
return accessory.plugin === 'homebridge-deconz' &&
|
35
|
+
accessory.context != null &&
|
36
|
+
accessory.context.className === 'Gateway'
|
37
|
+
})
|
38
|
+
const result = {}
|
39
|
+
for (const gateway of cachedGateways) {
|
40
|
+
if (gateway.context.uiPort == null) {
|
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()
|
55
|
+
|
56
|
+
const form = homebridge.createForm(
|
57
|
+
{
|
58
|
+
schema: {
|
59
|
+
type: 'object',
|
60
|
+
properties: {
|
61
|
+
config: {
|
62
|
+
title: 'Gateways',
|
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
|
+
})
|
202
|
+
|
203
|
+
}
|
204
|
+
|
205
|
+
async function showFormGateways (gateway) {
|
206
|
+
homebridge.showSpinner()
|
207
|
+
const cachedAccessories = await homebridge.getCachedAccessories()
|
208
|
+
const cachedGateways = cachedAccessories.filter((accessory) => {
|
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
|
+
}
|
282
|
+
|
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
|
+
}
|
330
|
+
|
331
|
+
async function showFormGatewaySettings (gateway, device) {
|
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({
|
356
|
+
schema: {
|
357
|
+
type: 'object',
|
358
|
+
properties: {
|
359
|
+
expose: {
|
360
|
+
title: 'Expose',
|
361
|
+
type: 'boolean'
|
362
|
+
},
|
363
|
+
lights: {
|
364
|
+
title: 'Lights',
|
365
|
+
type: 'boolean',
|
366
|
+
},
|
367
|
+
sensors: {
|
368
|
+
title: 'Sensors',
|
369
|
+
type: 'boolean',
|
370
|
+
},
|
371
|
+
groups: {
|
372
|
+
title: 'Groups',
|
373
|
+
type: 'boolean',
|
374
|
+
},
|
375
|
+
schedules: {
|
376
|
+
title: 'Schedules',
|
377
|
+
type: 'boolean',
|
378
|
+
},
|
379
|
+
logLevel: {
|
380
|
+
title: 'Log Level',
|
381
|
+
type: 'string',
|
382
|
+
oneOf: ['0', '1', '2', '3'].map((level) => { return { title: level, enum: [level] } }),
|
383
|
+
required: true,
|
384
|
+
condition: {
|
385
|
+
functionBody: 'return model.expose'
|
386
|
+
}
|
387
|
+
},
|
388
|
+
lightsDevice: {
|
389
|
+
title: 'Device',
|
390
|
+
type: 'string',
|
391
|
+
oneOf: values.lights,
|
392
|
+
required: true
|
393
|
+
},
|
394
|
+
sensorsDevice: {
|
395
|
+
title: 'Device',
|
396
|
+
type: 'string',
|
397
|
+
oneOf: values.sensors,
|
398
|
+
required: true
|
399
|
+
},
|
400
|
+
groupsDevice: {
|
401
|
+
title: 'Device',
|
402
|
+
type: 'string',
|
403
|
+
oneOf: values.groups,
|
404
|
+
required: true
|
405
|
+
}
|
406
|
+
}
|
407
|
+
},
|
408
|
+
layout: [
|
409
|
+
{
|
410
|
+
type: 'fieldset',
|
411
|
+
title: `${gateway.context.host} Gateway Settings`
|
412
|
+
},
|
413
|
+
'expose',
|
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',
|
431
|
+
items: [
|
432
|
+
{
|
433
|
+
type: 'tabs',
|
434
|
+
tabs: [
|
435
|
+
{
|
436
|
+
title: 'Lights',
|
437
|
+
items: [
|
438
|
+
'lightsDevice'
|
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
|
+
}
|
459
|
+
}
|
460
|
+
]
|
461
|
+
}, data, 'Device Settings', 'Done')
|
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
|
+
}
|
470
|
+
|
471
|
+
async function showFormDeviceSettings (gateway, device) {
|
472
|
+
homebridge.showSpinner()
|
473
|
+
homebridge.hideSpinner()
|
474
|
+
const form = homebridge.createForm({
|
475
|
+
schema: {
|
476
|
+
type: 'object',
|
477
|
+
properties: {
|
478
|
+
gateway: {
|
479
|
+
type: 'string'
|
480
|
+
},
|
481
|
+
device: {
|
482
|
+
type: 'string'
|
483
|
+
}
|
484
|
+
}
|
485
|
+
}
|
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
|
+
}
|
498
|
+
|
499
|
+
(async () => {
|
500
|
+
try {
|
501
|
+
await showFormPluginConfig()
|
502
|
+
} catch (error) {
|
503
|
+
console.error(error)
|
504
|
+
}
|
505
|
+
})()
|
506
|
+
</script>
|
package/lib/Deconz/ApiClient.js
CHANGED
@@ -81,9 +81,8 @@ class ApiClient extends homebridgeLib.HttpClient {
|
|
81
81
|
* @return {integer} lux - The value in lux.
|
82
82
|
*/
|
83
83
|
static lightLevelToLux (v) {
|
84
|
-
|
85
|
-
|
86
|
-
return Math.max(0.0001, Math.min(lux, 100000))
|
84
|
+
v = Math.max(0, Math.min(v, 60001))
|
85
|
+
return v ? Math.round(Math.pow(10, (v - 1) / 10000) * 10) / 10 : 0.0001
|
87
86
|
}
|
88
87
|
|
89
88
|
/** Create a new instance of a Deconz.Client.
|
@@ -149,7 +148,7 @@ class ApiClient extends homebridgeLib.HttpClient {
|
|
149
148
|
maxSockets: _options.maxSockets,
|
150
149
|
path: '/api',
|
151
150
|
timeout: _options.timeout,
|
152
|
-
validStatusCodes: [200, 400, 403
|
151
|
+
validStatusCodes: [200, 400, 403] //, 404]
|
153
152
|
}
|
154
153
|
if (_options.phoscon) {
|
155
154
|
// options.headers = { Accept: 'application/vnd.ddel.v1' }
|
package/lib/Deconz/Discovery.js
CHANGED
@@ -44,7 +44,7 @@ class Discovery extends events.EventEmitter {
|
|
44
44
|
*/
|
45
45
|
async config (host) {
|
46
46
|
const client = new homebridgeLib.HttpClient({
|
47
|
-
host
|
47
|
+
host,
|
48
48
|
json: true,
|
49
49
|
path: '/api',
|
50
50
|
timeout: this._options.timeout
|
@@ -78,12 +78,15 @@ class Discovery extends events.EventEmitter {
|
|
78
78
|
if (
|
79
79
|
body != null && typeof body === 'object' &&
|
80
80
|
typeof body.apiversion === 'string' &&
|
81
|
-
/
|
81
|
+
/[0-9A-Fa-f]{16}/.test(body.bridgeid) &&
|
82
82
|
typeof body.devicename === 'string' &&
|
83
83
|
typeof body.name === 'string' &&
|
84
84
|
typeof body.swversion === 'string'
|
85
85
|
) {
|
86
|
-
|
86
|
+
if (body.bridgeid.startsWith('00212E')) {
|
87
|
+
return body
|
88
|
+
}
|
89
|
+
throw new Error(`${body.bridgeid}: not a RaspBee/ConBee mac address`)
|
87
90
|
}
|
88
91
|
throw new Error('not a deCONZ gateway')
|
89
92
|
}
|
@@ -96,7 +99,7 @@ class Discovery extends events.EventEmitter {
|
|
96
99
|
*/
|
97
100
|
async description (host) {
|
98
101
|
const options = {
|
99
|
-
host
|
102
|
+
host,
|
100
103
|
timeout: this._options.timeout
|
101
104
|
}
|
102
105
|
const client = new homebridgeLib.HttpClient(options)
|
package/lib/Deconz/Resource.js
CHANGED
@@ -21,8 +21,9 @@ const sensorsPrios = [
|
|
21
21
|
'Power',
|
22
22
|
'Consumption',
|
23
23
|
'Temperature',
|
24
|
-
'
|
24
|
+
'Motion',
|
25
25
|
'OpenClose',
|
26
|
+
'AirPurifier',
|
26
27
|
'Thermostat'
|
27
28
|
]
|
28
29
|
|
@@ -256,7 +257,7 @@ class Resource {
|
|
256
257
|
get prio () {
|
257
258
|
if (this.rtype === 'groups') return -1
|
258
259
|
if (this.rtype === 'lights') return this.endpoint
|
259
|
-
return sensorsPrios.indexOf(this.
|
260
|
+
return sensorsPrios.indexOf(this.serviceName)
|
260
261
|
}
|
261
262
|
|
262
263
|
/** The resource path of the resource, e.g. `/lights/1`.
|
@@ -270,7 +271,7 @@ class Resource {
|
|
270
271
|
* corresponding HomeKit service, or `null` for unsupported and unknown
|
271
272
|
* resources.
|
272
273
|
*
|
273
|
-
* This is derived from the resource type and`type` in the resource body.
|
274
|
+
* This is derived from the resource type and `type` in the resource body.
|
274
275
|
* @type {string}
|
275
276
|
*/
|
276
277
|
get serviceName () {
|
@@ -305,6 +306,8 @@ class Resource {
|
|
305
306
|
}
|
306
307
|
} else { // (this.rtype === 'sensors')
|
307
308
|
switch (this.body.type) {
|
309
|
+
case 'CLIPAirPurifier':
|
310
|
+
case 'ZHAAirPurifier': return 'AirPurifier'
|
308
311
|
case 'ZHAAirQuality':
|
309
312
|
case 'CLIPAirQuality': return 'AirQuality'
|
310
313
|
case 'ZHAAlarm':
|
@@ -336,6 +339,7 @@ class Resource {
|
|
336
339
|
case 'CLIPPresence': return 'Motion'
|
337
340
|
case 'ZHAPressure':
|
338
341
|
case 'CLIPPressure': return 'AirPressure'
|
342
|
+
case 'ZHARelativeRotary': return 'Switch'
|
339
343
|
case 'ZHASpectral': return ''
|
340
344
|
case 'ZGPSwitch':
|
341
345
|
case 'ZHASwitch':
|
@@ -879,6 +883,18 @@ class Resource {
|
|
879
883
|
break
|
880
884
|
}
|
881
885
|
break
|
886
|
+
case 'RDM002': // Hue tap dial switch
|
887
|
+
dots = true
|
888
|
+
if (this.endpoint === '01') {
|
889
|
+
buttons.push([1, '1', SINGLE | LONG, true])
|
890
|
+
buttons.push([2, '2', SINGLE | LONG, true])
|
891
|
+
buttons.push([3, '3', SINGLE | LONG, true])
|
892
|
+
buttons.push([4, '4', SINGLE | LONG, true])
|
893
|
+
} else if (this.endpoint === '14') {
|
894
|
+
buttons.push([5, 'Right Turn', SINGLE])
|
895
|
+
buttons.push([6, 'Left Turn', SINGLE])
|
896
|
+
}
|
897
|
+
break
|
882
898
|
case 'ROM001': // Hue smart button
|
883
899
|
buttons.push([1, 'Button', SINGLE | LONG, true])
|
884
900
|
break
|
@@ -932,6 +948,7 @@ class Resource {
|
|
932
948
|
break
|
933
949
|
case 'Sunricher':
|
934
950
|
switch (this.model) {
|
951
|
+
case 'ZG2833K4_EU06': // Sunricher 4-button remote
|
935
952
|
case 'ZG2833K8_EU05': // Sunricher 8-button remote, see #529.
|
936
953
|
if (this.endpoint === '01') {
|
937
954
|
buttons.push([1, 'On 1', SINGLE | LONG])
|
@@ -0,0 +1,38 @@
|
|
1
|
+
// homebridge-deconz/lib/DeconzAccessory/Thermostat.js
|
2
|
+
// Copyright © 2022 Erik Baauw. All rights reserved.
|
3
|
+
//
|
4
|
+
// Homebridge plugin for deCONZ.
|
5
|
+
|
6
|
+
'use strict'
|
7
|
+
|
8
|
+
const DeconzAccessory = require('../DeconzAccessory')
|
9
|
+
|
10
|
+
class AirPurifier extends DeconzAccessory {
|
11
|
+
/** Instantiate a delegate for an accessory corresponding to a device.
|
12
|
+
* @param {DeconzAccessory.Gateway} gateway - The gateway.
|
13
|
+
* @param {Deconz.Device} device - The device.
|
14
|
+
*/
|
15
|
+
constructor (gateway, device, settings = {}) {
|
16
|
+
super(gateway, device, gateway.Accessory.Categories.AIR_PURIFIER)
|
17
|
+
this.identify()
|
18
|
+
|
19
|
+
this.service = this.createService(device.resource, { primaryService: true })
|
20
|
+
|
21
|
+
for (const subtype in device.resourceBySubtype) {
|
22
|
+
const resource = device.resourceBySubtype[subtype]
|
23
|
+
if (subtype === device.primary) {
|
24
|
+
continue
|
25
|
+
}
|
26
|
+
this.createService(resource)
|
27
|
+
}
|
28
|
+
|
29
|
+
this.createSettingsService()
|
30
|
+
|
31
|
+
setImmediate(() => {
|
32
|
+
this.debug('initialised')
|
33
|
+
this.emit('initialised')
|
34
|
+
})
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
module.exports = AirPurifier
|