Skip to content

Commit 9a94890

Browse files
fix: Improve Tuya BAC-003 support (#8694)
1 parent 301258d commit 9a94890

File tree

1 file changed

+88
-76
lines changed

1 file changed

+88
-76
lines changed

src/devices/tuya.ts

+88-76
Original file line numberDiff line numberDiff line change
@@ -3430,60 +3430,74 @@ const definitions: DefinitionWithExtend[] = [
34303430
description: 'FCU thermostat temperature controller',
34313431
fromZigbee: [tuya.fz.datapoints],
34323432
toZigbee: [tuya.tz.datapoints],
3433-
onEvent: tuya.onEventSetTime,
3433+
onEvent: tuya.onEventSetLocalTime,
34343434
configure: tuya.configureMagicPacket,
3435-
exposes: [
3436-
e
3437-
.climate()
3438-
.withLocalTemperature(ea.STATE)
3439-
.withSystemMode(['off', 'cool', 'heat', 'fan_only'], ea.STATE_SET)
3440-
.withFanMode(['low', 'medium', 'high', 'auto'], ea.STATE_SET)
3441-
.withSetpoint('current_heating_setpoint', 5, 35, 1, ea.STATE_SET)
3442-
.withPreset(['auto', 'manual'])
3443-
.withLocalTemperatureCalibration(-3, 3, 1, ea.STATE_SET),
3444-
e
3445-
.numeric('deadzone_temperature', ea.STATE_SET)
3446-
.withUnit('°C')
3447-
.withValueMax(5)
3448-
.withValueMin(1)
3449-
.withValueStep(1)
3450-
.withPreset('default', 1, 'Default value')
3451-
.withDescription('The delta between local_temperature and current_heating_setpoint to trigger activity'),
3452-
e.child_lock(),
3435+
options: [
34533436
e
3454-
.text('schedule', ea.STATE_SET)
3455-
.withDescription(
3456-
'Schedule will work with "auto" preset. In this mode, the device executes ' +
3457-
'a preset week programming temperature time and temperature. Schedule can contains 12 segments. ' +
3458-
'All 12 segments should be defined. It should be defined in the following format: "hh:mm/tt". ' +
3459-
'Segments should be divided by space symbol. ' +
3460-
'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"',
3461-
),
3437+
.enum('control_sequence_of_operation', ea.SET, ['cooling_only', 'cooling_and_heating_4-pipes'])
3438+
.withDescription('Operating environment of the thermostat'),
34623439
],
3440+
exposes: (device, options) => {
3441+
const system_modes = ['off', 'cool', 'heat', 'fan_only'];
3442+
3443+
// Device can operate either in 2-pipe or 4-pipe configuration
3444+
// For 2-pipe configurations remove 'heat' mode
3445+
switch (options?.control_sequence_of_operation) {
3446+
case 'cooling_only':
3447+
system_modes.splice(2, 1);
3448+
break;
3449+
}
3450+
3451+
return [
3452+
e
3453+
.climate()
3454+
.withLocalTemperature(ea.STATE)
3455+
.withSystemMode(system_modes, ea.STATE_SET)
3456+
.withFanMode(['low', 'medium', 'high', 'auto'], ea.STATE_SET)
3457+
.withSetpoint('current_heating_setpoint', 5, 35, 1, ea.STATE_SET)
3458+
.withPreset(['auto', 'manual'])
3459+
.withLocalTemperatureCalibration(-3, 3, 1, ea.STATE_SET),
3460+
e.child_lock(),
3461+
e
3462+
.composite('schedule', 'schedule', ea.STATE_SET)
3463+
.withFeature(e.text('weekdays', ea.SET).withDescription('Schedule (1-5), 4 periods in format "hh:mm/tt".'))
3464+
.withFeature(e.text('saturday', ea.SET).withDescription('Schedule (6), 4 periods in format "hh:mm/tt".'))
3465+
.withFeature(e.text('sunday', ea.SET).withDescription('Schedule (7), 4 periods in format "hh:mm/tt".'))
3466+
.withDescription('Auto-mode schedule, 4 periods each per category. Example: "06:00/20 11:30/21 13:30/22 17:30/23.5".'),
3467+
e.max_temperature().withValueMin(35).withValueMax(45).withPreset('default', 35, 'Default value'),
3468+
e
3469+
.numeric('deadzone_temperature', ea.STATE_SET)
3470+
.withUnit('°C')
3471+
.withValueMax(5)
3472+
.withValueMin(1)
3473+
.withValueStep(1)
3474+
.withPreset('default', 1, 'Default value')
3475+
.withDescription('The delta between local_temperature and current_heating_setpoint to trigger activity'),
3476+
];
3477+
},
34633478
meta: {
3479+
publishDuplicateTransaction: true,
34643480
tuyaDatapoints: [
34653481
[
34663482
1,
34673483
null,
34683484
{
34693485
from: (v, meta) => {
34703486
return v === true
3471-
? {state: 'ON', system_mode: meta.state.system_mode_device ? meta.state.system_mode_device : 'cool'}
3472-
: {state: 'OFF', system_mode: 'off'};
3487+
? {system_mode: meta.state.system_mode_device ? meta.state.system_mode_device : 'cool'}
3488+
: {system_mode: 'off'};
34733489
},
34743490
},
34753491
],
34763492
[
3477-
null,
3493+
2,
34783494
'system_mode',
34793495
{
34803496
// Extend system_mode to support 'off' in addition to 'cool', 'heat' and 'fan_only'
34813497
to: async (v: string, meta) => {
34823498
const entity = meta.device.endpoints[0];
3483-
34843499
// Power State
34853500
await tuya.sendDataPointBool(entity, 1, v !== 'off', 'dataRequest', 1);
3486-
34873501
switch (v) {
34883502
case 'cool':
34893503
await tuya.sendDataPointEnum(entity, 2, 0, 'dataRequest', 1);
@@ -3496,35 +3510,16 @@ const definitions: DefinitionWithExtend[] = [
34963510
break;
34973511
}
34983512
},
3499-
},
3500-
],
3501-
[
3502-
2,
3503-
null,
3504-
{
3505-
// Map system_mode back to both 'state' and 'system_mode'
35063513
from: (v: number, meta) => {
35073514
const modes = ['cool', 'heat', 'fan_only'];
3508-
3509-
return {
3510-
system_mode: modes[v],
3511-
system_mode_device: modes[v],
3512-
};
3515+
meta.state.system_mode_device = modes[v];
3516+
return modes[v];
35133517
},
35143518
},
35153519
],
35163520
[4, 'preset', tuya.valueConverterBasic.lookup({manual: true, auto: false})],
3517-
[16, 'current_cooling_setpoint', tuya.valueConverter.raw],
35183521
[16, 'current_heating_setpoint', tuya.valueConverter.raw],
3519-
[
3520-
16,
3521-
null,
3522-
{
3523-
from: (v, meta) => {
3524-
return {current_cooling_setpoint: v, current_heating_setpoint: v};
3525-
},
3526-
},
3527-
],
3522+
[19, 'max_temperature', tuya.valueConverter.raw],
35283523
[24, 'local_temperature', tuya.valueConverter.divideBy10],
35293524
[26, 'deadzone_temperature', tuya.valueConverter.raw],
35303525
[27, 'local_temperature_calibration', tuya.valueConverter.localTemperatureCalibration],
@@ -3534,33 +3529,50 @@ const definitions: DefinitionWithExtend[] = [
35343529
101,
35353530
'schedule',
35363531
{
3537-
to: (v: string, meta) => {
3538-
const regex = /((?<h>[01][0-9]|2[0-3]):(?<m>[0-5][0-9])\/(?<t>[0-3][0-9](\.[0,5]|)))/gm;
3539-
const matches = [...v.matchAll(regex)];
3532+
to: (v: {weekdays: string; saturday: string; sunday: string}, meta) => {
3533+
const periods = (value: string) => {
3534+
const regex = /((?<h>[01][0-9]|2[0-3]):(?<m>[0-5][0-9])\/(?<t>[0-3][0-9](\.[0,5]|)))/gm;
3535+
const matches = [...value.matchAll(regex)];
35403536

3541-
if (matches.length == 12) {
3542-
return matches.reduce((arr, m) => {
3543-
arr.push(parseInt(m.groups.h));
3544-
arr.push(parseInt(m.groups.m));
3545-
arr.push(parseFloat(m.groups.t) * 2);
3546-
return arr;
3547-
}, []);
3548-
}
3537+
if (matches.length == 4) {
3538+
return matches.reduce((arr, m) => {
3539+
arr.push(parseInt(m.groups.h));
3540+
arr.push(parseInt(m.groups.m));
3541+
arr.push(parseFloat(m.groups.t) * 2);
3542+
return arr;
3543+
}, []);
3544+
}
35493545

3550-
logger.warning('Ignoring invalid or incomplete schedule', NS);
3551-
},
3552-
from: (v: number[], meta) => {
3553-
let r = '';
3546+
logger.warning('Ignoring invalid or incomplete schedule', NS);
3547+
};
35543548

3555-
for (let i = 0; i < 12; i++) {
3556-
r += `${v[i * 3].toString().padStart(2, '0')}:${v[i * 3 + 1].toString().padStart(2, '0')}/${v[i * 3 + 2] / 2}`;
3549+
const schedule = [...periods(v['weekdays']), ...periods(v['saturday']), ...periods(v['sunday'])];
35573550

3558-
if (i < 11) {
3559-
r += ' ';
3560-
}
3561-
}
3551+
return schedule;
3552+
},
3553+
from: (v: number[], meta) => {
3554+
const format = (data: number[]) => {
3555+
return data.reduce((a, v, i) => {
3556+
switch (i % 3) {
3557+
// Hour
3558+
case 0:
3559+
return `${a}${i > 0 ? ' ' : ''}${v.toString().padStart(2, '0')}`;
3560+
// Minute
3561+
case 1:
3562+
return `${a}:${v.toString().padStart(2, '0')}`;
3563+
break;
3564+
// Setpoint
3565+
case 2:
3566+
return `${a}/${v / 2}`;
3567+
}
3568+
}, '');
3569+
};
35623570

3563-
return r;
3571+
return {
3572+
weekdays: format(v.slice(0, 12)),
3573+
saturday: format(v.slice(1 * 12, 2 * 12)),
3574+
sunday: format(v.slice(2 * 12, 3 * 12)),
3575+
};
35643576
},
35653577
},
35663578
],

0 commit comments

Comments
 (0)