zigbee-herdsman-converters 14.0.512 → 14.0.515

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.
@@ -532,6 +532,24 @@ const converters = {
532
532
  }
533
533
  }
534
534
 
535
+ // onLevel - range 0x00 to 0xff - optional
536
+ // Any value outside of MinLevel to MaxLevel, including 0xff and 0x00, is interpreted as "previous".
537
+ if (msg.data.hasOwnProperty('onLevel') && (msg.data['onLevel'] !== undefined)) {
538
+ result.level_config.on_level = Number(msg.data['onLevel']);
539
+ if (result.level_config.on_level === 255) {
540
+ result.level_config.on_level = 'previous';
541
+ }
542
+ }
543
+
544
+ // options - 8-bit map
545
+ // bit 0: ExecuteIfOff - when 0, Move commands are ignored if the device is off;
546
+ // when 1, CurrentLevel can be changed while the device is off.
547
+ // bit 1: CoupleColorTempToLevel - when 1, changes to level also change color temperature.
548
+ // (What this means is not defined, but it's most likely to be "dim to warm".)
549
+ if (msg.data.hasOwnProperty('options') && msg.data['options'] !== undefined) {
550
+ result.level_config.execute_if_off = !!(Number(msg.data['options']) & 1);
551
+ }
552
+
535
553
  if (Object.keys(result.level_config).length > 0) {
536
554
  return result;
537
555
  }
@@ -1699,7 +1717,7 @@ const converters = {
1699
1717
  result.hysterersis = precisionRound(data[0x100A], 2) / 10;
1700
1718
  }
1701
1719
  if (data.hasOwnProperty(0x100B)) { // DisplayAutoOffEnable
1702
- const lookup = {0: 'enable', 1: 'disable'};
1720
+ const lookup = {0: 'enabled', 1: 'disabled'};
1703
1721
  result.display_auto_off_enabled = lookup[data[0x100B]];
1704
1722
  }
1705
1723
  if (data.hasOwnProperty(0x2001)) { // AlarmAirTempOverValue
@@ -4341,7 +4359,8 @@ const converters = {
4341
4359
  // DP2: Smart Air Box: co2, Smart Air Housekeeper: MP25
4342
4360
  case tuya.dataPoints.tuyaSabCO2:
4343
4361
  if (meta.device.manufacturerName === '_TZE200_dwcarsat') {
4344
- if (value === 0xaaab) return; // Ignore: https://github.com/Koenkk/zigbee2mqtt/issues/11033#issuecomment-1109808552
4362
+ // Ignore: https://github.com/Koenkk/zigbee2mqtt/issues/11033#issuecomment-1109808552
4363
+ if (value === 0xaaac || value === 0xaaab) return;
4345
4364
  return {pm25: calibrateAndPrecisionRoundOptions(value, options, 'pm25')};
4346
4365
  } else if (meta.device.manufacturerName === '_TZE200_ryfmq5rl') {
4347
4366
  return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd') / 100};
@@ -5909,6 +5928,29 @@ const converters = {
5909
5928
  return result;
5910
5929
  },
5911
5930
  },
5931
+ xiaomi_curtain_hagl04_status: {
5932
+ cluster: 'genMultistateOutput',
5933
+ type: ['attributeReport'],
5934
+ convert: (model, msg, publish, options, meta) => {
5935
+ let running = false;
5936
+ const data = msg.data;
5937
+ const lookup = {
5938
+ 0: 'closing',
5939
+ 1: 'opening',
5940
+ 2: 'stop',
5941
+ };
5942
+ if (data && data.hasOwnProperty('presentValue')) {
5943
+ const value = data['presentValue'];
5944
+ if (value < 2) {
5945
+ running = true;
5946
+ }
5947
+ return {
5948
+ motor_state: lookup[value],
5949
+ running: running,
5950
+ };
5951
+ }
5952
+ },
5953
+ },
5912
5954
  xiaomi_curtain_acn002_status: {
5913
5955
  cluster: 'genMultistateOutput',
5914
5956
  type: ['attributeReport'],
@@ -8496,6 +8538,73 @@ const converters = {
8496
8538
  return result;
8497
8539
  },
8498
8540
  },
8541
+ ZG204ZL_lms: {
8542
+ cluster: 'manuSpecificTuya',
8543
+ type: ['commandDataResponse', 'commandDataReport'],
8544
+ convert: (model, msg, publish, options, meta) => {
8545
+ const result = {};
8546
+ for (const dpValue of msg.data.dpValues) {
8547
+ const dp = dpValue.dp;
8548
+ const value = tuya.getDataValue(dpValue);
8549
+ switch (dp) {
8550
+ case tuya.dataPoints.lmsState:
8551
+ result.occupancy = (value === 0);
8552
+ break;
8553
+ case tuya.dataPoints.lmsBattery:
8554
+ result.battery = value;
8555
+ break;
8556
+ case tuya.dataPoints.lmsSensitivity:
8557
+ result.sensitivity = {'0': 'low', '1': 'medium', '2': 'high'}[value];
8558
+ break;
8559
+ case tuya.dataPoints.lmsKeepTime:
8560
+ result.keep_time = {'0': '10', '1': '30', '2': '60', '3': '120'}[value];
8561
+ break;
8562
+ case tuya.dataPoints.lmsIlluminance:
8563
+ result.illuminance = value;
8564
+ break;
8565
+ default:
8566
+ meta.logger.warn(`zigbee-herdsman-converters:ZG204ZL_lms: NOT RECOGNIZED DP #${
8567
+ dp} with data ${JSON.stringify(dpValue)}`);
8568
+ }
8569
+ }
8570
+ return result;
8571
+ },
8572
+ },
8573
+ moes_cover: {
8574
+ cluster: 'manuSpecificTuya',
8575
+ type: ['commandDataResponse', 'commandDataReport'],
8576
+ options: [exposes.options.invert_cover()],
8577
+ convert: (model, msg, publish, options, meta) => {
8578
+ const dpValue = tuya.firstDpValue(msg, meta, 'moes_cover');
8579
+ const dp = dpValue.dp;
8580
+ const value = tuya.getDataValue(dpValue);
8581
+ let result = null;
8582
+ switch (dp) {
8583
+ case tuya.dataPoints.coverPosition: {
8584
+ const invert = !tuya.isCoverInverted(meta.device.manufacturerName) ?
8585
+ !options.invert_cover : options.invert_cover;
8586
+ const position = invert ? 100 - value : value;
8587
+ result = {position: position};
8588
+ break;
8589
+ }
8590
+ case tuya.dataPoints.state:
8591
+ result = {state: {0: 'OPEN', 1: 'STOP', 2: 'CLOSE'}[value], running: {0: true, 1: false, 2: true}[value]};
8592
+ break;
8593
+ case tuya.dataPoints.moesCoverBacklight:
8594
+ result = {backlight: {false: 'OFF', true: 'ON'}[value]};
8595
+ break;
8596
+ case tuya.dataPoints.moesCoverCalibration:
8597
+ result = {calibration: {0: 'ON', 1: 'OFF'}[value]};
8598
+ break;
8599
+ case tuya.dataPoints.moesCoverMotorReversal:
8600
+ result = {motor_reversal: {0: 'OFF', 1: 'ON'}[value]};
8601
+ break;
8602
+ default:
8603
+ meta.logger.warn(`fromZigbee.moes_cover: NOT RECOGNIZED DP ${dp} with data ${JSON.stringify(dpValue)}`);
8604
+ }
8605
+ return result;
8606
+ },
8607
+ },
8499
8608
  // #endregion
8500
8609
 
8501
8610
  // #region Ignore converters (these message dont need parsing).
@@ -535,12 +535,41 @@ const converters = {
535
535
  Object.assign(state, {current_level_startup: startUpCurrentLevelValue});
536
536
  }
537
537
 
538
+ // onLevel - range 0x00 to 0xff - optional
539
+ // Any value outside of MinLevel to MaxLevel, including 0xff and 0x00, is interpreted as "previous".
540
+ if (value.hasOwnProperty('on_level')) {
541
+ let onLevel = value.on_level;
542
+ if (typeof onLevel === 'string' && onLevel.toLowerCase() == 'previous') {
543
+ onLevel = 255;
544
+ } else {
545
+ onLevel = Number(onLevel);
546
+ }
547
+ if (onLevel > 255) onLevel = 254;
548
+ if (onLevel < 1) onLevel = 1;
549
+ await entity.write('genLevelCtrl', {onLevel}, utils.getOptions(meta.mapped, entity));
550
+ Object.assign(state, {on_level: onLevel == 255 ? 'previous' : onLevel});
551
+ }
552
+
553
+ // options - 8-bit map
554
+ // bit 0: ExecuteIfOff - when 0, Move commands are ignored if the device is off;
555
+ // when 1, CurrentLevel can be changed while the device is off.
556
+ // bit 1: CoupleColorTempToLevel - when 1, changes to level also change color temperature.
557
+ // (What this means is not defined, but it's most likely to be "dim to warm".)
558
+ if (value.hasOwnProperty('execute_if_off')) {
559
+ const executeIfOffValue = !!value.execute_if_off;
560
+ await entity.write('genLevelCtrl', {options: executeIfOffValue ? 1 : 0}, utils.getOptions(meta.mapped, entity));
561
+ Object.assign(state, {execute_if_off: executeIfOffValue});
562
+ }
563
+
538
564
  if (Object.keys(state).length > 0) {
539
565
  return {state: {level_config: state}};
540
566
  }
541
567
  },
542
568
  convertGet: async (entity, key, meta) => {
543
- for (const attribute of ['onOffTransitionTime', 'onTransitionTime', 'offTransitionTime', 'startUpCurrentLevel']) {
569
+ for (const attribute of [
570
+ 'onOffTransitionTime', 'onTransitionTime', 'offTransitionTime', 'startUpCurrentLevel',
571
+ 'onLevel', 'options',
572
+ ]) {
544
573
  try {
545
574
  await entity.read('genLevelCtrl', [attribute]);
546
575
  } catch (ex) {
@@ -3092,10 +3121,10 @@ const converters = {
3092
3121
  const payload = {0x1009: {value: value * 2, type: 0x20}};
3093
3122
  await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3094
3123
  } else if (key==='hysterersis') {
3095
- const payload = {0x100A: {value: value* 10, type: 0x20}};
3124
+ const payload = {0x100A: {value: value * 10, type: 0x20}};
3096
3125
  await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3097
3126
  } else if (key==='display_auto_off_enabled') {
3098
- const lookup = {'enable': 0, 'disabled': 1};
3127
+ const lookup = {'enabled': 0, 'disabled': 1};
3099
3128
  const payload = {0x100B: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3100
3129
  await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3101
3130
  } else if (key==='alarm_airtemp_overvalue') {
@@ -7436,6 +7465,70 @@ const converters = {
7436
7465
  }
7437
7466
  },
7438
7467
  },
7468
+ ZG204ZL_lms: {
7469
+ key: ['sensitivity', 'keep_time'],
7470
+ convertSet: async (entity, key, value, meta) => {
7471
+ switch (key) {
7472
+ case 'sensitivity':
7473
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsSensitivity, {'low': 0, 'medium': 1, 'high': 2}[value]);
7474
+ break;
7475
+ case 'keep_time':
7476
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsKeepTime, {'10': 0, '30': 1, '60': 2, '120': 3}[value]);
7477
+ break;
7478
+ default: // Unknown key
7479
+ meta.logger.warn(`tz.ZG204ZL_lms: Unhandled key ${key}`);
7480
+ }
7481
+ },
7482
+ convertGet: async (entity, key, meta) => {
7483
+ switch (key) {
7484
+ case 'sensitivity':
7485
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsSensitivity, 0, 'dataQuery' );
7486
+ break;
7487
+ case 'keep_time':
7488
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsKeepTime, 0, 'dataQuery' );
7489
+ break;
7490
+ default: // Unknown key
7491
+ meta.logger.warn(`Unhandled key toZigbee.ZG204ZL_lms.convertGet ${key}`);
7492
+ }
7493
+ },
7494
+ },
7495
+ moes_cover: {
7496
+ key: ['backlight', 'calibration', 'motor_reversal', 'state', 'position'],
7497
+ options: [exposes.options.invert_cover()],
7498
+ convertSet: async (entity, key, value, meta) => {
7499
+ switch (key) {
7500
+ case 'position':
7501
+ if (value >= 0 && value <= 100) {
7502
+ const invert = !tuya.isCoverInverted(meta.device.manufacturerName) ?
7503
+ !meta.options.invert_cover : meta.options.invert_cover;
7504
+ const position = invert ? 100 - value : value;
7505
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.coverPosition, position);
7506
+ return {position: value};
7507
+ }
7508
+ break;
7509
+ case 'state': {
7510
+ const state = {'OPEN': 0, 'STOP': 1, 'CLOSE': 2}[value.toUpperCase()];
7511
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.state, state);
7512
+ break;
7513
+ }
7514
+ case 'backlight': {
7515
+ const backlight = value.toUpperCase() === 'ON' ? true : false;
7516
+ await tuya.sendDataPointBool(entity, tuya.dataPoints.moesCoverBacklight, backlight);
7517
+ return {backlight: value};
7518
+ }
7519
+ case 'calibration': {
7520
+ const calibration = value.toUpperCase() === 'ON' ? 0 : 1;
7521
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.moesCoverCalibration, calibration);
7522
+ break;
7523
+ }
7524
+ case 'motor_reversal': {
7525
+ const motorReversal = value.toUpperCase() === 'ON' ? 1 : 0;
7526
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.moesCoverMotorReversal, motorReversal);
7527
+ return {motor_reversal: value};
7528
+ }
7529
+ }
7530
+ },
7531
+ },
7439
7532
  // #endregion
7440
7533
 
7441
7534
  // #region Ignore converters
@@ -22,5 +22,6 @@ module.exports = [
22
22
  exposes: [e.lock(), e.battery(), e.sound_volume(),
23
23
  e.action(['zigbee_unlock', 'lock', 'rfid_unlock', 'keypad_unlock']),
24
24
  exposes.binary('auto_relock', ea.STATE_SET, true, false).withDescription('Auto relock after 7 seconds.')],
25
+ whiteLabel: [{vendor: 'Datek Wireless', model: 'EasyCode903G2.1'}],
25
26
  },
26
27
  ];
package/devices/ikea.js CHANGED
@@ -36,9 +36,12 @@ const bulbOnEvent = async (type, data, device, options, state) => {
36
36
 
37
37
  // NOTE: execute_if_off default is false
38
38
  // we only restore if true, to save unneeded network writes
39
- if (state.color_options.execute_if_off === true) {
39
+ if (state.color_options !== undefined && state.color_options.execute_if_off === true) {
40
40
  device.endpoints[0].write('lightingColorCtrl', {'options': 1});
41
41
  }
42
+ if (state.level_config !== undefined && state.level_config.execute_if_off === true) {
43
+ device.endpoints[0].write('genLevelCtrl', {'options': 1});
44
+ }
42
45
  }
43
46
  };
44
47