Compare commits

...

6 Commits

Author SHA1 Message Date
epenet
24b1476f7c More 2025-12-19 09:01:10 +00:00
epenet
f3cc052cd3 More 2025-12-19 08:56:05 +00:00
epenet
42e8f653de More 2025-12-19 08:55:20 +00:00
epenet
8a4a2c5d1d More 2025-12-19 08:44:15 +00:00
epenet
e0c1fc05a7 Update fan 2025-12-18 19:44:03 +00:00
epenet
e30da2c01a Make Tuya DeviceWrapper a generic class 2025-12-18 19:40:41 +00:00
14 changed files with 60 additions and 44 deletions

View File

@@ -20,7 +20,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeEnumWrapper, DPCodeRawWrapper
from .models import DeviceWrapper, DPCodeEnumWrapper, DPCodeRawWrapper
from .type_information import EnumTypeInformation
ALARM: dict[DeviceCategory, tuple[AlarmControlPanelEntityDescription, ...]] = {
@@ -170,9 +170,9 @@ class TuyaAlarmEntity(TuyaEntity, AlarmControlPanelEntity):
device_manager: Manager,
description: AlarmControlPanelEntityDescription,
*,
action_wrapper: _AlarmActionWrapper,
changed_by_wrapper: _AlarmChangedByWrapper | None,
state_wrapper: _AlarmStateWrapper,
action_wrapper: DeviceWrapper[str],
changed_by_wrapper: DeviceWrapper[str] | None,
state_wrapper: DeviceWrapper[AlarmControlPanelState],
) -> None:
"""Init Tuya Alarm."""
super().__init__(device, device_manager)

View File

@@ -19,7 +19,12 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBitmapBitWrapper, DPCodeBooleanWrapper, DPCodeWrapper
from .models import (
DeviceWrapper,
DPCodeBitmapBitWrapper,
DPCodeBooleanWrapper,
DPCodeWrapper,
)
@dataclass(frozen=True)
@@ -450,7 +455,7 @@ class TuyaBinarySensorEntity(TuyaEntity, BinarySensorEntity):
device: CustomerDevice,
device_manager: Manager,
description: TuyaBinarySensorEntityDescription,
dpcode_wrapper: DPCodeWrapper,
dpcode_wrapper: DeviceWrapper[bool],
) -> None:
"""Init Tuya binary sensor."""
super().__init__(device, device_manager)

View File

@@ -13,7 +13,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBooleanWrapper
from .models import DeviceWrapper, DPCodeBooleanWrapper
BUTTONS: dict[DeviceCategory, tuple[ButtonEntityDescription, ...]] = {
DeviceCategory.HXD: (
@@ -107,7 +107,7 @@ class TuyaButtonEntity(TuyaEntity, ButtonEntity):
device: CustomerDevice,
device_manager: Manager,
description: ButtonEntityDescription,
dpcode_wrapper: DPCodeBooleanWrapper,
dpcode_wrapper: DeviceWrapper[bool],
) -> None:
"""Init Tuya button."""
super().__init__(device, device_manager)

View File

@@ -13,7 +13,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBooleanWrapper
from .models import DeviceWrapper, DPCodeBooleanWrapper
CAMERAS: tuple[DeviceCategory, ...] = (
DeviceCategory.DGHSXJ,
@@ -70,8 +70,8 @@ class TuyaCameraEntity(TuyaEntity, CameraEntity):
device: CustomerDevice,
device_manager: Manager,
*,
motion_detection_switch: DPCodeBooleanWrapper | None = None,
recording_status: DPCodeBooleanWrapper | None = None,
motion_detection_switch: DeviceWrapper[bool] | None = None,
recording_status: DeviceWrapper[bool] | None = None,
) -> None:
"""Init Tuya Camera."""
super().__init__(device, device_manager)

View File

@@ -332,13 +332,13 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity):
device_manager: Manager,
description: TuyaClimateEntityDescription,
*,
current_humidity_wrapper: _RoundedIntegerWrapper | None,
current_temperature_wrapper: DPCodeIntegerWrapper | None,
current_humidity_wrapper: DeviceWrapper[int] | None,
current_temperature_wrapper: DeviceWrapper[float] | None,
fan_mode_wrapper: DPCodeEnumWrapper | None,
hvac_mode_wrapper: DPCodeEnumWrapper | None,
set_temperature_wrapper: DPCodeIntegerWrapper | None,
swing_wrapper: _SwingModeWrapper | None,
switch_wrapper: DPCodeBooleanWrapper | None,
switch_wrapper: DeviceWrapper[bool] | None,
target_humidity_wrapper: _RoundedIntegerWrapper | None,
temperature_unit: UnitOfTemperature,
) -> None:

View File

@@ -340,11 +340,11 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
device_manager: Manager,
description: TuyaCoverEntityDescription,
*,
current_position: _DPCodePercentageMappingWrapper | None,
current_position: DeviceWrapper[int] | None,
current_state_wrapper: _IsClosedWrapper | None,
instruction_wrapper: DeviceWrapper | None,
set_position: _DPCodePercentageMappingWrapper | None,
tilt_position: _DPCodePercentageMappingWrapper | None,
instruction_wrapper: DeviceWrapper[str] | None,
set_position: DeviceWrapper[int] | None,
tilt_position: DeviceWrapper[int] | None,
) -> None:
"""Init Tuya Cover."""
super().__init__(device, device_manager)
@@ -358,7 +358,7 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
self._set_position = set_position
self._tilt_position = tilt_position
if instruction_wrapper and instruction_wrapper.options is not None:
if instruction_wrapper and instruction_wrapper.options:
if "open" in instruction_wrapper.options:
self._attr_supported_features |= CoverEntityFeature.OPEN
if "close" in instruction_wrapper.options:

View File

@@ -70,14 +70,14 @@ class TuyaEntity(Entity):
self.device_manager.send_commands, self.device.id, commands
)
def _read_wrapper(self, wrapper: DeviceWrapper | None) -> Any | None:
def _read_wrapper[T](self, wrapper: DeviceWrapper[T] | None) -> T | None:
"""Read the wrapper device status."""
if wrapper is None:
return None
return wrapper.read_device_status(self.device)
async def _async_send_wrapper_updates(
self, wrapper: DeviceWrapper | None, value: Any
async def _async_send_wrapper_updates[T](
self, wrapper: DeviceWrapper[T] | None, value: T
) -> None:
"""Send command to the device."""
if wrapper is None:

View File

@@ -23,7 +23,12 @@ from homeassistant.util.percentage import (
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBooleanWrapper, DPCodeEnumWrapper, DPCodeIntegerWrapper
from .models import (
DeviceWrapper,
DPCodeBooleanWrapper,
DPCodeEnumWrapper,
DPCodeIntegerWrapper,
)
from .type_information import IntegerTypeInformation
from .util import RemapHelper, get_dpcode
@@ -173,11 +178,11 @@ class TuyaFanEntity(TuyaEntity, FanEntity):
device: CustomerDevice,
device_manager: Manager,
*,
direction_wrapper: _DirectionEnumWrapper | None,
mode_wrapper: DPCodeEnumWrapper | None,
oscillate_wrapper: DPCodeBooleanWrapper | None,
speed_wrapper: _FanSpeedEnumWrapper | _FanSpeedIntegerWrapper | None,
switch_wrapper: DPCodeBooleanWrapper | None,
direction_wrapper: DeviceWrapper[str] | None,
mode_wrapper: DeviceWrapper[str] | None,
oscillate_wrapper: DeviceWrapper[bool] | None,
speed_wrapper: DeviceWrapper[int] | None,
switch_wrapper: DeviceWrapper[bool] | None,
) -> None:
"""Init Tuya Fan Device."""
super().__init__(device, device_manager)

View File

@@ -20,7 +20,12 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBooleanWrapper, DPCodeEnumWrapper, DPCodeIntegerWrapper
from .models import (
DeviceWrapper,
DPCodeBooleanWrapper,
DPCodeEnumWrapper,
DPCodeIntegerWrapper,
)
from .util import ActionDPCodeNotFoundError, get_dpcode
@@ -136,9 +141,9 @@ class TuyaHumidifierEntity(TuyaEntity, HumidifierEntity):
device_manager: Manager,
description: TuyaHumidifierEntityDescription,
*,
current_humidity_wrapper: _RoundedIntegerWrapper | None = None,
current_humidity_wrapper: DeviceWrapper[int] | None = None,
mode_wrapper: DPCodeEnumWrapper | None = None,
switch_wrapper: DPCodeBooleanWrapper | None = None,
switch_wrapper: DeviceWrapper[bool] | None = None,
target_humidity_wrapper: _RoundedIntegerWrapper | None = None,
) -> None:
"""Init Tuya (de)humidifier."""

View File

@@ -31,6 +31,7 @@ from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, WorkMode
from .entity import TuyaEntity
from .models import (
DeviceWrapper,
DPCodeBooleanWrapper,
DPCodeEnumWrapper,
DPCodeIntegerWrapper,
@@ -673,11 +674,11 @@ class TuyaLightEntity(TuyaEntity, LightEntity):
device_manager: Manager,
description: TuyaLightEntityDescription,
*,
brightness_wrapper: _BrightnessWrapper | None,
color_data_wrapper: _ColorDataWrapper | None,
brightness_wrapper: DeviceWrapper[int] | None,
color_data_wrapper: DeviceWrapper[tuple[float, float, float]] | None,
color_mode_wrapper: DPCodeEnumWrapper | None,
color_temp_wrapper: _ColorTempWrapper | None,
switch_wrapper: DPCodeBooleanWrapper,
color_temp_wrapper: DeviceWrapper[int] | None,
switch_wrapper: DeviceWrapper[bool],
) -> None:
"""Init TuyaHaLight."""
super().__init__(device, device_manager)

View File

@@ -18,17 +18,17 @@ from .type_information import (
)
class DeviceWrapper:
class DeviceWrapper[T]:
"""Base device wrapper."""
options: list[str] | None = None
def read_device_status(self, device: CustomerDevice) -> Any | None:
def read_device_status(self, device: CustomerDevice) -> T | None:
"""Read device status and convert to a Home Assistant value."""
raise NotImplementedError
def get_update_commands(
self, device: CustomerDevice, value: Any
self, device: CustomerDevice, value: T
) -> list[dict[str, Any]]:
"""Generate update commands for a Home Assistant action."""
raise NotImplementedError

View File

@@ -19,7 +19,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBooleanWrapper
from .models import DeviceWrapper, DPCodeBooleanWrapper
SIRENS: dict[DeviceCategory, tuple[SirenEntityDescription, ...]] = {
DeviceCategory.CO2BJ: (
@@ -94,7 +94,7 @@ class TuyaSirenEntity(TuyaEntity, SirenEntity):
device: CustomerDevice,
device_manager: Manager,
description: SirenEntityDescription,
dpcode_wrapper: DPCodeBooleanWrapper,
dpcode_wrapper: DeviceWrapper[bool],
) -> None:
"""Init Tuya Siren."""
super().__init__(device, device_manager)

View File

@@ -27,7 +27,7 @@ from homeassistant.helpers.issue_registry import (
from . import TuyaConfigEntry
from .const import DOMAIN, TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBooleanWrapper
from .models import DeviceWrapper, DPCodeBooleanWrapper
@dataclass(frozen=True, kw_only=True)
@@ -1027,7 +1027,7 @@ class TuyaSwitchEntity(TuyaEntity, SwitchEntity):
device: CustomerDevice,
device_manager: Manager,
description: SwitchEntityDescription,
dpcode_wrapper: DPCodeBooleanWrapper,
dpcode_wrapper: DeviceWrapper[bool],
) -> None:
"""Init TuyaHaSwitch."""
super().__init__(device, device_manager)

View File

@@ -17,7 +17,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
from .entity import TuyaEntity
from .models import DPCodeBooleanWrapper
from .models import DeviceWrapper, DPCodeBooleanWrapper
VALVES: dict[DeviceCategory, tuple[ValveEntityDescription, ...]] = {
DeviceCategory.SFKZQ: (
@@ -122,7 +122,7 @@ class TuyaValveEntity(TuyaEntity, ValveEntity):
device: CustomerDevice,
device_manager: Manager,
description: ValveEntityDescription,
dpcode_wrapper: DPCodeBooleanWrapper,
dpcode_wrapper: DeviceWrapper[bool],
) -> None:
"""Init TuyaValveEntity."""
super().__init__(device, device_manager)