zigbee-herdsman-converters 21.26.0 → 21.27.1

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.
package/devices/tuya.js CHANGED
@@ -1126,7 +1126,7 @@ const definitions = [
1126
1126
  },
1127
1127
  },
1128
1128
  {
1129
- fingerprint: tuya.fingerprint('TS0601', ['_TZE200_vvmbj46n']),
1129
+ fingerprint: tuya.fingerprint('TS0601', ['_TZE200_vvmbj46n', '_TZE284_vvmbj46n']),
1130
1130
  model: 'ZTH05Z',
1131
1131
  vendor: 'Tuya',
1132
1132
  description: 'Temperature and humidity sensor',
@@ -2665,6 +2665,7 @@ const definitions = [
2665
2665
  '_TZE204_6wi2mope',
2666
2666
  '_TZE204_iik0pquw',
2667
2667
  '_TZE204_aagrxlbd',
2668
+ '_TZE204_f5efvtbv',
2668
2669
  ]),
2669
2670
  model: 'TS0601_switch_4_gang_1',
2670
2671
  vendor: 'Tuya',
@@ -2684,6 +2685,7 @@ const definitions = [
2684
2685
  { vendor: 'Somgoms', model: 'ZSQB-SMB-ZB' },
2685
2686
  { vendor: 'Moes', model: 'WS-EUB1-ZG' },
2686
2687
  { vendor: 'AVATTO', model: 'ZGB-WS-EU' },
2688
+ tuya.whitelabel('AVATTO', 'WSMD-4', '4 gang switch', ['_TZE204_f5efvtbv']),
2687
2689
  ],
2688
2690
  meta: {
2689
2691
  multiEndpoint: true,
@@ -3415,35 +3417,51 @@ const definitions = [
3415
3417
  description: 'FCU thermostat temperature controller',
3416
3418
  fromZigbee: [tuya.fz.datapoints],
3417
3419
  toZigbee: [tuya.tz.datapoints],
3418
- onEvent: tuya.onEventSetTime,
3420
+ onEvent: tuya.onEventSetLocalTime,
3419
3421
  configure: tuya.configureMagicPacket,
3420
- exposes: [
3421
- e
3422
- .climate()
3423
- .withLocalTemperature(ea.STATE)
3424
- .withSystemMode(['off', 'cool', 'heat', 'fan_only'], ea.STATE_SET)
3425
- .withFanMode(['low', 'medium', 'high', 'auto'], ea.STATE_SET)
3426
- .withSetpoint('current_heating_setpoint', 5, 35, 1, ea.STATE_SET)
3427
- .withPreset(['auto', 'manual'])
3428
- .withLocalTemperatureCalibration(-3, 3, 1, ea.STATE_SET),
3429
- e
3430
- .numeric('deadzone_temperature', ea.STATE_SET)
3431
- .withUnit('°C')
3432
- .withValueMax(5)
3433
- .withValueMin(1)
3434
- .withValueStep(1)
3435
- .withPreset('default', 1, 'Default value')
3436
- .withDescription('The delta between local_temperature and current_heating_setpoint to trigger activity'),
3437
- e.child_lock(),
3422
+ options: [
3438
3423
  e
3439
- .text('schedule', ea.STATE_SET)
3440
- .withDescription('Schedule will work with "auto" preset. In this mode, the device executes ' +
3441
- 'a preset week programming temperature time and temperature. Schedule can contains 12 segments. ' +
3442
- 'All 12 segments should be defined. It should be defined in the following format: "hh:mm/tt". ' +
3443
- 'Segments should be divided by space symbol. ' +
3444
- 'Example: "06:00/20 11:30/21 13:30/22 17:30/23 06:00/24 12:00/23 14:30/22 17:30/21 06:00/19 12:30/20 14:30/21 18:30/20"'),
3424
+ .enum('control_sequence_of_operation', ea.SET, ['cooling_only', 'cooling_and_heating_4-pipes'])
3425
+ .withDescription('Operating environment of the thermostat'),
3445
3426
  ],
3427
+ exposes: (device, options) => {
3428
+ const system_modes = ['off', 'cool', 'heat', 'fan_only'];
3429
+ // Device can operate either in 2-pipe or 4-pipe configuration
3430
+ // For 2-pipe configurations remove 'heat' mode
3431
+ switch (options?.control_sequence_of_operation) {
3432
+ case 'cooling_only':
3433
+ system_modes.splice(2, 1);
3434
+ break;
3435
+ }
3436
+ return [
3437
+ e
3438
+ .climate()
3439
+ .withLocalTemperature(ea.STATE)
3440
+ .withSystemMode(system_modes, ea.STATE_SET)
3441
+ .withFanMode(['low', 'medium', 'high', 'auto'], ea.STATE_SET)
3442
+ .withSetpoint('current_heating_setpoint', 5, 35, 1, ea.STATE_SET)
3443
+ .withPreset(['auto', 'manual'])
3444
+ .withLocalTemperatureCalibration(-3, 3, 1, ea.STATE_SET),
3445
+ e.child_lock(),
3446
+ e
3447
+ .composite('schedule', 'schedule', ea.STATE_SET)
3448
+ .withFeature(e.text('weekdays', ea.SET).withDescription('Schedule (1-5), 4 periods in format "hh:mm/tt".'))
3449
+ .withFeature(e.text('saturday', ea.SET).withDescription('Schedule (6), 4 periods in format "hh:mm/tt".'))
3450
+ .withFeature(e.text('sunday', ea.SET).withDescription('Schedule (7), 4 periods in format "hh:mm/tt".'))
3451
+ .withDescription('Auto-mode schedule, 4 periods each per category. Example: "06:00/20 11:30/21 13:30/22 17:30/23.5".'),
3452
+ e.max_temperature().withValueMin(35).withValueMax(45).withPreset('default', 35, 'Default value'),
3453
+ e
3454
+ .numeric('deadzone_temperature', ea.STATE_SET)
3455
+ .withUnit('°C')
3456
+ .withValueMax(5)
3457
+ .withValueMin(1)
3458
+ .withValueStep(1)
3459
+ .withPreset('default', 1, 'Default value')
3460
+ .withDescription('The delta between local_temperature and current_heating_setpoint to trigger activity'),
3461
+ ];
3462
+ },
3446
3463
  meta: {
3464
+ publishDuplicateTransaction: true,
3447
3465
  tuyaDatapoints: [
3448
3466
  [
3449
3467
  1,
@@ -3451,13 +3469,13 @@ const definitions = [
3451
3469
  {
3452
3470
  from: (v, meta) => {
3453
3471
  return v === true
3454
- ? { state: 'ON', system_mode: meta.state.system_mode_device ? meta.state.system_mode_device : 'cool' }
3455
- : { state: 'OFF', system_mode: 'off' };
3472
+ ? { system_mode: meta.state.system_mode_device ? meta.state.system_mode_device : 'cool' }
3473
+ : { system_mode: 'off' };
3456
3474
  },
3457
3475
  },
3458
3476
  ],
3459
3477
  [
3460
- null,
3478
+ 2,
3461
3479
  'system_mode',
3462
3480
  {
3463
3481
  // Extend system_mode to support 'off' in addition to 'cool', 'heat' and 'fan_only'
@@ -3477,34 +3495,16 @@ const definitions = [
3477
3495
  break;
3478
3496
  }
3479
3497
  },
3480
- },
3481
- ],
3482
- [
3483
- 2,
3484
- null,
3485
- {
3486
- // Map system_mode back to both 'state' and 'system_mode'
3487
3498
  from: (v, meta) => {
3488
3499
  const modes = ['cool', 'heat', 'fan_only'];
3489
- return {
3490
- system_mode: modes[v],
3491
- system_mode_device: modes[v],
3492
- };
3500
+ meta.state.system_mode_device = modes[v];
3501
+ return modes[v];
3493
3502
  },
3494
3503
  },
3495
3504
  ],
3496
3505
  [4, 'preset', tuya.valueConverterBasic.lookup({ manual: true, auto: false })],
3497
- [16, 'current_cooling_setpoint', tuya.valueConverter.raw],
3498
3506
  [16, 'current_heating_setpoint', tuya.valueConverter.raw],
3499
- [
3500
- 16,
3501
- null,
3502
- {
3503
- from: (v, meta) => {
3504
- return { current_cooling_setpoint: v, current_heating_setpoint: v };
3505
- },
3506
- },
3507
- ],
3507
+ [19, 'max_temperature', tuya.valueConverter.raw],
3508
3508
  [24, 'local_temperature', tuya.valueConverter.divideBy10],
3509
3509
  [26, 'deadzone_temperature', tuya.valueConverter.raw],
3510
3510
  [27, 'local_temperature_calibration', tuya.valueConverter.localTemperatureCalibration],
@@ -3515,27 +3515,43 @@ const definitions = [
3515
3515
  'schedule',
3516
3516
  {
3517
3517
  to: (v, meta) => {
3518
- const regex = /((?<h>[01][0-9]|2[0-3]):(?<m>[0-5][0-9])\/(?<t>[0-3][0-9](\.[0,5]|)))/gm;
3519
- const matches = [...v.matchAll(regex)];
3520
- if (matches.length == 12) {
3521
- return matches.reduce((arr, m) => {
3522
- arr.push(parseInt(m.groups.h));
3523
- arr.push(parseInt(m.groups.m));
3524
- arr.push(parseFloat(m.groups.t) * 2);
3525
- return arr;
3526
- }, []);
3527
- }
3528
- logger_1.logger.warning('Ignoring invalid or incomplete schedule', NS);
3518
+ const periods = (value) => {
3519
+ const regex = /((?<h>[01][0-9]|2[0-3]):(?<m>[0-5][0-9])\/(?<t>[0-3][0-9](\.[0,5]|)))/gm;
3520
+ const matches = [...value.matchAll(regex)];
3521
+ if (matches.length == 4) {
3522
+ return matches.reduce((arr, m) => {
3523
+ arr.push(parseInt(m.groups.h));
3524
+ arr.push(parseInt(m.groups.m));
3525
+ arr.push(parseFloat(m.groups.t) * 2);
3526
+ return arr;
3527
+ }, []);
3528
+ }
3529
+ logger_1.logger.warning('Ignoring invalid or incomplete schedule', NS);
3530
+ };
3531
+ const schedule = [...periods(v['weekdays']), ...periods(v['saturday']), ...periods(v['sunday'])];
3532
+ return schedule;
3529
3533
  },
3530
3534
  from: (v, meta) => {
3531
- let r = '';
3532
- for (let i = 0; i < 12; i++) {
3533
- r += `${v[i * 3].toString().padStart(2, '0')}:${v[i * 3 + 1].toString().padStart(2, '0')}/${v[i * 3 + 2] / 2}`;
3534
- if (i < 11) {
3535
- r += ' ';
3536
- }
3537
- }
3538
- return r;
3535
+ const format = (data) => {
3536
+ return data.reduce((a, v, i) => {
3537
+ switch (i % 3) {
3538
+ // Hour
3539
+ case 0:
3540
+ return `${a}${i > 0 ? ' ' : ''}${v.toString().padStart(2, '0')}`;
3541
+ // Minute
3542
+ case 1:
3543
+ return `${a}:${v.toString().padStart(2, '0')}`;
3544
+ // Setpoint
3545
+ case 2:
3546
+ return `${a}/${v / 2}`;
3547
+ }
3548
+ }, '');
3549
+ };
3550
+ return {
3551
+ weekdays: format(v.slice(0, 12)),
3552
+ saturday: format(v.slice(1 * 12, 2 * 12)),
3553
+ sunday: format(v.slice(2 * 12, 3 * 12)),
3554
+ };
3539
3555
  },
3540
3556
  },
3541
3557
  ],
@@ -11217,13 +11233,12 @@ const definitions = [
11217
11233
  onEvent: tuya.onEventSetLocalTime,
11218
11234
  configure: tuya.configureMagicPacket,
11219
11235
  exposes: [
11220
- e.binary('state', ea.STATE_SET, 'ON', 'OFF').withDescription('Turn the thermostat ON/OFF'),
11221
11236
  e.child_lock(),
11222
11237
  e
11223
11238
  .climate()
11224
11239
  .withLocalTemperature(ea.STATE)
11225
11240
  .withSetpoint('current_heating_setpoint', 5, 35, 1, ea.STATE_SET)
11226
- .withSystemMode(['cool', 'heat', 'fan_only'], ea.STATE_SET)
11241
+ .withSystemMode(['off', 'cool', 'heat', 'fan_only'], ea.STATE_SET)
11227
11242
  .withFanMode(['low', 'medium', 'high', 'auto'], ea.STATE_SET)
11228
11243
  .withLocalTemperatureCalibration(-5, 5, 0.5, ea.STATE_SET),
11229
11244
  e.min_temperature().withValueMin(5).withValueMax(15),
@@ -11238,8 +11253,45 @@ const definitions = [
11238
11253
  ],
11239
11254
  meta: {
11240
11255
  tuyaDatapoints: [
11241
- [1, 'state', tuya.valueConverter.onOff],
11242
- [2, 'system_mode', tuya.valueConverterBasic.lookup({ cool: tuya.enum(0), heat: tuya.enum(1), fan_only: tuya.enum(2) })],
11256
+ [
11257
+ 1,
11258
+ null,
11259
+ {
11260
+ from: (v, meta) => {
11261
+ return v === true
11262
+ ? { system_mode: meta.state.system_mode_device ? meta.state.system_mode_device : 'cool' }
11263
+ : { system_mode: 'off' };
11264
+ },
11265
+ },
11266
+ ],
11267
+ [
11268
+ 2,
11269
+ 'system_mode',
11270
+ {
11271
+ // Extend system_mode to support 'off' in addition to 'cool', 'heat' and 'fan_only'
11272
+ to: async (v, meta) => {
11273
+ const entity = meta.device.endpoints[0];
11274
+ // Power State
11275
+ await tuya.sendDataPointBool(entity, 1, v !== 'off', 'dataRequest', 1);
11276
+ switch (v) {
11277
+ case 'cool':
11278
+ await tuya.sendDataPointEnum(entity, 2, 0, 'dataRequest', 1);
11279
+ break;
11280
+ case 'heat':
11281
+ await tuya.sendDataPointEnum(entity, 2, 1, 'dataRequest', 1);
11282
+ break;
11283
+ case 'fan_only':
11284
+ await tuya.sendDataPointEnum(entity, 2, 2, 'dataRequest', 1);
11285
+ break;
11286
+ }
11287
+ },
11288
+ from: (v, meta) => {
11289
+ const modes = ['cool', 'heat', 'fan_only'];
11290
+ meta.state.system_mode_device = modes[v];
11291
+ return modes[v];
11292
+ },
11293
+ },
11294
+ ],
11243
11295
  [4, 'eco_mode', tuya.valueConverter.onOff],
11244
11296
  [16, 'current_heating_setpoint', tuya.valueConverter.divideBy10],
11245
11297
  [19, 'max_temperature', tuya.valueConverter.divideBy10],