zigbee-herdsman-converters 14.0.442 → 14.0.445

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.
@@ -20,6 +20,7 @@ const constants = require('../lib/constants');
20
20
  const libColor = require('../lib/color');
21
21
  const utils = require('../lib/utils');
22
22
  const exposes = require('../lib/exposes');
23
+ const xiaomi = require('../lib/xiaomi');
23
24
 
24
25
  const converters = {
25
26
  // #region Generic/recommended converters
@@ -423,11 +424,11 @@ const converters = {
423
424
  // This is for occupancy sensor that send motion start AND stop messages
424
425
  cluster: 'msOccupancySensing',
425
426
  type: ['attributeReport', 'readResponse'],
426
- options: [exposes.options.no_occupancy_since()],
427
+ options: [exposes.options.no_occupancy_since_false()],
427
428
  convert: (model, msg, publish, options, meta) => {
428
429
  if (msg.data.hasOwnProperty('occupancy')) {
429
430
  const payload = {occupancy: (msg.data.occupancy % 2) > 0};
430
- utils.noOccupancySince(msg.endpoint, payload, options, publish);
431
+ utils.noOccupancySince(msg.endpoint, options, publish, payload.occupancy ? 'stop' : 'start');
431
432
  return payload;
432
433
  }
433
434
  },
@@ -438,7 +439,7 @@ const converters = {
438
439
  // Therefore we need to publish the no_motion detected by ourselves.
439
440
  cluster: 'msOccupancySensing',
440
441
  type: ['attributeReport', 'readResponse'],
441
- options: [exposes.options.occupancy_timeout(), exposes.options.no_occupancy_since()],
442
+ options: [exposes.options.occupancy_timeout(), exposes.options.no_occupancy_since_true()],
442
443
  convert: (model, msg, publish, options, meta) => {
443
444
  if (msg.data.occupancy !== 1) {
444
445
  // In case of 0 no occupancy is reported.
@@ -463,7 +464,7 @@ const converters = {
463
464
  }
464
465
 
465
466
  const payload = {occupancy: true};
466
- utils.noOccupancySince(msg.endpoint, payload, options, publish);
467
+ utils.noOccupancySince(msg.endpoint, options, publish, 'start');
467
468
  return payload;
468
469
  },
469
470
  },
@@ -2789,6 +2790,33 @@ const converters = {
2789
2790
  }
2790
2791
  },
2791
2792
  },
2793
+ livolo_new_switch_state_4gang: {
2794
+ cluster: 'genPowerCfg',
2795
+ type: ['raw'],
2796
+ convert: (model, msg, publish, options, meta) => {
2797
+ const stateHeader = Buffer.from([122, 209]);
2798
+ if (msg.data.indexOf(stateHeader) === 0) {
2799
+ if (msg.data[10] === 7) {
2800
+ const status = msg.data[14];
2801
+ return {
2802
+ state_left: status & 1 ? 'ON' : 'OFF',
2803
+ state_right: status & 2 ? 'ON' : 'OFF',
2804
+ state_bottom_left: status & 4 ? 'ON' : 'OFF',
2805
+ state_bottom_right: status & 8 ? 'ON' : 'OFF',
2806
+ };
2807
+ }
2808
+ if (msg.data[10] === 13) {
2809
+ const status = msg.data[13];
2810
+ return {
2811
+ state_left: status & 1 ? 'ON' : 'OFF',
2812
+ state_right: status & 2 ? 'ON' : 'OFF',
2813
+ state_bottom_left: status & 4 ? 'ON' : 'OFF',
2814
+ state_bottom_right: status & 8 ? 'ON' : 'OFF',
2815
+ };
2816
+ }
2817
+ }
2818
+ },
2819
+ },
2792
2820
  livolo_curtain_switch_state: {
2793
2821
  cluster: 'genPowerCfg',
2794
2822
  type: ['raw'],
@@ -2948,7 +2976,8 @@ const converters = {
2948
2976
  meta.device.modelID = 'TI0001-socket';
2949
2977
  meta.device.save();
2950
2978
  }
2951
- if (msg.data.includes(Buffer.from([19, 1, 0]), 13)) {
2979
+ // No need to detect this switches, will be done by universal procedure
2980
+ /* if (msg.data.includes(Buffer.from([19, 1, 0]), 13)) {
2952
2981
  // new switch, hack
2953
2982
  meta.device.modelID = 'TI0001-switch';
2954
2983
  meta.device.save();
@@ -2957,7 +2986,7 @@ const converters = {
2957
2986
  // new switch, hack
2958
2987
  meta.device.modelID = 'TI0001-switch-2gang';
2959
2988
  meta.device.save();
2960
- }
2989
+ }*/
2961
2990
  if (msg.data.includes(Buffer.from([19, 5, 0]), 13)) {
2962
2991
  if (meta.logger) meta.logger.debug('Detected Livolo Curtain Switch');
2963
2992
  // curtain switch, hack
@@ -5230,40 +5259,27 @@ const converters = {
5230
5259
  type: ['attributeReport', 'readResponse'],
5231
5260
  options: [exposes.options.precision('temperature'), exposes.options.calibration('temperature')],
5232
5261
  convert: (model, msg, publish, options, meta) => {
5233
- if (msg.data['65281']) {
5234
- const data = msg.data['65281'];
5235
- const payload = {};
5236
-
5237
- if (data.hasOwnProperty('100')) {
5238
- if (['QBKG03LM', 'QBKG12LM', 'LLKZMK11LM'].includes(model.model)) {
5239
- const mapping = model.model === 'LLKZMK11LM' ? ['l1', 'l2'] : ['left', 'right'];
5240
- payload[`state_${mapping[0]}`] = data['100'] === 1 ? 'ON' : 'OFF';
5241
- payload[`state_${mapping[1]}`] = data['101'] === 1 ? 'ON' : 'OFF';
5242
- } else {
5243
- payload.state = data['100'] === 1 ? 'ON' : 'OFF';
5244
- }
5245
- }
5246
-
5247
- if (data.hasOwnProperty('152')) {
5248
- payload.power = precisionRound(data['152'], 2);
5249
- }
5250
-
5251
- if (data.hasOwnProperty('149')) {
5252
- // Consumption is deprecated
5253
- payload.consumption = precisionRound(data['149'], 2);
5254
- payload.energy = precisionRound(data['149'], 2);
5255
- }
5256
-
5257
- if (data.hasOwnProperty('3')) {
5258
- payload.temperature = calibrateAndPrecisionRoundOptions(data['3'], options, 'temperature');
5259
- }
5260
-
5261
- if (data.hasOwnProperty('150')) {
5262
- payload.voltage = precisionRound(data['150'] * 0.1, 1);
5263
- }
5264
-
5265
- return payload;
5262
+ const payload = xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data);
5263
+ return payload;
5264
+ },
5265
+ },
5266
+ xiaomi_basic_raw: {
5267
+ cluster: 'genBasic',
5268
+ type: ['raw'],
5269
+ options: (definition) => {
5270
+ const result = [];
5271
+ if (definition.exposes.find((e) => e.name === 'temperature')) {
5272
+ result.push(exposes.options.precision('temperature'), exposes.options.calibration('temperature'));
5273
+ }
5274
+ return result;
5275
+ },
5276
+ convert: (model, msg, publish, options, meta) => {
5277
+ let payload = {};
5278
+ if (Buffer.isBuffer(msg.data)) {
5279
+ const dataObject = xiaomi.buffer2DataObject(meta, model, msg.data);
5280
+ payload = xiaomi.numericAttributes2Payload(msg, meta, model, options, dataObject);
5266
5281
  }
5282
+ return payload;
5267
5283
  },
5268
5284
  },
5269
5285
  aqara_opple: {
@@ -5280,226 +5296,7 @@ const converters = {
5280
5296
  return result;
5281
5297
  },
5282
5298
  convert: (model, msg, publish, options, meta) => {
5283
- const payload = {};
5284
- if (msg.data.hasOwnProperty('247')) {
5285
- const data = msg.data['247'];
5286
- // Xiaomi struct parsing
5287
- const length = data.length;
5288
- for (let i=0; i < length; i++) {
5289
- const index = data[i];
5290
- let value = null;
5291
- switch (data[i+1]) {
5292
- case 16:
5293
- // 0x10 ZclBoolean
5294
- value = data.readUInt8(i+2);
5295
- i += 2;
5296
- break;
5297
- case 32:
5298
- // 0x20 Zcl8BitUint
5299
- value = data.readUInt8(i+2);
5300
- i += 2;
5301
- break;
5302
- case 33:
5303
- // 0x21 Zcl16BitUint
5304
- value = data.readUInt16LE(i+2);
5305
- i += 3;
5306
- break;
5307
- case 34:
5308
- // 0x22 Zcl24BitUint
5309
- value = data.readUIntLE(i+2, 3);
5310
- i += 4;
5311
- break;
5312
- case 35:
5313
- // 0x23 Zcl32BitUint
5314
- value = data.readUInt32LE(i+2);
5315
- i += 5;
5316
- break;
5317
- case 36:
5318
- // 0x24 Zcl40BitUint
5319
- value = data.readUIntLE(i+2, 5);
5320
- i += 6;
5321
- break;
5322
- case 37:
5323
- // 0x25 Zcl48BitUint
5324
- value = data.readUIntLE(i+2, 6);
5325
- i += 7;
5326
- break;
5327
- case 38:
5328
- // 0x26 Zcl56BitUint
5329
- value = data.readUIntLE(i+2, 7);
5330
- i += 8;
5331
- break;
5332
- case 39:
5333
- // 0x27 Zcl64BitUint
5334
- value = data.readBigUInt64BE(i+2);
5335
- i += 9;
5336
- break;
5337
- case 40:
5338
- // 0x28 Zcl8BitInt
5339
- value = data.readInt8(i+2);
5340
- i += 2;
5341
- break;
5342
- case 41:
5343
- // 0x29 Zcl16BitInt
5344
- value = data.readInt16LE(i+2);
5345
- i += 3;
5346
- break;
5347
- case 42:
5348
- // 0x2A Zcl24BitInt
5349
- value = data.readIntLE(i+2, 3);
5350
- i += 4;
5351
- break;
5352
- case 43:
5353
- // 0x2B Zcl32BitInt
5354
- value = data.readInt32LE(i+2);
5355
- i += 5;
5356
- break;
5357
- case 44:
5358
- // 0x2C Zcl40BitInt
5359
- value = data.readIntLE(i+2, 5);
5360
- i += 6;
5361
- break;
5362
- case 45:
5363
- // 0x2D Zcl48BitInt
5364
- value = data.readIntLE(i+2, 6);
5365
- i += 7;
5366
- break;
5367
- case 46:
5368
- // 0x2E Zcl56BitInt
5369
- value = data.readIntLE(i+2, 7);
5370
- i += 8;
5371
- break;
5372
- case 47:
5373
- // 0x2F Zcl64BitInt
5374
- value = data.readBigInt64BE(i+2);
5375
- i += 9;
5376
- break;
5377
- case 57:
5378
- // 0x39 ZclSingleFloat
5379
- value = data.readFloatLE(i+2);
5380
- i += 5;
5381
- break;
5382
- case 58:
5383
- // 0x3a ZclDoubleFloat
5384
- value = data.readDoubleLE(i+2);
5385
- i += 5;
5386
- break;
5387
- default:
5388
- if (meta.logger) meta.logger.debug(`${model.zigbeeModel}: unknown vtype=${data[i+1]}, pos=${i+1}`);
5389
- }
5390
-
5391
- if (index == 1) {
5392
- payload.voltage = value;
5393
- payload.battery = batteryVoltageToPercentage(value, '3V_2100');
5394
- } else if (index === 3) {
5395
- if (!['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
5396
- payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
5397
- }
5398
- } else if (index === 5) {
5399
- if (['JT-BZ-01AQ/A', 'RTCZCGQ11LM'].includes(model.model)) payload.power_outage_count = value;
5400
- } else if (index === 100) {
5401
- if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5402
- const mapping = model.model === 'QBCZ15LM' ? 'relay' : 'left';
5403
- payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5404
- } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
5405
- payload.click_mode = {1: 'fast', 2: 'multi'}[value];
5406
- } else if (['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
5407
- // We don't know what the value means for these devices.
5408
- // https://github.com/Koenkk/zigbee2mqtt/issues/11126
5409
- } else {
5410
- payload.state = value === 1 ? 'ON' : 'OFF';
5411
- }
5412
- } else if (index === 101) {
5413
- if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5414
- const mapping = model.model === 'QBCZ15LM' ? 'usb' : 'right';
5415
- payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5416
- } else if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
5417
- payload.state_center = value === 1 ? 'ON' : 'OFF';
5418
- } else if (['RTCGQ12LM'].includes(model.model)) {
5419
- payload.illuminance = calibrateAndPrecisionRoundOptions(value, options, 'illuminance');
5420
- } else if (['ZNJLBL01LM'].includes(model.model)) {
5421
- payload.battery = value;
5422
- }
5423
- } else if (index ===102 ) {
5424
- if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
5425
- payload.state_right = value === 1 ? 'ON' : 'OFF';
5426
- } else if (['RTCZCGQ11LM'].includes(model.model)) {
5427
- payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
5428
- 5: 'left_leave', 6: 'approach', 7: 'away', 255: null}[value];
5429
- }
5430
- } else if (index === 103) {
5431
- if (['RTCZCGQ11LM'].includes(model.model)) payload.monitoring_mode = value === 1 ? 'left_right' : 'undirected';
5432
- } else if (index === 105) {
5433
- if (['RTCGQ13LM'].includes(model.model)) {
5434
- payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[value];
5435
- } else if (['RTCZCGQ11LM'].includes(model.model)) {
5436
- payload.approach_distance = {0: 'far', 1: 'medium', 2: 'near'}[value];
5437
- }
5438
- } else if (index === 149) {
5439
- payload.energy = precisionRound(value, 2); // 0x95
5440
- // Consumption is deprecated
5441
- payload.consumption = payload.energy;
5442
- } else if (index === 150) payload.voltage = precisionRound(value * 0.1, 1); // 0x96
5443
- else if (index === 151) payload.current = precisionRound(value * 0.001, 4); // 0x97
5444
- else if (index === 152) payload.power = precisionRound(value, 2); // 0x98
5445
- else if (index === 159) payload.gas_sensitivity = {1: '15%LEL', 2: '10%LEL'}[value]; // JT-BZ-01AQ/A
5446
- else if (index === 160) payload.gas = value === 1; // JT-BZ-01AQ/A
5447
- else if (index === 161) payload.gas_density = value; // JT-BZ-01AQ/A
5448
- else if (index === 162) payload.test = value === 1; // JT-BZ-01AQ/A
5449
- else if (index === 163) payload.mute = value === 1; // JT-BZ-01AQ/A
5450
- else if (index === 164) payload.state = value === 1 ? 'preparation' : 'work'; // JT-BZ-01AQ/A
5451
- else if (index === 166) payload.linkage_alarm = value === 1; // JT-BZ-01AQ/A
5452
- else if (meta.logger) meta.logger.debug(`${model.zigbeeModel}: unknown index ${index} with value ${value}`);
5453
- }
5454
- }
5455
-
5456
- if (msg.data.hasOwnProperty('0')) payload.detection_period = msg.data['0'];
5457
- if (msg.data.hasOwnProperty('2')) {
5458
- if (['JT-BZ-01AQ/A'].includes(model.model)) payload.power_outage_count = msg.data['2'];
5459
- }
5460
- if (msg.data.hasOwnProperty('4')) payload.mode_switch = {4: 'anti_flicker_mode', 1: 'quick_mode'}[msg.data['4']];
5461
- if (msg.data.hasOwnProperty('10')) payload.switch_type = {1: 'toggle', 2: 'momentary'}[msg.data['10']];
5462
- if (msg.data.hasOwnProperty('240')) payload.flip_indicator_light = msg.data['240'] === 1 ? 'ON' : 'OFF';
5463
- if (msg.data.hasOwnProperty('258')) payload.detection_interval = msg.data['258'];
5464
- if (msg.data.hasOwnProperty('268')) {
5465
- if (['RTCGQ13LM'].includes(model.model)) {
5466
- payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[msg.data['268']];
5467
- } else if (['JT-BZ-01AQ/A'].includes(model.model)) {
5468
- payload.gas_sensitivity = {1: '15%LEL', 2: '10%LEL'}[msg.data['268']];
5469
- }
5470
- }
5471
- if (msg.data.hasOwnProperty('293')) payload.click_mode = {1: 'fast', 2: 'multi'}[msg.data['293']];
5472
- if (msg.data.hasOwnProperty('294')) payload.mute = msg.data['294'] === 1; // JT-BZ-01AQ/A
5473
- if (msg.data.hasOwnProperty('295')) payload.test = msg.data['295'] === 1; // JT-BZ-01AQ/A
5474
- if (msg.data.hasOwnProperty('313')) payload.state = msg.data['313'] === 1 ? 'preparation' : 'work'; // JT-BZ-01AQ/A
5475
- if (msg.data.hasOwnProperty('314')) payload.gas = msg.data['314'] === 1; // JT-BZ-01AQ/A
5476
- if (msg.data.hasOwnProperty('315')) payload.gas_density = msg.data['315']; // JT-BZ-01AQ/A
5477
- if (msg.data.hasOwnProperty('322')) payload.presence = msg.data['322'] === 1; // RTCZCGQ11LM
5478
- if (msg.data.hasOwnProperty('323')) {
5479
- payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
5480
- 5: 'left_leave', 6: 'approach', 7: 'away'}[msg.data['323']]; // RTCZCGQ11LM
5481
- }
5482
- // RTCZCGQ11LM
5483
- if (msg.data.hasOwnProperty('324')) payload.monitoring_mode = msg.data['324'] === 1 ? 'left_right' : 'undirected';
5484
- // RTCZCGQ11LM
5485
- if (msg.data.hasOwnProperty('326')) payload.approach_distance = {0: 'far', 1: 'medium', 2: 'near'}[msg.data['326']];
5486
- if (msg.data.hasOwnProperty('331')) payload.linkage_alarm = msg.data['331'] === 1; // JT-BZ-01AQ/A
5487
- if (msg.data.hasOwnProperty('512')) {
5488
- if (['ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM'].includes(model.model)) {
5489
- payload.button_lock = msg.data['512'] === 1 ? 'OFF' : 'ON';
5490
- } else {
5491
- const mode = {0x01: 'control_relay', 0x00: 'decoupled'}[msg.data['512']];
5492
- payload[postfixWithEndpointName('operation_mode', msg, model)] = mode;
5493
- }
5494
- }
5495
- if (msg.data.hasOwnProperty('513')) payload.power_outage_memory = msg.data['513'] === 1;
5496
- if (msg.data.hasOwnProperty('514')) payload.auto_off = msg.data['514'] === 1;
5497
- if (msg.data.hasOwnProperty('515')) payload.led_disabled_night = msg.data['515'] === 1;
5498
- if (msg.data.hasOwnProperty('519')) payload.consumer_connected = msg.data['519'] === 1;
5499
- if (msg.data.hasOwnProperty('523')) payload.overload_protection = precisionRound(msg.data['523'], 2);
5500
- if (msg.data.hasOwnProperty('550')) payload.button_switch_mode = msg.data['550'] === 1 ? 'relay_and_usb' : 'relay';
5501
- if (msg.data['mode'] !== undefined) payload.operation_mode = ['command', 'event'][msg.data['mode']];
5502
- if (msg.data.hasOwnProperty('1289')) payload.dimmer_mode = {3: 'rgbw', 1: 'dual_ct'}[msg.data['1289']];
5299
+ const payload = xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data);
5503
5300
  return payload;
5504
5301
  },
5505
5302
  },
@@ -1599,13 +1599,23 @@ const converters = {
1599
1599
  }
1600
1600
 
1601
1601
  const state = value.toLowerCase();
1602
+ let oldstate = 1;
1603
+ if (state === 'on') {
1604
+ oldstate = 108;
1605
+ }
1606
+ let channel = 1.0;
1602
1607
  const postfix = meta.endpoint_name || 'left';
1603
1608
  await entity.command('genOnOff', 'toggle', {}, {transactionSequenceNumber: 0});
1604
1609
  const payloadOn = {0x0001: {value: Buffer.from([1, 0, 0, 0, 0, 0, 0, 0]), type: 1}};
1605
1610
  const payloadOff = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 1}};
1606
1611
  const payloadOnRight = {0x0001: {value: Buffer.from([2, 0, 0, 0, 0, 0, 0, 0]), type: 2}};
1607
1612
  const payloadOffRight = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 2}};
1613
+ const payloadOnBottomLeft = {0x0001: {value: Buffer.from([4, 0, 0, 0, 0, 0, 0, 0]), type: 4}};
1614
+ const payloadOffBottomLeft = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 4}};
1615
+ const payloadOnBottomRight = {0x0001: {value: Buffer.from([8, 0, 0, 0, 0, 0, 0, 0]), type: 136}};
1616
+ const payloadOffBottomRight = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 136}};
1608
1617
  if (postfix === 'left') {
1618
+ await entity.command('genLevelCtrl', 'moveToLevelWithOnOff', {level: oldstate, transtime: channel});
1609
1619
  await entity.write('genPowerCfg', (state === 'on') ? payloadOn : payloadOff,
1610
1620
  {
1611
1621
  manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
@@ -1613,12 +1623,28 @@ const converters = {
1613
1623
  });
1614
1624
  return {state: {state_left: value.toUpperCase()}, readAfterWriteTime: 250};
1615
1625
  } else if (postfix === 'right') {
1626
+ channel = 2.0;
1627
+ await entity.command('genLevelCtrl', 'moveToLevelWithOnOff', {level: oldstate, transtime: channel});
1616
1628
  await entity.write('genPowerCfg', (state === 'on') ? payloadOnRight : payloadOffRight,
1617
1629
  {
1618
1630
  manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
1619
1631
  reservedBits: 3, direction: 1, transactionSequenceNumber: 0xe9,
1620
1632
  });
1621
1633
  return {state: {state_right: value.toUpperCase()}, readAfterWriteTime: 250};
1634
+ } else if (postfix === 'bottom_right') {
1635
+ await entity.write('genPowerCfg', (state === 'on') ? payloadOnBottomRight : payloadOffBottomRight,
1636
+ {
1637
+ manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
1638
+ reservedBits: 3, direction: 1, transactionSequenceNumber: 0xe9,
1639
+ });
1640
+ return {state: {state_bottom_right: value.toUpperCase()}, readAfterWriteTime: 250};
1641
+ } else if (postfix === 'bottom_left') {
1642
+ await entity.write('genPowerCfg', (state === 'on') ? payloadOnBottomLeft : payloadOffBottomLeft,
1643
+ {
1644
+ manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
1645
+ reservedBits: 3, direction: 1, transactionSequenceNumber: 0xe9,
1646
+ });
1647
+ return {state: {state_bottom_left: value.toUpperCase()}, readAfterWriteTime: 250};
1622
1648
  }
1623
1649
  return {state: {state: value.toUpperCase()}, readAfterWriteTime: 250};
1624
1650
  },
@@ -13,7 +13,7 @@ const tzLocal = {
13
13
  convertSet: async (entity, key, value, meta) => {
14
14
  const state = value.toLowerCase();
15
15
  utils.validateValue(state, ['toggle', 'off', 'on']);
16
- const endpoint = meta.device.getEndpoint(3);
16
+ const endpoint = meta.mapped.model === 'AU-A1ZBDSS' ? meta.device.getEndpoint(3) : meta.device.getEndpoint(2);
17
17
  await endpoint.command('genOnOff', state, {});
18
18
  return {state: {backlight_led: state.toUpperCase()}};
19
19
  },
package/devices/hive.js CHANGED
@@ -291,7 +291,7 @@ module.exports = [
291
291
  .withDescription('Period in minutes for which the setpoint hold will be active. 65535 = attribute not' +
292
292
  ' used. 0 to 360 to match the remote display').withEndpoint('heat'),
293
293
  exposes.climate().withSetpoint('occupied_heating_setpoint', 22, 22, 1).withLocalTemperature()
294
- .withSystemMode(['off', 'auto', 'heat']).withRunningState(['idle', 'heat']).withEndpoint('water'),
294
+ .withSystemMode(['off', 'auto', 'heat', 'emergency_heating']).withRunningState(['idle', 'heat']).withEndpoint('water'),
295
295
  exposes.binary('temperature_setpoint_hold', ea.ALL, true, false)
296
296
  .withDescription('Prevent changes. `false` = run normally. `true` = prevent from making changes.' +
297
297
  ' Must be set to `false` when system_mode = off or `true` for heat').withEndpoint('water'),
@@ -342,7 +342,7 @@ module.exports = [
342
342
  .withDescription('Period in minutes for which the setpoint hold will be active. 65535 = attribute not' +
343
343
  ' used. 0 to 360 to match the remote display').withEndpoint('heat'),
344
344
  exposes.climate().withSetpoint('occupied_heating_setpoint', 22, 22, 1).withLocalTemperature()
345
- .withSystemMode(['off', 'auto', 'heat']).withRunningState(['idle', 'heat']).withEndpoint('water'),
345
+ .withSystemMode(['off', 'auto', 'heat', 'emergency_heating']).withRunningState(['idle', 'heat']).withEndpoint('water'),
346
346
  exposes.binary('temperature_setpoint_hold', ea.ALL, true, false)
347
347
  .withDescription('Prevent changes. `false` = run normally. `true` = prevent from making changes.' +
348
348
  ' Must be set to `false` when system_mode = off or `true` for heat').withEndpoint('water'),
@@ -393,7 +393,7 @@ module.exports = [
393
393
  .withDescription('Period in minutes for which the setpoint hold will be active. 65535 = attribute not' +
394
394
  ' used. 0 to 360 to match the remote display').withEndpoint('heat'),
395
395
  exposes.climate().withSetpoint('occupied_heating_setpoint', 22, 22, 1).withLocalTemperature()
396
- .withSystemMode(['off', 'auto', 'heat']).withRunningState(['idle', 'heat']).withEndpoint('water'),
396
+ .withSystemMode(['off', 'auto', 'heat', 'emergency_heating']).withRunningState(['idle', 'heat']).withEndpoint('water'),
397
397
  exposes.binary('temperature_setpoint_hold', ea.ALL, true, false)
398
398
  .withDescription('Prevent changes. `false` = run normally. `true` = prevent from making changes.' +
399
399
  ' Must be set to `false` when system_mode = off or `true` for heat').withEndpoint('water'),
package/devices/livolo.js CHANGED
@@ -20,13 +20,18 @@ module.exports = [
20
20
  {
21
21
  zigbeeModel: ['TI0001 '],
22
22
  model: 'TI0001',
23
- description: 'Zigbee switch (1 and 2 gang)',
23
+ description: 'Zigbee switch (1, 2, 3, 4 gang)',
24
24
  vendor: 'Livolo',
25
- exposes: [e.switch().withEndpoint('left'), e.switch().withEndpoint('right')],
26
- fromZigbee: [fz.livolo_switch_state, fz.livolo_switch_state_raw],
27
- toZigbee: [tz.livolo_switch_on_off],
25
+ exposes: [
26
+ e.switch().withEndpoint('left'),
27
+ e.switch().withEndpoint('right'),
28
+ e.switch().withEndpoint('bottom_left'),
29
+ e.switch().withEndpoint('bottom_right'),
30
+ ],
31
+ fromZigbee: [fz.livolo_switch_state, fz.livolo_switch_state_raw, fz.livolo_new_switch_state_4gang],
32
+ toZigbee: [tz.livolo_socket_switch_on_off],
28
33
  endpoint: (device) => {
29
- return {'left': 6, 'right': 6};
34
+ return {'left': 6, 'right': 6, 'bottom_left': 6, 'bottom_right': 6};
30
35
  },
31
36
  configure: poll,
32
37
  onEvent: async (type, data, device) => {
@@ -41,6 +46,18 @@ module.exports = [
41
46
  globalStore.putValue(device, 'interval', interval);
42
47
  }
43
48
  }
49
+ if (data.cluster === 'genPowerCfg' && data.type === 'raw') {
50
+ const dp = data.data[10];
51
+ if (data.data[0] === 0x7a && data.data[1] === 0xd1) {
52
+ const endpoint = device.getEndpoint(6);
53
+ if (dp === 0x01) {
54
+ const options = {manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
55
+ reservedBits: 3, direction: 1, writeUndiv: true};
56
+ const payload = {0x2002: {value: [0, 0, 0, 0, 0, 0, 0], type: 0x0e}};
57
+ await endpoint.readResponse('genPowerCfg', 0xe9, payload, options);
58
+ }
59
+ }
60
+ }
44
61
  },
45
62
  },
46
63
  {
@@ -14,7 +14,8 @@ module.exports = [
14
14
  description: 'Flood light with motion sensor light outdoor',
15
15
  fromZigbee: extend.light_onoff_brightness().fromZigbee.concat([fz.ias_occupancy_alarm_1]),
16
16
  toZigbee: extend.light_onoff_brightness().toZigbee,
17
- exposes: [e.occupancy(), e.light_brightness()],
17
+ exposes: extend.light_onoff_brightness().exposes.concat([e.occupancy()]),
18
+ ota: ota.zigbeeOTA,
18
19
  },
19
20
  {
20
21
  zigbeeModel: ['E21-N13A'],
@@ -29,7 +30,22 @@ module.exports = [
29
30
  model: 'E21-N1EA',
30
31
  vendor: 'Sengled',
31
32
  description: 'Smart LED multicolor A19 bulb',
32
- extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]}),
33
+ fromZigbee: extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]}).fromZigbee.concat([fz.metering]),
34
+ toZigbee: extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]}).toZigbee,
35
+ exposes: extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]}).exposes.concat([e.power(), e.energy()]),
36
+ ota: ota.zigbeeOTA,
37
+ configure: async (device, coordinatorEndpoint, logger) => {
38
+ await extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]})
39
+ .configure(device, coordinatorEndpoint, logger);
40
+ device.powerSource = 'Mains (single phase)';
41
+ device.save();
42
+
43
+ const endpoint = device.getEndpoint(1);
44
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'seMetering']);
45
+ await reporting.readMeteringMultiplierDivisor(endpoint);
46
+ await reporting.currentSummDelivered(endpoint);
47
+ await reporting.instantaneousDemand(endpoint);
48
+ },
33
49
  },
34
50
  {
35
51
  zigbeeModel: ['E12-N1E'],
@@ -62,6 +78,7 @@ module.exports = [
62
78
  description: 'Element classic (A19)',
63
79
  fromZigbee: extend.light_onoff_brightness().fromZigbee.concat([fz.metering]),
64
80
  toZigbee: extend.light_onoff_brightness().toZigbee,
81
+ ota: ota.zigbeeOTA,
65
82
  configure: async (device, coordinatorEndpoint, logger) => {
66
83
  await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
67
84
  device.powerSource = 'Mains (single phase)';
@@ -73,7 +90,7 @@ module.exports = [
73
90
  await reporting.currentSummDelivered(endpoint);
74
91
  await reporting.instantaneousDemand(endpoint);
75
92
  },
76
- exposes: [e.power(), e.energy(), e.light_brightness()],
93
+ exposes: extend.light_onoff_brightness().exposes.concat([e.power(), e.energy()]),
77
94
  },
78
95
  {
79
96
  zigbeeModel: ['E11-G23', 'E11-G33'],
@@ -149,8 +166,22 @@ module.exports = [
149
166
  model: 'E1F-N5E',
150
167
  vendor: 'Sengled',
151
168
  description: 'Element color plus E12',
152
- extend: extend.light_onoff_brightness_colortemp_color(),
169
+ fromZigbee: extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]}).fromZigbee.concat([fz.metering]),
170
+ toZigbee: extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]}).toZigbee,
171
+ exposes: extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]}).exposes.concat([e.power(), e.energy()]),
153
172
  ota: ota.zigbeeOTA,
173
+ configure: async (device, coordinatorEndpoint, logger) => {
174
+ await extend.light_onoff_brightness_colortemp_color({colorTempRange: [154, 500]})
175
+ .configure(device, coordinatorEndpoint, logger);
176
+ device.powerSource = 'Mains (single phase)';
177
+ device.save();
178
+
179
+ const endpoint = device.getEndpoint(1);
180
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'seMetering']);
181
+ await reporting.readMeteringMultiplierDivisor(endpoint);
182
+ await reporting.currentSummDelivered(endpoint);
183
+ await reporting.instantaneousDemand(endpoint);
184
+ },
154
185
  },
155
186
  {
156
187
  zigbeeModel: ['E12-N14'],
@@ -206,15 +237,17 @@ module.exports = [
206
237
  await reporting.instantaneousDemand(endpoint);
207
238
  },
208
239
  exposes: [e.switch(), e.power(), e.energy()],
240
+ ota: ota.zigbeeOTA,
209
241
  },
210
242
  {
211
243
  zigbeeModel: ['E1E-G7F'],
212
244
  model: 'E1E-G7F',
213
245
  vendor: 'Sengled',
214
- description: 'Smart switch ',
246
+ description: 'Smart switch',
215
247
  fromZigbee: [fz.E1E_G7F_action],
216
248
  exposes: [e.action(['on', 'up', 'down', 'off', 'on_double', 'on_long', 'off_double', 'off_long'])],
217
249
  toZigbee: [],
250
+ ota: ota.zigbeeOTA,
218
251
  },
219
252
  {
220
253
  zigbeeModel: ['E11-N1G'],
@@ -222,6 +255,7 @@ module.exports = [
222
255
  vendor: 'Sengled',
223
256
  description: 'Vintage LED edison bulb (ST19)',
224
257
  extend: extend.light_onoff_brightness(),
258
+ ota: ota.zigbeeOTA,
225
259
  },
226
260
  {
227
261
  zigbeeModel: ['E1F-N9G'],
@@ -229,5 +263,6 @@ module.exports = [
229
263
  vendor: 'Sengled',
230
264
  description: 'Smart LED filament candle (E12)',
231
265
  extend: extend.light_onoff_brightness(),
266
+ ota: ota.zigbeeOTA,
232
267
  },
233
268
  ];
@@ -157,7 +157,10 @@ module.exports = [
157
157
  const endpoint = device.getEndpoint(1);
158
158
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement']);
159
159
  await reporting.onOff(endpoint);
160
- await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
160
+ try {
161
+ // https://github.com/Koenkk/zigbee2mqtt/issues/11706
162
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
163
+ } catch (error) {/* Fails for some*/}
161
164
  await reporting.activePower(endpoint);
162
165
  await reporting.rmsCurrent(endpoint);
163
166
  await reporting.rmsVoltage(endpoint, {change: 10});