From 5f314c40df671c4104d387e8a13cf8a04dcd7699 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 8 Oct 2025 09:06:56 +0000 Subject: [PATCH] Add climate/switch --- homeassistant/components/tuya/climate.py | 32 ++++++++- homeassistant/components/tuya/cover.py | 16 ++--- homeassistant/components/tuya/select.py | 8 +-- homeassistant/components/tuya/sensor.py | 31 +++++--- homeassistant/components/tuya/switch.py | 31 +++++++- .../tuya/xternal_tuya_device_quirks/cl.py | 22 +++--- .../tuya/xternal_tuya_device_quirks/wk.py | 39 ++++++++++ .../tuya/xternal_tuya_quirks/climate.py | 27 +++++++ .../tuya/xternal_tuya_quirks/cover.py | 10 +-- .../tuya/xternal_tuya_quirks/device_quirk.py | 72 +++++++++++++++---- .../tuya/xternal_tuya_quirks/select.py | 8 +-- .../tuya/xternal_tuya_quirks/sensor.py | 17 ++++- .../tuya/xternal_tuya_quirks/switch.py | 23 ++++++ .../tuya/xternal_tuya_quirks/utils.py | 18 +++++ .../tuya/snapshots/test_sensor.ambr | 56 +++++++++++++++ 15 files changed, 347 insertions(+), 63 deletions(-) create mode 100644 homeassistant/components/tuya/xternal_tuya_device_quirks/wk.py create mode 100644 homeassistant/components/tuya/xternal_tuya_quirks/climate.py create mode 100644 homeassistant/components/tuya/xternal_tuya_quirks/switch.py create mode 100644 homeassistant/components/tuya/xternal_tuya_quirks/utils.py diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index fe11d2668ef8..488865b7bd31 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -2,7 +2,7 @@ from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, replace from typing import TYPE_CHECKING, Any from tuya_sharing import CustomerDevice, Manager @@ -28,6 +28,8 @@ from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, DPType from .entity import TuyaEntity from .models import IntegerTypeData from .util import get_dpcode +from .xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY +from .xternal_tuya_quirks.climate import CommonClimateType, TuyaClimateDefinition TUYA_HVAC_TO_HA = { "auto": HVACMode.HEAT_COOL, @@ -75,6 +77,22 @@ CLIMATE_DESCRIPTIONS: dict[DeviceCategory, TuyaClimateEntityDescription] = { ), } +COMMON_CLIMATE_DEFINITIONS: dict[CommonClimateType, TuyaClimateEntityDescription] = { + CommonClimateType.SWITCH_ONLY_HEAT_COOL: TuyaClimateEntityDescription( + key="tbc", + switch_only_hvac_mode=HVACMode.HEAT_COOL, + ) +} + + +def _create_quirk_description( + definition: TuyaClimateDefinition, +) -> TuyaClimateEntityDescription: + return replace( + COMMON_CLIMATE_DEFINITIONS[definition.common_type], + key=definition.key, + ) + async def async_setup_entry( hass: HomeAssistant, @@ -90,7 +108,17 @@ async def async_setup_entry( entities: list[TuyaClimateEntity] = [] for device_id in device_ids: device = manager.device_map[device_id] - if device and device.category in CLIMATE_DESCRIPTIONS: + if quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device): + entities.extend( + TuyaClimateEntity( + device, + manager, + _create_quirk_description(definition), + hass.config.units.temperature_unit, + ) + for definition in quirk.climate_definitions + ) + elif device.category in CLIMATE_DESCRIPTIONS: entities.append( TuyaClimateEntity( device, diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index 2b31f17150e7..9c52faba299e 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -2,7 +2,7 @@ from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, replace from typing import TYPE_CHECKING, Any from tuya_sharing import CustomerDevice, Manager @@ -24,7 +24,7 @@ from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, DPType from .entity import TuyaEntity from .models import EnumTypeData, IntegerTypeData from .util import get_dpcode -from .xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY, parse_enum +from .xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY from .xternal_tuya_quirks.cover import CommonCoverType, TuyaCoverDefinition @@ -156,14 +156,12 @@ COMMON_COVER_DEFINITIONS: dict[CommonCoverType, TuyaCoverEntityDescription] = { def _create_quirk_description( definition: TuyaCoverDefinition, ) -> TuyaCoverEntityDescription: - common_definition = COMMON_COVER_DEFINITIONS[definition.common_type] - return TuyaCoverEntityDescription( + return replace( + COMMON_COVER_DEFINITIONS[definition.common_type], key=definition.key, - device_class=common_definition.device_class, - translation_key=common_definition.translation_key, - current_state=parse_enum(DPCode, definition.current_state_dp_code), - current_position=parse_enum(DPCode, definition.current_position_dp_code), - set_position=parse_enum(DPCode, definition.set_position_dp_code), + current_state=definition.current_state_dp_code, + current_position=definition.current_position_dp_code, + set_position=definition.set_position_dp_code, ) diff --git a/homeassistant/components/tuya/select.py b/homeassistant/components/tuya/select.py index b553bfbe095c..b283e2c96217 100644 --- a/homeassistant/components/tuya/select.py +++ b/homeassistant/components/tuya/select.py @@ -2,6 +2,8 @@ from __future__ import annotations +from dataclasses import replace + from tuya_sharing import CustomerDevice, Manager from homeassistant.components.select import SelectEntity, SelectEntityDescription @@ -356,11 +358,9 @@ COMMON_SELECT_DEFINITIONS: dict[CommonSelectType, SelectEntityDescription] = { def _create_quirk_description( definition: TuyaSelectDefinition, ) -> SelectEntityDescription: - common_definition = COMMON_SELECT_DEFINITIONS[definition.common_type] - return SelectEntityDescription( + return replace( + COMMON_SELECT_DEFINITIONS[definition.common_type], key=DPCode(definition.key), - translation_key=common_definition.translation_key, - entity_category=common_definition.entity_category, ) diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index cefa2e365546..d66008158c44 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable -from dataclasses import dataclass +from dataclasses import dataclass, replace from typing import Any from tuya_sharing import CustomerDevice, Manager @@ -73,7 +73,12 @@ class TuyaSensorEntityDescription(SensorEntityDescription): complex_type: type[ComplexValue] | None = None subkey: str | None = None - state_conversion: Callable[[Any], StateType] | None = None + state_conversion: ( + Callable[ + [CustomerDevice, EnumTypeData | IntegerTypeData | None, Any], StateType + ] + | None + ) = None # Commonly used battery sensors, that are reused in the sensors down below. @@ -960,7 +965,9 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = { translation_key="wind_direction", device_class=SensorDeviceClass.WIND_DIRECTION, state_class=SensorStateClass.MEASUREMENT, - state_conversion=lambda state: _WIND_DIRECTIONS.get(str(state)), + state_conversion=lambda _device, _dptype, state: _WIND_DIRECTIONS.get( + str(state) + ), ), TuyaSensorEntityDescription( key=DPCode.DEW_POINT_TEMP, @@ -1627,23 +1634,27 @@ SENSORS[DeviceCategory.DGHSXJ] = SENSORS[DeviceCategory.SP] SENSORS[DeviceCategory.PC] = SENSORS[DeviceCategory.KG] COMMON_SENSOR_DEFINITIONS: dict[CommonSensorType, TuyaSensorEntityDescription] = { + CommonSensorType.TEMPERATURE: TuyaSensorEntityDescription( + key="tbc", + translation_key="temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), CommonSensorType.TIME_TOTAL: TuyaSensorEntityDescription( key="tbc", translation_key="last_operation_duration", entity_category=EntityCategory.DIAGNOSTIC, - ) + ), } def _create_quirk_description( definition: TuyaSensorDefinition, ) -> TuyaSensorEntityDescription: - common_definition = COMMON_SENSOR_DEFINITIONS[definition.common_type] - return TuyaSensorEntityDescription( + return replace( + COMMON_SENSOR_DEFINITIONS[definition.common_type], key=DPCode(definition.key), - translation_key=common_definition.translation_key, - device_class=common_definition.device_class, - entity_category=common_definition.entity_category, + state_conversion=definition.state_conversion, ) @@ -1782,7 +1793,7 @@ class TuyaSensorEntity(TuyaEntity, SensorEntity): # Convert value, if required if (convert := self.entity_description.state_conversion) is not None: - return convert(value) + return convert(self.device, self._type_data, value) # Scale integer/float value if isinstance(self._type_data, IntegerTypeData): diff --git a/homeassistant/components/tuya/switch.py b/homeassistant/components/tuya/switch.py index a12562b455fe..95462c042bae 100644 --- a/homeassistant/components/tuya/switch.py +++ b/homeassistant/components/tuya/switch.py @@ -2,7 +2,7 @@ from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, replace from typing import Any from tuya_sharing import CustomerDevice, Manager @@ -27,6 +27,8 @@ from homeassistant.helpers.issue_registry import ( from . import TuyaConfigEntry from .const import DOMAIN, TUYA_DISCOVERY_NEW, DeviceCategory, DPCode from .entity import TuyaEntity +from .xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY +from .xternal_tuya_quirks.switch import CommonSwitchType, TuyaSwitchDefinition @dataclass(frozen=True, kw_only=True) @@ -898,6 +900,23 @@ SWITCHES[DeviceCategory.CZ] = SWITCHES[DeviceCategory.PC] # Smart Camera - Low power consumption camera (duplicate of `sp`) SWITCHES[DeviceCategory.DGHSXJ] = SWITCHES[DeviceCategory.SP] +COMMON_SWITCH_DEFINITIONS: dict[CommonSwitchType, SwitchEntityDescription] = { + CommonSwitchType.CHILD_LOCK: SwitchEntityDescription( + key="tbc", + translation_key="child_lock", + entity_category=EntityCategory.CONFIG, + ) +} + + +def _create_quirk_description( + definition: TuyaSwitchDefinition, +) -> SwitchEntityDescription: + return replace( + COMMON_SWITCH_DEFINITIONS[definition.common_type], + key=DPCode(definition.key), + ) + async def async_setup_entry( hass: HomeAssistant, @@ -914,7 +933,15 @@ async def async_setup_entry( entities: list[TuyaSwitchEntity] = [] for device_id in device_ids: device = manager.device_map[device_id] - if descriptions := SWITCHES.get(device.category): + if quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device): + entities.extend( + TuyaSwitchEntity( + device, manager, _create_quirk_description(definition) + ) + for definition in quirk.switch_definitions + if definition.key in device.status + ) + elif descriptions := SWITCHES.get(device.category): entities.extend( TuyaSwitchEntity(device, manager, description) for description in descriptions diff --git a/homeassistant/components/tuya/xternal_tuya_device_quirks/cl.py b/homeassistant/components/tuya/xternal_tuya_device_quirks/cl.py index 5e49bbfbbbaa..5458ec12b56e 100644 --- a/homeassistant/components/tuya/xternal_tuya_device_quirks/cl.py +++ b/homeassistant/components/tuya/xternal_tuya_device_quirks/cl.py @@ -2,6 +2,7 @@ from __future__ import annotations +from ..const import DPCode from ..xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY, TuyaDeviceQuirk from ..xternal_tuya_quirks.cover import CommonCoverType from ..xternal_tuya_quirks.select import CommonSelectType @@ -13,17 +14,14 @@ from ..xternal_tuya_quirks.sensor import CommonSensorType TuyaDeviceQuirk() .applies_to(category="cl", product_id="g1cp07dsqnbdbbki") .add_common_cover( - key="control", + key=DPCode.CONTROL, common_type=CommonCoverType.CURTAIN, - current_position_dp_code="percent_control", - current_state_dp_code="control", - set_position_dp_code="percent_control", - set_state_dp_code="control", + current_position_dp_code=DPCode.PERCENT_CONTROL, + set_position_dp_code=DPCode.PERCENT_CONTROL, ) .add_common_select( - key="control_back_mode", + key=DPCode.CONTROL_BACK_MODE, common_type=CommonSelectType.CONTROL_BACK_MODE, - dp_code="control_back_mode", ) .register(TUYA_QUIRKS_REGISTRY) ) @@ -33,19 +31,17 @@ from ..xternal_tuya_quirks.sensor import CommonSensorType TuyaDeviceQuirk() .applies_to(category="cl", product_id="lfkr93x0ukp5gaia") .add_common_cover( - key="control", + key=DPCode.CONTROL, common_type=CommonCoverType.CURTAIN, - current_state_dp_code="control", + current_state_dp_code=DPCode.CONTROL, ) .add_common_select( - key="control_back_mode", + key=DPCode.CONTROL_BACK_MODE, common_type=CommonSelectType.CONTROL_BACK_MODE, - dp_code="control_back_mode", ) .add_common_sensor( - key="time_total", + key=DPCode.TIME_TOTAL, common_type=CommonSensorType.TIME_TOTAL, - dp_code="time_total", ) .register(TUYA_QUIRKS_REGISTRY) ) diff --git a/homeassistant/components/tuya/xternal_tuya_device_quirks/wk.py b/homeassistant/components/tuya/xternal_tuya_device_quirks/wk.py new file mode 100644 index 000000000000..1f6c6b73c139 --- /dev/null +++ b/homeassistant/components/tuya/xternal_tuya_device_quirks/wk.py @@ -0,0 +1,39 @@ +"""Quirks for Tuya.""" + +from __future__ import annotations + +from typing import cast + +from ..const import DPCode +from ..models import IntegerTypeData +from ..xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY, TuyaDeviceQuirk +from ..xternal_tuya_quirks.climate import CommonClimateType +from ..xternal_tuya_quirks.sensor import CommonSensorType +from ..xternal_tuya_quirks.switch import CommonSwitchType +from ..xternal_tuya_quirks.utils import scale_value_force_scale_1 + +( + # This model has percent_state and percent_control but percent_state never + # gets updated - force percent_control instead + TuyaDeviceQuirk() + .applies_to(category="wk", product_id="IAYz2WK1th0cMLmL") + .add_common_climate( + key="wk", # to avoid breaking change + common_type=CommonClimateType.SWITCH_ONLY_HEAT_COOL, + switch_dp_code=DPCode.SWITCH, + current_temperature_dp_code=DPCode.UPPER_TEMP, + set_temperature_dp_code=DPCode.TEMP_SET, + ) + .add_common_sensor( + key=DPCode.UPPER_TEMP, + common_type=CommonSensorType.TEMPERATURE, + state_conversion=lambda _device, dptype, value: scale_value_force_scale_1( + cast(IntegerTypeData, dptype), cast(float, value) + ), + ) + .add_common_switch( + key=DPCode.CHILD_LOCK, + common_type=CommonSwitchType.CHILD_LOCK, + ) + .register(TUYA_QUIRKS_REGISTRY) +) diff --git a/homeassistant/components/tuya/xternal_tuya_quirks/climate.py b/homeassistant/components/tuya/xternal_tuya_quirks/climate.py new file mode 100644 index 000000000000..6613b3391643 --- /dev/null +++ b/homeassistant/components/tuya/xternal_tuya_quirks/climate.py @@ -0,0 +1,27 @@ +"""Common climate quirks for Tuya devices.""" + +from __future__ import annotations + +from dataclasses import dataclass +from enum import StrEnum + +from ..const import DPCode + + +class CommonClimateType(StrEnum): + """Common climate types.""" + + SWITCH_ONLY_HEAT_COOL = "switch_only_heat_cool" + + +@dataclass(kw_only=True) +class TuyaClimateDefinition: + """Definition for a climate entity.""" + + key: str + + common_type: CommonClimateType + + current_temperature_dp_code: DPCode | None = None + set_temperature_dp_code: DPCode | None = None + switch_dp_code: DPCode | None = None diff --git a/homeassistant/components/tuya/xternal_tuya_quirks/cover.py b/homeassistant/components/tuya/xternal_tuya_quirks/cover.py index 93dbfdec1da7..cd2018b33052 100644 --- a/homeassistant/components/tuya/xternal_tuya_quirks/cover.py +++ b/homeassistant/components/tuya/xternal_tuya_quirks/cover.py @@ -5,6 +5,8 @@ from __future__ import annotations from dataclasses import dataclass from enum import StrEnum +from ..const import DPCode + class CommonCoverType(StrEnum): """Common cover types.""" @@ -20,7 +22,7 @@ class TuyaCoverDefinition: common_type: CommonCoverType - current_position_dp_code: str | None = None - current_state_dp_code: str | None = None - set_position_dp_code: str | None = None - set_state_dp_code: str + current_position_dp_code: DPCode | None = None + current_state_dp_code: DPCode | None = None + set_position_dp_code: DPCode | None = None + set_state_dp_code: DPCode diff --git a/homeassistant/components/tuya/xternal_tuya_quirks/device_quirk.py b/homeassistant/components/tuya/xternal_tuya_quirks/device_quirk.py index 117e6e72057c..d96852b9f8d0 100644 --- a/homeassistant/components/tuya/xternal_tuya_quirks/device_quirk.py +++ b/homeassistant/components/tuya/xternal_tuya_quirks/device_quirk.py @@ -2,13 +2,20 @@ from __future__ import annotations +from collections.abc import Callable import inspect import pathlib -from typing import TYPE_CHECKING, Self +from typing import TYPE_CHECKING, Any, Self +from tuya_sharing import CustomerDevice + +from ..const import DPCode +from ..models import EnumTypeData, IntegerTypeData +from .climate import CommonClimateType, TuyaClimateDefinition from .cover import CommonCoverType, TuyaCoverDefinition from .select import CommonSelectType, TuyaSelectDefinition from .sensor import CommonSensorType, TuyaSensorDefinition +from .switch import CommonSwitchType, TuyaSwitchDefinition if TYPE_CHECKING: from .registry import QuirksRegistry @@ -18,16 +25,20 @@ class TuyaDeviceQuirk: """Quirk for Tuya device.""" _applies_to: list[tuple[str, str]] + climate_definitions: list[TuyaClimateDefinition] cover_definitions: list[TuyaCoverDefinition] select_definitions: list[TuyaSelectDefinition] sensor_definitions: list[TuyaSensorDefinition] + switch_definitions: list[TuyaSwitchDefinition] def __init__(self) -> None: """Initialize the quirk.""" self._applies_to = [] + self.climate_definitions = [] self.cover_definitions = [] self.select_definitions = [] self.sensor_definitions = [] + self.switch_definitions = [] current_frame = inspect.currentframe() if TYPE_CHECKING: @@ -48,15 +59,36 @@ class TuyaDeviceQuirk: for category, product_id in self._applies_to: registry.register(category, product_id, self) - def add_common_cover( + def add_common_climate( self, *, key: str, + common_type: CommonClimateType, + current_temperature_dp_code: DPCode | None = None, + set_temperature_dp_code: DPCode | None = None, + switch_dp_code: DPCode | None = None, + ) -> Self: + """Add climate definition.""" + self.climate_definitions.append( + TuyaClimateDefinition( + key=key, + common_type=common_type, + switch_dp_code=switch_dp_code, + current_temperature_dp_code=current_temperature_dp_code, + set_temperature_dp_code=set_temperature_dp_code, + ) + ) + return self + + def add_common_cover( + self, + *, + key: DPCode, common_type: CommonCoverType, - current_position_dp_code: str | None = None, - current_state_dp_code: str | None = None, - set_position_dp_code: str | None = None, - set_state_dp_code: str | None = None, + current_position_dp_code: DPCode | None = None, + current_state_dp_code: DPCode | None = None, + set_position_dp_code: DPCode | None = None, + set_state_dp_code: DPCode | None = None, ) -> Self: """Add cover definition.""" self.cover_definitions.append( @@ -74,16 +106,14 @@ class TuyaDeviceQuirk: def add_common_select( self, *, - key: str, + key: DPCode, common_type: CommonSelectType, - dp_code: str | None = None, ) -> Self: """Add select definition.""" self.select_definitions.append( TuyaSelectDefinition( key=key, common_type=common_type, - dp_code=dp_code or key, ) ) return self @@ -91,16 +121,34 @@ class TuyaDeviceQuirk: def add_common_sensor( self, *, - key: str, + key: DPCode, common_type: CommonSensorType, - dp_code: str | None = None, + state_conversion: Callable[ + [CustomerDevice, EnumTypeData | IntegerTypeData | None, Any], Any + ] + | None = None, ) -> Self: """Add sensor definition.""" self.sensor_definitions.append( TuyaSensorDefinition( key=key, common_type=common_type, - dp_code=dp_code or key, + state_conversion=state_conversion, + ) + ) + return self + + def add_common_switch( + self, + *, + key: DPCode, + common_type: CommonSwitchType, + ) -> Self: + """Add switch definition.""" + self.switch_definitions.append( + TuyaSwitchDefinition( + key=key, + common_type=common_type, ) ) return self diff --git a/homeassistant/components/tuya/xternal_tuya_quirks/select.py b/homeassistant/components/tuya/xternal_tuya_quirks/select.py index cda0e5281fe5..5d439cc567e3 100644 --- a/homeassistant/components/tuya/xternal_tuya_quirks/select.py +++ b/homeassistant/components/tuya/xternal_tuya_quirks/select.py @@ -1,10 +1,12 @@ -"""Common cover quirks for Tuya devices.""" +"""Common select quirks for Tuya devices.""" from __future__ import annotations from dataclasses import dataclass from enum import StrEnum +from ..const import DPCode + class CommonSelectType(StrEnum): """Common select types.""" @@ -16,8 +18,6 @@ class CommonSelectType(StrEnum): class TuyaSelectDefinition: """Definition for a select entity.""" - key: str + key: DPCode common_type: CommonSelectType - - dp_code: str diff --git a/homeassistant/components/tuya/xternal_tuya_quirks/sensor.py b/homeassistant/components/tuya/xternal_tuya_quirks/sensor.py index 4d5a34f2668c..bbe8a1b2d144 100644 --- a/homeassistant/components/tuya/xternal_tuya_quirks/sensor.py +++ b/homeassistant/components/tuya/xternal_tuya_quirks/sensor.py @@ -1,14 +1,22 @@ -"""Common cover quirks for Tuya devices.""" +"""Common sensor quirks for Tuya devices.""" from __future__ import annotations +from collections.abc import Callable from dataclasses import dataclass from enum import StrEnum +from typing import Any + +from tuya_sharing import CustomerDevice + +from ..const import DPCode +from ..models import EnumTypeData, IntegerTypeData class CommonSensorType(StrEnum): """Common sensor types.""" + TEMPERATURE = "temperature" TIME_TOTAL = "time_total" @@ -16,8 +24,11 @@ class CommonSensorType(StrEnum): class TuyaSensorDefinition: """Definition for a sensor entity.""" - key: str + key: DPCode common_type: CommonSensorType - dp_code: str + state_conversion: ( + Callable[[CustomerDevice, EnumTypeData | IntegerTypeData | None, Any], Any] + | None + ) = None diff --git a/homeassistant/components/tuya/xternal_tuya_quirks/switch.py b/homeassistant/components/tuya/xternal_tuya_quirks/switch.py new file mode 100644 index 000000000000..37b09b4a89fe --- /dev/null +++ b/homeassistant/components/tuya/xternal_tuya_quirks/switch.py @@ -0,0 +1,23 @@ +"""Common switch quirks for Tuya devices.""" + +from __future__ import annotations + +from dataclasses import dataclass +from enum import StrEnum + +from ..const import DPCode + + +class CommonSwitchType(StrEnum): + """Common switch types.""" + + CHILD_LOCK = "child_lock" + + +@dataclass(kw_only=True) +class TuyaSwitchDefinition: + """Definition for a switch entity.""" + + key: DPCode + + common_type: CommonSwitchType diff --git a/homeassistant/components/tuya/xternal_tuya_quirks/utils.py b/homeassistant/components/tuya/xternal_tuya_quirks/utils.py new file mode 100644 index 000000000000..b9c20679c5a4 --- /dev/null +++ b/homeassistant/components/tuya/xternal_tuya_quirks/utils.py @@ -0,0 +1,18 @@ +"""Common utility functions for Tuya quirks.""" + +from typing import Any + +from ..models import IntegerTypeData + + +def scale_value(value: float, step: float, scale: float) -> Any: + """Official scaling function from Tuya. + + See https://support.tuya.com/en/help/_detail/Kadi66s463e2q + """ + return step * value / (10**scale) + + +def scale_value_force_scale_1(dptype: IntegerTypeData, value: float) -> float: + """Some devices have incorrect scale, force scale=1.""" + return scale_value(value, dptype.step, 1) diff --git a/tests/components/tuya/snapshots/test_sensor.ambr b/tests/components/tuya/snapshots/test_sensor.ambr index 6d20cc5c03da..e9f35adda014 100644 --- a/tests/components/tuya/snapshots/test_sensor.ambr +++ b/tests/components/tuya/snapshots/test_sensor.ambr @@ -5857,6 +5857,62 @@ 'state': '219.72', }) # --- +# name: test_platform_setup_and_discovery[sensor.el_termostato_de_la_cocina_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.el_termostato_de_la_cocina_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'temperature', + 'unique_id': 'tuya.LmLMc0ht1KW2zYAIkwupper_temp', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.el_termostato_de_la_cocina_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'El termostato de la cocina Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.el_termostato_de_la_cocina_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '27.5', + }) +# --- # name: test_platform_setup_and_discovery[sensor.elivco_kitchen_socket_current-entry] EntityRegistryEntrySnapshot({ 'aliases': set({