diff --git a/tests/test_tuya_thermostat.py b/tests/test_tuya_thermostat.py index 4818ebfde8..220d8294fa 100644 --- a/tests/test_tuya_thermostat.py +++ b/tests/test_tuya_thermostat.py @@ -6,50 +6,84 @@ from tests.common import ClusterListener import zhaquirks +from zhaquirks.tuya import TUYA_MCU_VERSION_RSP from zhaquirks.tuya.mcu import TuyaMCUCluster zhaquirks.setup() +ZCL_TUYA_VERSION_RSP = b"\x09\x06\x11\x01\x6d\x82" +ZCL_TUYA_SET_TIME = b"\x09\x12\x24\x0d\x00" + @pytest.mark.parametrize( - "msg,attr,value", + "manuf,msg,attr,value", [ ( + "_TZE204_p3lqqy2r", b"\t\x13\x02\x00\x06\x01\x01\x00\x01\x01", Thermostat.AttributeDefs.system_mode, Thermostat.SystemMode.Heat, ), # Set to heat, dp 1 ( + "_TZE204_p3lqqy2r", b"\t\x16\x02\x00\t\x18\x02\x00\x04\x00\x00\x00\x18", Thermostat.AttributeDefs.local_temperature, 2400, ), # Current temp 24, dp 24 ( + "_TZE204_p3lqqy2r", b"\t\x15\x02\x00\x08\x10\x02\x00\x04\x00\x00\x00\x19", Thermostat.AttributeDefs.occupied_heating_setpoint, 2500, ), # Setpoint to 25, dp 16 ( + "_TZE204_p3lqqy2r", b"\t\x17\x02\x00\n\x1c\x02\x00\x04\x00\x00\x00\x00", Thermostat.AttributeDefs.local_temperature_calibration, 0, ), # Local calibration to 0, dp 28 ( + "_TZE204_p3lqqy2r", b"\t\x1c\x02\x00\x0fh\x01\x00\x01\x01", Thermostat.AttributeDefs.running_state, Thermostat.RunningState.Heat_State_On, ), # Running state, dp 104 ( + "_TZE204_p3lqqy2r", b"\t\x1d\x02\x00\x10k\x02\x00\x04\x00\x00\x00\x1b", Thermostat.AttributeDefs.max_heat_setpoint_limit, 2700, ), # Max heat set point, dp 107 + ( + "_TZE204_lzriup1j", + b"\t\x13\x02\x00\x06\x01\x01\x00\x01\x01", + Thermostat.AttributeDefs.system_mode, + Thermostat.SystemMode.Heat, + ), # Set to heat, dp 1 + ( + "_TZE200_viy9ihs7", + b"\t\x13\x02\x00\x06\x01\x01\x00\x01\x01", + Thermostat.AttributeDefs.system_mode, + Thermostat.SystemMode.Heat, + ), # Set to heat, dp 1 + ( + "_TZE204_xnbkhhdr", + b"\t\x13\x02\x00\x06\x01\x01\x00\x01\x01", + Thermostat.AttributeDefs.system_mode, + Thermostat.SystemMode.Heat, + ), # Set to heat, dp 1 + ( + "_TZE284_xnbkhhdr", + b"\t\x13\x02\x00\x06\x01\x01\x00\x01\x01", + Thermostat.AttributeDefs.system_mode, + Thermostat.SystemMode.Heat, + ), # Set to heat, dp 1 ], ) -async def test_handle_get_data(zigpy_device_from_v2_quirk, msg, attr, value): +async def test_handle_get_data(zigpy_device_from_v2_quirk, manuf, msg, attr, value): """Test handle_get_data for multiple attributes.""" - quirked = zigpy_device_from_v2_quirk("_TZE204_p3lqqy2r", "TS0601") + quirked = zigpy_device_from_v2_quirk(manuf, "TS0601") ep = quirked.endpoints[1] assert ep.tuya_manufacturer is not None @@ -69,3 +103,21 @@ async def test_handle_get_data(zigpy_device_from_v2_quirk, msg, attr, value): assert thermostat_listener.attribute_updates[0][1] == value assert ep.thermostat.get(attr.id) == value + + +async def test_tuya_no_mcu_version(zigpy_device_from_v2_quirk): + """Test lack of TUYA_MCU_VERSION_RSP messages.""" + + tuya_device = zigpy_device_from_v2_quirk("_TZE284_xnbkhhdr", "TS0601") + + tuya_cluster = tuya_device.endpoints[1].tuya_manufacturer + cluster_listener = ClusterListener(tuya_cluster) + + assert len(cluster_listener.attribute_updates) == 0 + + # simulate a TUYA_MCU_VERSION_RSP message + hdr, args = tuya_cluster.deserialize(ZCL_TUYA_VERSION_RSP) + assert hdr.command_id == TUYA_MCU_VERSION_RSP + + tuya_cluster.handle_message(hdr, args) + assert len(cluster_listener.attribute_updates) == 0 diff --git a/zhaquirks/tuya/ts0601_thermostat.py b/zhaquirks/tuya/ts0601_thermostat.py deleted file mode 100644 index 36dc2f985d..0000000000 --- a/zhaquirks/tuya/ts0601_thermostat.py +++ /dev/null @@ -1,217 +0,0 @@ -"""Tuya TS0601 Thermostat.""" - -from zigpy.quirks.v2.homeassistant import ( - UnitOfElectricCurrent, - UnitOfElectricPotential, - UnitOfEnergy, - UnitOfPower, - UnitOfTemperature, -) -from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass -from zigpy.types import t -from zigpy.zcl import foundation -from zigpy.zcl.clusters.hvac import Thermostat - -from zhaquirks.tuya.builder import TuyaQuirkBuilder -from zhaquirks.tuya.mcu import TuyaAttributesCluster - - -class RegulatorPeriod(t.enum8): - """Tuya Regulator Period enum.""" - - FifteenMin = 0x00 - ThirtyMin = 0x01 - FortyFiveMin = 0x02 - SixtyMin = 0x03 - NinetyMin = 0x04 - - -class ThermostatMode(t.enum8): - """Tuya Thermostat mode.""" - - Regulator = 0x00 - Thermostat = 0x01 - - -class PresetMode(t.enum8): - """Tuya PresetMode enum.""" - - Manual = 0x00 - Home = 0x01 - Away = 0x02 - - -class SensorMode(t.enum8): - """Tuya SensorMode enum.""" - - Air = 0x00 - Floor = 0x01 - Both = 0x02 - - -class TuyaThermostat(Thermostat, TuyaAttributesCluster): - """Tuya local thermostat cluster.""" - - manufacturer_id_override: t.uint16_t = foundation.ZCLHeader.NO_MANUFACTURER_ID - - _CONSTANT_ATTRIBUTES = { - Thermostat.AttributeDefs.ctrl_sequence_of_oper.id: Thermostat.ControlSequenceOfOperation.Heating_Only - } - - def __init__(self, *args, **kwargs): - """Init a TuyaThermostat cluster.""" - super().__init__(*args, **kwargs) - self.add_unsupported_attribute( - Thermostat.AttributeDefs.setpoint_change_source.id - ) - self.add_unsupported_attribute( - Thermostat.AttributeDefs.setpoint_change_source_timestamp.id - ) - self.add_unsupported_attribute(Thermostat.AttributeDefs.pi_heating_demand.id) - - -( - TuyaQuirkBuilder("_TZE204_p3lqqy2r", "TS0601") - .tuya_dp( - dp_id=1, - ep_attribute=TuyaThermostat.ep_attribute, - attribute_name=TuyaThermostat.AttributeDefs.system_mode.name, - converter=lambda x: 0x00 if not x else 0x04, - dp_converter=lambda x: x != 0x00, - ) - .tuya_enum( - dp_id=2, - attribute_name="preset_mode", - enum_class=PresetMode, - translation_key="preset_mode", - fallback_name="Preset mode", - ) - .tuya_dp( - dp_id=16, - ep_attribute=TuyaThermostat.ep_attribute, - attribute_name=TuyaThermostat.AttributeDefs.occupied_heating_setpoint.name, - converter=lambda x: x * 100, - dp_converter=lambda x: x // 100, - ) - .tuya_dp( - dp_id=24, - ep_attribute=TuyaThermostat.ep_attribute, - attribute_name=TuyaThermostat.AttributeDefs.local_temperature.name, - converter=lambda x: x * 100, - ) - .tuya_dp( - dp_id=28, - ep_attribute=TuyaThermostat.ep_attribute, - attribute_name=Thermostat.AttributeDefs.local_temperature_calibration.name, - converter=lambda x: x * 100, - dp_converter=lambda x: x // 100, - ) - .tuya_switch( - dp_id=30, - attribute_name="child_lock", - translation_key="child_lock", - fallback_name="Child lock", - ) - .tuya_sensor( - dp_id=101, - attribute_name="local_temperature_floor", - type=t.int16s, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - unit=UnitOfTemperature.CELSIUS, - translation_key="local_temperature_floor", - fallback_name="Floor temperature", - ) - .tuya_enum( - dp_id=102, - attribute_name="temperature_sensor_select", - enum_class=SensorMode, - translation_key="sensor_mode", - fallback_name="Sensor mode", - ) - .tuya_dp( - dp_id=104, - ep_attribute=TuyaThermostat.ep_attribute, - attribute_name=TuyaThermostat.AttributeDefs.running_state.name, - converter=lambda x: 0x00 if not x else 0x01, - ) - .tuya_binary_sensor( - dp_id=106, - attribute_name="window_detection", - translation_key="window_detection", - fallback_name="Open window detection", - ) - .tuya_dp( - dp_id=107, - ep_attribute=TuyaThermostat.ep_attribute, - attribute_name=TuyaThermostat.AttributeDefs.max_heat_setpoint_limit.name, - converter=lambda x: x * 100, - dp_converter=lambda x: x // 100, - ) - .tuya_enum( - dp_id=108, - attribute_name="thermostat_mode", - enum_class=ThermostatMode, - translation_key="thermostat_mode", - fallback_name="Thermostat mode", - ) - .tuya_enum( - dp_id=109, - attribute_name="regulator_period", - enum_class=RegulatorPeriod, - translation_key="regulator_period", - fallback_name="Regulator period", - ) - .tuya_number( - dp_id=110, - attribute_name="regulator_set_point", - type=t.uint16_t, - unit=UnitOfTemperature.CELSIUS, - min_value=0, - max_value=100, - step=1, - translation_key="regulator_set_point", - fallback_name="Regulator set point", - ) - .adds(TuyaThermostat) - .tuya_sensor( - dp_id=120, - attribute_name="current", - type=t.int16s, - divisor=10, - device_class=SensorDeviceClass.CURRENT, - state_class=SensorStateClass.MEASUREMENT, - unit=UnitOfElectricCurrent.AMPERE, - fallback_name="Current", - ) - .tuya_sensor( - dp_id=121, - attribute_name="voltage", - type=t.int16s, - device_class=SensorDeviceClass.VOLTAGE, - state_class=SensorStateClass.MEASUREMENT, - unit=UnitOfElectricPotential.VOLT, - fallback_name="Voltage", - ) - .tuya_sensor( - dp_id=122, - attribute_name="power", - type=t.int16s, - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - unit=UnitOfPower.WATT, - fallback_name="Power", - ) - .tuya_sensor( - dp_id=123, - attribute_name="energy", - type=t.int16s, - divisor=100, - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.TOTAL, - unit=UnitOfEnergy.KILO_WATT_HOUR, - fallback_name="Energy", - ) - .skip_configuration() - .add_to_registry() -) diff --git a/zhaquirks/tuya/tuya_thermostat.py b/zhaquirks/tuya/tuya_thermostat.py new file mode 100644 index 0000000000..632c07923e --- /dev/null +++ b/zhaquirks/tuya/tuya_thermostat.py @@ -0,0 +1,451 @@ +"""Tuya TS0601 Thermostat.""" + +import copy + +from zigpy.quirks.v2 import BinarySensorDeviceClass, EntityType +from zigpy.quirks.v2.homeassistant import ( + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, +) +from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass +from zigpy.types import t +from zigpy.zcl import foundation +from zigpy.zcl.clusters.hvac import Thermostat + +from zhaquirks.tuya import TUYA_SET_TIME, TuyaTimePayload +from zhaquirks.tuya.builder import TuyaQuirkBuilder +from zhaquirks.tuya.mcu import TuyaAttributesCluster, TuyaMCUCluster + + +class RegulatorPeriod(t.enum8): + """Tuya regulator period enum.""" + + FifteenMin = 0x00 + ThirtyMin = 0x01 + FortyFiveMin = 0x02 + SixtyMin = 0x03 + NinetyMin = 0x04 + + +class ThermostatMode(t.enum8): + """Tuya thermostat mode.""" + + Regulator = 0x00 + Thermostat = 0x01 + + +class PresetModeV01(t.enum8): + """Tuya preset mode v01 enum.""" + + Manual = 0x00 + Home = 0x01 + Away = 0x02 + + +class PresetModeV02(t.enum8): + """Tuya preset mode v02 enum.""" + + Manual = 0x00 + Auto = 0x01 + Temporary_Manual = 0x02 + + +class PresetModeV03(t.enum8): + """Tuya preset mode v03 enum.""" + + Auto = 0x00 + Manual = 0x01 + Temporary_Manual = 0x02 + + +class SensorMode(t.enum8): + """Tuya sensor mode enum.""" + + Air = 0x00 + Floor = 0x01 + Both = 0x02 + + +class BacklightMode(t.enum8): + """Tuya backlight mode enum.""" + + Off = 0x00 + Low = 0x01 + Medium = 0x02 + High = 0x03 + + +class WorkingDayV01(t.enum8): + """Tuya Working day v01 enum.""" + + Disabled = 0x00 + Six_One = 0x01 + Five_Two = 0x02 + Seven = 0x03 + + +class WorkingDayV02(t.enum8): + """Tuya Working day v02 enum.""" + + Disabled = 0x00 + Five_Two = 0x01 + Six_One = 0x02 + Seven = 0x03 + + +class TuyaThermostat(Thermostat, TuyaAttributesCluster): + """Tuya local thermostat cluster.""" + + manufacturer_id_override: t.uint16_t = foundation.ZCLHeader.NO_MANUFACTURER_ID + + _CONSTANT_ATTRIBUTES = { + Thermostat.AttributeDefs.ctrl_sequence_of_oper.id: Thermostat.ControlSequenceOfOperation.Heating_Only + } + + def __init__(self, *args, **kwargs): + """Init a TuyaThermostat cluster.""" + super().__init__(*args, **kwargs) + self.add_unsupported_attribute( + Thermostat.AttributeDefs.setpoint_change_source.id + ) + self.add_unsupported_attribute( + Thermostat.AttributeDefs.setpoint_change_source_timestamp.id + ) + self.add_unsupported_attribute(Thermostat.AttributeDefs.pi_heating_demand.id) + + +class NoManufTimeNoVersionRespTuyaMCUCluster(TuyaMCUCluster): + """Tuya Manufacturer Cluster with set_time mod.""" + + # Deepcopy required to override 'set_time', without, it will revert + server_commands = copy.deepcopy(TuyaMCUCluster.server_commands) + server_commands.update( + { + TUYA_SET_TIME: foundation.ZCLCommandDef( + "set_time", + {"time": TuyaTimePayload}, + False, + is_manufacturer_specific=False, + ), + } + ) + + def handle_mcu_version_response( + self, payload: TuyaMCUCluster.MCUVersion + ) -> foundation.Status: + """Handle MCU version response.""" + return foundation.Status.SUCCESS + + +( + TuyaQuirkBuilder("_TZE204_p3lqqy2r", "TS0601") + .tuya_dp( + dp_id=1, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.system_mode.name, + converter=lambda x: 0x00 if not x else 0x04, + dp_converter=lambda x: x != 0x00, + ) + .tuya_enum( + dp_id=2, + attribute_name="preset_mode", + enum_class=PresetModeV01, + translation_key="preset_mode", + fallback_name="Preset mode", + ) + .tuya_dp( + dp_id=16, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.occupied_heating_setpoint.name, + converter=lambda x: x * 100, + dp_converter=lambda x: x // 100, + ) + .tuya_dp( + dp_id=24, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.local_temperature.name, + converter=lambda x: x * 100, + ) + .tuya_dp( + dp_id=28, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=Thermostat.AttributeDefs.local_temperature_calibration.name, + converter=lambda x: x * 100, + dp_converter=lambda x: x // 100, + ) + .tuya_switch( + dp_id=30, + attribute_name="child_lock", + translation_key="child_lock", + fallback_name="Child lock", + ) + .tuya_sensor( + dp_id=101, + attribute_name="local_temperature_floor", + type=t.int16s, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + unit=UnitOfTemperature.CELSIUS, + translation_key="local_temperature_floor", + fallback_name="Floor temperature", + ) + .tuya_enum( + dp_id=102, + attribute_name="temperature_sensor_select", + enum_class=SensorMode, + translation_key="sensor_mode", + fallback_name="Sensor mode", + ) + .tuya_dp( + dp_id=104, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.running_state.name, + converter=lambda x: 0x00 if not x else 0x01, + ) + .tuya_binary_sensor( + dp_id=106, + attribute_name="window_detection", + translation_key="window_detection", + fallback_name="Open window detection", + ) + .tuya_dp( + dp_id=107, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.max_heat_setpoint_limit.name, + converter=lambda x: x * 100, + dp_converter=lambda x: x // 100, + ) + .tuya_enum( + dp_id=108, + attribute_name="thermostat_mode", + enum_class=ThermostatMode, + translation_key="thermostat_mode", + fallback_name="Thermostat mode", + ) + .tuya_enum( + dp_id=109, + attribute_name="regulator_period", + enum_class=RegulatorPeriod, + translation_key="regulator_period", + fallback_name="Regulator period", + ) + .tuya_number( + dp_id=110, + attribute_name="regulator_set_point", + type=t.uint16_t, + unit=UnitOfTemperature.CELSIUS, + min_value=0, + max_value=100, + step=1, + translation_key="regulator_set_point", + fallback_name="Regulator set point", + ) + .adds(TuyaThermostat) + .tuya_sensor( + dp_id=120, + attribute_name="current", + type=t.int16s, + divisor=10, + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + unit=UnitOfElectricCurrent.AMPERE, + fallback_name="Current", + ) + .tuya_sensor( + dp_id=121, + attribute_name="voltage", + type=t.int16s, + device_class=SensorDeviceClass.VOLTAGE, + state_class=SensorStateClass.MEASUREMENT, + unit=UnitOfElectricPotential.VOLT, + fallback_name="Voltage", + ) + .tuya_sensor( + dp_id=122, + attribute_name="power", + type=t.int16s, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + unit=UnitOfPower.WATT, + fallback_name="Power", + ) + .tuya_sensor( + dp_id=123, + attribute_name="energy", + type=t.int16s, + divisor=100, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL, + unit=UnitOfEnergy.KILO_WATT_HOUR, + fallback_name="Energy", + ) + .skip_configuration() + .add_to_registry() +) + + +# Tuya ZWT198/ZWT100-BH Avatto wall thermostat +base_avatto_quirk = ( + TuyaQuirkBuilder() + .tuya_dp( + dp_id=1, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.system_mode.name, + converter=lambda x: 0x00 if not x else 0x04, + dp_converter=lambda x: x != 0x00, + ) + .tuya_dp( + dp_id=2, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.occupied_heating_setpoint.name, + converter=lambda x: x * 10, + dp_converter=lambda x: x // 10, + ) + .tuya_dp( + dp_id=3, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.local_temperature.name, + converter=lambda x: x * 10, + ) + .tuya_switch( + dp_id=9, + attribute_name="child_lock", + translation_key="child_lock", + fallback_name="Child lock", + ) + .tuya_binary_sensor( + dp_id=11, + attribute_name="fault_alarm", + entity_type=EntityType.STANDARD, + device_class=BinarySensorDeviceClass.PROBLEM, + translation_key="fault_alarm", + fallback_name="Fault alarm", + ) + .tuya_dp( + dp_id=15, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.max_heat_setpoint_limit.name, + converter=lambda x: x * 10, + dp_converter=lambda x: x // 10, + ) + .tuya_dp( + dp_id=19, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.local_temperature_calibration.name, + converter=lambda x: x * 10, + dp_converter=lambda x: x // 10, + ) + .tuya_dp( + dp_id=101, + ep_attribute=TuyaThermostat.ep_attribute, + attribute_name=TuyaThermostat.AttributeDefs.running_state.name, + converter=lambda x: 0x00 if not x else 0x01, + ) + .tuya_switch( + dp_id=102, + attribute_name="frost_protection", + translation_key="frost_protection", + fallback_name="Frost protection", + ) + .tuya_switch( + dp_id=103, + attribute_name="factory_reset", + translation_key="factory_reset", + fallback_name="Factory reset", + ) + .tuya_enum( + dp_id=106, + attribute_name="temperature_sensor_select", + enum_class=SensorMode, + translation_key="sensor_mode", + fallback_name="Sensor mode", + ) + .tuya_number( + dp_id=107, + attribute_name="deadzone_temperature", + type=t.uint16_t, + unit=UnitOfTemperature.CELSIUS, + min_value=0.1, + max_value=10, + step=0.1, + translation_key="deadzone_temperature", + fallback_name="Deadzone temperature", + ) + # 109 ZWT198 schedule, skipped + .tuya_enum( + dp_id=110, + attribute_name="backlight_mode", + enum_class=BacklightMode, + translation_key="backlight_mode", + fallback_name="Backlight mode", + ) + .adds(TuyaThermostat) + .skip_configuration() +) + + +( + base_avatto_quirk.clone() + .applies_to("_TZE204_lzriup1j", "TS0601") + .tuya_enum( + dp_id=4, + attribute_name="preset_mode", + enum_class=PresetModeV02, + translation_key="preset_mode", + fallback_name="Preset mode", + ) + .tuya_enum( + dp_id=104, + attribute_name="working_day", + enum_class=WorkingDayV02, + translation_key="working_day", + fallback_name="Working day", + ) + .add_to_registry(replacement_cluster=NoManufTimeNoVersionRespTuyaMCUCluster) +) + + +( + base_avatto_quirk.clone() + .applies_to("_TZE200_viy9ihs7", "TS0601") + .tuya_enum( + dp_id=4, + attribute_name="preset_mode", + enum_class=PresetModeV03, + translation_key="preset_mode", + fallback_name="Preset mode", + ) + .tuya_enum( + dp_id=104, + attribute_name="working_day", + enum_class=WorkingDayV01, + translation_key="working_day", + fallback_name="Working day", + ) + .add_to_registry(replacement_cluster=NoManufTimeNoVersionRespTuyaMCUCluster) +) + + +( + base_avatto_quirk.clone() + .applies_to("_TZE204_xnbkhhdr", "TS0601") + .applies_to("_TZE284_xnbkhhdr", "TS0601") + .tuya_enum( + dp_id=4, + attribute_name="preset_mode", + enum_class=PresetModeV03, + translation_key="preset_mode", + fallback_name="Preset mode", + ) + .tuya_enum( + dp_id=104, + attribute_name="working_day", + enum_class=WorkingDayV02, + translation_key="working_day", + fallback_name="Working day", + ) + .add_to_registry(replacement_cluster=NoManufTimeNoVersionRespTuyaMCUCluster) +)