zigbee-herdsman-converters 14.0.403 → 14.0.407

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.
@@ -2522,8 +2522,8 @@ const converters = {
2522
2522
  return {battpercentage: value};
2523
2523
  case tuya.dataPoints.neoAOMelody: // 0x21 [5] Melody
2524
2524
  return {melody: value};
2525
- case tuya.dataPoints.neoAOVolume: // 0x5 [0]/[1]/[2] Volume 0-max, 2-low
2526
- return {volume: {2: 'low', 1: 'medium', 0: 'high'}[value]};
2525
+ case tuya.dataPoints.neoAOVolume: // 0x5 [0]/[1]/[2] Volume 0-low, 2-max
2526
+ return {volume: {0: 'low', 1: 'medium', 2: 'high'}[value]};
2527
2527
  default: // Unknown code
2528
2528
  meta.logger.warn(`Unhandled DP #${dp}: ${JSON.stringify(msg.data)}`);
2529
2529
  }
@@ -3211,6 +3211,10 @@ const converters = {
3211
3211
  result[postfixWithEndpointName('pi_heating_demand', msg, model)] =
3212
3212
  precisionRound(msg.data['pIHeatingDemand'], 0);
3213
3213
  }
3214
+ if (msg.data.hasOwnProperty('danfossWindowOpenFeatureEnable')) {
3215
+ result[postfixWithEndpointName('window_open_feature', msg, model)] =
3216
+ (msg.data['danfossWindowOpenFeatureEnable'] === 1);
3217
+ }
3214
3218
  if (msg.data.hasOwnProperty('danfossWindowOpenInternal')) {
3215
3219
  result[postfixWithEndpointName('window_open_internal', msg, model)] =
3216
3220
  constants.danfossWindowOpen.hasOwnProperty(msg.data['danfossWindowOpenInternal']) ?
@@ -3815,19 +3819,21 @@ const converters = {
3815
3819
  return {open_window_temperature: (value / 10).toFixed(1)};
3816
3820
  case tuya.dataPoints.tvErrorStatus:
3817
3821
  return {fault_alarm: value};
3818
- case tuya.dataPoints.tvHolidayMode:
3819
- return {holiday_mode_date: value};
3820
-
3822
+ case tuya.dataPoints.tvHolidayMode: {
3823
+ const sy = value.slice(0, 4); const sm = value.slice(4, 6); const sd = value.slice(6, 8);
3824
+ const sh = value.slice(8, 10); const smi = value.slice(10, 12); const ey = value.slice(12, 16);
3825
+ const em = value.slice(16, 18); const ed = value.slice(18, 20); const eh = value.slice(20, 22);
3826
+ const emi = value.slice(22, 24);
3827
+ const hMode = 'start --> ' + sy + ' - ' + sm + ' - ' + sd + ' ' + sh + ' : ' + smi +
3828
+ ' stop --> ' + ey + ' - ' + em + ' - ' + ed + ' ' + eh + ' : ' + emi;
3829
+ return {holiday_start_stop: hMode};
3830
+ }
3821
3831
  case tuya.dataPoints.tvBoostMode:
3822
- // Online ?
3832
+ // 115 online / Is the device online
3823
3833
  return {online: value ? 'ON' : 'OFF'};
3824
3834
  case tuya.dataPoints.tvWorkingDay:
3825
- // tvWorkingDay: 31,
3835
+ // DP-31, Send and Report, ENUM, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
3826
3836
  return {working_day: value};
3827
- case tuya.dataPoints.tvWeekSchedule:
3828
- // tvWeekSchedule: 106, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
3829
- return {week_schedule: value};
3830
-
3831
3837
  case tuya.dataPoints.tvMondaySchedule:
3832
3838
  return {schedule_monday:
3833
3839
  ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
@@ -5027,6 +5033,9 @@ const converters = {
5027
5033
  // dimmer
5028
5034
  else if (option0 === 0x0101) payload.device_mode = 'dimmer_on';
5029
5035
  else if (option0 === 0x0100) payload.device_mode = 'dimmer_off';
5036
+ // pilot wire
5037
+ else if (option0 === 0x0002) payload.device_mode = 'pilot_on';
5038
+ else if (option0 === 0x0001) payload.device_mode = 'pilot_off';
5030
5039
  // unknown case
5031
5040
  else {
5032
5041
  meta.logger.warn(`device_mode ${option0} not recognized, please fix me`);
@@ -5035,6 +5044,26 @@ const converters = {
5035
5044
  return payload;
5036
5045
  },
5037
5046
  },
5047
+ legrand_cable_outlet_mode: {
5048
+ cluster: '64576',
5049
+ type: ['readResponse'],
5050
+ convert: (model, msg, publish, options, meta) => {
5051
+ const payload = {};
5052
+ const mode = msg.data['0'];
5053
+
5054
+ if (mode === 0x00) payload.cable_outlet_mode = 'comfort';
5055
+ else if (mode === 0x01) payload.cable_outlet_mode = 'comfort-1';
5056
+ else if (mode === 0x02) payload.cable_outlet_mode = 'comfort-2';
5057
+ else if (mode === 0x03) payload.cable_outlet_mode = 'eco';
5058
+ else if (mode === 0x04) payload.cable_outlet_mode = 'frost_protection';
5059
+ else if (mode === 0x05) payload.cable_outlet_mode = 'off';
5060
+ else {
5061
+ meta.logger.warn(`Bad mode : ${mode}`);
5062
+ payload.cable_outlet_mode = 'unknown';
5063
+ }
5064
+ return payload;
5065
+ },
5066
+ },
5038
5067
  legrand_power_alarm: {
5039
5068
  cluster: 'haElectricalMeasurement',
5040
5069
  type: ['attributeReport', 'readResponse'],
@@ -5255,13 +5284,16 @@ const converters = {
5255
5284
  payload.battery = batteryVoltageToPercentage(value, '3V_2100');
5256
5285
  } else if (index === 3) payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
5257
5286
  else if (index === 5) {
5258
- if (['JT-BZ-01AQ/A'].includes(model.model)) payload.power_outage_count = value;
5287
+ if (['JT-BZ-01AQ/A', 'RTCZCGQ11LM'].includes(model.model)) payload.power_outage_count = value;
5259
5288
  } else if (index === 100) {
5260
5289
  if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5261
5290
  const mapping = model.model === 'QBCZ15LM' ? 'relay' : 'left';
5262
5291
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5263
5292
  } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
5264
5293
  payload.click_mode = {1: 'fast', 2: 'multi'}[value];
5294
+ } else if (['WXCJKG11LM ', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
5295
+ // We don't know what the value means for these devices.
5296
+ // https://github.com/Koenkk/zigbee2mqtt/issues/11126
5265
5297
  } else {
5266
5298
  payload.state = value === 1 ? 'ON' : 'OFF';
5267
5299
  }
@@ -5273,13 +5305,24 @@ const converters = {
5273
5305
  payload.state_center = value === 1 ? 'ON' : 'OFF';
5274
5306
  } else if (['RTCGQ12LM'].includes(model.model)) {
5275
5307
  payload.illuminance = calibrateAndPrecisionRoundOptions(value, options, 'illuminance');
5308
+ } else if (['ZNJLBL01LM'].includes(model.model)) {
5309
+ payload.battery = value;
5276
5310
  }
5277
5311
  } else if (index ===102 ) {
5278
5312
  if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
5279
5313
  payload.state_right = value === 1 ? 'ON' : 'OFF';
5314
+ } else if (['RTCZCGQ11LM'].includes(model.model)) {
5315
+ payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
5316
+ 5: 'left_leave', 6: 'approach', 7: 'away', 255: null}[value];
5280
5317
  }
5281
- } else if (index === 105) payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[value]; // RTCGQ13LM
5282
- else if (index === 149) {
5318
+ } else if (index ===103) payload.monitoring_mode = value === 1 ? 'left_right' : 'undirected'; // RTCZCGQ11LM
5319
+ else if (index === 105) {
5320
+ if (['RTCGQ13LM'].includes(model.model)) {
5321
+ payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[value];
5322
+ } else if (['RTCZCGQ11LM'].includes(model.model)) {
5323
+ payload.approach_distance = {0: 'far', 1: 'medium', 2: 'near'}[value];
5324
+ }
5325
+ } else if (index === 149) {
5283
5326
  payload.energy = precisionRound(value, 2); // 0x95
5284
5327
  // Consumption is deprecated
5285
5328
  payload.consumption = payload.energy;
@@ -5318,6 +5361,15 @@ const converters = {
5318
5361
  if (msg.data.hasOwnProperty('313')) payload.state = msg.data['313'] === 1 ? 'preparation' : 'work'; // JT-BZ-01AQ/A
5319
5362
  if (msg.data.hasOwnProperty('314')) payload.gas = msg.data['314'] === 1; // JT-BZ-01AQ/A
5320
5363
  if (msg.data.hasOwnProperty('315')) payload.gas_density = msg.data['315']; // JT-BZ-01AQ/A
5364
+ if (msg.data.hasOwnProperty('322')) payload.presence = msg.data['322'] === 1; // RTCZCGQ11LM
5365
+ if (msg.data.hasOwnProperty('323')) {
5366
+ payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
5367
+ 5: 'left_leave', 6: 'approach', 7: 'away'}[msg.data['323']]; // RTCZCGQ11LM
5368
+ }
5369
+ // RTCZCGQ11LM
5370
+ if (msg.data.hasOwnProperty('324')) payload.monitoring_mode = msg.data['324'] === 1 ? 'left_right' : 'undirected';
5371
+ // RTCZCGQ11LM
5372
+ if (msg.data.hasOwnProperty('326')) payload.approach_distance = {0: 'far', 1: 'medium', 2: 'near'}[msg.data['326']];
5321
5373
  if (msg.data.hasOwnProperty('331')) payload.linkage_alarm = msg.data['331'] === 1; // JT-BZ-01AQ/A
5322
5374
  if (msg.data.hasOwnProperty('512')) {
5323
5375
  if (['ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM'].includes(model.model)) {
@@ -5334,6 +5386,7 @@ const converters = {
5334
5386
  if (msg.data.hasOwnProperty('523')) payload.overload_protection = precisionRound(msg.data['523'], 2);
5335
5387
  if (msg.data.hasOwnProperty('550')) payload.button_switch_mode = msg.data['550'] === 1 ? 'relay_and_usb' : 'relay';
5336
5388
  if (msg.data['mode'] !== undefined) payload.operation_mode = ['command', 'event'][msg.data['mode']];
5389
+ if (msg.data.hasOwnProperty('1289')) payload.dimmer_mode = {3: 'rgbw', 1: 'dual_ct'}[msg.data['1289']];
5337
5390
  return payload;
5338
5391
  },
5339
5392
  },
@@ -5403,7 +5456,8 @@ const converters = {
5403
5456
  }
5404
5457
 
5405
5458
  let buttonLookup = null;
5406
- if (['WXKG02LM_rev2', 'WXKG07LM', 'WXKG17LM'].includes(model.model)) buttonLookup = {1: 'left', 2: 'right', 3: 'both'};
5459
+ if (['WXKG02LM_rev2', 'WXKG07LM', 'WXKG15LM', 'WXKG17LM',
5460
+ 'WRS-R02'].includes(model.model)) buttonLookup = {1: 'left', 2: 'right', 3: 'both'};
5407
5461
  if (['QBKG12LM', 'QBKG24LM'].includes(model.model)) buttonLookup = {5: 'left', 6: 'right', 7: 'both'};
5408
5462
  if (['QBKG39LM', 'QBKG41LM', 'WS-EUK02', 'WS-EUK04', 'QBKG20LM', 'QBKG31LM'].includes(model.model)) {
5409
5463
  buttonLookup = {41: 'left', 42: 'right', 51: 'both'};
@@ -5806,6 +5860,40 @@ const converters = {
5806
5860
  }
5807
5861
  },
5808
5862
  },
5863
+ xiaomi_curtain_acn002_position: {
5864
+ cluster: 'genAnalogOutput',
5865
+ type: ['attributeReport', 'readResponse'],
5866
+ options: [exposes.options.invert_cover()],
5867
+ convert: (model, msg, publish, options, meta) => {
5868
+ let position = precisionRound(msg.data['presentValue'], 2);
5869
+ position = options.invert_cover ? 100 - position : position;
5870
+ return {position: position};
5871
+ },
5872
+ },
5873
+ xiaomi_curtain_acn002_status: {
5874
+ cluster: 'genMultistateOutput',
5875
+ type: ['attributeReport', 'readResponse'],
5876
+ convert: (model, msg, publish, options, meta) => {
5877
+ let running = false;
5878
+ const data = msg.data;
5879
+ const lookup = {
5880
+ 0: 'declining',
5881
+ 1: 'rising',
5882
+ 2: 'pause',
5883
+ 3: 'blocked',
5884
+ };
5885
+ if (data && data.hasOwnProperty('presentValue')) {
5886
+ const value = data['presentValue'];
5887
+ if (value < 2) {
5888
+ running = true;
5889
+ }
5890
+ return {
5891
+ motor_state: lookup[value],
5892
+ running: running,
5893
+ };
5894
+ }
5895
+ },
5896
+ },
5809
5897
  xiaomi_operation_mode_basic: {
5810
5898
  cluster: 'genBasic',
5811
5899
  type: ['attributeReport', 'readResponse'],
@@ -8154,7 +8242,7 @@ const converters = {
8154
8242
  cluster: 'genOnOff',
8155
8243
  type: 'raw',
8156
8244
  convert: (model, msg, publish, options, meta) => {
8157
- if (hasAlreadyProcessedMessage(msg, msg.data[2])) return;
8245
+ if (hasAlreadyProcessedMessage(msg, msg.data[1])) return;
8158
8246
  let action;
8159
8247
  if (msg.data[2] == 253) {
8160
8248
  action = {0: 'single', 1: 'double', 2: 'hold'}[msg.data[3]];
@@ -54,6 +54,14 @@ const converters = {
54
54
  meta.logger.info(`Wrote '${JSON.stringify(value.payload)}' to '${value.cluster}'`);
55
55
  },
56
56
  },
57
+ command: {
58
+ key: ['command'],
59
+ convertSet: async (entity, key, value, meta) => {
60
+ const options = utils.getOptions(meta.mapped, entity);
61
+ await entity.command(value.cluster, value.command, (value.hasOwnProperty('payload') ? value.payload : {}), options);
62
+ meta.logger.info(`Invoked '${value.cluster}.${value.command}' with payload '${JSON.stringify(value.payload)}'`);
63
+ },
64
+ },
57
65
  factory_reset: {
58
66
  key: ['reset'],
59
67
  convertSet: async (entity, key, value, meta) => {
@@ -2006,6 +2014,36 @@ const converters = {
2006
2014
  await entity.read('aqaraOpple', [0x010c], manufacturerOptions.xiaomi);
2007
2015
  },
2008
2016
  },
2017
+ RTCZCGQ11LM_presence: {
2018
+ key: ['presence'],
2019
+ convertGet: async (entity, key, meta) => {
2020
+ await entity.read('aqaraOpple', [0x0142], manufacturerOptions.xiaomi);
2021
+ },
2022
+ },
2023
+ RTCZCGQ11LM_monitoring_mode: {
2024
+ key: ['monitoring_mode'],
2025
+ convertSet: async (entity, key, value, meta) => {
2026
+ value = value.toLowerCase();
2027
+ const lookup = {'undirected': 0, 'left_right': 1};
2028
+ await entity.write('aqaraOpple', {0x0144: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
2029
+ return {state: {monitoring_mode: value}};
2030
+ },
2031
+ convertGet: async (entity, key, meta) => {
2032
+ await entity.read('aqaraOpple', [0x0144], manufacturerOptions.xiaomi);
2033
+ },
2034
+ },
2035
+ RTCZCGQ11LM_approach_distance: {
2036
+ key: ['approach_distance'],
2037
+ convertSet: async (entity, key, value, meta) => {
2038
+ value = value.toLowerCase();
2039
+ const lookup = {'far': 0, 'medium': 1, 'near': 2};
2040
+ await entity.write('aqaraOpple', {0x0146: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
2041
+ return {state: {approach_distance: value}};
2042
+ },
2043
+ convertGet: async (entity, key, meta) => {
2044
+ await entity.read('aqaraOpple', [0x0146], manufacturerOptions.xiaomi);
2045
+ },
2046
+ },
2009
2047
  ZigUP_lock: {
2010
2048
  key: ['led'],
2011
2049
  convertSet: async (entity, key, value, meta) => {
@@ -2040,7 +2078,7 @@ const converters = {
2040
2078
  convertSet: async (entity, key, value, meta) => {
2041
2079
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'SSM-U01', 'SSM-U02', 'DLKZMK11LM', 'DLKZMK12LM',
2042
2080
  'WS-EUK01', 'WS-EUK02', 'WS-EUK03', 'WS-EUK04', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2043
- 'QBKG31LM', 'QBKG34LM', 'QBKG38LM', 'QBKG39LM', 'QBKG40LM', 'QBKG41LM'].includes(meta.mapped.model)) {
2081
+ 'QBKG31LM', 'QBKG34LM', 'QBKG38LM', 'QBKG39LM', 'QBKG40LM', 'QBKG41LM', 'ZNDDMK11LM'].includes(meta.mapped.model)) {
2044
2082
  await entity.write('aqaraOpple', {0x0201: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
2045
2083
  } else if (['ZNCZ02LM', 'QBCZ11LM'].includes(meta.mapped.model)) {
2046
2084
  const payload = value ?
@@ -2063,7 +2101,7 @@ const converters = {
2063
2101
  convertGet: async (entity, key, meta) => {
2064
2102
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'SSM-U01', 'SSM-U02', 'DLKZMK11LM', 'DLKZMK12LM',
2065
2103
  'WS-EUK01', 'WS-EUK02', 'WS-EUK03', 'WS-EUK04', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2066
- 'QBKG31LM', 'QBKG34LM', 'QBKG38LM', 'QBKG39LM', 'QBKG40LM', 'QBKG41LM'].includes(meta.mapped.model)) {
2104
+ 'QBKG31LM', 'QBKG34LM', 'QBKG38LM', 'QBKG39LM', 'QBKG40LM', 'QBKG41LM', 'ZNDDMK11LM'].includes(meta.mapped.model)) {
2067
2105
  await entity.read('aqaraOpple', [0x0201]);
2068
2106
  } else if (['ZNCZ02LM', 'QBCZ11LM', 'ZNCZ11LM'].includes(meta.mapped.model)) {
2069
2107
  await entity.read('aqaraOpple', [0xFFF0]);
@@ -2206,6 +2244,25 @@ const converters = {
2206
2244
  await entity.read('aqaraOpple', [0x00F0], manufacturerOptions.xiaomi);
2207
2245
  },
2208
2246
  },
2247
+ xiaomi_dimmer_mode: {
2248
+ key: ['dimmer_mode'],
2249
+ convertSet: async (entity, key, value, meta) => {
2250
+ const lookup = {'rgbw': 3, 'dual_ct': 1};
2251
+ value = value.toLowerCase();
2252
+ if (['rgbw'].includes(value)) {
2253
+ await entity.write('aqaraOpple', {0x0509: {value: lookup[value], type: 0x23}}, manufacturerOptions.xiaomi);
2254
+ await entity.write('aqaraOpple', {0x050F: {value: 1, type: 0x23}}, manufacturerOptions.xiaomi);
2255
+ } else {
2256
+ await entity.write('aqaraOpple', {0x0509: {value: lookup[value], type: 0x23}}, manufacturerOptions.xiaomi);
2257
+ // Turn on dimming channel 1 and channel 2
2258
+ await entity.write('aqaraOpple', {0x050F: {value: 3, type: 0x23}}, manufacturerOptions.xiaomi);
2259
+ }
2260
+ return {state: {dimmer_mode: value}};
2261
+ },
2262
+ convertGet: async (entity, key, value, meta) => {
2263
+ await entity.read('aqaraOpple', [0x0509], manufacturerOptions.xiaomi);
2264
+ },
2265
+ },
2209
2266
  xiaomi_switch_operation_mode_basic: {
2210
2267
  key: ['operation_mode'],
2211
2268
  convertSet: async (entity, key, value, meta) => {
@@ -2333,7 +2390,12 @@ const converters = {
2333
2390
  options: [exposes.options.invert_cover()],
2334
2391
  convertSet: async (entity, key, value, meta) => {
2335
2392
  if (key === 'state' && typeof value === 'string' && value.toLowerCase() === 'stop') {
2336
- await entity.command('closuresWindowCovering', 'stop', {}, utils.getOptions(meta.mapped, entity));
2393
+ if (meta.mapped.model == 'ZNJLBL01LM') {
2394
+ const payload = {'presentValue': 2};
2395
+ await entity.write('genMultistateOutput', payload);
2396
+ } else {
2397
+ await entity.command('closuresWindowCovering', 'stop', {}, utils.getOptions(meta.mapped, entity));
2398
+ }
2337
2399
 
2338
2400
  // Xiaomi curtain does not send position update on stop, request this.
2339
2401
  await entity.read('genAnalogOutput', [0x0055]);
@@ -2355,6 +2417,12 @@ const converters = {
2355
2417
  await entity.read('genAnalogOutput', [0x0055]);
2356
2418
  },
2357
2419
  },
2420
+ xiaomi_curtain_acn002_status: {
2421
+ key: ['motor_state'],
2422
+ convertGet: async (entity, key, meta) => {
2423
+ await entity.read('genMultistateOutput', [0x0055]);
2424
+ },
2425
+ },
2358
2426
  ledvance_commands: {
2359
2427
  /* deprectated osram_*/
2360
2428
  key: ['set_transition', 'remember_state', 'osram_set_transition', 'osram_remember_state'],
@@ -2684,6 +2752,16 @@ const converters = {
2684
2752
  await entity.read('hvacThermostat', ['danfossTriggerTime'], manufacturerOptions.danfoss);
2685
2753
  },
2686
2754
  },
2755
+ danfoss_window_open_feature: {
2756
+ key: ['window_open_feature'],
2757
+ convertSet: async (entity, key, value, meta) => {
2758
+ await entity.write('hvacThermostat', {'danfossWindowOpenFeatureEnable': value}, manufacturerOptions.danfoss);
2759
+ return {readAfterWriteTime: 200, state: {'window_open_feature': value}};
2760
+ },
2761
+ convertGet: async (entity, key, meta) => {
2762
+ await entity.read('hvacThermostat', ['danfossWindowOpenFeatureEnable'], manufacturerOptions.danfoss);
2763
+ },
2764
+ },
2687
2765
  danfoss_window_open_internal: {
2688
2766
  key: ['window_open_internal'],
2689
2767
  convertGet: async (entity, key, meta) => {
@@ -2967,8 +3045,8 @@ const converters = {
2967
3045
  key: [
2968
3046
  'child_lock', 'open_window', 'open_window_temperature', 'frost_protection', 'heating_stop',
2969
3047
  'current_heating_setpoint', 'local_temperature_calibration', 'preset', 'boost_timeset_countdown',
2970
- 'holiday_mode_date', 'holiday_temperature', 'comfort_temperature', 'eco_temperature',
2971
- 'working_day', 'week_schedule', 'week', 'online',
3048
+ 'holiday_start_stop', 'holiday_temperature', 'comfort_temperature', 'eco_temperature',
3049
+ 'working_day', 'week_schedule_programming', 'online', 'holiday_mode_date',
2972
3050
  ],
2973
3051
  convertSet: async (entity, key, value, meta) => {
2974
3052
  switch (key) {
@@ -2981,6 +3059,7 @@ const converters = {
2981
3059
  await tuya.sendDataPointBool(entity, tuya.dataPoints.tvHeatingStop, 1);
2982
3060
  } else {
2983
3061
  await tuya.sendDataPointBool(entity, tuya.dataPoints.tvHeatingStop, 0);
3062
+ await utils.sleep(500);
2984
3063
  await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, 1 /* manual */);
2985
3064
  }
2986
3065
  break;
@@ -2989,6 +3068,7 @@ const converters = {
2989
3068
  await tuya.sendDataPointBool(entity, tuya.dataPoints.tvFrostDetection, 1);
2990
3069
  } else {
2991
3070
  await tuya.sendDataPointBool(entity, tuya.dataPoints.tvFrostDetection, 0);
3071
+ await utils.sleep(500);
2992
3072
  await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, 1 /* manual */);
2993
3073
  }
2994
3074
  break;
@@ -3005,6 +3085,7 @@ const converters = {
3005
3085
  break;
3006
3086
  case 'current_heating_setpoint':
3007
3087
  await tuya.sendDataPointValue(entity, tuya.dataPoints.tvHeatingSetpoint, value * 10);
3088
+ await utils.sleep(500);
3008
3089
  await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, 1 /* manual */);
3009
3090
  break;
3010
3091
  case 'holiday_temperature':
@@ -3023,19 +3104,26 @@ const converters = {
3023
3104
  case 'open_window_temperature':
3024
3105
  await tuya.sendDataPointValue(entity, tuya.dataPoints.tvOpenWindowTemp, value * 10);
3025
3106
  break;
3026
- case 'holiday_mode_date':
3027
- await tuya.sendDataPointBitmap(entity, tuya.dataPoints.tvHolidayMode, value);
3028
- break;
3029
-
3107
+ case 'holiday_start_stop': {
3108
+ const numberPattern = /\d+/g;
3109
+ value = value.match(numberPattern).join([]).toString();
3110
+ return tuya.sendDataPointStringBuffer(entity, tuya.dataPoints.tvHolidayMode, value);
3111
+ }
3030
3112
  case 'online':
3031
- // 115 ????? online
3113
+ // 115 online / Is the device online
3032
3114
  await tuya.sendDataPointBool(entity, tuya.dataPoints.tvBoostMode, value === 'ON');
3033
3115
  break;
3034
- case 'week': {
3035
- const weekLookup = {'5+2': 0, '6+1': 1, '7': 2};
3036
- const week = weekLookup[value];
3037
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvWorkingDay, week);
3038
- return {state: {week: value}};}
3116
+ case 'working_day': {
3117
+ // DP-31, Send and Report, ENUM, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
3118
+ const workLookup = {'0': 0, '1': 1, '2': 2, '3': 3};
3119
+ const workDay = workLookup[value];
3120
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvWorkingDay, workDay);
3121
+ return {state: {working_day: value}};
3122
+ }
3123
+ case 'week_schedule_programming':
3124
+ // DP-106, Send Only, raw, week_program3_day
3125
+ await tuya.sendDataPointRaw(entity, tuya.dataPoints.tvWeekSchedule, value);
3126
+ break;
3039
3127
 
3040
3128
  default: // Unknown key
3041
3129
  meta.logger.warn(`toZigbee.tvtwo_thermostat: Unhandled key ${key}`);
@@ -4450,6 +4538,9 @@ const converters = {
4450
4538
  // contactor
4451
4539
  'switch': 0x0003,
4452
4540
  'auto': 0x0004,
4541
+ // pilot wire
4542
+ 'pilot_on': 0x0002,
4543
+ 'pilot_off': 0x0001,
4453
4544
  };
4454
4545
 
4455
4546
  value = value.toLowerCase();
@@ -4462,6 +4553,15 @@ const converters = {
4462
4553
  await entity.read('manuSpecificLegrandDevices', [0x0000, 0x0001, 0x0002], manufacturerOptions.legrand);
4463
4554
  },
4464
4555
  },
4556
+ legrand_cableOutletMode: {
4557
+ key: ['cable_outlet_mode'],
4558
+ convertSet: async (entity, key, value, meta) => {
4559
+ meta.logger.warn('Feature under development !');
4560
+ },
4561
+ convertGet: async (entity, key, meta) => {
4562
+ await entity.read(64576, [0x0000], manufacturerOptions.legrand);
4563
+ },
4564
+ },
4465
4565
  legrand_powerAlarm: {
4466
4566
  key: ['power_alarm'],
4467
4567
  convertSet: async (entity, key, value, meta) => {
@@ -6669,7 +6769,7 @@ const converters = {
6669
6769
  await tuya.sendDataPoints(entity, [
6670
6770
  tuya.dpValueFromEnum(tuya.dataPoints.silvercrestChangeMode, tuya.silvercrestModes.white),
6671
6771
  tuya.dpValueFromIntValue(tuya.dataPoints.dimmerLevel, newValue),
6672
- ], 'dataRequest', 1);
6772
+ ], 'dataRequest');
6673
6773
 
6674
6774
  return {state: (key == 'white_brightness') ? {white_brightness: value} : {brightness: value}};
6675
6775
  } else if (key == 'color_temp') {
@@ -6696,7 +6796,7 @@ const converters = {
6696
6796
  await tuya.sendDataPoints(entity, [
6697
6797
  tuya.dpValueFromEnum(tuya.dataPoints.silvercrestChangeMode, tuya.silvercrestModes.white),
6698
6798
  tuya.dpValueFromIntValue(tuya.dataPoints.silvercrestSetColorTemp, data),
6699
- ], 'dataRequest', 1);
6799
+ ], 'dataRequest');
6700
6800
 
6701
6801
  return {state: {color_temp: value}};
6702
6802
  } else if (key == 'color' || (separateWhite && (key == 'brightness'))) {
@@ -6775,13 +6875,7 @@ const converters = {
6775
6875
  tuya.dpValueFromStringBuffer(tuya.dataPoints.silvercrestSetColor, data),
6776
6876
  ];
6777
6877
 
6778
- if (separateWhite && meta.state.white_brightness != undefined) {
6779
- // restore white state
6780
- const newValue = utils.mapNumberRange(meta.state.white_brightness, 0, 255, 0, 1000);
6781
- commands.push(tuya.dpValueFromEnum(tuya.dataPoints.silvercrestChangeMode, tuya.silvercrestModes.white));
6782
- commands.push(tuya.dpValueFromIntValue(tuya.dataPoints.dimmerLevel, newValue));
6783
- }
6784
- await tuya.sendDataPoints(entity, commands, 'dataRequest', 1);
6878
+ await tuya.sendDataPoints(entity, commands, 'dataRequest');
6785
6879
 
6786
6880
  return {state: newState};
6787
6881
  }
package/devices/acova.js CHANGED
@@ -3,33 +3,64 @@ const fz = require('../converters/fromZigbee');
3
3
  const tz = require('../converters/toZigbee');
4
4
  const reporting = require('../lib/reporting');
5
5
 
6
- module.exports = [{
7
- zigbeeModel: ['PERCALE2 D1.00P1.01Z1.00'],
8
- model: 'PERCALE2',
9
- vendor: 'Acova',
10
- description: 'Percale 2 heater',
11
- fromZigbee: [fz.thermostat, fz.hvac_user_interface],
12
- toZigbee: [
13
- tz.thermostat_local_temperature,
14
- tz.thermostat_system_mode,
15
- tz.thermostat_occupied_heating_setpoint,
16
- tz.thermostat_unoccupied_heating_setpoint,
17
- tz.thermostat_occupied_cooling_setpoint,
18
- tz.thermostat_running_state,
19
- ],
20
- exposes: [
21
- exposes.climate()
22
- .withSetpoint('occupied_heating_setpoint', 7, 28, 0.5)
23
- .withLocalTemperature()
24
- .withSystemMode(['off', 'heat', 'auto'])
25
- .withRunningState(['idle', 'heat']),
26
- ],
27
- configure: async (device, coordinatorEndpoint, logger) => {
28
- const endpoint = device.getEndpoint(1);
29
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'hvacThermostat']);
30
- await reporting.thermostatTemperature(endpoint);
31
- await reporting.thermostatRunningState(endpoint);
32
- await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
33
- await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
6
+ module.exports = [
7
+ {
8
+ zigbeeModel: ['PERCALE2 D1.00P1.01Z1.00'],
9
+ model: 'PERCALE2',
10
+ vendor: 'Acova',
11
+ description: 'Percale 2 heater',
12
+ fromZigbee: [fz.thermostat, fz.hvac_user_interface],
13
+ toZigbee: [
14
+ tz.thermostat_local_temperature,
15
+ tz.thermostat_system_mode,
16
+ tz.thermostat_occupied_heating_setpoint,
17
+ tz.thermostat_unoccupied_heating_setpoint,
18
+ tz.thermostat_occupied_cooling_setpoint,
19
+ tz.thermostat_running_state,
20
+ ],
21
+ exposes: [
22
+ exposes.climate()
23
+ .withSetpoint('occupied_heating_setpoint', 7, 28, 0.5)
24
+ .withLocalTemperature()
25
+ .withSystemMode(['off', 'heat', 'auto'])
26
+ .withRunningState(['idle', 'heat']),
27
+ ],
28
+ configure: async (device, coordinatorEndpoint, logger) => {
29
+ const endpoint = device.getEndpoint(1);
30
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'hvacThermostat']);
31
+ await reporting.thermostatTemperature(endpoint);
32
+ await reporting.thermostatRunningState(endpoint);
33
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
34
+ await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
35
+ },
34
36
  },
35
- }];
37
+ {
38
+ zigbeeModel: ['ALCANTARA2 D1.00P1.02Z1.00\u0000\u0000\u0000\u0000\u0000\u0000'],
39
+ model: 'ALCANTARA2',
40
+ vendor: 'Acova',
41
+ description: 'Alcantara 2 heater',
42
+ fromZigbee: [fz.thermostat, fz.hvac_user_interface],
43
+ toZigbee: [
44
+ tz.thermostat_local_temperature,
45
+ tz.thermostat_system_mode,
46
+ tz.thermostat_occupied_heating_setpoint,
47
+ tz.thermostat_unoccupied_heating_setpoint,
48
+ tz.thermostat_running_state,
49
+ ],
50
+ exposes: [
51
+ exposes.climate()
52
+ .withSetpoint('occupied_heating_setpoint', 7, 28, 0.5)
53
+ .withLocalTemperature()
54
+ .withSystemMode(['off', 'heat', 'auto'])
55
+ .withRunningState(['idle', 'heat']),
56
+ ],
57
+ configure: async (device, coordinatorEndpoint, logger) => {
58
+ const endpoint = device.getEndpoint(1);
59
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'hvacThermostat']);
60
+ await reporting.thermostatTemperature(endpoint);
61
+ await reporting.thermostatRunningState(endpoint);
62
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
63
+ await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
64
+ },
65
+ },
66
+ ];
@@ -22,7 +22,8 @@ module.exports = [
22
22
  tz.danfoss_window_open_internal, tz.danfoss_window_open_external, tz.danfoss_load_estimate,
23
23
  tz.danfoss_viewing_direction, tz.danfoss_external_measured_room_sensor, tz.danfoss_radiator_covered,
24
24
  tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tz.danfoss_load_balancing_enable, tz.danfoss_load_room_mean,
25
- tz.thermostat_weekly_schedule, tz.thermostat_clear_weekly_schedule, tz.thermostat_programming_operation_mode],
25
+ tz.thermostat_weekly_schedule, tz.thermostat_clear_weekly_schedule, tz.thermostat_programming_operation_mode,
26
+ tz.danfoss_window_open_feature],
26
27
  exposes: [e.battery(), e.keypad_lockout(), e.programming_operation_mode(),
27
28
  exposes.binary('mounted_mode_active', ea.STATE_GET, true, false)
28
29
  .withDescription('Is the unit in mounting mode. This is set to `false` for mounted (already on ' +
@@ -53,6 +54,8 @@ module.exports = [
53
54
  exposes.binary('radiator_covered', ea.ALL, true, false)
54
55
  .withDescription('Set if the TRV should solely rely on external_measured_room_sensor or operate in offset mode. ' +
55
56
  '`false` = Auto Offset Mode or `true` = Room Sensor Mode'),
57
+ exposes.binary('window_open_feature', ea.ALL, true, false)
58
+ .withDescription('Whether or not the window open feature is enabled'),
56
59
  exposes.numeric('window_open_internal', ea.STATE_GET).withValueMin(0).withValueMax(4)
57
60
  .withDescription('0=Quarantine, 1=Windows are closed, 2=Hold - Windows are maybe about to open, ' +
58
61
  '3=Open window detected, 4=In window open state from external but detected closed locally'),
@@ -114,6 +117,7 @@ module.exports = [
114
117
  }], options);
115
118
 
116
119
  await endpoint.read('hvacThermostat', [
120
+ 'danfossWindowOpenFeatureEnable',
117
121
  'danfossWindowOpenExternal',
118
122
  'danfossDayOfWeek',
119
123
  'danfossTriggerTime',
@@ -231,6 +231,14 @@ module.exports = [
231
231
  extend: gledoptoExtend.light_onoff_brightness_colortemp_color(),
232
232
  meta: {disableDefaultResponse: true},
233
233
  },
234
+ {
235
+ zigbeeModel: ['GL-C-003P'],
236
+ model: 'GL-C-003P',
237
+ vendor: 'Gledopto',
238
+ ota: ota.zigbeeOTA,
239
+ description: 'Zigbee LED Controller RGB (pro)',
240
+ extend: gledoptoExtend.light_onoff_brightness_color(),
241
+ },
234
242
  {
235
243
  zigbeeModel: ['GL-C-008P'],
236
244
  model: 'GL-C-008P',
package/devices/jasco.js CHANGED
@@ -1,5 +1,7 @@
1
1
  const reporting = require('../lib/reporting');
2
2
  const extend = require('../lib/extend');
3
+ const exposes = require('../lib/exposes');
4
+ const e = exposes.presets;
3
5
 
4
6
  module.exports = [
5
7
  {
@@ -15,4 +17,19 @@ module.exports = [
15
17
  await reporting.onOff(endpoint);
16
18
  },
17
19
  },
20
+ {
21
+ zigbeeModel: ['43132'],
22
+ model: '43132',
23
+ vendor: 'Jasco',
24
+ description: 'Zigbee smart outlet',
25
+ extend: extend.switch(),
26
+ exposes: [e.switch(), e.power(), e.energy()],
27
+ configure: async (device, coordinatorEndpoint, logger) => {
28
+ const endpoint = device.getEndpoint(1);
29
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'seMetering']);
30
+ await reporting.onOff(endpoint);
31
+ await reporting.readMeteringMultiplierDivisor(endpoint);
32
+ await reporting.instantaneousDemand(endpoint);
33
+ },
34
+ },
18
35
  ];