From eb20292683ecb9cfc06439971286b46e09e2eb60 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 10 Jul 2025 15:54:05 +0200 Subject: [PATCH] Move tuya models to separate module (#148550) --- .../components/tuya/alarm_control_panel.py | 3 +- homeassistant/components/tuya/climate.py | 3 +- homeassistant/components/tuya/cover.py | 3 +- homeassistant/components/tuya/entity.py | 120 +---------------- homeassistant/components/tuya/fan.py | 3 +- homeassistant/components/tuya/humidifier.py | 3 +- homeassistant/components/tuya/light.py | 3 +- homeassistant/components/tuya/models.py | 124 ++++++++++++++++++ homeassistant/components/tuya/number.py | 3 +- homeassistant/components/tuya/sensor.py | 3 +- homeassistant/components/tuya/vacuum.py | 3 +- 11 files changed, 144 insertions(+), 127 deletions(-) create mode 100644 homeassistant/components/tuya/models.py diff --git a/homeassistant/components/tuya/alarm_control_panel.py b/homeassistant/components/tuya/alarm_control_panel.py index 4972fe88339..61985fb7622 100644 --- a/homeassistant/components/tuya/alarm_control_panel.py +++ b/homeassistant/components/tuya/alarm_control_panel.py @@ -20,7 +20,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import EnumTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import EnumTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 991c3589e12..734f6ba7f7a 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -25,7 +25,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData TUYA_HVAC_TO_HA = { "auto": HVACMode.HEAT_COOL, diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index 015daae4212..a385a35d903 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -21,7 +21,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/entity.py b/homeassistant/components/tuya/entity.py index cc258560067..4158650b062 100644 --- a/homeassistant/components/tuya/entity.py +++ b/homeassistant/components/tuya/entity.py @@ -2,11 +2,7 @@ from __future__ import annotations -import base64 -from dataclasses import dataclass -import json -import struct -from typing import Any, Literal, Self, overload +from typing import Any, Literal, overload from tuya_sharing import CustomerDevice, Manager @@ -15,7 +11,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity from .const import DOMAIN, LOGGER, TUYA_HA_SIGNAL_UPDATE_ENTITY, DPCode, DPType -from .util import remap_value +from .models import EnumTypeData, IntegerTypeData _DPTYPE_MAPPING: dict[str, DPType] = { "Bitmap": DPType.RAW, @@ -29,118 +25,6 @@ _DPTYPE_MAPPING: dict[str, DPType] = { } -@dataclass -class IntegerTypeData: - """Integer Type Data.""" - - dpcode: DPCode - min: int - max: int - scale: float - step: float - unit: str | None = None - type: str | None = None - - @property - def max_scaled(self) -> float: - """Return the max scaled.""" - return self.scale_value(self.max) - - @property - def min_scaled(self) -> float: - """Return the min scaled.""" - return self.scale_value(self.min) - - @property - def step_scaled(self) -> float: - """Return the step scaled.""" - return self.step / (10**self.scale) - - def scale_value(self, value: float) -> float: - """Scale a value.""" - return value / (10**self.scale) - - def scale_value_back(self, value: float) -> int: - """Return raw value for scaled.""" - return int(value * (10**self.scale)) - - def remap_value_to( - self, - value: float, - to_min: float = 0, - to_max: float = 255, - reverse: bool = False, - ) -> float: - """Remap a value from this range to a new range.""" - return remap_value(value, self.min, self.max, to_min, to_max, reverse) - - def remap_value_from( - self, - value: float, - from_min: float = 0, - from_max: float = 255, - reverse: bool = False, - ) -> float: - """Remap a value from its current range to this range.""" - return remap_value(value, from_min, from_max, self.min, self.max, reverse) - - @classmethod - def from_json(cls, dpcode: DPCode, data: str) -> IntegerTypeData | None: - """Load JSON string and return a IntegerTypeData object.""" - if not (parsed := json.loads(data)): - return None - - return cls( - dpcode, - min=int(parsed["min"]), - max=int(parsed["max"]), - scale=float(parsed["scale"]), - step=max(float(parsed["step"]), 1), - unit=parsed.get("unit"), - type=parsed.get("type"), - ) - - -@dataclass -class EnumTypeData: - """Enum Type Data.""" - - dpcode: DPCode - range: list[str] - - @classmethod - def from_json(cls, dpcode: DPCode, data: str) -> EnumTypeData | None: - """Load JSON string and return a EnumTypeData object.""" - if not (parsed := json.loads(data)): - return None - return cls(dpcode, **parsed) - - -@dataclass -class ElectricityTypeData: - """Electricity Type Data.""" - - electriccurrent: str | None = None - power: str | None = None - voltage: str | None = None - - @classmethod - def from_json(cls, data: str) -> Self: - """Load JSON string and return a ElectricityTypeData object.""" - return cls(**json.loads(data.lower())) - - @classmethod - def from_raw(cls, data: str) -> Self: - """Decode base64 string and return a ElectricityTypeData object.""" - raw = base64.b64decode(data) - voltage = struct.unpack(">H", raw[0:2])[0] / 10.0 - electriccurrent = struct.unpack(">L", b"\x00" + raw[2:5])[0] / 1000.0 - power = struct.unpack(">L", b"\x00" + raw[5:8])[0] / 1000.0 - return cls( - electriccurrent=str(electriccurrent), power=str(power), voltage=str(voltage) - ) - - class TuyaEntity(Entity): """Tuya base device.""" diff --git a/homeassistant/components/tuya/fan.py b/homeassistant/components/tuya/fan.py index f2d856b6d86..f96ea2c0a65 100644 --- a/homeassistant/components/tuya/fan.py +++ b/homeassistant/components/tuya/fan.py @@ -22,7 +22,8 @@ from homeassistant.util.percentage import ( from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import EnumTypeData, IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import EnumTypeData, IntegerTypeData TUYA_SUPPORT_TYPE = { "cs", # Dehumidifier diff --git a/homeassistant/components/tuya/humidifier.py b/homeassistant/components/tuya/humidifier.py index f8fd9237ffc..6539d98e9d8 100644 --- a/homeassistant/components/tuya/humidifier.py +++ b/homeassistant/components/tuya/humidifier.py @@ -19,7 +19,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/light.py b/homeassistant/components/tuya/light.py index 37c79b952d4..3f8fc7d0fb9 100644 --- a/homeassistant/components/tuya/light.py +++ b/homeassistant/components/tuya/light.py @@ -25,7 +25,8 @@ from homeassistant.util import color as color_util from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType, WorkMode -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData from .util import remap_value diff --git a/homeassistant/components/tuya/models.py b/homeassistant/components/tuya/models.py new file mode 100644 index 00000000000..b4afca83a85 --- /dev/null +++ b/homeassistant/components/tuya/models.py @@ -0,0 +1,124 @@ +"""Tuya Home Assistant Base Device Model.""" + +from __future__ import annotations + +import base64 +from dataclasses import dataclass +import json +import struct +from typing import Self + +from .const import DPCode +from .util import remap_value + + +@dataclass +class IntegerTypeData: + """Integer Type Data.""" + + dpcode: DPCode + min: int + max: int + scale: float + step: float + unit: str | None = None + type: str | None = None + + @property + def max_scaled(self) -> float: + """Return the max scaled.""" + return self.scale_value(self.max) + + @property + def min_scaled(self) -> float: + """Return the min scaled.""" + return self.scale_value(self.min) + + @property + def step_scaled(self) -> float: + """Return the step scaled.""" + return self.step / (10**self.scale) + + def scale_value(self, value: float) -> float: + """Scale a value.""" + return value / (10**self.scale) + + def scale_value_back(self, value: float) -> int: + """Return raw value for scaled.""" + return int(value * (10**self.scale)) + + def remap_value_to( + self, + value: float, + to_min: float = 0, + to_max: float = 255, + reverse: bool = False, + ) -> float: + """Remap a value from this range to a new range.""" + return remap_value(value, self.min, self.max, to_min, to_max, reverse) + + def remap_value_from( + self, + value: float, + from_min: float = 0, + from_max: float = 255, + reverse: bool = False, + ) -> float: + """Remap a value from its current range to this range.""" + return remap_value(value, from_min, from_max, self.min, self.max, reverse) + + @classmethod + def from_json(cls, dpcode: DPCode, data: str) -> IntegerTypeData | None: + """Load JSON string and return a IntegerTypeData object.""" + if not (parsed := json.loads(data)): + return None + + return cls( + dpcode, + min=int(parsed["min"]), + max=int(parsed["max"]), + scale=float(parsed["scale"]), + step=max(float(parsed["step"]), 1), + unit=parsed.get("unit"), + type=parsed.get("type"), + ) + + +@dataclass +class EnumTypeData: + """Enum Type Data.""" + + dpcode: DPCode + range: list[str] + + @classmethod + def from_json(cls, dpcode: DPCode, data: str) -> EnumTypeData | None: + """Load JSON string and return a EnumTypeData object.""" + if not (parsed := json.loads(data)): + return None + return cls(dpcode, **parsed) + + +@dataclass +class ElectricityTypeData: + """Electricity Type Data.""" + + electriccurrent: str | None = None + power: str | None = None + voltage: str | None = None + + @classmethod + def from_json(cls, data: str) -> Self: + """Load JSON string and return a ElectricityTypeData object.""" + return cls(**json.loads(data.lower())) + + @classmethod + def from_raw(cls, data: str) -> Self: + """Decode base64 string and return a ElectricityTypeData object.""" + raw = base64.b64decode(data) + voltage = struct.unpack(">H", raw[0:2])[0] / 10.0 + electriccurrent = struct.unpack(">L", b"\x00" + raw[2:5])[0] / 1000.0 + power = struct.unpack(">L", b"\x00" + raw[5:8])[0] / 1000.0 + return cls( + electriccurrent=str(electriccurrent), power=str(power), voltage=str(voltage) + ) diff --git a/homeassistant/components/tuya/number.py b/homeassistant/components/tuya/number.py index ddee46b8799..b5b8437ea8b 100644 --- a/homeassistant/components/tuya/number.py +++ b/homeassistant/components/tuya/number.py @@ -16,7 +16,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import TuyaConfigEntry from .const import DEVICE_CLASS_UNITS, DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData # All descriptions can be found here. Mostly the Integer data types in the # default instructions set of each category end up being a number. diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index 5151f39eb26..b45b8214bff 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -35,7 +35,8 @@ from .const import ( DPType, UnitOfMeasurement, ) -from .entity import ElectricityTypeData, EnumTypeData, IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import ElectricityTypeData, EnumTypeData, IntegerTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/vacuum.py b/homeassistant/components/tuya/vacuum.py index f722fd918ca..d61a624f027 100644 --- a/homeassistant/components/tuya/vacuum.py +++ b/homeassistant/components/tuya/vacuum.py @@ -17,7 +17,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import EnumTypeData, IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import EnumTypeData, IntegerTypeData TUYA_MODE_RETURN_HOME = "chargego" TUYA_STATUS_TO_HA = {