Move quirks to sub-folder

This commit is contained in:
epenet
2025-11-03 08:29:40 +00:00
parent 9369cf82fb
commit b169bf0bf0
5 changed files with 204 additions and 1 deletions
+26 -1
View File
@@ -23,6 +23,7 @@ from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, DPType
from .entity import TuyaEntity
from .models import EnumTypeData, IntegerTypeData
from .quirks import TUYA_QUIRKS_REGISTRY, TuyaCoverDefinition, parse_enum
from .util import get_dpcode
@@ -164,6 +165,19 @@ def _apply_quirk(
return description
def _create_quirk_description(
definition: TuyaCoverDefinition,
) -> TuyaCoverEntityDescription:
return TuyaCoverEntityDescription(
key=DPCode(definition.key),
translation_key=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),
device_class=parse_enum(CoverDeviceClass, definition.device_class),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: TuyaConfigEntry,
@@ -178,7 +192,18 @@ async def async_setup_entry(
entities: list[TuyaCoverEntity] = []
for device_id in device_ids:
device = manager.device_map[device_id]
if descriptions := COVERS.get(device.category):
if quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device):
entities.extend(
TuyaCoverEntity(
device, manager, _create_quirk_description(definition)
)
for definition in quirk.cover_definitions
if (
definition.key in device.function
or definition.key in device.status_range
)
)
elif descriptions := COVERS.get(device.category):
entities.extend(
TuyaCoverEntity(device, manager, _apply_quirk(device, description))
for description in descriptions
@@ -0,0 +1,32 @@
"""Quirks for Tuya."""
from __future__ import annotations
from .device_quirk import DeviceQuirk, TuyaCoverDefinition, TuyaCoverDeviceClass
from .homeassistant import parse_enum
from .registry import QuirksRegistry
__all__ = [
"QUIRKS_REGISTRY",
"QuirksRegistry",
"TuyaCoverDefinition",
"TuyaCoverDeviceClass",
"parse_enum",
]
TUYA_QUIRKS_REGISTRY = QuirksRegistry()
(
# This model has percent_control / percent_state / situation_set
# but they never get updated - use control instead to get the state
DeviceQuirk()
.applies_to(category="cl", product_id="lfkr93x0ukp5gaia")
.add_cover(
key="control",
translation_key="curtain",
translation_string="curtain",
current_state_dp_code="control",
device_class=TuyaCoverDeviceClass.CURTAIN,
)
.register(TUYA_QUIRKS_REGISTRY)
)
@@ -0,0 +1,79 @@
"""Quirks registry."""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING, Self
from .homeassistant import TuyaCoverDeviceClass
if TYPE_CHECKING:
from .registry import QuirksRegistry
@dataclass
class BaseTuyaDefinition:
"""Definition for a Tuya device."""
key: str
translation_key: str
translation_string: str
@dataclass(kw_only=True)
class TuyaCoverDefinition(BaseTuyaDefinition):
"""Definition for a cover device."""
device_class: TuyaCoverDeviceClass | None = None
current_state_dp_code: str | None = None
current_position_dp_code: str | None = None
set_position_dp_code: str | None = None
@dataclass
class DeviceQuirk:
"""Quirk for Tuya device."""
_applies_to: list[tuple[str, str]]
cover_definitions: list[TuyaCoverDefinition]
def __init__(self) -> None:
"""Initialize the quirk."""
self._applies_to = []
self.cover_definitions = []
def applies_to(self, *, category: str, product_id: str) -> Self:
"""Set the device type the quirk applies to."""
self._applies_to.append((category, product_id))
return self
def register(self, registry: QuirksRegistry) -> None:
"""Register the quirk in the registry."""
for category, product_id in self._applies_to:
registry.register(category, product_id, self)
def add_cover(
self,
*,
key: str,
translation_key: str,
translation_string: str,
device_class: TuyaCoverDeviceClass | None = None,
current_state_dp_code: str | None = None,
current_position_dp_code: str | None = None,
set_position_dp_code: str | None = None,
) -> Self:
"""Add cover quirk."""
self.cover_definitions.append(
TuyaCoverDefinition(
key=key,
translation_key=translation_key,
translation_string=translation_string,
device_class=device_class,
current_state_dp_code=current_state_dp_code,
current_position_dp_code=current_position_dp_code,
set_position_dp_code=set_position_dp_code,
)
)
return self
@@ -0,0 +1,31 @@
"""Quirks registry."""
from __future__ import annotations
from enum import StrEnum
def parse_enum[T: StrEnum](enum_class: type[T], value: str | None) -> T | None:
"""Parse a string to an enum member, or return None if value is None."""
if value is None:
return None
try:
return enum_class(value)
except ValueError:
return None
class TuyaCoverDeviceClass(StrEnum):
"""Device class for cover."""
# Keep synchronized with homeassistant
AWNING = "awning"
BLIND = "blind"
CURTAIN = "curtain"
DAMPER = "damper"
DOOR = "door"
GARAGE = "garage"
GATE = "gate"
SHADE = "shade"
SHUTTER = "shutter"
WINDOW = "window"
@@ -0,0 +1,36 @@
"""Quirks registry."""
from __future__ import annotations
from typing import TYPE_CHECKING, Self
from tuya_sharing import CustomerDevice
if TYPE_CHECKING:
from .device_quirk import DeviceQuirk
class QuirksRegistry:
"""Registry for Tuya quirks."""
instance: Self
_quirks: dict[str, dict[str, DeviceQuirk]]
def __new__(cls) -> Self:
"""Create a new class."""
if not hasattr(cls, "instance"):
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self) -> None:
"""Initialize the registry."""
self._quirks = {}
def register(self, category: str, product_id: str, quirk: DeviceQuirk) -> None:
"""Register a quirk for a specific device type."""
self._quirks.setdefault(category, {})[product_id] = quirk
def get_quirk_for_device(self, device: CustomerDevice) -> DeviceQuirk | None:
"""Get the quirk for a specific device."""
return self._quirks.get(device.category, {}).get(device.product_id)