homebridge-deconz 0.1.9 → 0.1.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.
package/cli/deconz.js CHANGED
@@ -514,10 +514,7 @@ class Main extends CommandLineTool {
514
514
 
515
515
  this.bridgeid = this.gatewayConfig.bridgeid
516
516
  if (this.clargs.options.apiKey == null) {
517
- if (
518
- this.gateways[this.bridgeid] != null &&
519
- this.gateways[this.bridgeid].apiKey != null
520
- ) {
517
+ if (this.gateways[this.bridgeid]?.apiKey != null) {
521
518
  this.clargs.options.apiKey = this.gateways[this.bridgeid].apiKey
522
519
  } else if (process.env.DECONZ_API_KEY != null) {
523
520
  this.clargs.options.apiKey = process.env.DECONZ_API_KEY
@@ -32,8 +32,7 @@ async function showFormPluginConfig () {
32
32
  console.log('%s: cachedAccessories: %o', config.name, cachedAccessories)
33
33
  const cachedGateways = cachedAccessories.filter((accessory) => {
34
34
  return accessory.plugin === 'homebridge-deconz' &&
35
- accessory.context != null &&
36
- accessory.context.className === 'Gateway'
35
+ accessory.context?.className === 'Gateway'
37
36
  })
38
37
  const result = {}
39
38
  for (const gateway of cachedGateways) {
@@ -207,8 +206,7 @@ async function showFormGateways (gateway) {
207
206
  const cachedAccessories = await homebridge.getCachedAccessories()
208
207
  const cachedGateways = cachedAccessories.filter((accessory) => {
209
208
  return accessory.plugin === 'homebridge-deconz' &&
210
- accessory.context != null &&
211
- accessory.context.className === 'Gateway'
209
+ accessory.context?.className === 'Gateway'
212
210
  })
213
211
  const result = {}
214
212
  for (const gateway of cachedGateways) {
@@ -249,7 +247,7 @@ async function showFormGateways (gateway) {
249
247
  // layout: null,
250
248
  // form: null
251
249
  // }, {
252
- // gateway: gateway != null ? gateway : gateways[0]
250
+ // gateway: gateway ?? gateways[0]
253
251
  // }, 'Gateway Settings', 'Homebridge Settings')
254
252
  const form = homebridge.createForm({
255
253
  footerDisplay: 'For a detailed description, see the [wiki](https://github.com/ebaauw/homebridge-deconz/wiki/Configuration).',
@@ -5,13 +5,12 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const { Colour, OptionParser } = require('homebridge-lib')
8
+ const { OptionParser } = require('homebridge-lib')
9
9
  const Deconz = require('../Deconz')
10
10
  const DeconzAccessory = require('../DeconzAccessory')
11
11
  const DeconzService = require('../DeconzService')
12
12
 
13
13
  const { toInstance, toInt, toObject, toString } = OptionParser
14
- const { defaultGamut } = Colour
15
14
  const { buttonEvent } = Deconz.ApiClient
16
15
  const { SINGLE, DOUBLE, LONG } = DeconzService.Button
17
16
  const rtypes = ['lights', 'sensors', 'groups']
@@ -30,51 +29,6 @@ const sensorsPrios = [
30
29
 
31
30
  // =============================================================================
32
31
 
33
- // See: http://www.developers.meethue.com/documentation/supported-lights
34
-
35
- const hueGamutType = { // Color gamut per light model.
36
- A: { // Color Lights
37
- r: [0.7040, 0.2960],
38
- g: [0.2151, 0.7106],
39
- b: [0.1380, 0.0800]
40
- },
41
- B: { // Extended Color Lights
42
- r: [0.6750, 0.3220],
43
- g: [0.4090, 0.5180],
44
- b: [0.1670, 0.0400]
45
- },
46
- C: { // next gen Extended Color Lights
47
- r: [0.6920, 0.3080],
48
- g: [0.1700, 0.7000],
49
- b: [0.1530, 0.0480]
50
- }
51
- }
52
-
53
- const hueGamutTypeByModel = {
54
- LCT001: 'B', // Hue bulb A19
55
- LCT002: 'B', // Hue Spot BR30
56
- LCT003: 'B', // Hue Spot GU10
57
- LCT007: 'B', // Hue bulb A19
58
- LCT010: 'C', // Hue bulb A19
59
- LCT011: 'C', // Hue BR30
60
- LCT012: 'C', // Hue Color Candle
61
- LCT014: 'C', // Hue bulb A19
62
- LCT015: 'C', // Hue bulb A19
63
- LCT016: 'C', // Hue bulb A19
64
- LLC005: 'A', // Living Colors Gen3 Bloom, Aura
65
- LLC006: 'A', // Living Colors Gen3 Iris
66
- LLC007: 'A', // Living Colors Gen3 Bloom, Aura
67
- LLC010: 'A', // Hue Living Colors Iris
68
- LLC011: 'A', // Hue Living Colors Bloom
69
- LLC012: 'A', // Hue Living Colors Bloom
70
- LLC013: 'A', // Disney Living Colors
71
- LLC014: 'A', // Living Colors Gen3 Bloom, Aura
72
- LLC020: 'C', // Hue Go
73
- LLM001: 'B', // Color Light Module
74
- LST001: 'A', // Hue LightStrips
75
- LST002: 'C' // Hue LightStrips Plus
76
- }
77
-
78
32
  const hueTapMap = {
79
33
  34: 1002, // press 1
80
34
  16: 2002, // press 2
@@ -99,9 +53,9 @@ class Resource {
99
53
  toString('uniqueid', uniqueid, true)
100
54
  const a = uniqueid.replace(/:/g, '').toUpperCase().split('-')
101
55
  return {
102
- mac: a.length > 0 ? a[0] : null,
103
- endpoint: a.length > 1 ? a[1] : null,
104
- cluster: a.length > 2 ? a[2] : null
56
+ mac: a?.[0],
57
+ endpoint: a?.[1],
58
+ cluster: a?.[2]
105
59
  }
106
60
  }
107
61
 
@@ -175,6 +129,11 @@ class Resource {
175
129
  * @type {boolean}
176
130
  */
177
131
  this.zigbee = true
132
+ } else if (this.isDeconzClip()) {
133
+ const a = body.uniqueid.split('-')
134
+ this.id = gateway.id + '-M' + a?.[0]
135
+ this.subtype = a?.[1]
136
+ this.zigbee = false
178
137
  } else {
179
138
  this.subtype = rtype[0].toUpperCase() + rid
180
139
  this.id = gateway.id + '-' + this.subtype
@@ -211,60 +170,16 @@ class Resource {
211
170
  ? body.swversion == null ? '0.0.0' : body.swversion
212
171
  : gateway.values.software
213
172
 
214
- switch (this.serviceName) {
215
- case 'Light': {
216
- if (body.action != null) {
217
- Object.assign(body.state, body.action)
218
- delete body.state.on
219
- }
220
- this.capabilities = {
221
- on: body.state.on !== undefined,
222
- bri: body.state.bri !== undefined,
223
- ct: body.state.ct !== undefined,
224
- ctMax: (body.ctmax != null && body.ctmax !== 0 && body.ctmax !== 65535)
225
- ? body.ctmax
226
- : 500,
227
- ctMin: (body.ctmin != null && body.ctmin !== 0)
228
- ? body.ctmin
229
- : 153,
230
- xy: body.state.xy !== undefined,
231
- gamut: defaultGamut,
232
- alert: body.state.alert !== undefined,
233
- colorLoop: body.state.effect !== undefined
234
- }
235
- if (body.capabilities != null) {
236
- const cap = body.capabilities
237
- if (cap.color != null) {
238
- if (cap.color.ct != null) {
239
- this.capabilities.ctMax = cap.color.ct.max
240
- this.capabilities.ctMin = cap.color.ct.min
241
- }
242
- if (cap.color.effects != null && cap.color.effects.length > 2) {
243
- this.capabilities.effects = cap.color.effects.slice(2).map((effect) => {
244
- return effect[0].toUpperCase() + effect.slice(1)
245
- })
246
- }
247
- if (cap.color.xy != null) {
248
- this.capabilities.gamut = {
249
- r: cap.color.xy.red,
250
- g: cap.color.xy.green,
251
- b: cap.color.xy.blue
252
- }
253
- }
254
- }
255
- }
256
- break
257
- }
258
- case 'WarningDevice':
259
- this.capabilities = {}
260
- break
261
- case 'WindowCovering':
262
- this.capabilities = {}
263
- break
264
- default:
265
- this.capabilities = {}
266
- break
267
- }
173
+ /** The name of the {@link DeconzService} subclass of the delegate of the
174
+ * corresponding HomeKit service, or `null` for unsupported and unknown
175
+ * resources.
176
+ *
177
+ * This is derived from the resource type and `type` in the resource body.
178
+ * @type {string}
179
+ */
180
+ this.serviceName = this._serviceName
181
+
182
+ this.capabilities = {}
268
183
 
269
184
  const f = 'patch' + this.serviceName
270
185
  if (typeof this[f] === 'function') {
@@ -277,8 +192,12 @@ class Resource {
277
192
  * @type {integer}
278
193
  */
279
194
  get prio () {
280
- if (this.rtype === 'groups') return -1
281
- if (this.rtype === 'lights') return 0xFF - this.endpoint
195
+ if (this.rtype === 'groups') {
196
+ return -1
197
+ }
198
+ if (this.rtype === 'lights' || this.isDeconzClip()) {
199
+ return 0xFF - this.endpoint
200
+ }
282
201
  return sensorsPrios.indexOf(this.serviceName)
283
202
  }
284
203
 
@@ -289,14 +208,7 @@ class Resource {
289
208
  */
290
209
  get rpath () { return '/' + this.rtype + '/' + this.rid }
291
210
 
292
- /** The name of the {@link DeconzService} subclass of the delegate of the
293
- * corresponding HomeKit service, or `null` for unsupported and unknown
294
- * resources.
295
- *
296
- * This is derived from the resource type and `type` in the resource body.
297
- * @type {string}
298
- */
299
- get serviceName () {
211
+ get _serviceName () {
300
212
  if (this.rtype === 'groups') {
301
213
  return 'Light'
302
214
  } else if (this.rtype === 'lights') {
@@ -314,11 +226,11 @@ class Resource {
314
226
  // case 'Door Lock': return null
315
227
  // case 'Door Lock Unit': return null
316
228
  case 'Fan': return 'Light'
317
- case 'On/Off light switch': return 'Light'
229
+ case 'On/Off light switch': return 'Switch'
318
230
  case 'On/Off light': return 'Light'
319
- case 'On/Off output': return 'Light'
320
- case 'On/Off plug-in unit': return 'Light'
321
- case 'Smart plug': return 'Light'
231
+ case 'On/Off output': return 'Outlet'
232
+ case 'On/Off plug-in unit': return 'Outlet'
233
+ case 'Smart plug': return 'Outlet'
322
234
  case 'Configuration tool': return ''
323
235
  case 'Range extender': return ''
324
236
  case 'Warning device': return 'WarningDevice'
@@ -327,55 +239,34 @@ class Resource {
327
239
  default: return null
328
240
  }
329
241
  } else { // (this.rtype === 'sensors')
330
- switch (this.body.type) {
331
- case 'CLIPAirPurifier':
332
- case 'ZHAAirPurifier': return 'AirPurifier'
333
- case 'ZHAAirQuality':
334
- case 'CLIPAirQuality': return 'AirQuality'
335
- case 'ZHAAlarm':
336
- case 'CLIPAlarm': return 'Alarm'
337
- case 'ZHABattery':
338
- case 'CLIPBattery': return 'Battery'
339
- case 'ZHACarbonMonoxide':
340
- case 'CLIPCarbonMonoxide': return 'CarbonMonoxide'
341
- case 'ZHAConsumption':
342
- case 'CLIPConsumption': return 'Consumption'
343
- // case 'ZHADoorLock':
344
- // case 'CLIPDoorLock': return null
242
+ const type = /^(CLIP|ZHA|ZGP)?([A-Za-z]*)$/.exec(this.body.type)?.[2]
243
+ switch (type) {
244
+ case 'AirPurifier': return 'AirPurifier'
245
+ case 'AirQuality': return 'AirQuality'
246
+ case 'Alarm': return 'Alarm'
247
+ case 'Battery': return 'Battery'
248
+ case 'CarbonMonoxide': return 'CarbonMonoxide'
249
+ case 'Consumption': return 'Consumption'
250
+ // case 'DoorLock': return null
345
251
  case 'Daylight': return 'Daylight'
346
- case 'ZHAFire':
347
- case 'CLIPFire': return 'Smoke'
348
- case 'CLIPGenericFlag': return 'Flag'
349
- case 'CLIPGenericStatus': return 'Status'
350
- case 'ZHAHumidity':
351
- case 'CLIPHumidity': return 'Humidity'
352
- case 'ZHALightLevel':
353
- case 'CLIPLightLevel': return 'LightLevel'
354
- // case 'ZHAMoisture':
355
- // case 'CLIPMoisture': return null
356
- case 'ZHAOpenClose':
357
- case 'CLIPOpenClose': return 'Contact'
358
- case 'ZHAPower':
359
- case 'CLIPPower': return 'Power'
360
- case 'ZHAPresence':
361
- case 'CLIPPresence': return 'Motion'
362
- case 'ZHAPressure':
363
- case 'CLIPPressure': return 'AirPressure'
364
- case 'ZHARelativeRotary': return 'Switch'
365
- case 'ZHASpectral': return ''
366
- case 'ZGPSwitch':
367
- case 'ZHASwitch':
368
- case 'CLIPSwitch': return 'Switch'
369
- case 'ZHATemperature':
370
- case 'CLIPTemperature': return 'Temperature'
371
- case 'ZHAThermostat':
372
- case 'CLIPThermostat': return 'Thermostat'
373
- case 'ZHATime':
374
- case 'CLIPTime': return ''
375
- case 'ZHAVibration':
376
- case 'CLIPVibration': return 'Motion'
377
- case 'ZHAWater':
378
- case 'CLIPWater': return 'Leak'
252
+ case 'Fire': return 'Smoke'
253
+ case 'GenericFlag': return 'Flag'
254
+ case 'GenericStatus': return 'Status'
255
+ case 'Humidity': return 'Humidity'
256
+ case 'LightLevel': return 'LightLevel'
257
+ case 'Moisture': return 'Humidity'
258
+ case 'OpenClose': return 'Contact'
259
+ case 'Power': return 'Power'
260
+ case 'Presence': return 'Motion'
261
+ case 'Pressure': return 'AirPressure'
262
+ case 'RelativeRotary': return 'Label'
263
+ case 'Spectral': return ''
264
+ case 'Switch': return 'Label'
265
+ case 'Temperature': return 'Temperature'
266
+ case 'Thermostat': return 'Thermostat'
267
+ case 'Time': return ''
268
+ case 'Vibration': return 'Motion'
269
+ case 'Water': return 'Leak'
379
270
  default: return null
380
271
  }
381
272
  }
@@ -386,34 +277,7 @@ class Resource {
386
277
  */
387
278
  patchLight (gateway) {
388
279
  switch (this.manufacturer) {
389
- case 'Busch-Jaeger':
390
- // See: https://www.busch-jaeger.de/en/products/product-solutions/dimmer/busch-radio-controlled-dimmer-zigbee-light-link/
391
- if (
392
- this.model === 'RM01' && // 6715 U-500 with 6736-84.
393
- this.capabilities.bri && this.body.type === 'On/Off light' // Issue #241
394
- ) {
395
- gateway.vdebug(
396
- '%s: ignoring state.bri for %s', this.rpath, this.body.type
397
- )
398
- this.capabilities.bri = false
399
- }
400
- break
401
- case 'dresden elektronik':
402
- // See: https://www.dresden-elektronik.de/funktechnik/solutions/wireless-light-control/wireless-ballasts/?L=1
403
- this.capabilities.computesXy = true
404
- break
405
- case 'FeiBit':
406
- if (this.model === 'FNB56-SKT1EHG1.2') { // issue #361
407
- this.body.type = 'On/Off plug-in unit'
408
- }
409
- break
410
280
  case 'GLEDOPTO':
411
- // See: https://www.led-trading.de/zigbee-kompatibel-controller-led-lichtsteuerung
412
- this.capabilities.gamut = {
413
- r: [0.7006, 0.2993],
414
- g: [0.1387, 0.8148],
415
- b: [0.1510, 0.0227]
416
- }
417
281
  if (this.model === 'GLEDOPTO') { // Issue #244
418
282
  if (
419
283
  this.endpoint === '0A' &&
@@ -444,37 +308,6 @@ class Resource {
444
308
  gateway.vdebug('%s: set model to %j', this.rpath, this.model)
445
309
  }
446
310
  break
447
- case 'IKEA of Sweden':
448
- // See: http://www.ikea.com/us/en/catalog/categories/departments/lighting/smart_lighting/
449
- this.capabilities.gamut = defaultGamut // Issue #956
450
- this.capabilities.noTransition = true
451
- if (this.model === 'TRADFRI bulb E27 CWS 806lm') {
452
- this.capabilities.computesXy = true
453
- this.capabilities.gamut = {
454
- r: [0.68, 0.31],
455
- g: [0.11, 0.82],
456
- b: [0.13, 0.04]
457
- }
458
- }
459
- break
460
- case 'innr':
461
- // See: https://shop.innrlighting.com/en/shop
462
- this.capabilities.gamut = { // Issue #152
463
- r: [0.8817, 0.1033],
464
- g: [0.2204, 0.7758],
465
- b: [0.0551, 0.1940]
466
- }
467
- if (this.model === 'SP 120') { // smart plug
468
- this.capabilities.bri = false
469
- }
470
- break
471
- case 'LEDVANCE':
472
- this.capabilities.gamut = {
473
- r: [0.6972, 0.3027],
474
- g: [0.1737, 0.6991],
475
- b: [0.1227, 0.0959]
476
- }
477
- break
478
311
  case 'LIDL Livarno Lux':
479
312
  this.capabilities.ctMax = 454 // 2200 K
480
313
  this.capabilities.ctMin = 153 // 6500 K
@@ -489,98 +322,53 @@ class Resource {
489
322
  ]
490
323
  }
491
324
  break
492
- case 'MLI': // Issue #439
493
- this.capabilities.gamut = {
494
- r: [0.68, 0.31],
495
- g: [0.11, 0.82],
496
- b: [0.13, 0.04]
497
- }
498
- if (this.capabilities.colorLoop) {
499
- this.capabilities.effects = [
500
- 'Sunset', 'Party', 'Worklight', 'Campfire', 'Romance', 'Nightlight'
501
- ]
502
- }
503
- break
504
- case 'OSRAM':
505
- this.capabilities.gamut = {
506
- r: [0.6850, 0.3149],
507
- g: [0.1780, 0.7253],
508
- b: [0.1241, 0.0578]
509
- }
510
- break
511
- case 'Philips':
512
- case 'Signify Netherlands B.V.':
513
- // See: http://www.developers.meethue.com/documentation/supported-lights
514
- this.manufacturer = 'Signify Netherlands B.V.'
515
- this.capabilities.breathe = true
516
- this.capabilities.computesXy = true
517
- if (this.body.capabilities == null) {
518
- const gamut = hueGamutTypeByModel[this.model] || 'C'
519
- this.capabilities.gamut = hueGamutType[gamut]
520
- }
521
- break
522
325
  default:
523
326
  break
524
327
  }
525
328
  }
526
329
 
330
+ /** Check whether resource is a CLIP sensor for Homebridge deCONZ.
331
+ * For these, we use the following conventions:
332
+ * - Use `uniqueid` to indicate device ID and subtype;
333
+ * - Use `swversion` to indicate readonly and range for `Flag` and `Status`.
334
+ */
335
+ isDeconzClip () {
336
+ return this.rtype === 'sensors' &&
337
+ this.body.type.startsWith('CLIP') &&
338
+ this.body.modelid === this.body.type && (
339
+ this.body.manufacturername === 'homebridge-deconz' ||
340
+ this.body.manufacturername === 'homebridge-hue'
341
+ )
342
+ }
343
+
527
344
  /** Patch a resource corresponding to a `Flag` service.
528
- * @param {DeconzAccessory.Gateway} gateway - The gateway.
529
345
  */
530
- patchFlag (gateway) {
531
- if (
532
- this.body.manufacturername === 'homebridge-hue' &&
533
- this.body.modelid === 'CLIPGenericFlag' &&
534
- this.body.swversion === '0'
535
- ) {
346
+ patchFlag () {
347
+ if (this.isDeconzClip() && this.body.swversion === '0') {
536
348
  this.capabilities.readonly = true
537
349
  }
538
350
  }
539
351
 
540
- /** Patch a resource corresponding to a `Flag` service.
541
- * @param {DeconzAccessory.Gateway} gateway - The gateway.
352
+ /** Patch a resource corresponding to a `Status` service.
542
353
  */
543
- patchStatus (gateway) {
544
- if (
545
- this.body.manufacturername === 'homebridge-hue' &&
546
- this.body.modelid === 'CLIPGenericStatus'
547
- ) {
354
+ patchStatus () {
355
+ if (this.isDeconzClip()) {
548
356
  const a = this.body.swversion.split(',')
549
357
  const min = parseInt(a[0])
550
358
  const max = parseInt(a[1])
551
- const step = parseInt(a[2])
552
- // Eve 3.1 displays the following controls, depending on the properties:
553
- // 1. {minValue: 0, maxValue: 1, minStep: 1} switch
554
- // 2. {minValue: a, maxValue: b, minStep: 1}, 1 < b - a <= 20 down|up
555
- // 3. {minValue: a, maxValue: b}, (a, b) != (0, 1) slider
556
- // 4. {minValue: a, maxValue: b, minStep: 1}, b - a > 20 slider
557
- // Avoid the following bugs:
558
- // 5. {minValue: 0, maxValue: 1} nothing
559
- // 6. {minValue: a, maxValue: b, minStep: 1}, b - a = 1 switch*
560
- // *) switch sends values 0 and 1 instead of a and b;
561
359
  if (min === 0 && max === 0) {
562
360
  this.capabilities.readonly = true
563
361
  } else if (min >= -127 && max <= 127 && min < max) {
564
- if (min === 0 && max === 1) {
565
- // Workaround Eve bug (case 5 above).
566
- this.capabilities.props = { minValue: min, maxValue: max, minStep: 1 }
567
- } else if (max - min === 1) {
568
- // Workaround Eve bug (case 6 above).
569
- this.capabilities.props = { minValue: min, maxValue: max }
570
- } else if (step !== 1) {
571
- // Default to slider for backwards compatibility.
572
- this.capabilities.props = { minValue: min, maxValue: max }
573
- } else {
574
- this.capabilities.props = { minValue: min, maxValue: max, minStep: 1 }
575
- }
362
+ this.capabilities.min = min
363
+ this.capabilities.max = max
576
364
  }
577
365
  }
578
366
  }
579
367
 
580
- /** Patch a resource corresponding to a `Switch` service.
368
+ /** Patch a resource corresponding to a `Label` service.
581
369
  * @param {DeconzAccessory.Gateway} gateway - The gateway.
582
370
  */
583
- patchSwitch (gateway) {
371
+ patchLabel (gateway) {
584
372
  const buttons = []
585
373
  let dots = false
586
374
  switch (this.manufacturer) {
@@ -671,6 +459,15 @@ class Resource {
671
459
  buttons.push([3, 'Turn Left', SINGLE])
672
460
  }
673
461
  break
462
+ case 'SYMFONISK sound remote gen2':
463
+ buttons.push([1, 'Play', SINGLE])
464
+ buttons.push([2, 'Plus', SINGLE | LONG, true])
465
+ buttons.push([3, 'Minus', SINGLE | LONG, true])
466
+ buttons.push([4, 'Previous', SINGLE])
467
+ buttons.push([5, 'Next', SINGLE])
468
+ buttons.push([6, 'One Dot', SINGLE | DOUBLE | LONG])
469
+ buttons.push([7, 'Two Dots', SINGLE | DOUBLE | LONG])
470
+ break
674
471
  case 'TRADFRI SHORTCUT Button':
675
472
  buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
676
473
  break
@@ -784,12 +581,16 @@ class Resource {
784
581
  }
785
582
  }
786
583
  break
787
- case 'lumi.sensor_86sw1': // Xiaomi wall switch (single button)
788
584
  case 'lumi.ctrl_ln1.aq1':
585
+ case 'lumi.sensor_86sw1': // Xiaomi wall switch (single button).
586
+ case 'lumi.switch.l1aeu1': // Xiaomi Aqara H1, see #1149.
587
+ case 'lumi.switch.n1aeu1': // Xiaomi Aqara H1, see #1149.
789
588
  buttons.push([1, 'Button', SINGLE | DOUBLE])
790
589
  break
791
- case 'lumi.sensor_86sw2': // Xiaomi wall switch (two buttons)
792
590
  case 'lumi.ctrl_ln2.aq1':
591
+ case 'lumi.sensor_86sw2': // Xiaomi wall switch (two buttons).
592
+ case 'lumi.switch.l2aeu1': // Xiaomi Aqara H2, see #1149.
593
+ case 'lumi.switch.n2aeu1': // Xiaomi Aqara H2, see #1149.
793
594
  buttons.push([1, 'Left', SINGLE | DOUBLE])
794
595
  buttons.push([2, 'Right', SINGLE | DOUBLE])
795
596
  buttons.push([3, 'Both', SINGLE | DOUBLE])
@@ -1255,9 +1056,8 @@ class Resource {
1255
1056
  }
1256
1057
 
1257
1058
  /** Patch a resource corresponding to a `Thermostat` service.
1258
- * @param {DeconzAccessory.Gateway} gateway - The gateway.
1259
1059
  */
1260
- patchThermostat (gateway) {
1060
+ patchThermostat () {
1261
1061
  if (this.manufacturer === 'ELKO' && this.model === 'Super TR') {
1262
1062
  this.capabilities.heatValue = 'heat'
1263
1063
  } else {
@@ -1266,9 +1066,8 @@ class Resource {
1266
1066
  }
1267
1067
 
1268
1068
  /** Patch a resource corresponding to a `WindowCovering` service.
1269
- * @param {DeconzAccessory.Gateway} gateway - The gateway.
1270
1069
  */
1271
- patchWindowCovering (gateway) {
1070
+ patchWindowCovering () {
1272
1071
  if (this.manufacturer === 'LUMI' && this.model === 'lumi.curtain.acn002') {
1273
1072
  this.capabilities.maxSpeed = 2
1274
1073
  this.capabilities.positionChange = true