zigbee-herdsman-converters 14.0.411 → 14.0.415

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.
@@ -2420,6 +2420,12 @@ const converters = {
2420
2420
  convert: (model, msg, publish, options, meta) => {
2421
2421
  const dpValue = tuya.firstDpValue(msg, meta, 'moes_switch');
2422
2422
  const dp = dpValue.dp;
2423
+
2424
+ // tuya_switch datapoints
2425
+ if (dp >= 1 && dp <= 4) {
2426
+ return null;
2427
+ }
2428
+
2423
2429
  const value = tuya.getDataValue(dpValue);
2424
2430
 
2425
2431
  switch (dp) {
@@ -2637,6 +2643,31 @@ const converters = {
2637
2643
  }
2638
2644
  },
2639
2645
  },
2646
+ ts011f_plug_indicator_mode: {
2647
+ cluster: 'genOnOff',
2648
+ type: ['attributeReport', 'readResponse'],
2649
+ convert: (model, msg, publish, options, meta) => {
2650
+ const property = 'tuyaBacklightMode'; // 0x8001 or 32769
2651
+ if (msg.data.hasOwnProperty(property)) {
2652
+ const value = msg.data[property];
2653
+ const lookup = {0: 'off', 1: 'off/on', 2: 'on/off', 3: 'on'};
2654
+ if (lookup.hasOwnProperty(value)) {
2655
+ return {indicator_mode: lookup[value]};
2656
+ }
2657
+ }
2658
+ },
2659
+ },
2660
+ ts011f_plug_child_mode: {
2661
+ cluster: 'genOnOff',
2662
+ type: ['attributeReport', 'readResponse'],
2663
+ convert: (model, msg, publish, options, meta) => {
2664
+ const property = (0x8000).toString(); // 32768
2665
+ if (msg.data.hasOwnProperty(property)) {
2666
+ const value = msg.data[property];
2667
+ return {child_lock: value ? 'LOCK' : 'UNLOCK'};
2668
+ }
2669
+ },
2670
+ },
2640
2671
  WSZ01_on_off_action: {
2641
2672
  cluster: 65029,
2642
2673
  type: 'raw',
@@ -5059,27 +5090,28 @@ const converters = {
5059
5090
  }
5060
5091
  },
5061
5092
  },
5062
- legrand_device_mode: {
5093
+ legrand_cluster_fc01: {
5063
5094
  cluster: 'manuSpecificLegrandDevices',
5064
5095
  type: ['readResponse'],
5065
5096
  convert: (model, msg, publish, options, meta) => {
5066
5097
  const payload = {};
5067
- const option0 = msg.data['0'];
5068
- // Beware that mode depends on device type
5069
- // contactor
5070
- if (option0 === 0x0003) payload.device_mode = 'switch';
5071
- else if (option0 === 0x0004) payload.device_mode = 'auto';
5072
- // dimmer
5073
- else if (option0 === 0x0101) payload.device_mode = 'dimmer_on';
5074
- else if (option0 === 0x0100) payload.device_mode = 'dimmer_off';
5075
- // pilot wire
5076
- else if (option0 === 0x0002) payload.device_mode = 'pilot_on';
5077
- else if (option0 === 0x0001) payload.device_mode = 'pilot_off';
5078
- // unknown case
5079
- else {
5080
- meta.logger.warn(`device_mode ${option0} not recognized, please fix me`);
5081
- payload.device_mode = 'unknown';
5098
+
5099
+ if (msg.data.hasOwnProperty('0')) {
5100
+ const option0 = msg.data['0'];
5101
+
5102
+ if (option0 === 0x0001) payload.device_mode = 'pilot_off';
5103
+ else if (option0 === 0x0002) payload.device_mode = 'pilot_on';
5104
+ else if (option0 === 0x0003) payload.device_mode = 'switch';
5105
+ else if (option0 === 0x0004) payload.device_mode = 'auto';
5106
+ else if (option0 === 0x0100) payload.device_mode = 'dimmer_off';
5107
+ else if (option0 === 0x0101) payload.device_mode = 'dimmer_on';
5108
+ else {
5109
+ meta.logger.warn(`device_mode ${option0} not recognized, please fix me`);
5110
+ payload.device_mode = 'unknown';
5111
+ }
5082
5112
  }
5113
+ if (msg.data.hasOwnProperty('1')) payload.permanent_led = msg.data['1'] === 0x00 ? 'OFF' : 'ON';
5114
+ if (msg.data.hasOwnProperty('2')) payload.led_when_on = msg.data['2'] === 0x00 ? 'OFF' : 'ON';
5083
5115
  return payload;
5084
5116
  },
5085
5117
  },
@@ -5329,8 +5361,11 @@ const converters = {
5329
5361
  if (index == 1) {
5330
5362
  payload.voltage = value;
5331
5363
  payload.battery = batteryVoltageToPercentage(value, '3V_2100');
5332
- } else if (index === 3) payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
5333
- else if (index === 5) {
5364
+ } else if (index === 3) {
5365
+ if (!['WXCJKG11LM ', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
5366
+ payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
5367
+ }
5368
+ } else if (index === 5) {
5334
5369
  if (['JT-BZ-01AQ/A', 'RTCZCGQ11LM'].includes(model.model)) payload.power_outage_count = value;
5335
5370
  } else if (index === 100) {
5336
5371
  if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
@@ -5338,7 +5373,7 @@ const converters = {
5338
5373
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5339
5374
  } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
5340
5375
  payload.click_mode = {1: 'fast', 2: 'multi'}[value];
5341
- } else if (['WXCJKG11LM ', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
5376
+ } else if (['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
5342
5377
  // We don't know what the value means for these devices.
5343
5378
  // https://github.com/Koenkk/zigbee2mqtt/issues/11126
5344
5379
  } else {
@@ -8300,79 +8335,89 @@ const converters = {
8300
8335
  return {action};
8301
8336
  },
8302
8337
  },
8303
- ZMAM02: {
8338
+ ZMAM02_cover: {
8304
8339
  cluster: 'manuSpecificTuya',
8305
- type: ['commandDataResponse', 'commandDataReport', 'commandSetDataresponse'],
8340
+ type: ['commandDataReport', 'commandDataResponse'],
8341
+ options: [exposes.options.invert_cover()],
8306
8342
  convert: (model, msg, publish, options, meta) => {
8307
- const result = {};
8308
- // let value = tuya.getDataValue(dpValue);
8309
- // let result = null;
8310
- for (const dpValue of msg.data.dpValues) {
8311
- const value = tuya.getDataValue(dpValue);
8312
- switch (dpValue.dp) {
8313
- case tuya.dataPoints.AM02Control:
8314
- result.control = tuya.ZMAM02.AM02Control[value];
8315
- break;
8316
- case tuya.dataPoints.AM02PercentControl:
8317
- result.percent_control = value;
8343
+ const dpValue = tuya.firstDpValue(msg, meta, 'ZMAM02_cover');
8344
+ const dp = dpValue.dp;
8345
+ const value = tuya.getDataValue(dpValue);
8346
+ switch (dp) {
8347
+ case tuya.dataPoints.coverPosition: // Started moving to position (triggered from Zigbee)
8348
+ case tuya.dataPoints.coverArrived: { // Arrived at position
8349
+ const running = dp === tuya.dataPoints.coverArrived ? false : true;
8350
+ const invert = tuya.isCoverInverted(meta.device.manufacturerName) ? !options.invert_cover : options.invert_cover;
8351
+ const position = invert ? 100 - (value & 0xFF) : (value & 0xFF);
8352
+ if (position > 0 && position <= 100) {
8353
+ return {running, position, state: 'OPEN'};
8354
+ } else if (position == 0) { // Report fully closed
8355
+ return {running, position, state: 'CLOSE'};
8356
+ } else {
8357
+ return {running}; // Not calibrated yet, no position is available
8358
+ }
8359
+ }
8360
+ case tuya.dataPoints.coverSpeed: // Cover is reporting its current speed setting
8361
+ return {motor_speed: value};
8362
+ case tuya.dataPoints.state: // Ignore the cover state, it's not reliable between different covers!
8363
+ case tuya.dataPoints.coverChange: // Ignore manual cover change, it's not reliable between different covers!
8364
+ break;
8365
+ case tuya.dataPoints.config: // Returned by configuration set; ignore
8366
+ break;
8367
+ case tuya.dataPoints.AM02MotorWorkingMode:
8368
+ switch (value) {
8369
+ case 0: // continuous 1
8370
+ return {motor_working_mode: 'continuous'};
8371
+ case 1: // intermittently
8372
+ return {motor_working_mode: 'intermittently'};
8373
+ default:
8374
+ meta.logger.warn('ZMAM02: ' +
8375
+ `Mode ${value} is not recognized.`);
8318
8376
  break;
8319
- case tuya.dataPoints.AM02ControlBackMode:
8320
- result.control_back_mode = tuya.ZMAM02.AM02Direction[value];
8377
+ }
8378
+ break;
8379
+ case tuya.dataPoints.AM02Border:
8380
+ switch (value) {
8381
+ case 0: // up
8382
+ return {border: 'up'};
8383
+ case 1: // down
8384
+ return {border: 'down'};
8385
+ case 2: // down_delete
8386
+ return {border: 'down_delete'};
8387
+ default:
8388
+ meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
8389
+ `Mode ${value} is not recognized.`);
8321
8390
  break;
8322
- case tuya.dataPoints.AM02MotorWorkingMode:
8323
- switch (value) {
8324
- case 0: // continuous 1
8325
- result.motor_working_mode = 'continuous';
8326
- break;
8327
- case 1: // intermittently
8328
- result.motor_working_mode = 'intermittently';
8329
- break;
8330
- default:
8331
- meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
8332
- `Mode ${value} is not recognized.`);
8333
- break;
8334
- }
8391
+ }
8392
+ break;
8393
+ case tuya.dataPoints.AM02Direction:
8394
+ switch (value) {
8395
+ case 0:
8396
+ return {motor_direction: 'forward'};
8397
+ case 1:
8398
+ return {motor_direction: 'back'};
8399
+ default:
8400
+ meta.logger.warn('ZMAM02: ' +
8401
+ `Mode ${value} is not recognized.`);
8335
8402
  break;
8336
- case tuya.dataPoints.AM02Border:
8337
- switch (value) {
8338
- case 0: // up
8339
- result.border = 'up';
8340
- break;
8341
- case 1: // down
8342
- result.border = 'down';
8343
- break;
8344
- case 2: // down_delete
8345
- result.border = 'down_delete';
8346
- break;
8347
- default:
8348
- meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
8403
+ }
8404
+ break;
8405
+ case tuya.dataPoints.AM02Mode:
8406
+ switch (value) {
8407
+ case 0: // morning
8408
+ return {mode: 'morning'};
8409
+ case 1: // night
8410
+ return {mode: 'night'};
8411
+ default:
8412
+ meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
8349
8413
  `Mode ${value} is not recognized.`);
8350
- break;
8351
- }
8352
- break;
8353
- case tuya.dataPoints.AM02PercentState:
8354
- result.percent_state = value;
8355
- break;
8356
- case tuya.dataPoints.AM02Mode:
8357
- switch (value) {
8358
- case 0: // morning
8359
- result.mode = 'morning';
8360
- break;
8361
- case 1: // night
8362
- result.mode = 'night';
8363
- break;
8364
- default:
8365
- meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
8366
- `Mode ${value} is not recognized.`);
8367
- break;
8368
- }
8369
8414
  break;
8370
- default:
8371
- meta.logger.warn(`fromZigbee.Zemismart Shader Konverter (Zm_AM02): NOT RECOGNIZED ` +
8372
- `DP #${dpValue.dp} with data ${JSON.stringify(dpValue)}`);
8373
8415
  }
8416
+ break;
8417
+ default: // Unknown code
8418
+ meta.logger.warn(`ZMAM02_cover: Unhandled DP #${dp} for ${meta.device.manufacturerName}:
8419
+ ${JSON.stringify(dpValue)}`);
8374
8420
  }
8375
- return result;
8376
8421
  },
8377
8422
  },
8378
8423
  // #endregion
@@ -2211,7 +2211,7 @@ const converters = {
2211
2211
  key: ['led_disabled_night'],
2212
2212
  convertSet: async (entity, key, value, meta) => {
2213
2213
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2214
- 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2214
+ 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM', 'SSM-U01'].includes(meta.mapped.model)) {
2215
2215
  await entity.write('aqaraOpple', {0x0203: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
2216
2216
  } else if (['ZNCZ11LM'].includes(meta.mapped.model)) {
2217
2217
  const payload = value ?
@@ -2226,7 +2226,7 @@ const converters = {
2226
2226
  },
2227
2227
  convertGet: async (entity, key, meta) => {
2228
2228
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2229
- 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2229
+ 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM', 'SSM-U01'].includes(meta.mapped.model)) {
2230
2230
  await entity.read('aqaraOpple', [0x0203], manufacturerOptions.xiaomi);
2231
2231
  } else {
2232
2232
  throw new Error('Not supported');
@@ -3218,7 +3218,7 @@ const converters = {
3218
3218
  );
3219
3219
  break;
3220
3220
  case 'indicate_light':
3221
- await tuya.sendDataPointValue(
3221
+ await tuya.sendDataPointEnum(
3222
3222
  entity,
3223
3223
  tuya.dataPoints.moesSwitchIndicateLight,
3224
3224
  utils.getKey(tuya.moesSwitch.indicateLight, value),
@@ -4523,6 +4523,10 @@ const converters = {
4523
4523
  const enableLedIfOn = value === 'ON' || (value === 'OFF' ? false : !!value);
4524
4524
  const payload = {1: {value: enableLedIfOn, type: 16}};
4525
4525
  await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
4526
+ return {state: {'permanent_led': value}};
4527
+ },
4528
+ convertGet: async (entity, key, meta) => {
4529
+ await entity.read('manuSpecificLegrandDevices', [0x0001], manufacturerOptions.legrand);
4526
4530
  },
4527
4531
  },
4528
4532
  legrand_settingEnableLedIfOn: {
@@ -4534,15 +4538,10 @@ const converters = {
4534
4538
  const enableLedIfOn = value === 'ON' || (value === 'OFF' ? false : !!value);
4535
4539
  const payload = {2: {value: enableLedIfOn, type: 16}};
4536
4540
  await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
4541
+ return {state: {'led_when_on': value}};
4537
4542
  },
4538
- },
4539
- legrand_settingEnableDimmer: {
4540
- key: ['dimmer_enabled'],
4541
- convertSet: async (entity, key, value, meta) => {
4542
- // enable the dimmer, requires a recent firmware on the device
4543
- const enableDimmer = value === 'ON' || (value === 'OFF' ? false : !!value);
4544
- const payload = {0: {value: enableDimmer ? 0x0101 : 0x0100, type: 9}};
4545
- await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
4543
+ convertGet: async (entity, key, meta) => {
4544
+ await entity.read('manuSpecificLegrandDevices', [0x0002], manufacturerOptions.legrand);
4546
4545
  },
4547
4546
  },
4548
4547
  legrand_deviceMode: {
@@ -5995,6 +5994,32 @@ const converters = {
5995
5994
  await entity.read('genOnOff', ['tuyaBacklightMode']);
5996
5995
  },
5997
5996
  },
5997
+ ts011f_plug_indicator_mode: {
5998
+ key: ['indicator_mode'],
5999
+ convertSet: async (entity, key, value, meta) => {
6000
+ if (typeof value === 'string') {
6001
+ value = value.toLowerCase();
6002
+ const lookup = {'off': 0, 'off/on': 1, 'on/off': 2, 'on': 3};
6003
+ utils.validateValue(value, Object.keys(lookup));
6004
+ value = lookup[value];
6005
+ }
6006
+
6007
+ if (typeof value === 'number' && value >= 0 && value <= 3) {
6008
+ await entity.write('genOnOff', {tuyaBacklightMode: value});
6009
+ } else {
6010
+ meta.logger.warn(`toZigbee.ts011f_plug_indicator_mode: Unsupported value ${value}`);
6011
+ }
6012
+ },
6013
+ convertGet: async (entity, key, meta) => {
6014
+ await entity.read('genOnOff', ['tuyaBacklightMode']);
6015
+ },
6016
+ },
6017
+ ts011f_plug_child_mode: {
6018
+ key: ['child_lock'],
6019
+ convertSet: async (entity, key, value, meta) => {
6020
+ await entity.write('genOnOff', {0x8000: {value: value === 'LOCK', type: 0x10}});
6021
+ },
6022
+ },
5998
6023
  hy_thermostat: {
5999
6024
  key: [
6000
6025
  'child_lock', 'current_heating_setpoint', 'local_temperature_calibration',
@@ -6899,30 +6924,56 @@ const converters = {
6899
6924
  }
6900
6925
  },
6901
6926
  },
6902
- ZMAM02: {
6903
- key: ['control', 'percent_control', 'mode', 'control_back_mode', 'border', 'motor_working_mode'],
6927
+ ZMAM02_cover: {
6928
+ key: ['state', 'position', 'mode', 'motor_direction', 'border', 'motor_working_mode'],
6929
+ options: [exposes.options.invert_cover()],
6904
6930
  convertSet: async (entity, key, value, meta) => {
6931
+ if (key === 'position') {
6932
+ if (value >= 0 && value <= 100) {
6933
+ const invert = tuya.isCoverInverted(meta.device.manufacturerName) ?
6934
+ !meta.options.invert_cover : meta.options.invert_cover;
6935
+
6936
+ value = invert ? 100 - value : value;
6937
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.coverPosition, value);
6938
+ } else {
6939
+ throw new Error('TuYa_cover_control: Curtain motor position is out of range');
6940
+ }
6941
+ } else if (key === 'state') {
6942
+ const stateEnums = tuya.getCoverStateEnums(meta.device.manufacturerName);
6943
+ meta.logger.debug(`ZMAM02: Using state enums for ${meta.device.manufacturerName}:
6944
+ ${JSON.stringify(stateEnums)}`);
6945
+ value = value.toLowerCase();
6946
+ switch (value) {
6947
+ case 'close':
6948
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, stateEnums.close);
6949
+ break;
6950
+ case 'open':
6951
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, stateEnums.open);
6952
+ break;
6953
+ case 'stop':
6954
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, stateEnums.stop);
6955
+ break;
6956
+ default:
6957
+ throw new Error('ZMAM02: Invalid command received');
6958
+ }
6959
+ }
6905
6960
  switch (key) {
6906
- case 'control':
6907
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, utils.getKey(tuya.ZMAM02.AM02Control, value));
6908
- break;
6909
- case 'percent_control':
6910
- await tuya.sendDataPointValue(entity, tuya.dataPoints.AM02PercentState, value);
6911
- break;
6912
6961
  case 'mode':
6913
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Mode, utils.getKey(tuya.ZMAM02.AM02Mode, value));
6962
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Mode, utils.getKey(tuya.ZMLookups.AM02Mode, value));
6914
6963
  break;
6915
- case 'control_back_mode':
6916
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02ControlBackMode, utils.getKey(tuya.ZMAM02.AM02Direction, value));
6964
+ case 'motor_direction':
6965
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Direction, utils.getKey(tuya.ZMLookups.AM02Direction, value));
6917
6966
  break;
6918
6967
  case 'border':
6919
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Border, utils.getKey(tuya.ZMAM02.AM02Border, value));
6968
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Border, utils.getKey(tuya.ZMLookups.AM02Border, value));
6920
6969
  break;
6921
6970
  case 'motor_working_mode':
6922
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02MotorWorkingMode, utils.getKey(tuya.ZMAM02.AM02MotorMode, value));
6971
+ await tuya.sendDataPointEnum(
6972
+ entity,
6973
+ tuya.dataPoints.AM02MotorWorkingMode,
6974
+ utils.getKey(tuya.ZMLookups.AM02MotorWorkingMode,
6975
+ value));
6923
6976
  break;
6924
- default: // Unknown Key
6925
- meta.logger.warn(`toZigbee.ZMAM02: Unhandled Key ${key}`);
6926
6977
  }
6927
6978
  },
6928
6979
  },
@@ -12,12 +12,12 @@ module.exports = [
12
12
  model: 'K4003C/L4003C/N4003C/NT4003C',
13
13
  vendor: 'BTicino',
14
14
  description: 'Light switch with neutral',
15
- fromZigbee: [fz.identify, fz.on_off, fz.K4003C_binary_input],
15
+ fromZigbee: [fz.identify, fz.on_off, fz.K4003C_binary_input, fz.legrand_cluster_fc01],
16
16
  toZigbee: [tz.on_off, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn, tz.legrand_identify],
17
17
  exposes: [
18
18
  e.switch(), e.action(['identify', 'on', 'off']),
19
- exposes.binary('permanent_led', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
20
- exposes.binary('led_when_on', ea.STATE_SET, 'ON', 'OFF').withDescription('Enables the LED when the light is on'),
19
+ exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
20
+ exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on'),
21
21
  ],
22
22
  configure: async (device, coordinatorEndpoint, logger) => {
23
23
  const endpoint = device.getEndpoint(1);
@@ -30,17 +30,17 @@ module.exports = [
30
30
  vendor: 'BTicino',
31
31
  description: 'Dimmer switch with neutral',
32
32
  extend: extend.light_onoff_brightness({noConfigure: true}),
33
- fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration],
33
+ fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration, fz.legrand_cluster_fc01],
34
34
  toZigbee: [tz.light_onoff_brightness, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn,
35
- tz.legrand_settingEnableDimmer, tz.legrand_identify, tz.ballast_config],
35
+ tz.legrand_deviceMode, tz.legrand_identify, tz.ballast_config],
36
36
  exposes: [e.light_brightness(),
37
37
  exposes.numeric('ballast_minimum_level', ea.ALL).withValueMin(1).withValueMax(254)
38
38
  .withDescription('Specifies the minimum brightness value'),
39
39
  exposes.numeric('ballast_maximum_level', ea.ALL).withValueMin(1).withValueMax(254)
40
40
  .withDescription('Specifies the maximum brightness value'),
41
- exposes.binary('dimmer_enabled', ea.STATE_SET, 'ON', 'OFF').withDescription('Allow the device to change brightness'),
42
- exposes.binary('permanent_led', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
43
- exposes.binary('led_when_on', ea.STATE_SET, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
41
+ exposes.binary('device_mode', ea.ALL, 'dimmer_on', 'dimmer_off').withDescription('Allow the device to change brightness'),
42
+ exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
43
+ exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
44
44
  configure: async (device, coordinatorEndpoint, logger) => {
45
45
  await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
46
46
  const endpoint = device.getEndpoint(1);
@@ -76,10 +76,10 @@ module.exports = [
76
76
  description: 'DIN power consumption module (same as Legrand 412015)',
77
77
  vendor: 'BTicino',
78
78
  extend: extend.switch(),
79
- fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_device_mode, fz.ignore_basic_report, fz.ignore_genOta],
79
+ fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
80
80
  toZigbee: [tz.legrand_deviceMode, tz.on_off, tz.legrand_identify, tz.electrical_measurement_power],
81
81
  exposes: [exposes.switch().withState('state', true, 'On/off (works only if device is in "switch" mode)'),
82
- e.power().withAccess(ea.STATE_GET), exposes.enum( 'device_mode', ea.ALL, ['switch', 'auto'])
82
+ e.power().withAccess(ea.STATE_GET), exposes.enum('device_mode', ea.ALL, ['switch', 'auto'])
83
83
  .withDescription('switch: allow on/off, auto will use wired action via C1/C2 on contactor for example with HC/HP')],
84
84
  configure: async (device, coordinatorEndpoint, logger) => {
85
85
  const endpoint = device.getEndpoint(1);
@@ -375,4 +375,44 @@ module.exports = [
375
375
  },
376
376
  exposes: [e.battery(), e.illuminance(), e.temperature(), e.humidity(), e.pressure()],
377
377
  },
378
+ {
379
+ zigbeeModel: ['EFEKTA_eFlower_Pro'],
380
+ model: 'EFEKTA_eFlower_Pro',
381
+ vendor: 'Custom devices (DiY)',
382
+ description: '[Plant Wattering Sensor with e-ink display 2.13](https://efektalab.com/eFlowerPro)',
383
+ fromZigbee: [fz.temperature, fz.humidity, fz.illuminance, fz.soil_moisture, fz.battery],
384
+ toZigbee: [tz.factory_reset],
385
+ configure: async (device, coordinatorEndpoint, logger) => {
386
+ const firstEndpoint = device.getEndpoint(1);
387
+ await reporting.bind(firstEndpoint, coordinatorEndpoint, [
388
+ 'genPowerCfg', 'msTemperatureMeasurement', 'msRelativeHumidity', 'msIlluminanceMeasurement', 'msSoilMoisture']);
389
+ const overides = {min: 0, max: 21600, change: 0};
390
+ await reporting.batteryVoltage(firstEndpoint, overides);
391
+ await reporting.batteryPercentageRemaining(firstEndpoint, overides);
392
+ await reporting.temperature(firstEndpoint, overides);
393
+ await reporting.humidity(firstEndpoint, overides);
394
+ await reporting.illuminance(firstEndpoint, overides);
395
+ await reporting.soil_moisture(firstEndpoint, overides);
396
+ },
397
+ exposes: [e.soil_moisture(), e.battery(), e.illuminance(), e.temperature(), e.humidity()],
398
+ },
399
+ {
400
+ zigbeeModel: ['EFEKTA_eTH102'],
401
+ model: 'EFEKTA_eTH102',
402
+ vendor: 'Custom devices (DiY)',
403
+ description: '[Mini digital thermometer & hygrometer with e-ink1.02](http://efektalab.com/eTH102)',
404
+ fromZigbee: [fz.temperature, fz.humidity, fz.battery],
405
+ toZigbee: [tz.factory_reset],
406
+ configure: async (device, coordinatorEndpoint, logger) => {
407
+ const endpoint = device.getEndpoint(1);
408
+ await reporting.bind(endpoint, coordinatorEndpoint, [
409
+ 'genPowerCfg', 'msTemperatureMeasurement', 'msRelativeHumidity']);
410
+ const overides = {min: 0, max: 21600, change: 0};
411
+ await reporting.batteryVoltage(endpoint, overides);
412
+ await reporting.batteryPercentageRemaining(endpoint, overides);
413
+ await reporting.temperature(endpoint, overides);
414
+ await reporting.humidity(endpoint, overides);
415
+ },
416
+ exposes: [e.battery(), e.temperature(), e.humidity()],
417
+ },
378
418
  ];
@@ -39,7 +39,8 @@ module.exports = [
39
39
  description: 'Power plug',
40
40
  fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.device_temperature],
41
41
  toZigbee: [tz.on_off],
42
- exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), e.device_temperature()],
42
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), e.device_temperature(), e.ac_frequency()],
43
+ options: [exposes.options.precision(`ac_frequency`)],
43
44
  configure: async (device, coordinatorEndpoint, logger) => {
44
45
  const endpoint = device.getEndpoint(2);
45
46
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering', 'genDeviceTempCfg']);
@@ -52,6 +53,7 @@ module.exports = [
52
53
  await reporting.readMeteringMultiplierDivisor(endpoint);
53
54
  await reporting.currentSummDelivered(endpoint, {change: [0, 20]}); // Limit reports to once every 5m, or 0.02kWh
54
55
  await reporting.instantaneousDemand(endpoint, {min: constants.repInterval.MINUTES_5, change: 10});
56
+ await reporting.acFrequency(endpoint);
55
57
  },
56
58
  endpoint: (device) => {
57
59
  return {default: 2};
@@ -6,7 +6,8 @@ const ea = exposes.access;
6
6
 
7
7
  module.exports = [
8
8
  {
9
- fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_wt9agwf3'}],
9
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_wt9agwf3'},
10
+ {modelID: 'TS0601', manufacturerName: '_TZE200_5uodvhgc'}],
10
11
  model: 'FK_V02',
11
12
  vendor: 'FrankEver',
12
13
  description: 'Zigbee smart water valve',
@@ -62,10 +62,14 @@ const gledoptoExtend = {
62
62
  }),
63
63
  };
64
64
 
65
- const configureReadModelID = async (device) => {
65
+ const configureReadModelID = async (device, coordinatorEndpoint, logger) => {
66
66
  // https://github.com/Koenkk/zigbee-herdsman-converters/issues/3016#issuecomment-1027726604
67
67
  const endpoint = device.endpoints[0];
68
- await endpoint.read('genBasic', ['modelId']);
68
+ const oldModel = device.modelID;
69
+ const newModel = (await endpoint.read('genBasic', ['modelId'])).modelId;
70
+ if (oldModel != newModel) {
71
+ logger.info(`Detected Gledopto device mode change, from '${oldModel}' to '${newModel}'`);
72
+ }
69
73
  };
70
74
 
71
75
  module.exports = [
@@ -125,7 +129,7 @@ module.exports = [
125
129
  extend: gledoptoExtend.light_onoff_brightness_colortemp({noConfigure: true}),
126
130
  configure: async (device, coordinatorEndpoint, logger) => {
127
131
  await extend.light_onoff_brightness_colortemp().configure(device, coordinatorEndpoint, logger);
128
- await configureReadModelID(device);
132
+ await configureReadModelID(device, coordinatorEndpoint, logger);
129
133
  },
130
134
  },
131
135
  {
@@ -192,7 +196,7 @@ module.exports = [
192
196
  extend: gledoptoExtend.light_onoff_brightness_colortemp_color({noConfigure: true}),
193
197
  configure: async (device, coordinatorEndpoint, logger) => {
194
198
  await extend.light_onoff_brightness_colortemp_color().configure(device, coordinatorEndpoint, logger);
195
- await configureReadModelID(device);
199
+ await configureReadModelID(device, coordinatorEndpoint, logger);
196
200
  },
197
201
  },
198
202
  {
@@ -254,7 +258,7 @@ module.exports = [
254
258
  extend: gledoptoExtend.light_onoff_brightness_color({noConfigure: true}),
255
259
  configure: async (device, coordinatorEndpoint, logger) => {
256
260
  await extend.light_onoff_brightness_color().configure(device, coordinatorEndpoint, logger);
257
- await configureReadModelID(device);
261
+ await configureReadModelID(device, coordinatorEndpoint, logger);
258
262
  },
259
263
  },
260
264
  {
@@ -263,11 +267,12 @@ module.exports = [
263
267
  vendor: 'Gledopto',
264
268
  ota: ota.zigbeeOTA,
265
269
  description: 'Zigbee LED Controller RGB+CCT (pro)',
270
+ whiteLabel: [{vendor: 'Gledopto', model: 'GL-C-001P'}],
266
271
  extend: gledoptoExtend.light_onoff_brightness_colortemp_color({colorTempRange: [158, 495], noConfigure: true}),
267
272
  meta: {disableDefaultResponse: true},
268
273
  configure: async (device, coordinatorEndpoint, logger) => {
269
274
  await extend.light_onoff_brightness_colortemp_color().configure(device, coordinatorEndpoint, logger);
270
- await configureReadModelID(device);
275
+ await configureReadModelID(device, coordinatorEndpoint, logger);
271
276
  },
272
277
  },
273
278
  {
@@ -292,7 +297,7 @@ module.exports = [
292
297
  extend: gledoptoExtend.light_onoff_brightness({noConfigure: true}),
293
298
  configure: async (device, coordinatorEndpoint, logger) => {
294
299
  await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
295
- await configureReadModelID(device);
300
+ await configureReadModelID(device, coordinatorEndpoint, logger);
296
301
  },
297
302
  },
298
303
  {
@@ -564,6 +569,14 @@ module.exports = [
564
569
  description: 'Zigbee 10W Floodlight RGB+CCT (pro)',
565
570
  extend: gledoptoExtend.light_onoff_brightness_colortemp_color(),
566
571
  },
572
+ {
573
+ zigbeeModel: ['GL-FL-001P'],
574
+ model: 'GL-FL-001P',
575
+ vendor: 'Gledopto',
576
+ ota: ota.zigbeeOTA,
577
+ description: 'Zigbee 10W Floodlight RGB+CCT 12V Low Voltage (pro)',
578
+ extend: gledoptoExtend.light_onoff_brightness_colortemp_color({colorTempRange: [158, 495]}),
579
+ },
567
580
  {
568
581
  zigbeeModel: ['GL-FL-005TZ'],
569
582
  model: 'GL-FL-005TZ',
package/devices/heiman.js CHANGED
@@ -398,7 +398,7 @@ module.exports = [
398
398
  exposes: [e.temperature(), e.humidity(), e.battery()],
399
399
  },
400
400
  {
401
- fingerprint: [{modelID: 'SOS-EM', manufacturerName: 'HEIMAN'}],
401
+ fingerprint: [{modelID: 'SOS-EM', manufacturerName: 'HEIMAN'}, {modelID: 'SOS-EF-3.0', manufacturerName: 'HEIMAN'}],
402
402
  model: 'HS1EB/HS1EB-E',
403
403
  vendor: 'HEIMAN',
404
404
  description: 'Smart emergency button',