zigbee-herdsman-converters 14.0.496 → 14.0.499

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.
@@ -1652,6 +1652,77 @@ const converters = {
1652
1652
  // #endregion
1653
1653
 
1654
1654
  // #region Non-generic converters
1655
+ namron_thermostat: {
1656
+ cluster: 'hvacThermostat',
1657
+ type: ['attributeReport', 'readResponse'],
1658
+ convert: (model, msg, publish, options, meta) => {
1659
+ const result = {};
1660
+ const data = msg.data;
1661
+ if (data.hasOwnProperty(0x1000)) { // Display brightness
1662
+ const lookup = {0: 'low', 1: 'mid', 2: 'high'};
1663
+ result.lcd_brightness = lookup[data[0x1000]];
1664
+ }
1665
+ if (data.hasOwnProperty(0x1001)) { // Button vibration level
1666
+ const lookup = {0: 'off', 1: 'low', 2: 'high'};
1667
+ result.button_vibration_level = lookup[data[0x1001]];
1668
+ }
1669
+ if (data.hasOwnProperty(0x1002)) { // Floor sensor type
1670
+ const lookup = {1: '10k', 2: '15k', 3: '50k', 4: '100k', 5: '12k'};
1671
+ result.floor_sensor_type = lookup[data[0x1002]];
1672
+ }
1673
+ if (data.hasOwnProperty(0x1003)) { // Sensor
1674
+ const lookup = {0: 'air', 1: 'floor', 2: 'both'};
1675
+ result.sensor = lookup[data[0x1003]];
1676
+ }
1677
+ if (data.hasOwnProperty(0x1004)) { // PowerUpStatus
1678
+ const lookup = {0: 'default', 1: 'last_status'};
1679
+ result.powerup_status = lookup[data[0x1004]];
1680
+ }
1681
+ if (data.hasOwnProperty(0x1005)) { // FloorSensorCalibration
1682
+ result.floor_sensor_calibration = precisionRound(data[0x1005], 2) / 10;
1683
+ }
1684
+ if (data.hasOwnProperty(0x1006)) { // DryTime
1685
+ result.dry_time = data[0x1006];
1686
+ }
1687
+ if (data.hasOwnProperty(0x1007)) { // ModeAfterDry
1688
+ const lookup = {0: 'off', 1: 'manual', 2: 'auto', 3: 'away'};
1689
+ result.mode_after_dry = lookup[data[0x1007]];
1690
+ }
1691
+ if (data.hasOwnProperty(0x1008)) { // TemperatureDisplay
1692
+ const lookup = {0: 'room', 1: 'floor'};
1693
+ result.temperature_display = lookup[data[0x1008]];
1694
+ }
1695
+ if (data.hasOwnProperty(0x1009)) { // WindowOpenCheck
1696
+ result.window_open_check = data[0x1009];
1697
+ }
1698
+ if (data.hasOwnProperty(0x100A)) { // Hysterersis
1699
+ result.hysterersis = data[0x100A];
1700
+ }
1701
+ if (data.hasOwnProperty(0x100B)) { // DisplayAutoOffEnable
1702
+ const lookup = {0: 'enable', 1: 'disable'};
1703
+ result.display_auto_off_enabled = lookup[data[0x100B]];
1704
+ }
1705
+ if (data.hasOwnProperty(0x2001)) { // AlarmAirTempOverValue
1706
+ result.alarm_airtemp_overvalue = data[0x2001];
1707
+ }
1708
+ if (data.hasOwnProperty(0x2002)) { // Away Mode Set
1709
+ result.away_mode = data[0x2002] ? 'ON' : 'OFF';
1710
+ }
1711
+
1712
+ return result;
1713
+ },
1714
+ },
1715
+ namron_hvac_user_interface: {
1716
+ cluster: 'hvacUserInterfaceCfg',
1717
+ type: ['attributeReport', 'readResponse'],
1718
+ convert: (model, msg, publish, options, meta) => {
1719
+ const result = {};
1720
+ if (msg.data.hasOwnProperty('keypadLockout')) { // Set as child lock instead as keypadlockout
1721
+ result.child_lock = msg.data['keypadLockout'] === 0 ? 'UNLOCK' : 'LOCK';
1722
+ }
1723
+ return result;
1724
+ },
1725
+ },
1655
1726
  elko_thermostat: {
1656
1727
  cluster: 'hvacThermostat',
1657
1728
  type: ['attributeReport', 'readResponse'],
@@ -4223,7 +4294,6 @@ const converters = {
4223
4294
  const dpValue = tuya.firstDpValue(msg, meta, 'tuya_air_quality');
4224
4295
  const dp = dpValue.dp;
4225
4296
  const value = tuya.getDataValue(dpValue);
4226
- const swapPM25CO2 = ['_TZE200_ryfmq5rl', '_TZE200_dwcarsat'];
4227
4297
  switch (dp) {
4228
4298
  case tuya.dataPoints.tuyaSabTemp:
4229
4299
  return {temperature: calibrateAndPrecisionRoundOptions(value / 10, options, 'temperature')};
@@ -4231,15 +4301,17 @@ const converters = {
4231
4301
  return {humidity: calibrateAndPrecisionRoundOptions(value / 10, options, 'humidity')};
4232
4302
  // DP22: Smart Air Box: Formaldehyd, Smart Air Housekeeper: co2
4233
4303
  case tuya.dataPoints.tuyaSabFormaldehyd:
4234
- if (swapPM25CO2.includes(meta.device.manufacturerName)) {
4304
+ if (['_TZE200_dwcarsat', '_TZE200_ryfmq5rl'].includes(meta.device.manufacturerName)) {
4235
4305
  return {co2: calibrateAndPrecisionRoundOptions(value, options, 'co2')};
4236
4306
  } else {
4237
4307
  return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd')};
4238
4308
  }
4239
4309
  // DP2: Smart Air Box: co2, Smart Air Housekeeper: MP25
4240
4310
  case tuya.dataPoints.tuyaSabCO2:
4241
- if (swapPM25CO2.includes(meta.device.manufacturerName)) {
4311
+ if (meta.device.manufacturerName === '_TZE200_dwcarsat') {
4242
4312
  return {pm25: calibrateAndPrecisionRoundOptions(value, options, 'pm25')};
4313
+ } else if (meta.device.manufacturerName === '_TZE200_ryfmq5rl') {
4314
+ return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd')};
4243
4315
  } else {
4244
4316
  return {co2: calibrateAndPrecisionRoundOptions(value, options, 'co2')};
4245
4317
  }
@@ -4692,7 +4764,7 @@ const converters = {
4692
4764
  },
4693
4765
  tuya_data_point_dump: {
4694
4766
  cluster: 'manuSpecificTuya',
4695
- type: ['commandDataResponse', 'commandDataReport', 'commandActiveStatusReport'],
4767
+ type: ['commandDataResponse', 'commandDataReport', 'commandActiveStatusReport', 'commandActiveStatusReportAlt'],
4696
4768
  convert: (model, msg, publis, options, meta) => {
4697
4769
  // Don't use in production!
4698
4770
  // Used in: https://www.zigbee2mqtt.io/how_tos/how_to_support_new_tuya_devices.html
@@ -5800,19 +5872,9 @@ const converters = {
5800
5872
  return result;
5801
5873
  },
5802
5874
  },
5803
- xiaomi_curtain_acn002_position: {
5804
- cluster: 'genAnalogOutput',
5805
- type: ['attributeReport', 'readResponse'],
5806
- options: [exposes.options.invert_cover()],
5807
- convert: (model, msg, publish, options, meta) => {
5808
- let position = precisionRound(msg.data['presentValue'], 2);
5809
- position = options.invert_cover ? 100 - position : position;
5810
- return {position: position};
5811
- },
5812
- },
5813
5875
  xiaomi_curtain_acn002_status: {
5814
5876
  cluster: 'genMultistateOutput',
5815
- type: ['attributeReport', 'readResponse'],
5877
+ type: ['attributeReport'],
5816
5878
  convert: (model, msg, publish, options, meta) => {
5817
5879
  let running = false;
5818
5880
  const data = msg.data;
@@ -7255,22 +7317,34 @@ const converters = {
7255
7317
  },
7256
7318
  ZB006X_settings: {
7257
7319
  cluster: 'manuSpecificTuya',
7258
- type: ['commandDataResponse'],
7320
+ type: ['commandActiveStatusReport', 'commandActiveStatusReportAlt'],
7259
7321
  convert: (model, msg, publish, options, meta) => {
7260
7322
  const dpValue = tuya.firstDpValue(msg, meta, 'ZB006X_settings');
7261
7323
  const dp = dpValue.dp;
7262
7324
  const value = tuya.getDataValue(dpValue);
7263
- if (dp === 103) {
7264
- meta.logger.debug(`fromZigbee.ZB006X_settings: Found DP #${dp} with data ${JSON.stringify(dpValue)}`);
7265
- return {ext_switch_type: {0: 'unknown', 1: 'toggle_sw', 2: 'momentary_sw', 3: 'rotary_sw', 4: 'auto_config'}[value]};
7266
- } else if (dp === 105) {
7267
- meta.logger.debug(`fromZigbee.ZB006X_settings: Found DP #${dp} with data ${JSON.stringify(dpValue)}`);
7325
+ switch (dp) {
7326
+ case tuya.dataPoints.fantemPowerSupplyMode:
7327
+ return {power_supply_mode: {0: 'unknown', 1: 'no_neutral', 2: 'with_neutral'}[value]};
7328
+ case tuya.dataPoints.fantemExtSwitchType:
7329
+ return {ext_switch_type: {0: 'unknown', 1: 'toggle_sw', 2: 'momentary_sw', 3: 'rotary_sw',
7330
+ 4: 'auto_config'}[value]};
7331
+ case tuya.dataPoints.fantemLoadDetectionMode:
7268
7332
  return {load_detection_mode: {0: 'none', 1: 'first_power_on', 2: 'every_power_on'}[value]};
7269
- } else if (dp === 109) {
7270
- meta.logger.debug(`fromZigbee.ZB006X_settings: Found DP #${dp} with data ${JSON.stringify(dpValue)}`);
7271
- return {control_mode: {0: 'local', 1: 'remote', 2: 'both'}[value]};
7272
- } else {
7273
- meta.logger.warn(`fromZigbee.ZB006X_settings: Unrecognized DP #${dp} with data ${JSON.stringify(dpValue)}`);
7333
+ case tuya.dataPoints.fantemExtSwitchStatus:
7334
+ return {ext_switch_status: value};
7335
+ case tuya.dataPoints.fantemControlMode:
7336
+ return {control_mode: {0: 'ext_switch', 1: 'remote', 2: 'both'}[value]};
7337
+ case 111:
7338
+ // Value 0 is received after each device power-on. No idea what it means.
7339
+ return;
7340
+ case tuya.dataPoints.fantemLoadType:
7341
+ // Not sure if 0 is 'resistive' and 2 is 'resistive_inductive'.
7342
+ // If you see 'unknown', pls. check with Tuya gateway and app and update with label shown in Tuya app.
7343
+ return {load_type: {0: 'unknown', 1: 'resistive_capacitive', 2: 'unknown', 3: 'detecting'}[value]};
7344
+ case tuya.dataPoints.fantemLoadDimmable:
7345
+ return {load_dimmable: {0: 'unknown', 1: 'dimmable', 2: 'not_dimmable'}[value]};
7346
+ default:
7347
+ meta.logger.warn(`fz.ZB006X_settings: Unhandled DP|Value [${dp}|${value}][${JSON.stringify(dpValue)}]`);
7274
7348
  }
7275
7349
  },
7276
7350
  },
@@ -11,6 +11,7 @@ const libColor = require('../lib/color');
11
11
  const exposes = require('../lib/exposes');
12
12
 
13
13
  const manufacturerOptions = {
14
+ sunricher: {manufacturerCode: herdsman.Zcl.ManufacturerCode.SHENZHEN_SUNRICH},
14
15
  xiaomi: {manufacturerCode: herdsman.Zcl.ManufacturerCode.LUMI_UNITED_TECH, disableDefaultResponse: true},
15
16
  osram: {manufacturerCode: herdsman.Zcl.ManufacturerCode.OSRAM},
16
17
  eurotronic: {manufacturerCode: herdsman.Zcl.ManufacturerCode.JENNIC},
@@ -791,7 +792,7 @@ const converters = {
791
792
  },
792
793
  },
793
794
  light_onoff_brightness: {
794
- key: ['state', 'brightness', 'brightness_percent'],
795
+ key: ['state', 'brightness', 'brightness_percent', 'on_time'],
795
796
  options: [exposes.options.transition()],
796
797
  convertSet: async (entity, key, value, meta) => {
797
798
  const {message} = meta;
@@ -2438,6 +2439,20 @@ const converters = {
2438
2439
  } else {
2439
2440
  await entity.command('closuresWindowCovering', 'stop', {}, utils.getOptions(meta.mapped, entity));
2440
2441
  }
2442
+
2443
+ if (!['ZNCLDJ11LM', 'ZNJLBL01LM'].includes(meta.mapped.model)) {
2444
+ // The code below is originally added for ZNCLDJ11LM (Koenkk/zigbee2mqtt#4585).
2445
+ // However, in Koenkk/zigbee-herdsman-converters#4039 it was replaced by reading
2446
+ // directly from currentPositionLiftPercentage, so that device is excluded.
2447
+ // For ZNJLBL01LM, in Koenkk/zigbee-herdsman-converters#4163 the position is read
2448
+ // through onEvent each time the motor stops, so it becomes redundant, and the
2449
+ // device is excluded.
2450
+ // The code is left here to avoid breaking compatibility, ideally all devices using
2451
+ // this converter should be tested so the code can be adjusted/deleted.
2452
+
2453
+ // Xiaomi curtain does not send position update on stop, request this.
2454
+ await entity.read('genAnalogOutput', [0x0055]);
2455
+ }
2441
2456
  } else {
2442
2457
  const lookup = {'open': 100, 'close': 0, 'on': 100, 'off': 0};
2443
2458
 
@@ -3005,6 +3020,127 @@ const converters = {
3005
3020
  await entity.read('closuresWindowCovering', [isPosition ? 'currentPositionLiftPercentage' : 'currentPositionTiltPercentage']);
3006
3021
  },
3007
3022
  },
3023
+ namron_thermostat: {
3024
+ key: [
3025
+ 'lcd_brightness', 'button_vibration_level', 'floor_sensor_type', 'sensor', 'powerup_status', 'floor_sensor_calibration',
3026
+ 'dry_time', 'mode_after_dry', 'temperature_display', 'window_open_check', 'hysterersis', 'display_auto_off_enabled',
3027
+ 'alarm_airtemp_overvalue', 'away_mode',
3028
+ ],
3029
+ convertSet: async (entity, key, value, meta) => {
3030
+ if (key === 'lcd_brightness') {
3031
+ const lookup = {'low': 0, 'mid': 1, 'high': 2};
3032
+ const payload = {0x1000: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3033
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3034
+ } else if (key === 'button_vibration_level') {
3035
+ const lookup = {'off': 0, 'low': 1, 'high': 2};
3036
+ const payload = {0x1001: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3037
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3038
+ } else if (key === 'floor_sensor_type') {
3039
+ const lookup = {'10k': 1, '15k': 2, '50k': 3, '100k': 4, '12k': 5};
3040
+ const payload = {0x1002: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3041
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3042
+ } else if (key === 'sensor') {
3043
+ const lookup = {'air': 0, 'floor': 1, 'both': 2};
3044
+ const payload = {0x1003: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3045
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3046
+ } else if (key==='powerup_status') {
3047
+ const lookup = {'default': 0, 'last_status': 1};
3048
+ const payload = {0x1004: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3049
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3050
+ } else if (key==='floor_sensor_calibration') {
3051
+ const payload = {0x1005: {value: Math.round(value * 10), type: 0x28}}; // INT8S
3052
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3053
+ } else if (key==='dry_time') {
3054
+ const payload = {0x1006: {value: value, type: 0x20}}; // INT8U
3055
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3056
+ } else if (key==='mode_after_dry') {
3057
+ const lookup = {'off': 0, 'manual': 1, 'auto': 2, 'away': 3};
3058
+ const payload = {0x1007: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3059
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3060
+ } else if (key==='temperature_display') {
3061
+ const lookup = {'room': 0, 'floor': 1};
3062
+ const payload = {0x1008: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3063
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3064
+ } else if (key==='window_open_check') {
3065
+ const payload = {0x1009: {value: Math.round(value * 10), type: 0x20}};
3066
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3067
+ } else if (key==='hysterersis') {
3068
+ const payload = {0x100A: {value: Math.round(value * 10), type: 0x20}};
3069
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3070
+ } else if (key==='display_auto_off_enabled') {
3071
+ const lookup = {'enable': 0, 'disabled': 1};
3072
+ const payload = {0x100B: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
3073
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3074
+ } else if (key==='alarm_airtemp_overvalue') {
3075
+ const payload = {0x2001: {value: value, type: 0x20}};
3076
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3077
+ } else if (key==='away_mode') {
3078
+ const payload = {0x2002: {value: Number(value==='ON'), type: 0x30}};
3079
+ await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
3080
+ }
3081
+ },
3082
+ convertGet: async (entity, key, meta) => {
3083
+ switch (key) {
3084
+ case 'lcd_brightness':
3085
+ await entity.read('hvacThermostat', [0x1000], manufacturerOptions.sunricher);
3086
+ break;
3087
+ case 'button_vibration_level':
3088
+ await entity.read('hvacThermostat', [0x1001], manufacturerOptions.sunricher);
3089
+ break;
3090
+ case 'floor_sensor_type':
3091
+ await entity.read('hvacThermostat', [0x1002], manufacturerOptions.sunricher);
3092
+ break;
3093
+ case 'sensor':
3094
+ await entity.read('hvacThermostat', [0x1003], manufacturerOptions.sunricher);
3095
+ break;
3096
+ case 'powerup_status':
3097
+ await entity.read('hvacThermostat', [0x1004], manufacturerOptions.sunricher);
3098
+ break;
3099
+ case 'floor_sensor_calibration':
3100
+ await entity.read('hvacThermostat', [0x1005], manufacturerOptions.sunricher);
3101
+ break;
3102
+ case 'dry_time':
3103
+ await entity.read('hvacThermostat', [0x1006], manufacturerOptions.sunricher);
3104
+ break;
3105
+ case 'mode_after_dry':
3106
+ await entity.read('hvacThermostat', [0x1007], manufacturerOptions.sunricher);
3107
+ break;
3108
+ case 'temperature_display':
3109
+ await entity.read('hvacThermostat', [0x1008], manufacturerOptions.sunricher);
3110
+ break;
3111
+ case 'window_open_check':
3112
+ await entity.read('hvacThermostat', [0x1009], manufacturerOptions.sunricher);
3113
+ break;
3114
+ case 'hysterersis':
3115
+ await entity.read('hvacThermostat', [0x100A], manufacturerOptions.sunricher);
3116
+ break;
3117
+ case 'display_auto_off_enabled':
3118
+ await entity.read('hvacThermostat', [0x100B], manufacturerOptions.sunricher);
3119
+ break;
3120
+ case 'alarm_airtemp_overvalue':
3121
+ await entity.read('hvacThermostat', [0x2001], manufacturerOptions.sunricher);
3122
+ break;
3123
+ case 'away_mode':
3124
+ await entity.read('hvacThermostat', [0x2002], manufacturerOptions.sunricher);
3125
+ break;
3126
+
3127
+ default: // Unknown key
3128
+ throw new Error(`Unhandled key toZigbee.namron_thermostat.convertGet ${key}`);
3129
+ }
3130
+ },
3131
+
3132
+ },
3133
+ namron_thermostat_child_lock: {
3134
+ key: ['child_lock'],
3135
+ convertSet: async (entity, key, value, meta) => {
3136
+ const keypadLockout = Number(value==='LOCK');
3137
+ await entity.write('hvacUserInterfaceCfg', {keypadLockout});
3138
+ return {readAfterWriteTime: 250, state: {child_lock: value}};
3139
+ },
3140
+ convertGet: async (entity, key, meta) => {
3141
+ await entity.read('hvacUserInterfaceCfg', ['keypadLockout']);
3142
+ },
3143
+ },
3008
3144
  connecte_thermostat: {
3009
3145
  key: [
3010
3146
  'child_lock', 'current_heating_setpoint', 'local_temperature_calibration', 'max_temperature_protection', 'window_detection',
@@ -6373,17 +6509,19 @@ const converters = {
6373
6509
  convertSet: async (entity, key, value, meta) => {
6374
6510
  switch (key) {
6375
6511
  case 'ext_switch_type':
6376
- await tuya.sendDataPointEnum(entity, 103, {'unknown': 0, 'toggle_sw': 1,
6377
- 'momentary_sw': 2, 'rotary_sw': 3, 'auto_config': 4}[value]);
6512
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.fantemExtSwitchType, {'unknown': 0, 'toggle_sw': 1,
6513
+ 'momentary_sw': 2, 'rotary_sw': 3, 'auto_config': 4}[value], 'sendData');
6378
6514
  break;
6379
6515
  case 'load_detection_mode':
6380
- await tuya.sendDataPointEnum(entity, 105, {'none': 0, 'first_power_on': 1, 'every_power_on': 2}[value]);
6516
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.fantemLoadDetectionMode, {'none': 0, 'first_power_on': 1,
6517
+ 'every_power_on': 2}[value], 'sendData');
6381
6518
  break;
6382
6519
  case 'control_mode':
6383
- await tuya.sendDataPointEnum(entity, 109, {'local': 0, 'remote': 1, 'both': 2}[value]);
6520
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.fantemControlMode, {'ext_switch': 0, 'remote': 1,
6521
+ 'both': 2}[value], 'sendData');
6384
6522
  break;
6385
6523
  default: // Unknown key
6386
- throw new Error(`toZigbee.ZB006X_settings: Unhandled key ${key}`);
6524
+ throw new Error(`tz.ZB006X_settings: Unhandled key ${key}`);
6387
6525
  }
6388
6526
  },
6389
6527
  },
package/devices/awox.js CHANGED
@@ -224,11 +224,30 @@ module.exports = [
224
224
  },
225
225
  ],
226
226
  },
227
+ {
228
+ type: 'Router', manufacturerName: 'AwoX', modelID: 'TLSR82xx', endpoints: [
229
+ {
230
+ ID: 1,
231
+ profileID: 260,
232
+ deviceID: 268,
233
+ inputClusters: [0, 3, 4, 5, 6, 8, 768, 4096, 64599, 10],
234
+ outputClusters: [6],
235
+ },
236
+ {ID: 242, profileID: 41440, deviceID: 97, inputClusters: [], outputClusters: [33]},
237
+ {
238
+ ID: 3,
239
+ profileID: 4751,
240
+ deviceID: 268,
241
+ inputClusters: [65360, 65361],
242
+ outputClusters: [65360, 65361],
243
+ },
244
+ ],
245
+ },
227
246
  ],
228
247
  model: '33957',
229
248
  vendor: 'AwoX',
230
249
  description: 'LED light with color temperature',
231
- extend: extend.light_onoff_brightness_colortemp(),
250
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
232
251
  },
233
252
  {
234
253
  fingerprint: [
@@ -45,7 +45,7 @@ module.exports = [
45
45
  .withDescription('Whether or not the unit needs warm water. `false` No Heat Request or `true` Heat Request'),
46
46
  exposes.enum('setpoint_change_source', ea.STATE, ['manual', 'schedule', 'externally'])
47
47
  .withDescription('Values observed are `0` (manual), `1` (schedule) or `2` (externally)'),
48
- exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 32, 0.5).withLocalTemperature().withPiHeatingDemand()
48
+ exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 35, 0.5).withLocalTemperature().withPiHeatingDemand()
49
49
  .withSystemMode(['heat']).withRunningState(['idle', 'heat'], ea.STATE),
50
50
  exposes.numeric('external_measured_room_sensor', ea.ALL)
51
51
  .withDescription('If `radiator_covered` is `true`: Set at maximum 30 minutes interval but not more often than every ' +
@@ -79,7 +79,8 @@ module.exports = [
79
79
  .withDescription('Mean radiator load for room calculated by gateway for load balancing purposes (-8000=undefined)')
80
80
  .withValueMin(-8000).withValueMax(2000),
81
81
  exposes.numeric('load_estimate', ea.STATE_GET)
82
- .withDescription('Load estimate on this radiator'),
82
+ .withDescription('Load estimate on this radiator')
83
+ .withValueMin(-8000).withValueMax(3600),
83
84
  exposes.binary('preheat_status', ea.STATE_GET, true, false)
84
85
  .withDescription('Specific for pre-heat running in Zigbee Weekly Schedule mode'),
85
86
  exposes.enum('adaptation_run_status', ea.STATE_GET, ['none', 'in_progress', 'found', 'lost'])
package/devices/fantem.js CHANGED
@@ -5,6 +5,7 @@ const e = exposes.presets;
5
5
  const ea = exposes.access;
6
6
  const extend = require('../lib/extend');
7
7
  const reporting = require('../lib/reporting');
8
+ const tuya = require('../lib/tuya');
8
9
 
9
10
  module.exports = [
10
11
  {
@@ -13,21 +14,34 @@ module.exports = [
13
14
  vendor: 'Fantem',
14
15
  description: 'Smart dimmer module without neutral',
15
16
  extend: extend.light_onoff_brightness({noConfigure: true}),
16
- fromZigbee: [...extend.light_onoff_brightness({noConfigure: true}).fromZigbee, fz.command_on, fz.command_off,
17
- fz.command_move, fz.command_stop, fz.ZB006X_settings],
17
+ fromZigbee: [...extend.light_onoff_brightness({noConfigure: true}).fromZigbee,
18
+ fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.ZB006X_settings],
18
19
  toZigbee: [...extend.light_onoff_brightness({noConfigure: true}).toZigbee, tz.ZB006X_settings],
19
20
  exposes: [e.light_brightness(),
20
21
  e.action(['on', 'off', 'brightness_move_down', 'brightness_move_up', 'brightness_stop']),
22
+ exposes.enum('control_mode', ea.STATE_SET, ['ext_switch', 'remote', 'both']).withDescription('Control mode'),
21
23
  exposes.enum('ext_switch_type', ea.STATE_SET, ['unknown', 'toggle_sw', 'momentary_sw', 'rotary_sw', 'auto_config'])
22
24
  .withDescription('External switch type'),
25
+ exposes.numeric('ext_switch_status', ea.STATE).withDescription('External switch status')
26
+ .withValueMin(-10000).withValueMax(10000),
23
27
  exposes.enum('load_detection_mode', ea.STATE_SET, ['none', 'first_power_on', 'every_power_on'])
24
28
  .withDescription('Load detection mode'),
25
- exposes.enum('control_mode', ea.STATE_SET, ['local', 'remote', 'both']).withDescription('Control mode'),
29
+ // If you see load_type 'unknown', pls. check with Tuya gateway and app and update with label from Tuya app.
30
+ exposes.enum('load_type', ea.STATE, ['unknown', 'resistive_capacitive', 'unknown', 'detecting'])
31
+ .withDescription('Load type'),
32
+ exposes.enum('load_dimmable', ea.STATE, ['unknown', 'dimmable', 'not_dimmable'])
33
+ .withDescription('Load dimmable'),
34
+ exposes.enum('power_supply_mode', ea.STATE, ['unknown', 'no_neutral', 'with_neutral'])
35
+ .withDescription('Power supply mode'),
26
36
  ],
27
37
  meta: {disableActionGroup: true},
38
+ onEvent: tuya.onEventSetLocalTime,
28
39
  configure: async (device, coordinatorEndpoint, logger) => {
29
40
  await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
30
41
  const endpoint = device.getEndpoint(1);
42
+ // Enables reporting of physical state changes
43
+ // https://github.com/Koenkk/zigbee2mqtt/issues/9057#issuecomment-1007742130
44
+ await endpoint.read('genBasic', ['manufacturerName', 'zclVersion', 'appVersion', 'modelId', 'powerSource', 0xfffe]);
31
45
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
32
46
  await reporting.onOff(endpoint);
33
47
  },
package/devices/ikea.js CHANGED
@@ -796,10 +796,10 @@ module.exports = [
796
796
  extend: tradfriExtend.light_onoff_brightness(),
797
797
  },
798
798
  {
799
- zigbeeModel: ['TRADFRIbulbGU10WS345lm', 'TRADFRI bulb GU10 WW 345lm'],
799
+ zigbeeModel: ['TRADFRIbulbGU10WS345lm', 'TRADFRI bulb GU10 WW 345lm', 'TRADFRIbulbGU10WS380lm'],
800
800
  model: 'LED2005R5',
801
801
  vendor: 'IKEA',
802
- description: 'TRADFRI LED bulb GU10 345 lumen, dimmable, white spectrum',
802
+ description: 'TRADFRI LED bulb GU10 345/380 lumen, dimmable, white spectrum',
803
803
  extend: tradfriExtend.light_onoff_brightness_colortemp(),
804
804
  },
805
805
  {
@@ -41,7 +41,7 @@ module.exports = [
41
41
  exposes: [e.cover_position().setAccess('state', ea.ALL), e.temperature(), e.battery(), e.pressure()],
42
42
  },
43
43
  {
44
- zigbeeModel: ['SV02-410-MP-1.3', 'SV02-412-MP-1.3', 'SV02-610-MP-1.3', 'SV02-612-MP-1.3', 'SV02-410-MP-1.0'],
44
+ zigbeeModel: ['SV02-410-MP-1.3', 'SV02-412-MP-1.3', 'SV02-610-MP-1.3', 'SV02-612-MP-1.3', 'SV02-410-MP-1.0', 'SV02-410-MP-1.2'],
45
45
  model: 'SV02',
46
46
  vendor: 'Keen Home',
47
47
  description: 'Smart vent',
@@ -127,7 +127,7 @@ module.exports = [
127
127
  },
128
128
  },
129
129
  {
130
- fingerprint: [{modelID: 'TS110F', manufacturerName: '_TZ3000_92chsky7'}],
130
+ fingerprint: [{modelID: 'TS110F', manufacturerName: '_TZ3000_92chsky7'}, {modelID: 'TS110E', manufacturerName: '_TZ3210_4ubylghk'}],
131
131
  model: 'QS-Zigbee-D02-TRIAC-2C-L',
132
132
  vendor: 'Lonsonho',
133
133
  description: '2 gang smart dimmer switch module without neutral',
@@ -1,4 +1,5 @@
1
1
  const exposes = require('../lib/exposes');
2
+ const fz = require('../converters/fromZigbee');
2
3
  const tz = require('../converters/toZigbee');
3
4
  const e = exposes.presets;
4
5
  const ea = exposes.access;
@@ -47,4 +48,28 @@ module.exports = [
47
48
  vendor: 'Miboxer',
48
49
  extend: extend.light_onoff_brightness_colortemp_color({disableColorTempStartup: true, colorTempRange: [153, 500]}),
49
50
  },
51
+ {
52
+ fingerprint: [{manufacturerName: '_TZ3000_xwh1e22x'}],
53
+ model: 'FUT089Z',
54
+ vendor: 'MiBoxer',
55
+ description: 'RGB+CCT Remote',
56
+ fromZigbee: [fz.battery],
57
+ toZigbee: [],
58
+ exposes: [e.battery(), e.battery_voltage()],
59
+ configure: async (device, coordinatorEndpoint, logger) => {
60
+ const endpoint = device.getEndpoint(1);
61
+ await endpoint.read('genBasic', ['manufacturerName', 'zclVersion', 'appVersion', 'modelId', 'powerSource', 0xfffe]);
62
+ await endpoint.command('genGroups', 'miboxerSetZones', {zones: [
63
+ {zoneNum: 1, groupId: 101},
64
+ {zoneNum: 2, groupId: 102},
65
+ {zoneNum: 3, groupId: 103},
66
+ {zoneNum: 4, groupId: 104},
67
+ {zoneNum: 5, groupId: 105},
68
+ {zoneNum: 6, groupId: 106},
69
+ {zoneNum: 7, groupId: 107},
70
+ {zoneNum: 8, groupId: 108},
71
+ ]});
72
+ await endpoint.command('genBasic', 'tuyaSetup', {}, {disableDefaultResponse: true});
73
+ },
74
+ },
50
75
  ];
package/devices/namron.js CHANGED
@@ -263,29 +263,68 @@ module.exports = [
263
263
  model: '4512737/4512738',
264
264
  vendor: 'Namron',
265
265
  description: 'Touch termostat',
266
- fromZigbee: [fz.thermostat, fz.metering, fz.electrical_measurement, fz.hvac_user_interface],
266
+ fromZigbee: [fz.thermostat, fz.namron_thermostat, fz.metering, fz.electrical_measurement,
267
+ fz.namron_hvac_user_interface],
267
268
  toZigbee: [tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint, tz.thermostat_occupancy,
268
269
  tz.thermostat_local_temperature_calibration, tz.thermostat_local_temperature, tz.thermostat_outdoor_temperature,
269
270
  tz.thermostat_system_mode, tz.thermostat_control_sequence_of_operation, tz.thermostat_running_state,
270
- tz.thermostat_keypad_lockout],
271
+ tz.namron_thermostat_child_lock, tz.namron_thermostat],
271
272
  exposes: [
272
273
  e.local_temperature(),
273
274
  exposes.numeric('outdoor_temperature', ea.STATE_GET).withUnit('°C')
274
275
  .withDescription('Current temperature measured from the floor sensor'),
275
- e.keypad_lockout(),
276
276
  exposes.climate()
277
- .withSetpoint('occupied_heating_setpoint', 5, 50, 0.01)
277
+ .withSetpoint('occupied_heating_setpoint', 0, 40, 0.1)
278
278
  .withLocalTemperature()
279
279
  .withLocalTemperatureCalibration(-30, 30, 0.1)
280
280
  .withSystemMode(['off', 'auto', 'heat'])
281
281
  .withRunningState(['idle', 'heat']),
282
+ exposes.binary('away_mode', ea.ALL, 'ON', 'OFF')
283
+ .withDescription('Enable/disable away mode'),
284
+ exposes.binary('child_lock', ea.ALL, 'LOCK', 'UNLOCK')
285
+ .withDescription('Enables/disables physical input on the device'),
282
286
  e.power(), e.current(), e.voltage(), e.energy(),
287
+ exposes.enum('lcd_brightness', ea.ALL, ['low', 'mid', 'high'])
288
+ .withDescription('OLED brightness when operating the buttons. Default: Medium.'),
289
+ exposes.enum('button_vibration_level', ea.ALL, ['off', 'low', 'high'])
290
+ .withDescription('Key beep volume and vibration level. Default: Low.'),
291
+ exposes.enum('floor_sensor_type', ea.ALL, ['10k', '15k', '50k', '100k', '12k'])
292
+ .withDescription('Type of the external floor sensor. Default: NTC 10K/25.'),
293
+ exposes.enum('sensor', ea.ALL, ['air', 'floor', 'both'])
294
+ .withDescription('The sensor used for heat control. Default: Room Sensor.'),
295
+ exposes.enum('powerup_status', ea.ALL, ['default', 'last_status'])
296
+ .withDescription('The mode after a power reset. Default: Previous Mode.'),
297
+ exposes.numeric('floor_sensor_calibration', ea.ALL)
298
+ .withUnit('°C')
299
+ .withValueMin(-3).withValueMax(3).withValueStep(0.1)
300
+ .withDescription('The tempearatue calibration for the exernal floor sensor, between -3 and 3 in 0.1°C. Default: 0.'),
301
+ exposes.numeric('dry_time', ea.ALL)
302
+ .withUnit('min')
303
+ .withValueMin(5).withValueMax(100)
304
+ .withDescription('The duration of Dry Mode, between 5 and 100 minutes. Default: 5.'),
305
+ exposes.enum('mode_after_dry', ea.ALL, ['off', 'manual', 'auto', 'away'])
306
+ .withDescription('The mode after Dry Mode. Default: Auto.'),
307
+ exposes.enum('temperature_display', ea.ALL, ['room', 'floor'])
308
+ .withDescription('The temperature on the display. Default: Room Temperature.'),
309
+ exposes.numeric('window_open_check', ea.ALL)
310
+ .withUnit('°C')
311
+ .withValueMin(3).withValueMax(8).withValueStep(0.5)
312
+ .withDescription('The threshold to detect window open, between 3 and 8 in 0.5 °C. Default: 0 (disabled).'),
313
+ exposes.numeric('hysterersis', ea.ALL)
314
+ .withUnit('°C')
315
+ .withValueMin(5).withValueMax(20).withValueStep(0.1)
316
+ .withDescription('Hysteresis setting, between 5 and 20 in 0.1 °C. Default: 5.'),
317
+ exposes.enum('display_auto_off_enabled', ea.ALL, ['enable', 'disabled']),
318
+ exposes.numeric('alarm_airtemp_overvalue', ea.ALL)
319
+ .withUnit('°C')
320
+ .withValueMin(20).withValueMax(60).withValueStep(1)
321
+ .withDescription('Room temperature alarm threshold, between 20 and 60 in °C. 0 means disabled. Default: 45.'),
283
322
  ],
284
323
  configure: async (device, coordinatorEndpoint, logger) => {
285
324
  const endpoint = device.getEndpoint(1);
286
325
  const binds = [
287
- 'genBasic', 'genIdentify', 'genGroups', 'genScenes', 'hvacThermostat',
288
- 'seMetering', 'haElectricalMeasurement', 'genAlarms', 'msOccupancySensing', 'genTime', 'hvacUserInterfaceCfg',
326
+ 'genBasic', 'genIdentify', 'hvacThermostat', 'seMetering', 'haElectricalMeasurement', 'genAlarms',
327
+ 'msOccupancySensing', 'genTime', 'hvacUserInterfaceCfg',
289
328
  ];
290
329
  await reporting.bind(endpoint, coordinatorEndpoint, binds);
291
330
 
@@ -302,15 +341,130 @@ module.exports = [
302
341
  reportableChange: null,
303
342
  }]);
304
343
 
344
+ await endpoint.read('haElectricalMeasurement', ['acVoltageMultiplier', 'acVoltageDivisor', 'acCurrentMultiplier']);
345
+ await endpoint.read('haElectricalMeasurement', ['acCurrentDivisor']);
346
+
305
347
  await reporting.activePower(endpoint);
306
- await reporting.currentSummDelivered(endpoint);
307
- await reporting.readMeteringMultiplierDivisor(endpoint);
308
- await reporting.rmsCurrent(endpoint);
309
- await reporting.rmsVoltage(endpoint);
348
+ await reporting.rmsCurrent(endpoint, {min: 10, change: 10});
349
+ await reporting.rmsVoltage(endpoint, {min: 10});
310
350
  await reporting.readMeteringMultiplierDivisor(endpoint);
351
+ await reporting.currentSummDelivered(endpoint);
352
+
353
+ // Custom attributes
354
+ const options = {manufacturerCode: 0x1224};
355
+
356
+ // OperateDisplayLcdBrightnesss
357
+ await endpoint.configureReporting('hvacThermostat', [{
358
+ attribute: {ID: 0x1000, type: 0x30},
359
+ minimumReportInterval: 0,
360
+ maximumReportInterval: constants.repInterval.HOUR,
361
+ reportableChange: null}],
362
+ options);
363
+ // ButtonVibrationLevel
364
+ await endpoint.configureReporting('hvacThermostat', [{
365
+ attribute: {ID: 0x1001, type: 0x30},
366
+ minimumReportInterval: 0,
367
+ maximumReportInterval: constants.repInterval.HOUR,
368
+ reportableChange: null}],
369
+ options);
370
+ // FloorSensorType
371
+ await endpoint.configureReporting('hvacThermostat', [{
372
+ attribute: {ID: 0x1002, type: 0x30},
373
+ minimumReportInterval: 0,
374
+ maximumReportInterval: constants.repInterval.HOUR,
375
+ reportableChange: null}],
376
+ options);
377
+ // ControlType
378
+ await endpoint.configureReporting('hvacThermostat', [{
379
+ attribute: {ID: 0x1003, type: 0x30},
380
+ minimumReportInterval: 0,
381
+ maximumReportInterval: constants.repInterval.HOUR,
382
+ reportableChange: null}],
383
+ options);
384
+ // PowerUpStatus
385
+ await endpoint.configureReporting('hvacThermostat', [{
386
+ attribute: {ID: 0x1004, type: 0x30},
387
+ minimumReportInterval: 0,
388
+ maximumReportInterval: constants.repInterval.HOUR,
389
+ reportableChange: null}],
390
+ options);
391
+ // FloorSensorCalibration
392
+ await endpoint.configureReporting('hvacThermostat', [{
393
+ attribute: {ID: 0x1005, type: 0x28},
394
+ minimumReportInterval: 0,
395
+ maximumReportInterval: constants.repInterval.HOUR,
396
+ reportableChange: 0}],
397
+ options);
398
+ // DryTime
399
+ await endpoint.configureReporting('hvacThermostat', [{
400
+ attribute: {ID: 0x1006, type: 0x20},
401
+ minimumReportInterval: 0,
402
+ maximumReportInterval: constants.repInterval.HOUR,
403
+ reportableChange: 0}],
404
+ options);
405
+ // ModeAfterDry
406
+ await endpoint.configureReporting('hvacThermostat', [{
407
+ attribute: {ID: 0x1007, type: 0x30},
408
+ minimumReportInterval: 0,
409
+ maximumReportInterval: constants.repInterval.HOUR,
410
+ reportableChange: null}],
411
+ options);
412
+ // TemperatureDisplay
413
+ await endpoint.configureReporting('hvacThermostat', [{
414
+ attribute: {ID: 0x1008, type: 0x30},
415
+ minimumReportInterval: 0,
416
+ maximumReportInterval: constants.repInterval.HOUR,
417
+ reportableChange: null}],
418
+ options);
419
+ // WindowOpenCheck
420
+ await endpoint.configureReporting('hvacThermostat', [{
421
+ attribute: {ID: 0x1009, type: 0x20},
422
+ minimumReportInterval: 0,
423
+ maximumReportInterval: constants.repInterval.HOUR,
424
+ reportableChange: 0}],
425
+ options);
426
+
427
+ // Hysterersis
428
+ await endpoint.configureReporting('hvacThermostat', [{
429
+ attribute: {ID: 0x100A, type: 0x20},
430
+ minimumReportInterval: 0,
431
+ maximumReportInterval: constants.repInterval.HOUR,
432
+ reportableChange: 0}],
433
+ options);
434
+ // DisplayAutoOffEnable
435
+ await endpoint.configureReporting('hvacThermostat', [{
436
+ attribute: {ID: 0x100B, type: 0x30},
437
+ minimumReportInterval: 0,
438
+ maximumReportInterval: constants.repInterval.HOUR,
439
+ reportableChange: null}],
440
+ options);
441
+
442
+ // AlarmAirTempOverValue
443
+ await endpoint.configureReporting('hvacThermostat', [{
444
+ attribute: {ID: 0x2001, type: 0x20},
445
+ minimumReportInterval: 0,
446
+ maximumReportInterval: constants.repInterval.HOUR,
447
+ reportableChange: 0}],
448
+ options);
449
+ // Away Mode Set
450
+ await endpoint.configureReporting('hvacThermostat', [{
451
+ attribute: {ID: 0x2002, type: 0x30},
452
+ minimumReportInterval: 0,
453
+ maximumReportInterval: constants.repInterval.HOUR,
454
+ reportableChange: null}],
455
+ options);
456
+
457
+ // Device does not asks for the time with binding, we need to write time during configure
458
+ const time = Math.round(((new Date()).getTime() - constants.OneJanuary2000) / 1000);
459
+ const values = {time: time};
460
+ endpoint.write('genTime', values);
311
461
 
312
- // Trigger read
313
- await endpoint.read('hvacThermostat', ['systemMode', 'runningState', 'occupied_heating_setpoint']);
462
+ // Trigger initial read
463
+ await endpoint.read('hvacThermostat', ['systemMode', 'runningState', 'occupiedHeatingSetpoint']);
464
+ await endpoint.read('hvacThermostat', [0x1000, 0x1001, 0x1002, 0x1003], options);
465
+ await endpoint.read('hvacThermostat', [0x1004, 0x1005, 0x1006, 0x1007], options);
466
+ await endpoint.read('hvacThermostat', [0x1008, 0x1009, 0x100A, 0x100B], options);
467
+ await endpoint.read('hvacThermostat', [0x2001, 0x2002], options);
314
468
  },
315
469
  },
316
470
  ];
package/devices/niko.js CHANGED
@@ -5,33 +5,47 @@ const reporting = require('../lib/reporting');
5
5
  const e = exposes.presets;
6
6
  const ea = exposes.access;
7
7
 
8
- const fzLocal = {
8
+ const local = {
9
9
  fz: {
10
10
  switch_operation_mode: {
11
- cluster: 'manuSpecificNikoSwitchSetup',
11
+ cluster: 'manuSpecificNiko1',
12
12
  type: ['attributeReport', 'readResponse'],
13
13
  convert: (model, msg, publish, options, meta) => {
14
14
  const state = {};
15
- if (msg.data.hasOwnProperty('operationMode')) {
15
+ if (msg.data.hasOwnProperty('switchOperationMode')) {
16
16
  const operationModeProperty = `operation_mode${meta.endpoint_name ? `_${meta.endpoint_name}` : ''}`;
17
17
  const operationModeMap = {0x02: 'control_relay', 0x01: 'decoupled', 0x00: 'unknown'};
18
- state[operationModeProperty] = operationModeMap[msg.data.operationMode];
18
+ state[operationModeProperty] = operationModeMap[msg.data.switchOperationMode];
19
19
  }
20
20
  return state;
21
21
  },
22
22
  },
23
23
  switch_action: {
24
- cluster: 'manuSpecificNikoSwitch',
24
+ cluster: 'manuSpecificNiko2',
25
25
  type: ['attributeReport', 'readResponse'],
26
26
  convert: (model, msg, publish, options, meta) => {
27
27
  const state = {};
28
28
 
29
- if (msg.data.hasOwnProperty('action')) {
29
+ if (msg.data.hasOwnProperty('switchAction')) {
30
30
  // NOTE: a single press = two seperate values reported, 16 followed by 64
31
31
  // a hold/release cyle = three seperate values, 16, 32, and 48
32
32
  const actionProperty = `action${meta.endpoint_name ? `_${meta.endpoint_name}` : ''}`;
33
33
  const actionMap = {16: null, 64: 'single', 32: 'hold', 48: 'release'};
34
- state[actionProperty] = actionMap[msg.data.action];
34
+ state[actionProperty] = actionMap[msg.data.switchAction];
35
+ }
36
+ return state;
37
+ },
38
+ },
39
+ outlet: {
40
+ cluster: 'manuSpecificNiko1',
41
+ type: ['attributeReport', 'readResponse'],
42
+ convert: (model, msg, publish, options, meta) => {
43
+ const state = {};
44
+ if (msg.data.hasOwnProperty('outletChildLock')) {
45
+ state['child_lock'] = (msg.data['outletChildLock'] == 0 ? 'LOCK' : 'UNLOCK');
46
+ }
47
+ if (msg.data.hasOwnProperty('outletLedState')) {
48
+ state['led_enable'] = (msg.data['outletLedState'] == 1);
35
49
  }
36
50
  return state;
37
51
  },
@@ -48,12 +62,32 @@ const fzLocal = {
48
62
  throw new Error(`operation_mode was called with an invalid value (${value})`);
49
63
  } else {
50
64
  const operationModeProperty = `operation_mode${meta.endpoint_name ? `_${meta.endpoint_name}` : ''}`;
51
- await entity.write('manuSpecificNikoSwitchSetup', {'operationMode': operationModeLookup[value]});
65
+ await entity.write('manuSpecificNiko1', {'switchOperationMode': operationModeLookup[value]});
52
66
  return {state: {[operationModeProperty]: value.toLowerCase()}};
53
67
  }
54
68
  },
55
69
  convertGet: async (entity, key, meta) => {
56
- await entity.read('manuSpecificNikoSwitchSetup', ['operationMode']);
70
+ await entity.read('manuSpecificNiko1', ['switchOperationMode']);
71
+ },
72
+ },
73
+ outlet_child_lock: {
74
+ key: ['child_lock'],
75
+ convertSet: async (entity, key, value, meta) => {
76
+ await entity.write('manuSpecificNiko1', {'outletChildLock': ((value.toLowerCase() === 'lock') ? 0 : 1)});
77
+ return {state: {child_lock: ((value.toLowerCase() === 'lock') ? 'LOCK' : 'UNLOCK')}};
78
+ },
79
+ convertGet: async (entity, key, meta) => {
80
+ await entity.read('manuSpecificNiko1', ['outletChildLock']);
81
+ },
82
+ },
83
+ outlet_led_enable: {
84
+ key: ['led_enable'],
85
+ convertSet: async (entity, key, value, meta) => {
86
+ await entity.write('manuSpecificNiko1', {'outletLedState': ((value) ? 1 : 0)});
87
+ return {state: {led_enable: ((value) ? true : false)}};
88
+ },
89
+ convertGet: async (entity, key, meta) => {
90
+ await entity.read('manuSpecificNiko1', ['outletLedState']);
57
91
  },
58
92
  },
59
93
  },
@@ -65,8 +99,11 @@ module.exports = [
65
99
  model: '170-33505',
66
100
  vendor: 'Niko',
67
101
  description: 'Connected socket outlet',
68
- fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
69
- toZigbee: [tz.on_off, tz.electrical_measurement_power, tz.currentsummdelivered],
102
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, local.fz.outlet],
103
+ toZigbee: [
104
+ tz.on_off, tz.electrical_measurement_power, tz.currentsummdelivered,
105
+ local.tz.outlet_child_lock, local.tz.outlet_led_enable,
106
+ ],
70
107
  configure: async (device, coordinatorEndpoint, logger) => {
71
108
  const endpoint = device.getEndpoint(1);
72
109
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
@@ -83,11 +120,16 @@ module.exports = [
83
120
 
84
121
  await reporting.readMeteringMultiplierDivisor(endpoint);
85
122
  await reporting.currentSummDelivered(endpoint, {min: 60, change: 1});
123
+
124
+ await endpoint.read('manuSpecificNiko1', ['outletChildLock']);
125
+ await endpoint.read('manuSpecificNiko1', ['outletLedState']);
86
126
  },
87
127
  exposes: [
88
128
  e.switch(),
89
129
  e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(),
90
130
  e.energy().withAccess(ea.STATE_GET),
131
+ exposes.binary('child_lock', ea.ALL, 'LOCK', 'UNLOCK').withDescription('Enables/disables physical input on the device'),
132
+ exposes.binary('led_enable', ea.ALL, true, false).withDescription('Enable LED'),
91
133
  ],
92
134
  },
93
135
  {
@@ -134,13 +176,13 @@ module.exports = [
134
176
  model: '552-721X1',
135
177
  vendor: 'Niko',
136
178
  description: 'Single connectable switch',
137
- fromZigbee: [fz.on_off, fzLocal.fz.switch_operation_mode, fzLocal.fz.switch_action],
138
- toZigbee: [tz.on_off, fzLocal.tz.switch_operation_mode],
179
+ fromZigbee: [fz.on_off, local.fz.switch_operation_mode, local.fz.switch_action],
180
+ toZigbee: [tz.on_off, local.tz.switch_operation_mode],
139
181
  configure: async (device, coordinatorEndpoint, logger) => {
140
182
  const endpoint = device.getEndpoint(1);
141
183
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
142
184
  await reporting.onOff(endpoint);
143
- await endpoint.read('manuSpecificNikoSwitchSetup', ['operationMode']);
185
+ await endpoint.read('manuSpecificNiko1', ['switchOperationMode']);
144
186
  },
145
187
  exposes: [
146
188
  e.switch(),
@@ -153,8 +195,8 @@ module.exports = [
153
195
  model: '552-721X2',
154
196
  vendor: 'Niko',
155
197
  description: 'Double connectable switch',
156
- fromZigbee: [fz.on_off, fzLocal.fz.switch_operation_mode, fzLocal.fz.switch_action],
157
- toZigbee: [tz.on_off, fzLocal.tz.switch_operation_mode],
198
+ fromZigbee: [fz.on_off, local.fz.switch_operation_mode, local.fz.switch_action],
199
+ toZigbee: [tz.on_off, local.tz.switch_operation_mode],
158
200
  endpoint: (device) => {
159
201
  return {'l1': 1, 'l2': 2};
160
202
  },
@@ -166,8 +208,8 @@ module.exports = [
166
208
  await reporting.bind(ep2, coordinatorEndpoint, ['genOnOff']);
167
209
  await reporting.onOff(ep1);
168
210
  await reporting.onOff(ep2);
169
- await ep1.read('manuSpecificNikoSwitchSetup', ['operationMode']);
170
- await ep2.read('manuSpecificNikoSwitchSetup', ['operationMode']);
211
+ await ep1.read('manuSpecificNiko1', ['switchOperationMode']);
212
+ await ep2.read('manuSpecificNiko1', ['switchOperationMode']);
171
213
  },
172
214
  exposes: [
173
215
  e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'),
package/devices/orvibo.js CHANGED
@@ -6,6 +6,13 @@ const extend = require('../lib/extend');
6
6
  const e = exposes.presets;
7
7
 
8
8
  module.exports = [
9
+ {
10
+ zigbeeModel: ['4a33f5ea766a4c96a962b371ffde9943'],
11
+ model: 'DS20Z07B',
12
+ vendor: 'ORVIBO',
13
+ description: 'Downlight (S series)',
14
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [166, 370]}),
15
+ },
9
16
  {
10
17
  zigbeeModel: ['ORVIBO Socket', '93e29b89b2ee45bea5bdbb7679d75d24'],
11
18
  model: 'OR-ZB-S010-3C',
@@ -108,6 +108,15 @@ module.exports = [
108
108
  extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
109
109
  ota: ota.zigbeeOTA,
110
110
  },
111
+ {
112
+ zigbeeModel: ['915005996801', '915005996901'],
113
+ model: '915005996901',
114
+ vendor: 'Philips',
115
+ description: 'Hue white ambiance ceiling light Enrave L with Bluetooth',
116
+ meta: {turnsOffAtBrightness1: true},
117
+ extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
118
+ ota: ota.zigbeeOTA,
119
+ },
111
120
  {
112
121
  zigbeeModel: ['915005997001', '915005997101'],
113
122
  model: '915005997001',
package/devices/tuya.js CHANGED
@@ -167,7 +167,7 @@ module.exports = [
167
167
  description: 'Air quality sensor',
168
168
  fromZigbee: [fz.tuya_air_quality],
169
169
  toZigbee: [],
170
- exposes: [e.temperature(), e.humidity(), e.co2(), e.voc(), e.formaldehyd()],
170
+ exposes: [e.temperature(), e.humidity(), e.co2(), e.voc().withUnit('ppm'), e.formaldehyd()],
171
171
  },
172
172
  {
173
173
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_dwcarsat'}],
@@ -2174,7 +2174,8 @@ module.exports = [
2174
2174
  ],
2175
2175
  },
2176
2176
  {
2177
- fingerprint: [{modelID: 'TS004F', manufacturerName: '_TZ3000_4fjiwweb'}, {modelID: 'TS004F', manufacturerName: '_TZ3000_uri7ongn'}],
2177
+ fingerprint: [{modelID: 'TS004F', manufacturerName: '_TZ3000_4fjiwweb'}, {modelID: 'TS004F', manufacturerName: '_TZ3000_uri7ongn'},
2178
+ {modelID: 'TS004F', manufacturerName: '_TZ3000_ixla93vd'}],
2178
2179
  model: 'ERS-10TZBVK-AA',
2179
2180
  vendor: 'TuYa',
2180
2181
  description: 'Smart knob',
package/devices/xiaomi.js CHANGED
@@ -1348,9 +1348,16 @@ module.exports = [
1348
1348
  model: 'ZNJLBL01LM',
1349
1349
  description: 'Aqara roller shade companion E1',
1350
1350
  vendor: 'Xiaomi',
1351
- fromZigbee: [fz.xiaomi_curtain_acn002_position, fz.xiaomi_curtain_acn002_status, fz.xiaomi_curtain_position_tilt,
1352
- fz.ignore_basic_report, fz.aqara_opple],
1351
+ fromZigbee: [fz.xiaomi_curtain_position, fz.xiaomi_curtain_acn002_status, fz.ignore_basic_report, fz.aqara_opple],
1353
1352
  toZigbee: [tz.xiaomi_curtain_position_state, tz.xiaomi_curtain_acn002_battery, tz.xiaomi_curtain_acn002_charging_status],
1353
+ onEvent: async (type, data, device) => {
1354
+ if (type === 'message' && data.type === 'attributeReport' && data.cluster === 'genMultistateOutput' &&
1355
+ data.data.hasOwnProperty('presentValue') && data.data['presentValue'] > 1) {
1356
+ // Try to read the position after the motor stops, the device occasionally report wrong data right after stopping
1357
+ // Might need to add delay, seems to be working without one but needs more tests.
1358
+ await device.getEndpoint(1).read('genAnalogOutput', ['presentValue']);
1359
+ }
1360
+ },
1354
1361
  exposes: [e.cover_position().setAccess('state', ea.ALL), e.battery().withAccess(ea.STATE_GET),
1355
1362
  exposes.binary('charging_status', ea.STATE_GET, true, false)
1356
1363
  .withDescription('The current charging status.'),
package/lib/exposes.js CHANGED
@@ -550,7 +550,7 @@ module.exports = {
550
550
  fan: () => new Fan(),
551
551
  flip_indicator_light: () => new Binary('flip_indicator_light', access.ALL, 'ON', 'OFF').withDescription('After turn on, the indicator light turns on while switch is off, and vice versa'),
552
552
  force: () => new Enum('force', access.STATE_SET, ['normal', 'open', 'close']).withDescription('Force the valve position'),
553
- formaldehyd: () => new Numeric('formaldehyd', access.STATE).withDescription('The measured formaldehyd value'),
553
+ formaldehyd: () => new Numeric('formaldehyd', access.STATE).withDescription('The measured formaldehyd value').withUnit('mg/m³'),
554
554
  gas: () => new Binary('gas', access.STATE, true, false).withDescription('Indicates whether the device detected gas'),
555
555
  hcho: () => new Numeric('hcho', access.STATE).withUnit('mg/m³').withDescription('Measured Hcho value'),
556
556
  holiday_temperature: () => new Numeric('holiday_temperature', access.STATE_SET).withUnit('°C').withDescription('Holiday temperature').withValueMin(0).withValueMax(30),
package/lib/tuya.js CHANGED
@@ -449,16 +449,23 @@ const dataPoints = {
449
449
  silvercrestSetColor: 5,
450
450
  silvercrestSetEffect: 6,
451
451
  // Fantem
452
+ fantemPowerSupplyMode: 101,
452
453
  fantemReportingTime: 102,
454
+ fantemExtSwitchType: 103,
453
455
  fantemTempCalibration: 104,
454
456
  fantemHumidityCalibration: 105,
457
+ fantemLoadDetectionMode: 105,
455
458
  fantemLuxCalibration: 106,
459
+ fantemExtSwitchStatus: 106,
456
460
  fantemTemp: 107,
457
461
  fantemHumidity: 108,
458
462
  fantemMotionEnable: 109,
463
+ fantemControlMode: 109,
459
464
  fantemBattery: 110,
460
465
  fantemLedEnable: 111,
461
466
  fantemReportingEnable: 112,
467
+ fantemLoadType: 112,
468
+ fantemLoadDimmable: 113,
462
469
  // Woox
463
470
  wooxSwitch: 102,
464
471
  wooxBattery: 14,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.496",
3
+ "version": "14.0.499",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [