homebridge-deconz 0.0.6 → 0.0.11

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.
Files changed (58) hide show
  1. package/README.md +8 -0
  2. package/cli/deconz.js +980 -0
  3. package/config.schema.json +1 -1
  4. package/lib/Client/ApiError.js +42 -0
  5. package/lib/{DeconzClient.js → Deconz/ApiClient.js} +82 -158
  6. package/lib/Deconz/ApiError.js +42 -0
  7. package/lib/Deconz/ApiResponse.js +54 -0
  8. package/lib/Deconz/Device.js +100 -0
  9. package/lib/{DeconzDiscovery.js → Deconz/Discovery.js} +4 -3
  10. package/lib/Deconz/Resource.js +1206 -0
  11. package/lib/{DeconzWsClient.js → Deconz/WsClient.js} +59 -44
  12. package/lib/Deconz/index.js +21 -0
  13. package/lib/DeconzAccessory/Contact.js +54 -0
  14. package/lib/DeconzAccessory/Gateway.js +316 -374
  15. package/lib/DeconzAccessory/Light.js +72 -0
  16. package/lib/DeconzAccessory/Motion.js +51 -0
  17. package/lib/DeconzAccessory/Sensor.js +35 -0
  18. package/lib/DeconzAccessory/Temperature.js +63 -0
  19. package/lib/DeconzAccessory/Thermostat.js +50 -0
  20. package/lib/DeconzAccessory/WarningDevice.js +56 -0
  21. package/lib/DeconzAccessory/WindowCovering.js +47 -0
  22. package/lib/DeconzAccessory/index.js +216 -0
  23. package/lib/DeconzPlatform.js +8 -3
  24. package/lib/DeconzService/AirPressure.js +43 -0
  25. package/lib/DeconzService/AirQuality.js +20 -10
  26. package/lib/DeconzService/Alarm.js +16 -9
  27. package/lib/DeconzService/Battery.js +43 -0
  28. package/lib/DeconzService/Button.js +12 -2
  29. package/lib/DeconzService/CarbonMonoxide.js +38 -0
  30. package/lib/DeconzService/Consumption.js +65 -0
  31. package/lib/DeconzService/Contact.js +60 -0
  32. package/lib/DeconzService/Daylight.js +132 -0
  33. package/lib/DeconzService/DeviceSettings.js +13 -5
  34. package/lib/DeconzService/Flag.js +52 -0
  35. package/lib/DeconzService/GatewaySettings.js +8 -58
  36. package/lib/DeconzService/Humidity.js +37 -0
  37. package/lib/DeconzService/Leak.js +38 -0
  38. package/lib/DeconzService/Light.js +376 -0
  39. package/lib/DeconzService/LightLevel.js +54 -0
  40. package/lib/DeconzService/LightsResource.js +112 -0
  41. package/lib/DeconzService/Motion.js +101 -0
  42. package/lib/DeconzService/Outlet.js +76 -0
  43. package/lib/DeconzService/Power.js +83 -0
  44. package/lib/DeconzService/SensorsResource.js +96 -0
  45. package/lib/DeconzService/Smoke.js +38 -0
  46. package/lib/DeconzService/Status.js +53 -0
  47. package/lib/DeconzService/Switch.js +93 -0
  48. package/lib/DeconzService/Temperature.js +63 -0
  49. package/lib/DeconzService/Thermostat.js +175 -0
  50. package/lib/DeconzService/WarningDevice.js +68 -0
  51. package/lib/DeconzService/WindowCovering.js +139 -0
  52. package/lib/DeconzService/index.js +94 -0
  53. package/package.json +7 -4
  54. package/lib/DeconzAccessory/Device.js +0 -91
  55. package/lib/DeconzAccessory.js +0 -16
  56. package/lib/DeconzDevice.js +0 -245
  57. package/lib/DeconzService/Sensor.js +0 -58
  58. package/lib/DeconzService.js +0 -43
@@ -0,0 +1,1206 @@
1
+ // homebridge-deconz/lib/Deconz/Resource.js
2
+ // Copyright © 2022 Erik Baauw. All rights reserved.
3
+ //
4
+ // Homebridge plugin for deCONZ.
5
+
6
+ 'use strict'
7
+
8
+ const homebridgeLib = require('homebridge-lib')
9
+ const Deconz = require('../Deconz')
10
+ const DeconzAccessory = require('../DeconzAccessory')
11
+ const DeconzService = require('../DeconzService')
12
+
13
+ const { toInstance, toInt, toObject, toString } = homebridgeLib.OptionParser
14
+ const { defaultGamut } = homebridgeLib.Colour
15
+ const { buttonEvent } = Deconz.ApiClient
16
+ const { SINGLE, DOUBLE, LONG } = DeconzService.Button
17
+ const rtypes = ['lights', 'sensors', 'groups']
18
+
19
+ // From low to high.
20
+ const sensorsPrios = [
21
+ 'Power',
22
+ 'Consumption',
23
+ 'Temperature',
24
+ 'Presence',
25
+ 'OpenClose',
26
+ 'Thermostat'
27
+ ]
28
+
29
+ // =============================================================================
30
+
31
+ // See: http://www.developers.meethue.com/documentation/supported-lights
32
+
33
+ const hueGamutType = { // Color gamut per light model.
34
+ A: { // Color Lights
35
+ r: [0.7040, 0.2960],
36
+ g: [0.2151, 0.7106],
37
+ b: [0.1380, 0.0800]
38
+ },
39
+ B: { // Extended Color Lights
40
+ r: [0.6750, 0.3220],
41
+ g: [0.4090, 0.5180],
42
+ b: [0.1670, 0.0400]
43
+ },
44
+ C: { // next gen Extended Color Lights
45
+ r: [0.6920, 0.3080],
46
+ g: [0.1700, 0.7000],
47
+ b: [0.1530, 0.0480]
48
+ }
49
+ }
50
+
51
+ const hueGamutTypeByModel = {
52
+ LCT001: 'B', // Hue bulb A19
53
+ LCT002: 'B', // Hue Spot BR30
54
+ LCT003: 'B', // Hue Spot GU10
55
+ LCT007: 'B', // Hue bulb A19
56
+ LCT010: 'C', // Hue bulb A19
57
+ LCT011: 'C', // Hue BR30
58
+ LCT012: 'C', // Hue Color Candle
59
+ LCT014: 'C', // Hue bulb A19
60
+ LCT015: 'C', // Hue bulb A19
61
+ LCT016: 'C', // Hue bulb A19
62
+ LLC005: 'A', // Living Colors Gen3 Bloom, Aura
63
+ LLC006: 'A', // Living Colors Gen3 Iris
64
+ LLC007: 'A', // Living Colors Gen3 Bloom, Aura
65
+ LLC010: 'A', // Hue Living Colors Iris
66
+ LLC011: 'A', // Hue Living Colors Bloom
67
+ LLC012: 'A', // Hue Living Colors Bloom
68
+ LLC013: 'A', // Disney Living Colors
69
+ LLC014: 'A', // Living Colors Gen3 Bloom, Aura
70
+ LLC020: 'C', // Hue Go
71
+ LLM001: 'B', // Color Light Module
72
+ LST001: 'A', // Hue LightStrips
73
+ LST002: 'C' // Hue LightStrips Plus
74
+ }
75
+
76
+ const hueTapMap = {
77
+ 34: 1002, // press 1
78
+ 16: 2002, // press 2
79
+ 17: 3002, // press 3
80
+ 18: 4002, // press 4
81
+ 100: 5002, // press 1 and 2
82
+ 101: 0, // release 1 and 2
83
+ 98: 6002, // press 3 and 4
84
+ 99: 0 // release 3 and 4
85
+ }
86
+
87
+ /** Delegate class for a resource on a deCONZ gateway.
88
+ *
89
+ * @memberof Deconz
90
+ */
91
+ class Resource {
92
+ /** Parse the `uniqueid` in the resource body of a resource for a Zigbee device.
93
+ * @param {string} uniqueid - The `uniqueid`.
94
+ * @return {object} The Zigbee `mac`, `endpoint`, and `cluster`.
95
+ */
96
+ static parseUniqueid (uniqueid) {
97
+ toString('uniqueid', uniqueid, true)
98
+ const a = uniqueid.replace(/:/g, '').toUpperCase().split('-')
99
+ return {
100
+ mac: a.length > 0 ? a[0] : null,
101
+ endpoint: a.length > 1 ? a[1] : null,
102
+ cluster: a.length > 2 ? a[2] : null
103
+ }
104
+ }
105
+
106
+ /** Create a new instance of a delegate of a resource.
107
+ *
108
+ * @param {DeconzAccessory.Gateway} gateway - The gateway.
109
+ * @param {string} rtype - The resource type of the resource:
110
+ * `groups`, `lights`, or `sensors`.
111
+ * @param {integer} rid - The resource ID of the resource.
112
+ * @param {object} body - The body of the resource.
113
+ */
114
+ constructor (gateway, rtype, rid, body) {
115
+ toInstance('gateway', gateway, DeconzAccessory.Gateway)
116
+
117
+ /** The resource type of the resource: `groups`, `lights`, or `sensors`.
118
+ * @type {string}
119
+ */
120
+ this.rtype = toString('rtype', rtype, true)
121
+ if (!(rtypes.includes(rtype))) {
122
+ throw new RangeError(`rtype: ${rtype}: not a valid resource type`)
123
+ }
124
+
125
+ /** The resource ID of the resource.
126
+ * @type {integer}
127
+ */
128
+ this.rid = toInt('rid', rid)
129
+
130
+ /** The body of the resource.
131
+ * @type {object}
132
+ */
133
+ this.body = toObject('body', body)
134
+ toString('body.name', body.name, true)
135
+ toString('body.type', body.type, true)
136
+
137
+ if (
138
+ this.rtype === 'lights' ||
139
+ (this.rtype === 'sensors' && this.body.type.startsWith('Z'))
140
+ ) {
141
+ const { mac, endpoint, cluster } = Resource.parseUniqueid(body.uniqueid)
142
+
143
+ /** The device ID.
144
+ *
145
+ * For Zigbee devices, the device ID is based on the Zigbee mac address
146
+ * of the device, from the `uniqueid` in the body of the resource.
147
+ * For virtual devices, the device ID is based on the Zigbee mac address of
148
+ * the gateway, and on the resource type and resource ID of the resource.
149
+ * The UUID of the corresponding HomeKit accessory is based on the device ID.
150
+ * @type {string}
151
+ */
152
+ this.id = mac
153
+
154
+ /** The subtype of the corresponding HomeKit service.
155
+ *
156
+ * For Zigbee devices, the subtype is based on the Zigbee endpoint and
157
+ * cluster, from the `uniqueid` in the body of the resource.
158
+ * For virtual devices, the subtype is based on the resource type and
159
+ * resource ID.
160
+ * @type {string}
161
+ */
162
+ this.subtype = endpoint + (cluster == null ? '' : '-' + cluster)
163
+
164
+ /** Zigbee endpoint.
165
+ * @type {string}
166
+ */
167
+ this.endpoint = endpoint
168
+
169
+ /** Zigbee device vs virtual device.
170
+ *
171
+ * Derived from the resource type and, for `sensors`, on the `type` in the
172
+ * resource body.
173
+ * @type {boolean}
174
+ */
175
+ this.zigbee = true
176
+ } else {
177
+ this.subtype = rtype[0].toUpperCase() + rid
178
+ this.id = gateway.id + '-' + this.subtype
179
+ this.zigbee = false
180
+ }
181
+
182
+ /** The associated Homekit _Manufacturer_.
183
+ *
184
+ * For Zigbee devices, this is the sanitised `manufacturername` in the
185
+ * resource body.
186
+ * For virtual devices, this is the _Manufacturer_ for the gateway.
187
+ * @type {string}
188
+ */
189
+ this.manufacturer = this.zigbee
190
+ ? body.manufacturername
191
+ : gateway.values.manufacturer
192
+
193
+ /** The associated HomeKit _Model_.
194
+ *
195
+ * For Zigbee devices, this is the sanitised `modelid` in the
196
+ * resource body.
197
+ * For virtual devices, this is the `type` in the resource body.
198
+ * @type {string}
199
+ */
200
+ this.model = this.zigbee ? body.modelid : body.type
201
+
202
+ /** The associated HomeKit _Firmware Version_.
203
+ *
204
+ * For Zigbee devices, this is the sanitised `swversion` in the
205
+ * resource body.
206
+ * For virtual devices, this is the _Firmware Version_ for the gateway.
207
+ */
208
+ this.firmware = this.zigbee
209
+ ? body.swversion == null ? '0.0.0' : body.swversion
210
+ : gateway.values.software
211
+
212
+ switch (this.serviceName) {
213
+ case 'Light': {
214
+ if (body.action != null) {
215
+ Object.assign(body.state, body.action)
216
+ delete body.state.on
217
+ }
218
+ this.capabilities = {
219
+ on: body.state.on !== undefined,
220
+ bri: body.state.bri !== undefined,
221
+ ct: body.state.ct !== undefined,
222
+ ctMax: (body.ctmax != null && body.ctmax !== 0 && body.ctmax !== 65535)
223
+ ? body.ctmax
224
+ : 500,
225
+ ctMin: (body.ctmin != null && body.ctmin !== 0)
226
+ ? body.ctmin
227
+ : 153,
228
+ xy: body.state.xy !== undefined,
229
+ gamut: defaultGamut,
230
+ alert: body.state.alert !== undefined,
231
+ colorLoop: body.state.effect !== undefined
232
+ }
233
+ break
234
+ }
235
+ case 'WarningDevice':
236
+ this.capabilities = {}
237
+ break
238
+ case 'WindowCovering':
239
+ this.capabilities = {}
240
+ break
241
+ default:
242
+ this.capabilities = {}
243
+ break
244
+ }
245
+
246
+ const f = 'patch' + this.serviceName
247
+ if (typeof this[f] === 'function') {
248
+ this[f](gateway)
249
+ }
250
+ }
251
+
252
+ /** The priority of the resource, when determining the primary resource for a
253
+ * device.
254
+ * @type {integer}
255
+ */
256
+ get prio () {
257
+ if (this.rtype === 'groups') return -1
258
+ if (this.rtype === 'lights') return this.endpoint
259
+ return sensorsPrios.indexOf(this.type)
260
+ }
261
+
262
+ /** The resource path of the resource, e.g. `/lights/1`.
263
+ *
264
+ * This is derived from the resource type and resource ID.
265
+ * @type {string}
266
+ */
267
+ get rpath () { return '/' + this.rtype + '/' + this.rid }
268
+
269
+ /** The name of the {@link DeconzService} subclass of the delegate of the
270
+ * corresponding HomeKit service, or `null` for unsupported and unknown
271
+ * resources.
272
+ *
273
+ * This is derived from the resource type and`type` in the resource body.
274
+ * @type {string}
275
+ */
276
+ get serviceName () {
277
+ if (this.rtype === 'groups') {
278
+ return 'Light'
279
+ } else if (this.rtype === 'lights') {
280
+ switch (this.body.type) {
281
+ case 'Color dimmable light': return 'Light'
282
+ case 'Color light': return 'Light'
283
+ case 'Color temperature light': return 'Light'
284
+ case 'Dimmable light': return 'Light'
285
+ case 'Dimmable plug-in unit': return 'Light'
286
+ case 'Extended color light': return 'Light'
287
+ // case 'Consumption awareness device': return null
288
+ case 'Dimmer switch': return 'Light'
289
+ case 'Level control switch': return 'Light'
290
+ // case 'Level controllable output': return null
291
+ // case 'Door Lock': return null
292
+ // case 'Door Lock Unit': return null
293
+ case 'Fan': return 'Light'
294
+ case 'On/Off light switch': return 'Light'
295
+ case 'On/Off light': return 'Light'
296
+ case 'On/Off output': return 'Light'
297
+ case 'On/Off plug-in unit': return 'Light'
298
+ case 'Smart plug': return 'Light'
299
+ case 'Configuration tool': return ''
300
+ case 'Range extender': return ''
301
+ case 'Warning device': return 'WarningDevice'
302
+ case 'Window covering controller': return 'WindowCovering'
303
+ case 'Window covering device': return 'WindowCovering'
304
+ default: return null
305
+ }
306
+ } else { // (this.rtype === 'sensors')
307
+ switch (this.body.type) {
308
+ case 'ZHAAirQuality':
309
+ case 'CLIPAirQuality': return 'AirQuality'
310
+ case 'ZHAAlarm':
311
+ case 'CLIPAlarm': return 'Alarm'
312
+ case 'ZHABattery':
313
+ case 'CLIPBattery': return 'Battery'
314
+ case 'ZHACarbonMonoxide':
315
+ case 'CLIPCarbonMonoxide': return 'CarbonMonoxide'
316
+ case 'ZHAConsumption':
317
+ case 'CLIPConsumption': return 'Consumption'
318
+ // case 'ZHADoorLock':
319
+ // case 'CLIPDoorLock': return null
320
+ case 'Daylight': return 'Daylight'
321
+ case 'ZHAFire':
322
+ case 'CLIPFire': return 'Smoke'
323
+ case 'CLIPGenericFlag': return 'Flag'
324
+ case 'CLIPGenericStatus': return 'Status'
325
+ case 'ZHAHumidity':
326
+ case 'CLIPHumidity': return 'Humidity'
327
+ case 'ZHALightLevel':
328
+ case 'CLIPLightLevel': return 'LightLevel'
329
+ // case 'ZHAMoisture':
330
+ // case 'CLIPMoisture': return null
331
+ case 'ZHAOpenClose':
332
+ case 'CLIPOpenClose': return 'Contact'
333
+ case 'ZHAPower':
334
+ case 'CLIPPower': return 'Power'
335
+ case 'ZHAPresence':
336
+ case 'CLIPPresence': return 'Motion'
337
+ case 'ZHAPressure':
338
+ case 'CLIPPressure': return 'AirPressure'
339
+ case 'ZHASpectral': return ''
340
+ case 'ZGPSwitch':
341
+ case 'ZHASwitch':
342
+ case 'CLIPSwitch': return 'Switch'
343
+ case 'ZHATemperature':
344
+ case 'CLIPTemperature': return 'Temperature'
345
+ case 'ZHAThermostat':
346
+ case 'CLIPThermostat': return 'Thermostat'
347
+ case 'ZHATime':
348
+ case 'CLIPTime': return ''
349
+ case 'ZHAVibration':
350
+ case 'CLIPVibration': return 'Motion'
351
+ case 'ZHAWater':
352
+ case 'CLIPWater': return 'Leak'
353
+ default: return null
354
+ }
355
+ }
356
+ }
357
+
358
+ /** Patch a resource corresponding to a `Light` service.
359
+ * @param {DeconzAccessory.Gateway} gateway - The gateway.
360
+ */
361
+ patchLight (gateway) {
362
+ switch (this.manufacturer) {
363
+ case 'Busch-Jaeger':
364
+ // See: https://www.busch-jaeger.de/en/products/product-solutions/dimmer/busch-radio-controlled-dimmer-zigbee-light-link/
365
+ if (
366
+ this.model === 'RM01' && // 6715 U-500 with 6736-84.
367
+ this.capabilities.bri && this.body.type === 'On/Off light' // Issue #241
368
+ ) {
369
+ gateway.vdebug(
370
+ '%s: ignoring state.bri for %s', this.rpath, this.body.type
371
+ )
372
+ this.capabilities.bri = false
373
+ }
374
+ break
375
+ case 'dresden elektronik':
376
+ // See: https://www.dresden-elektronik.de/funktechnik/solutions/wireless-light-control/wireless-ballasts/?L=1
377
+ this.capabilities.computesXy = true
378
+ break
379
+ case 'FeiBit':
380
+ if (this.model === 'FNB56-SKT1EHG1.2') { // issue #361
381
+ this.body.type = 'On/Off plug-in unit'
382
+ }
383
+ break
384
+ case 'GLEDOPTO':
385
+ // See: https://www.led-trading.de/zigbee-kompatibel-controller-led-lichtsteuerung
386
+ this.capabilities.gamut = {
387
+ r: [0.7006, 0.2993],
388
+ g: [0.1387, 0.8148],
389
+ b: [0.1510, 0.0227]
390
+ }
391
+ if (this.model === 'GLEDOPTO') { // Issue #244
392
+ if (
393
+ this.endpoint === '0A' &&
394
+ this.body.type === 'Dimmable light' &&
395
+ this.firmware === '1.0.2'
396
+ ) {
397
+ this.model = 'RGBW'
398
+ } else if (
399
+ this.endpoint === '0B' &&
400
+ this.body.type === 'Color temperature light' &&
401
+ this.firmware === '1.3.002'
402
+ ) {
403
+ this.model = 'WW/CW'
404
+ } else if (
405
+ this.endpoint === '0B' &&
406
+ this.body.type === 'Extended color light' &&
407
+ this.firmware === '1.0.2'
408
+ ) {
409
+ this.model = 'RGB+CCT'
410
+ const device = gateway.deviceById[this.id]
411
+ if (device != null) {
412
+ this.model = 'RGBW'
413
+ this.capabilities.ct = false
414
+ }
415
+ } else {
416
+ return
417
+ }
418
+ gateway.vdebug('%s: set model to %j', this.rpath, this.model)
419
+ }
420
+ break
421
+ case 'IKEA of Sweden':
422
+ // See: http://www.ikea.com/us/en/catalog/categories/departments/lighting/smart_lighting/
423
+ this.capabilities.gamut = defaultGamut // Issue #956
424
+ this.capabilities.noTransition = true
425
+ if (this.model === 'TRADFRI bulb E27 CWS 806lm') {
426
+ this.capabilities.computesXy = true
427
+ this.capabilities.gamut = {
428
+ r: [0.68, 0.31],
429
+ g: [0.11, 0.82],
430
+ b: [0.13, 0.04]
431
+ }
432
+ }
433
+ break
434
+ case 'innr':
435
+ // See: https://shop.innrlighting.com/en/shop
436
+ this.capabilities.gamut = { // Issue #152
437
+ r: [0.8817, 0.1033],
438
+ g: [0.2204, 0.7758],
439
+ b: [0.0551, 0.1940]
440
+ }
441
+ if (this.model === 'SP 120') { // smart plug
442
+ this.capabilities.bri = false
443
+ }
444
+ break
445
+ case 'LIDL Livarno Lux':
446
+ this.capabilities.ctMax = 454 // 2200 K
447
+ this.capabilities.ctMin = 153 // 6500 K
448
+ if (this.model === 'HG06467') { // Xmas light strip
449
+ this.capabilities.colorLoop = false
450
+ this.capabilities.hs = true
451
+ this.capabilities.effects = [
452
+ 'Steady', 'Snow', 'Rainbow', 'Snake',
453
+ 'Twinkle', 'Fireworks', 'Flag', 'Waves',
454
+ 'Updown', 'Vintage', 'Fading', 'Collide',
455
+ 'Strobe', 'Sparkles', 'Carnival', 'Glow'
456
+ ]
457
+ }
458
+ break
459
+ case 'MLI': // Issue #439
460
+ this.capabilities.gamut = {
461
+ r: [0.68, 0.31],
462
+ g: [0.11, 0.82],
463
+ b: [0.13, 0.04]
464
+ }
465
+ if (this.capabilities.colorloop) {
466
+ this.capabilities.effects = [
467
+ 'Sunset', 'Party', 'Worklight', 'Campfire', 'Romance', 'Nightlight'
468
+ ]
469
+ }
470
+ break
471
+ case 'OSRAM':
472
+ this.capabilities.gamut = {
473
+ r: [0.6877, 0.3161],
474
+ g: [0.1807, 0.7282],
475
+ b: [0.1246, 0.0580]
476
+ }
477
+ break
478
+ case 'Philips':
479
+ case 'Signify Netherlands B.V.': {
480
+ // See: http://www.developers.meethue.com/documentation/supported-lights
481
+ this.manufacturer = 'Signify Netherlands B.V.'
482
+ this.capabilities.breathe = true
483
+ this.capabilities.computesXy = true
484
+ const gamut = hueGamutTypeByModel[this.model] || 'C'
485
+ this.capabilities.gamut = hueGamutType[gamut]
486
+ }
487
+ break
488
+ default:
489
+ break
490
+ }
491
+ }
492
+
493
+ /** Patch a resource corresponding to a `Flag` service.
494
+ * @param {DeconzAccessory.Gateway} gateway - The gateway.
495
+ */
496
+ patchFlag (gateway) {
497
+ if (
498
+ this.body.manufacturername === 'homebridge-hue' &&
499
+ this.body.modelid === 'CLIPGenericFlag' &&
500
+ this.body.swversion === '0'
501
+ ) {
502
+ this.capabilities.readonly = true
503
+ }
504
+ }
505
+
506
+ /** Patch a resource corresponding to a `Flag` service.
507
+ * @param {DeconzAccessory.Gateway} gateway - The gateway.
508
+ */
509
+ patchStatus (gateway) {
510
+ if (
511
+ this.body.manufacturername === 'homebridge-hue' &&
512
+ this.body.modelid === 'CLIPGenericStatus'
513
+ ) {
514
+ const a = this.body.swversion.split(',')
515
+ const min = parseInt(a[0])
516
+ const max = parseInt(a[1])
517
+ const step = parseInt(a[2])
518
+ // Eve 3.1 displays the following controls, depending on the properties:
519
+ // 1. {minValue: 0, maxValue: 1, minStep: 1} switch
520
+ // 2. {minValue: a, maxValue: b, minStep: 1}, 1 < b - a <= 20 down|up
521
+ // 3. {minValue: a, maxValue: b}, (a, b) != (0, 1) slider
522
+ // 4. {minValue: a, maxValue: b, minStep: 1}, b - a > 20 slider
523
+ // Avoid the following bugs:
524
+ // 5. {minValue: 0, maxValue: 1} nothing
525
+ // 6. {minValue: a, maxValue: b, minStep: 1}, b - a = 1 switch*
526
+ // *) switch sends values 0 and 1 instead of a and b;
527
+ if (min === 0 && max === 0) {
528
+ this.capabilities.readonly = true
529
+ } else if (min >= -127 && max <= 127 && min < max) {
530
+ if (min === 0 && max === 1) {
531
+ // Workaround Eve bug (case 5 above).
532
+ this.capabilities.props = { minValue: min, maxValue: max, minStep: 1 }
533
+ } else if (max - min === 1) {
534
+ // Workaround Eve bug (case 6 above).
535
+ this.capabilities.props = { minValue: min, maxValue: max }
536
+ } else if (step !== 1) {
537
+ // Default to slider for backwards compatibility.
538
+ this.capabilities.props = { minValue: min, maxValue: max }
539
+ } else {
540
+ this.capabilities.props = { minValue: min, maxValue: max, minStep: 1 }
541
+ }
542
+ }
543
+ }
544
+ }
545
+
546
+ /** Patch a resource corresponding to a `Switch` service.
547
+ * @param {DeconzAccessory.Gateway} gateway - The gateway.
548
+ */
549
+ patchSwitch (gateway) {
550
+ const buttons = []
551
+ let dots = false
552
+ switch (this.manufacturer) {
553
+ case 'Bitron Home':
554
+ switch (this.model) {
555
+ case '902010/23': // Bitron remote, see #639.
556
+ dots = true
557
+ buttons.push([1, 'Dim Up', SINGLE])
558
+ buttons.push([2, 'On', SINGLE])
559
+ buttons.push([3, 'Off', SINGLE])
560
+ buttons.push([4, 'Dim Down', SINGLE])
561
+ break
562
+ default:
563
+ break
564
+ }
565
+ break
566
+ case 'Busch-Jaeger':
567
+ switch (this.model) {
568
+ case 'RM01': // Busch-Jaeger Light Link control element (mains-powered)
569
+ case 'RB01': // Busch-Jaeger Light Link wall-mounted transmitter
570
+ if (this.endpoint === '0A') {
571
+ buttons.push([1, 'Button 1', SINGLE | LONG])
572
+ buttons.push([2, 'Button 2', SINGLE | LONG])
573
+ } else if (this.endpoint === '0B') {
574
+ buttons.push([3, 'Button 3', SINGLE | LONG])
575
+ buttons.push([4, 'Button 4', SINGLE | LONG])
576
+ } else if (this.endpoint === '0C') {
577
+ buttons.push([5, 'Button 5', SINGLE | LONG])
578
+ buttons.push([6, 'Button 6', SINGLE | LONG])
579
+ } else if (this.endpoint === '0D') {
580
+ buttons.push([7, 'Button 7', SINGLE | LONG])
581
+ buttons.push([8, 'Button 8', SINGLE | LONG])
582
+ }
583
+ break
584
+ default:
585
+ break
586
+ }
587
+ break
588
+ case 'Echostar':
589
+ switch (this.model) {
590
+ case 'Bell':
591
+ buttons.push([1, 'Front Doorbell', SINGLE])
592
+ buttons.push([2, 'Rear Doorbell', SINGLE])
593
+ break
594
+ default:
595
+ break
596
+ }
597
+ break
598
+ case 'ELKO':
599
+ switch (this.model) {
600
+ case 'ElkoDimmerRemoteZHA': // ELKO ESH 316 Endevender RF, see #922.
601
+ buttons.push([1, 'Press', SINGLE])
602
+ buttons.push([2, 'Dim Up', SINGLE])
603
+ buttons.push([3, 'Dim Down', SINGLE])
604
+ break
605
+ default:
606
+ break
607
+ }
608
+ break
609
+ case 'Heiman':
610
+ switch (this.model) {
611
+ case 'RC-EF-3.0':
612
+ dots = true
613
+ buttons.push([1, 'HomeMode', SINGLE])
614
+ buttons.push([2, 'Disarm', SINGLE])
615
+ buttons.push([3, 'SOS', SINGLE])
616
+ buttons.push([4, 'Arm', SINGLE])
617
+ break
618
+ default:
619
+ break
620
+ }
621
+ break
622
+ case 'IKEA of Sweden':
623
+ switch (this.model) {
624
+ case 'Remote Control N2':
625
+ buttons.push([1, 'Dim Up', SINGLE | LONG])
626
+ buttons.push([2, 'Dim Down', SINGLE | LONG])
627
+ buttons.push([3, 'Previous', SINGLE | LONG])
628
+ buttons.push([4, 'Next', SINGLE | LONG])
629
+ break
630
+ case 'SYMFONISK Sound Controller':
631
+ buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
632
+ if (this.body.mode === 1) {
633
+ buttons.push([2, 'Turn Right', LONG])
634
+ buttons.push([3, 'Turn Left', LONG])
635
+ } else {
636
+ buttons.push([2, 'Turn Right', SINGLE])
637
+ buttons.push([3, 'Turn Left', SINGLE])
638
+ }
639
+ break
640
+ case 'TRADFRI SHORTCUT Button':
641
+ buttons.push([1, 'Button', SINGLE | LONG])
642
+ break
643
+ case 'TRADFRI on/off switch':
644
+ buttons.push([1, 'On', SINGLE | LONG])
645
+ buttons.push([2, 'Off', SINGLE | LONG])
646
+ break
647
+ case 'TRADFRI open/close remote':
648
+ buttons.push([1, 'Open', SINGLE | LONG])
649
+ buttons.push([2, 'Close', SINGLE | LONG])
650
+ break
651
+ case 'TRADFRI remote control':
652
+ buttons.push([1, 'On/Off', SINGLE])
653
+ buttons.push([2, 'Dim Up', SINGLE | LONG])
654
+ buttons.push([3, 'Dim Down', SINGLE | LONG])
655
+ buttons.push([4, 'Previous', SINGLE | LONG])
656
+ buttons.push([5, 'Next', SINGLE | LONG])
657
+ break
658
+ case 'TRADFRI wireless dimmer':
659
+ if (this.obj.mode === 1) {
660
+ buttons.push([1, 'Turn Right', SINGLE | LONG])
661
+ buttons.push([2, 'Turn Left', SINGLE | LONG])
662
+ } else {
663
+ buttons.push([1, 'On', SINGLE])
664
+ buttons.push([2, 'Dim Up', SINGLE])
665
+ buttons.push([3, 'Dim Down', SINGLE])
666
+ buttons.push([4, 'Off', SINGLE])
667
+ }
668
+ break
669
+ default:
670
+ break
671
+ }
672
+ break
673
+ case 'Insta':
674
+ switch (this.model) {
675
+ case 'HS_4f_GJ_1': // Gira/Jung Light Link hand transmitter
676
+ case 'WS_3f_G_1': // Gira Light Link wall transmitter
677
+ case 'WS_4f_J_1': // Jung Light Link wall transmitter
678
+ buttons.push([1, 'Off', SINGLE | DOUBLE | LONG])
679
+ buttons.push([2, 'On', SINGLE | DOUBLE | LONG])
680
+ buttons.push([3, 'Scene 1', SINGLE])
681
+ buttons.push([4, 'Scene 2', SINGLE])
682
+ buttons.push([5, 'Scene 3', SINGLE])
683
+ buttons.push([6, 'Scene 4', SINGLE])
684
+ if (this.model !== 'WS_3f_G_1') {
685
+ buttons.push([7, 'Scene 5', SINGLE])
686
+ buttons.push([8, 'Scene 6', SINGLE])
687
+ }
688
+ break
689
+ default:
690
+ break
691
+ }
692
+ break
693
+ case 'LDS':
694
+ switch (this.model) {
695
+ case 'ZBT-DIMController-D0800':
696
+ buttons.push([1, 'On/Off', SINGLE])
697
+ buttons.push([2, 'Dim Up', SINGLE | LONG])
698
+ buttons.push([3, 'Dim Down', SINGLE | LONG])
699
+ buttons.push([4, 'Scene', SINGLE | LONG])
700
+ break
701
+ default:
702
+ break
703
+ }
704
+ break
705
+ case 'LIDL Livarno Lux':
706
+ switch (this.model) {
707
+ case 'HG06323':
708
+ buttons.push([1, 'On', SINGLE | DOUBLE | LONG])
709
+ buttons.push([2, 'Dim Up', SINGLE | LONG])
710
+ buttons.push([3, 'Dim Down', SINGLE | LONG])
711
+ buttons.push([4, 'Off', SINGLE])
712
+ break
713
+ default:
714
+ break
715
+ }
716
+ break
717
+ case 'LUMI':
718
+ switch (this.model) {
719
+ case 'lumi.ctrl_neutral1':
720
+ buttons.push([1, 'Button', SINGLE])
721
+ break
722
+ case 'lumi.ctrl_neutral2':
723
+ buttons.push([1, 'Left', SINGLE])
724
+ buttons.push([2, 'Right', SINGLE])
725
+ buttons.push([3, 'Both', SINGLE])
726
+ break
727
+ case 'lumi.remote.b1acn01':
728
+ case 'lumi.remote.b186acn01':
729
+ case 'lumi.remote.b186acn02':
730
+ buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
731
+ break
732
+ case 'lumi.remote.b28ac1':
733
+ case 'lumi.remote.b286acn01':
734
+ case 'lumi.remote.b286acn02':
735
+ buttons.push([1, 'Left', SINGLE | DOUBLE | LONG])
736
+ buttons.push([2, 'Right', SINGLE | DOUBLE | LONG])
737
+ buttons.push([3, 'Both', SINGLE | DOUBLE | LONG])
738
+ break
739
+ case 'lumi.remote.b286opcn01': // Xiaomi Aqara Opple, see #637.
740
+ case 'lumi.remote.b486opcn01': // Xiaomi Aqara Opple, see #637.
741
+ case 'lumi.remote.b686opcn01': // Xiaomi Aqara Opple, see #637.
742
+ buttons.push([1, '1', SINGLE | DOUBLE | LONG])
743
+ buttons.push([2, '2', SINGLE | DOUBLE | LONG])
744
+ if (this.model !== 'lumi.remote.b286opcn01') {
745
+ buttons.push([3, '3', SINGLE | DOUBLE | LONG])
746
+ buttons.push([4, '4', SINGLE | DOUBLE | LONG])
747
+ if (this.model === 'lumi.remote.b686opcn01') {
748
+ buttons.push([5, '5', SINGLE | DOUBLE | LONG])
749
+ buttons.push([6, '6', SINGLE | DOUBLE | LONG])
750
+ }
751
+ }
752
+ break
753
+ case 'lumi.sensor_86sw1': // Xiaomi wall switch (single button)
754
+ case 'lumi.ctrl_ln1.aq1':
755
+ buttons.push([1, 'Button', SINGLE | DOUBLE])
756
+ break
757
+ case 'lumi.sensor_86sw2': // Xiaomi wall switch (two buttons)
758
+ case 'lumi.ctrl_ln2.aq1':
759
+ buttons.push([1, 'Left', SINGLE | DOUBLE])
760
+ buttons.push([2, 'Right', SINGLE | DOUBLE])
761
+ buttons.push([3, 'Both', SINGLE | DOUBLE])
762
+ break
763
+ case 'lumi.sensor_cube':
764
+ case 'lumi.sensor_cube.aqgl01':
765
+ if (this.endpoint === '02') {
766
+ buttons.push([1, 'Side 1', SINGLE | DOUBLE | LONG])
767
+ buttons.push([2, 'Side 2', SINGLE | DOUBLE | LONG])
768
+ buttons.push([3, 'Side 3', SINGLE | DOUBLE | LONG])
769
+ buttons.push([4, 'Side 4', SINGLE | DOUBLE | LONG])
770
+ buttons.push([5, 'Side 5', SINGLE | DOUBLE | LONG])
771
+ buttons.push([6, 'Side 6', SINGLE | DOUBLE | LONG])
772
+ buttons.push([7, 'Cube', SINGLE | DOUBLE | LONG])
773
+ this.capabilities.toButtonEvent = (v) => {
774
+ const button = Math.floor(v / 1000)
775
+ let event = v % 1000
776
+ if (v === 7000) { // Wakeup
777
+ event = buttonEvent.SHORT_RELEASE
778
+ } else if (v === 7007 || v === 7008) { // Shake, Drop
779
+ } else if (event === 0) { // Push
780
+ event = buttonEvent.LONG_RELEASE
781
+ } else if (event === button) { // Double tap
782
+ event = buttonEvent.DOUBLE_PRESS
783
+ } else { // Flip
784
+ event = buttonEvent.SHORT_RELEASE
785
+ }
786
+ return button * 1000 + event
787
+ }
788
+ } else if (this.endpoint === '03') {
789
+ buttons.push([8, 'Turn Right', SINGLE | DOUBLE | LONG])
790
+ buttons.push([9, 'Turn Left', SINGLE | DOUBLE | LONG])
791
+ this.capabilities.toButtonEvent = (v) => {
792
+ const button = v > 0 ? 8 : 9
793
+ const event = Math.abs(v) < 4500
794
+ ? buttonEvent.SHORT_RELEASE
795
+ : Math.abs(v) < 9000
796
+ ? buttonEvent.DOUBLE_PRESS
797
+ : buttonEvent.LONG_RELEASE
798
+ return button * 1000 + event
799
+ }
800
+ }
801
+ break
802
+ case 'lumi.sensor_switch': // Xiaomi Mi wireless switch
803
+ case 'lumi.sensor_switch.aq2': // Xiaomi Aqara smart wireless switch
804
+ case 'lumi.sensor_switch.aq3': // Xiaomi Aqara smart wireless switch with gyro
805
+ buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
806
+ break
807
+ default:
808
+ break
809
+ }
810
+ break
811
+ case 'Lutron':
812
+ switch (this.model) {
813
+ case 'LZL4BWHL01 Remote': // Lutron Pico, see 102.
814
+ buttons.push([1, 'On', SINGLE])
815
+ buttons.push([2, 'Dim Up', LONG])
816
+ buttons.push([3, 'Dim Down', LONG])
817
+ buttons.push([4, 'Off', SINGLE])
818
+ break
819
+ case 'Z3-1BRL': // Lutron Aurora, see #522.
820
+ buttons.push([1, 'Button', SINGLE])
821
+ buttons.push([2, 'Turn Right', SINGLE])
822
+ buttons.push([3, 'Turn Left', SINGLE])
823
+ break
824
+ default:
825
+ break
826
+ }
827
+ break
828
+ case 'MLI':
829
+ switch (this.model) {
830
+ case 'ZBT-Remote-ALL-RGBW': // Tint remote control by Müller-Licht see deconz-rest-plugin#1209
831
+ buttons.push([1, 'On/Off', SINGLE])
832
+ buttons.push([2, 'Dim Up', SINGLE | LONG])
833
+ buttons.push([3, 'Dim Down', SINGLE | LONG])
834
+ buttons.push([4, 'Warm', SINGLE])
835
+ buttons.push([5, 'Cool', SINGLE])
836
+ buttons.push([6, 'Colour Wheel', SINGLE])
837
+ buttons.push([7, 'Work Light', SINGLE])
838
+ buttons.push([8, 'Sunset', SINGLE])
839
+ buttons.push([9, 'Party', SINGLE])
840
+ buttons.push([10, 'Night Light', SINGLE])
841
+ buttons.push([11, 'Campfire', SINGLE])
842
+ buttons.push([12, 'Romance', SINGLE])
843
+ break
844
+ default:
845
+ break
846
+ }
847
+ break
848
+ case 'OSRAM':
849
+ switch (this.model) {
850
+ case 'Lightify Switch Mini':
851
+ buttons.push([1, 'Up', SINGLE | LONG])
852
+ buttons.push([2, 'Down', SINGLE | LONG])
853
+ buttons.push([3, 'Middle', SINGLE | LONG])
854
+ break
855
+ default:
856
+ break
857
+ }
858
+ break
859
+ case 'Philips':
860
+ case 'Signify Netherlands B.V.':
861
+ switch (this.model) {
862
+ case 'RDM001': // Hue wall switch module
863
+ switch (this.body.config.devicemode) {
864
+ case 'singlerocker':
865
+ buttons.push([1, 'Rocker 1', SINGLE])
866
+ break
867
+ case 'singlepushbutton':
868
+ buttons.push([1, 'Push Button 1', SINGLE | LONG, true])
869
+ break
870
+ case 'dualrocker':
871
+ buttons.push([1, 'Rocker 1', SINGLE])
872
+ buttons.push([2, 'Rocker 2', SINGLE])
873
+ break
874
+ case 'dualpushbutton':
875
+ buttons.push([1, 'Push Button 1', SINGLE | LONG, true])
876
+ buttons.push([2, 'Push Button 2', SINGLE | LONG, true])
877
+ break
878
+ default:
879
+ break
880
+ }
881
+ break
882
+ case 'ROM001': // Hue smart button
883
+ buttons.push([1, 'Button', SINGLE | LONG, true])
884
+ break
885
+ case 'RWL020':
886
+ case 'RWL021': // Hue dimmer switch
887
+ buttons.push([1, 'On', SINGLE | LONG])
888
+ buttons.push([2, 'Dim Up', SINGLE | LONG, true])
889
+ buttons.push([3, 'Dim Down', SINGLE | LONG, true])
890
+ buttons.push([4, 'Off', SINGLE | LONG])
891
+ break
892
+ case 'RWL022': // Hue dimmer switch (2021)
893
+ buttons.push([1, 'On/Off', SINGLE | LONG])
894
+ buttons.push([2, 'Dim Up', SINGLE | LONG, true])
895
+ buttons.push([3, 'Dim Down', SINGLE | LONG, true])
896
+ buttons.push([4, 'Hue', SINGLE | LONG])
897
+ break
898
+ case 'ZGPSWITCH': // Hue tap
899
+ dots = true
900
+ buttons.push([1, '1', SINGLE])
901
+ buttons.push([2, '2', SINGLE])
902
+ buttons.push([3, '3', SINGLE])
903
+ buttons.push([4, '4', SINGLE])
904
+ buttons.push([5, '1 and 2', SINGLE])
905
+ buttons.push([6, '3 and 4', SINGLE])
906
+ this.capabilities.toButtonEvent = (v) => {
907
+ return hueTapMap[v]
908
+ }
909
+ break
910
+ default:
911
+ break
912
+ }
913
+ break
914
+ case 'PhilipsFoH':
915
+ if (this.model === 'FOHSWITCH') { // Friends-of-Hue switch
916
+ buttons.push([1, 'Top Left', SINGLE | LONG])
917
+ buttons.push([2, 'Bottom Left', SINGLE | LONG])
918
+ buttons.push([3, 'Top Right', SINGLE | LONG])
919
+ buttons.push([4, 'Bottom Right', SINGLE | LONG])
920
+ buttons.push([5, 'Top Both', SINGLE | LONG])
921
+ buttons.push([6, 'Bottom Both', SINGLE | LONG])
922
+ }
923
+ break
924
+ case 'Samjin':
925
+ switch (this.model) {
926
+ case 'button':
927
+ buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
928
+ break
929
+ default:
930
+ break
931
+ }
932
+ break
933
+ case 'Sunricher':
934
+ switch (this.model) {
935
+ case 'ZG2833K8_EU05': // Sunricher 8-button remote, see #529.
936
+ if (this.endpoint === '01') {
937
+ buttons.push([1, 'On 1', SINGLE | LONG])
938
+ buttons.push([2, 'Off 1', SINGLE | LONG])
939
+ } else if (this.endpoint === '02') {
940
+ buttons.push([3, 'On 2', SINGLE | LONG])
941
+ buttons.push([4, 'Off 2', SINGLE | LONG])
942
+ } else if (this.endpoint === '03') {
943
+ buttons.push([5, 'On 3', SINGLE | LONG])
944
+ buttons.push([6, 'Off 3', SINGLE | LONG])
945
+ } else if (this.endpoint === '04') {
946
+ buttons.push([7, 'On 4', SINGLE | LONG])
947
+ buttons.push([8, 'Off 4', SINGLE | LONG])
948
+ }
949
+ break
950
+ case 'ZG2833PAC': // Sunricher C4
951
+ buttons.push([1, 'Rocker 1', SINGLE])
952
+ buttons.push([2, 'Rocker 2', SINGLE])
953
+ buttons.push([3, 'Rocker 3', SINGLE])
954
+ buttons.push([4, 'Rocker 4', SINGLE])
955
+ break
956
+ case 'ZGRC-KEY-002': // Sunricher CCT remote, see #529.
957
+ buttons.push([1, 'On', SINGLE])
958
+ buttons.push([2, 'Off', SINGLE])
959
+ buttons.push([3, 'Dim', LONG])
960
+ buttons.push([4, 'C/W', SINGLE | LONG])
961
+ break
962
+ default:
963
+ break
964
+ }
965
+ break
966
+ case '_TZ3000_arfwfgoa':
967
+ switch (this.model) {
968
+ case 'TS0042': // Tuys 2-button switch, single endpoint
969
+ buttons.push([1, 'Left', SINGLE | DOUBLE | LONG])
970
+ buttons.push([2, 'Right', SINGLE | DOUBLE | LONG])
971
+ break
972
+ default:
973
+ break
974
+ }
975
+ break
976
+ case '_TZ3000_dfgbtub0':
977
+ case '_TZ3000_i3rjdrwu':
978
+ switch (this.model) {
979
+ case 'TS0042': // Tuya 2-button switch, see #1060.
980
+ if (this.endpoint === '01') {
981
+ buttons.push([1, 'Button 1', SINGLE | DOUBLE | LONG])
982
+ } else if (this.endpoint === '02') {
983
+ buttons.push([2, 'Button 2', SINGLE | DOUBLE | LONG])
984
+ }
985
+ break
986
+ default:
987
+ break
988
+ }
989
+ break
990
+ case '_TZ3000_pzui3skt':
991
+ switch (this.model) {
992
+ case 'TS0041': // Tuya 1-button switch
993
+ buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
994
+ break
995
+ default:
996
+ break
997
+ }
998
+ break
999
+ case '_TZ3000_rrjr1q0u':
1000
+ switch (this.model) {
1001
+ case 'TS0043': // Tuya 3-button switch
1002
+ buttons.push([1, 'Left', SINGLE | DOUBLE | LONG])
1003
+ buttons.push([2, 'Middle', SINGLE | DOUBLE | LONG])
1004
+ buttons.push([3, 'Right', SINGLE | DOUBLE | LONG])
1005
+ break
1006
+ default:
1007
+ break
1008
+ }
1009
+ break
1010
+ case '_TZ3000_vp6clf9d':
1011
+ switch (this.model) {
1012
+ case 'TS0044':
1013
+ buttons.push([1, 'Bottom Left', SINGLE | DOUBLE | LONG])
1014
+ buttons.push([2, 'Bottom Right', SINGLE | DOUBLE | LONG])
1015
+ buttons.push([3, 'Top Right', SINGLE | DOUBLE | LONG])
1016
+ buttons.push([4, 'Top Left', SINGLE | DOUBLE | LONG])
1017
+ break
1018
+ default:
1019
+ break
1020
+ }
1021
+ break
1022
+ case '_TZ3000_xabckq1v':
1023
+ switch (this.model) {
1024
+ case 'TS004F': // Tuya 4-button switch, single press only
1025
+ buttons.push([1, 'Top Left', SINGLE])
1026
+ buttons.push([2, 'Bottom Left', SINGLE])
1027
+ buttons.push([3, 'Top Right', SINGLE])
1028
+ buttons.push([4, 'Bottom Right', SINGLE])
1029
+ break
1030
+ default:
1031
+ break
1032
+ }
1033
+ break
1034
+ case 'dresden elektronik':
1035
+ switch (this.model) {
1036
+ case 'Kobold':
1037
+ buttons.push([1, 'Button', SINGLE | LONG])
1038
+ break
1039
+ case 'Lighting Switch':
1040
+ if (this.endpoint === '01') {
1041
+ if (this.body.mode !== 2) {
1042
+ gateway.vdebug(
1043
+ '%s: Lighting Switch mode %d instead of 2',
1044
+ this.rpath, this.body.mode
1045
+ )
1046
+ }
1047
+ buttons.push([1, 'Top Left', SINGLE | LONG])
1048
+ buttons.push([2, 'Bottom Left', SINGLE | LONG])
1049
+ buttons.push([3, 'Top Right', SINGLE | LONG])
1050
+ buttons.push([4, 'Bottom Right', SINGLE | LONG])
1051
+ }
1052
+ break
1053
+ case 'Scene Switch':
1054
+ buttons.push([1, 'On', SINGLE | LONG])
1055
+ buttons.push([2, 'Off', SINGLE | LONG])
1056
+ buttons.push([3, 'Scene 1', SINGLE])
1057
+ buttons.push([4, 'Scene 2', SINGLE])
1058
+ buttons.push([5, 'Scene 3', SINGLE])
1059
+ buttons.push([6, 'Scene 4', SINGLE])
1060
+ break
1061
+ default:
1062
+ break
1063
+ }
1064
+ break
1065
+ case 'eWeLink':
1066
+ switch (this.model) {
1067
+ case 'WB01':
1068
+ buttons.push([1, 'Press', SINGLE | DOUBLE | LONG])
1069
+ break
1070
+ default:
1071
+ break
1072
+ }
1073
+ break
1074
+ case 'icasa':
1075
+ switch (this.model) {
1076
+ case 'ICZB-KPD12':
1077
+ case 'ICZB-KPD14S':
1078
+ case 'ICZB-KPD18S':
1079
+ buttons.push([1, 'Off', SINGLE | LONG])
1080
+ buttons.push([2, 'On', SINGLE | LONG])
1081
+ if (this.model !== 'ICZB-KPD12') {
1082
+ buttons.push([3, 'S1', SINGLE])
1083
+ buttons.push([4, 'S2', SINGLE])
1084
+ if (this.model === 'ICZB-KPD18S') {
1085
+ buttons.push([5, 'S3', SINGLE])
1086
+ buttons.push([6, 'S4', SINGLE])
1087
+ buttons.push([7, 'S5', SINGLE])
1088
+ buttons.push([8, 'S6', SINGLE])
1089
+ }
1090
+ }
1091
+ break
1092
+ case 'ICZB-RM11S':
1093
+ buttons.push([1, '1 Off', SINGLE | LONG])
1094
+ buttons.push([2, '1 On', SINGLE | LONG])
1095
+ buttons.push([3, '2 Off', SINGLE | LONG])
1096
+ buttons.push([4, '2 On', SINGLE | LONG])
1097
+ buttons.push([5, '3 Off', SINGLE | LONG])
1098
+ buttons.push([6, '3 On', SINGLE | LONG])
1099
+ buttons.push([7, '4 Off', SINGLE | LONG])
1100
+ buttons.push([8, '4 On', SINGLE | LONG])
1101
+ buttons.push([9, 'S1', SINGLE])
1102
+ buttons.push([10, 'S2', SINGLE])
1103
+ break
1104
+ default:
1105
+ break
1106
+ }
1107
+ break
1108
+ case 'innr':
1109
+ switch (this.model) {
1110
+ case 'RC 110':
1111
+ if (this.endpoint === '01') {
1112
+ buttons.push([1, 'On/Off', SINGLE])
1113
+ buttons.push([2, 'Dim Up', SINGLE | LONG])
1114
+ buttons.push([3, 'Dim Down', SINGLE | LONG])
1115
+ buttons.push([4, '1', SINGLE])
1116
+ buttons.push([5, '2', SINGLE])
1117
+ buttons.push([6, '3', SINGLE])
1118
+ buttons.push([7, '4', SINGLE])
1119
+ buttons.push([8, '5', SINGLE])
1120
+ buttons.push([9, '6', SINGLE])
1121
+ for (let i = 1; i <= 6; i++) {
1122
+ const button = 7 + i * 3
1123
+ buttons.push([button, `On/Off ${i}`, SINGLE])
1124
+ buttons.push([button + 1, `Dim Up ${i}`, SINGLE | LONG])
1125
+ buttons.push([button + 2, `Dim Down ${i}`, SINGLE | LONG])
1126
+ }
1127
+ }
1128
+ break
1129
+ default:
1130
+ break
1131
+ }
1132
+ break
1133
+ case 'lk':
1134
+ switch (this.model) {
1135
+ case 'ZBT-DIMSwitch-D0001': // Linkind 1-Key Remote Control, see #949.
1136
+ buttons.push([1, 'Button', SINGLE | LONG])
1137
+ this.capabilities.homekitValue = (v) => { return 1 }
1138
+ break
1139
+ default:
1140
+ break
1141
+ }
1142
+ break
1143
+ case 'ubisys':
1144
+ switch (this.model) {
1145
+ case 'C4 (5504)':
1146
+ case 'C4-R (5604)':
1147
+ buttons.push([1, '1', SINGLE | LONG])
1148
+ buttons.push([2, '2', SINGLE | LONG])
1149
+ buttons.push([3, '3', SINGLE | LONG])
1150
+ buttons.push([4, '4', SINGLE | LONG])
1151
+ break
1152
+ case 'D1 (5503)':
1153
+ case 'D1-R (5603)':
1154
+ case 'S1-R (5601)':
1155
+ case 'S2 (5502)':
1156
+ case 'S2-R (5602)':
1157
+ buttons.push([1, '1', SINGLE | LONG])
1158
+ buttons.push([2, '2', SINGLE | LONG])
1159
+ break
1160
+ case 'S1 (5501)':
1161
+ buttons.push([1, '1', SINGLE | LONG])
1162
+ break
1163
+ default:
1164
+ break
1165
+ }
1166
+ break
1167
+ default:
1168
+ break
1169
+ }
1170
+ if (buttons.length > 0) {
1171
+ this.capabilities.buttons = {}
1172
+ for (const button of buttons) {
1173
+ this.capabilities.buttons[button[0]] = {
1174
+ label: button[1],
1175
+ events: button[2],
1176
+ hasRepeat: button[3]
1177
+ }
1178
+ }
1179
+ this.capabilities.namespace = dots
1180
+ ? gateway.Characteristics.hap.ServiceLabelNamespace.DOTS
1181
+ : gateway.Characteristics.hap.ServiceLabelNamespace.ARABIC_NUMERALS
1182
+ }
1183
+ }
1184
+
1185
+ /** Patch a resource corresponding to a `Thermostat` service.
1186
+ * @param {DeconzAccessory.Gateway} gateway - The gateway.
1187
+ */
1188
+ patchThermostat (gateway) {
1189
+ if (this.manufacturer === 'ELKO' && this.model === 'Super TR') {
1190
+ this.capbilities.heatValue = 'heat'
1191
+ } else {
1192
+ this.capbilities.heatValue = 'auto'
1193
+ }
1194
+ }
1195
+
1196
+ /** Patch a resource corresponding to a `WindowCovering` service.
1197
+ * @param {DeconzAccessory.Gateway} gateway - The gateway.
1198
+ */
1199
+ patchWindowCovering (gateway) {
1200
+ if (this.manufacturer === 'ELKO' && this.model === 'Super TR') {
1201
+ this.capabilities.positionChange = true
1202
+ }
1203
+ }
1204
+ }
1205
+
1206
+ module.exports = Resource