This commit is contained in:
epenet
2025-11-03 13:25:59 +00:00
parent 9fa0974c8c
commit 14bd55426d
9 changed files with 76 additions and 97 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ from .const import (
TUYA_DISCOVERY_NEW,
TUYA_HA_SIGNAL_UPDATE_ENTITY,
)
from .tuya_device_handlers import register_tuya_quirks
from .tuya_device_handlers.devices import register_tuya_quirks
# Suppress logs from the library, it logs unneeded on error
logging.getLogger("tuya_sharing").setLevel(logging.CRITICAL)
@@ -2,12 +2,6 @@
from __future__ import annotations
import importlib
import logging
import pathlib
import pkgutil
import sys
from .base_quirk import (
TuyaClimateDefinition,
TuyaCoverDefinition,
@@ -19,8 +13,6 @@ from .base_quirk import (
from .registry import QuirksRegistry
from .utils import parse_enum
_LOGGER = logging.getLogger(__name__)
__all__ = [
"TUYA_QUIRKS_REGISTRY",
"QuirksRegistry",
@@ -34,52 +26,3 @@ __all__ = [
]
TUYA_QUIRKS_REGISTRY = QuirksRegistry()
def register_tuya_quirks(custom_quirks_path: str | None = None) -> None:
"""Register all available quirks.
- remove custom quirks from `custom_quirks_path`
- add quirks from `devices` subfolder
- add custom quirks from `custom_quirks_path`
"""
if custom_quirks_path is not None:
TUYA_QUIRKS_REGISTRY.purge_custom_quirks(custom_quirks_path)
# Import all quirks in the `tuya_device_handlers` package first
from . import devices # noqa: PLC0415
for _importer, modname, _ispkg in pkgutil.walk_packages(
path=devices.__path__,
prefix=devices.__name__ + ".",
):
_LOGGER.warning("Loading quirks module %r", modname)
importlib.import_module(modname)
if custom_quirks_path is None:
return
path = pathlib.Path(custom_quirks_path)
_LOGGER.debug("Loading custom quirks from %r", path)
loaded = False
# Treat the custom quirk path (e.g. `/config/tuya_quirks/`) itself as a module
for importer, modname, _ispkg in pkgutil.walk_packages(path=[str(path)]):
_LOGGER.debug("Loading custom quirk module %r", modname)
try:
spec = importer.find_spec(modname) # type: ignore[call-arg]
module = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
sys.modules[modname] = module
spec.loader.exec_module(module) # type: ignore[union-attr]
except Exception:
_LOGGER.exception("Unexpected exception importing custom quirk %r", modname)
else:
loaded = True
if loaded:
_LOGGER.warning(
"Loaded custom quirks. Please contribute them to https://github.com/TBD"
)
@@ -38,7 +38,9 @@ class TuyaClimateDefinition(BaseTuyaDefinition):
switch_only_hvac_mode: TuyaClimateHVACMode
current_temperature_dp_code: TuyaDPCode | None = None
current_temperature_state_conversion: TuyaIntegerConversionFunction | None = None
target_temperature_dp_code: TuyaDPCode | None = None
target_temperature_state_conversion: TuyaIntegerConversionFunction | None = None
target_temperature_command_conversion: TuyaIntegerConversionFunction | None = None
@@ -108,8 +110,10 @@ class TuyaDeviceQuirk:
key: str,
# Climate specific
switch_only_hvac_mode: TuyaClimateHVACMode,
current_temperature_dp_code: TuyaDPCode | None = None,
current_temperature_state_conversion: TuyaIntegerConversionFunction
| None = None,
target_temperature_dp_code: TuyaDPCode | None = None,
target_temperature_state_conversion: TuyaIntegerConversionFunction
| None = None,
target_temperature_command_conversion: TuyaIntegerConversionFunction
@@ -120,7 +124,9 @@ class TuyaDeviceQuirk:
TuyaClimateDefinition(
key=key,
switch_only_hvac_mode=switch_only_hvac_mode,
current_temperature_dp_code=current_temperature_dp_code,
current_temperature_state_conversion=current_temperature_state_conversion,
target_temperature_dp_code=target_temperature_dp_code,
target_temperature_state_conversion=target_temperature_state_conversion,
target_temperature_command_conversion=target_temperature_command_conversion,
)
@@ -12,7 +12,9 @@ class TuyaDPCode(StrEnum):
CONTROL = "control"
CONTROL_BACK_MODE = "control_back_mode"
PERCENT_CONTROL = "percent_control"
TEMP_SET = "temp_set"
TIME_TOTAL = "time_total"
UPPER_TEMP = "upper_temp"
class TuyaDeviceCategory(StrEnum):
@@ -5,8 +5,6 @@ from __future__ import annotations
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, Protocol
from .utils import scale_value, scale_value_back
if TYPE_CHECKING:
from tuya_sharing import CustomerDevice
@@ -33,17 +31,3 @@ type TuyaIntegerConversionFunction = Callable[
dptype: The DP type data (IntegerTypeData).
value: The value to convert.
"""
def scale_value_fixed_scale_1(
_: CustomerDevice, dptype: TuyaIntegerDefinition, value: Any
) -> float:
"""Scale value, overriding scale to be 1."""
return scale_value(value, dptype.step, 1)
def scale_value_back_fixed_scale_1(
_: CustomerDevice, dptype: TuyaIntegerDefinition, value: Any
) -> int:
"""Unscale value, overriding scale to be 1."""
return scale_value_back(value, dptype.step, 1)
@@ -1,3 +1,60 @@
"""Quirks for Tuya."""
from __future__ import annotations
import importlib
import logging
import pathlib
import pkgutil
import sys
from .. import TUYA_QUIRKS_REGISTRY
_LOGGER = logging.getLogger(__name__)
def register_tuya_quirks(custom_quirks_path: str | None = None) -> None:
"""Register all available quirks.
- remove custom quirks from `custom_quirks_path`
- add quirks from `devices` subfolder
- add custom quirks from `custom_quirks_path`
"""
if custom_quirks_path is not None:
TUYA_QUIRKS_REGISTRY.purge_custom_quirks(custom_quirks_path)
# Import all quirks in the `tuya_device_handlers` package first
for _importer, modname, _ispkg in pkgutil.walk_packages(
path=__path__,
prefix=__name__ + ".",
):
_LOGGER.debug("Loading quirks module %r", modname)
importlib.import_module(modname)
if custom_quirks_path is None:
return
path = pathlib.Path(custom_quirks_path)
_LOGGER.debug("Loading custom quirks from %r", path)
loaded = False
# Treat the custom quirk path (e.g. `/config/tuya_quirks/`) itself as a module
for importer, modname, _ispkg in pkgutil.walk_packages(path=[str(path)]):
_LOGGER.debug("Loading custom quirk module %r", modname)
try:
spec = importer.find_spec(modname) # type: ignore[call-arg]
module = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
sys.modules[modname] = module
spec.loader.exec_module(module) # type: ignore[union-attr]
except Exception:
_LOGGER.exception("Unexpected exception importing custom quirk %r", modname)
else:
loaded = True
if loaded:
_LOGGER.warning(
"Loaded custom quirks. Please contribute them to https://github.com/TBD"
)
@@ -4,19 +4,21 @@ from __future__ import annotations
from ... import TUYA_QUIRKS_REGISTRY, TuyaDeviceQuirk
from ...const import TuyaDeviceCategory, TuyaDPCode
from ...conversion import scale_value_back_fixed_scale_1, scale_value_fixed_scale_1
from ...homeassistant import TuyaClimateHVACMode, TuyaEntityCategory
(
# This model has invalid scale 0 for temperature dps - force scale 1
TuyaDeviceQuirk()
.applies_to(category=TuyaDeviceCategory.WK, product_id="IAYz2WK1th0cMLmL")
.add_climate(
key="wk",
switch_only_hvac_mode=TuyaClimateHVACMode.HEAT_COOL,
current_temperature_state_conversion=scale_value_fixed_scale_1,
target_temperature_state_conversion=scale_value_fixed_scale_1,
target_temperature_command_conversion=scale_value_back_fixed_scale_1,
current_temperature_dp_code=TuyaDPCode.UPPER_TEMP,
# UPPER_TEMP uses incorrect scale 1 / step 5 - convert to proper temperature
current_temperature_state_conversion=lambda _device, _def, value: value / 2,
# TEMP_SET uses incorrect scale 0 / step 5 - convert to proper temperature
target_temperature_dp_code=TuyaDPCode.TEMP_SET,
target_temperature_state_conversion=lambda _device, _def, value: value / 2,
target_temperature_command_conversion=lambda _device, _def, value: value * 2,
)
.add_switch(
key=TuyaDPCode.CHILD_LOCK,
@@ -5,23 +5,6 @@ from __future__ import annotations
from enum import StrEnum
def scale_value(value: int, step: float, scale: float) -> float:
"""Official scaling function from Tuya.
See https://support.tuya.com/en/help/_detail/Kadi66s463e2q
"""
return step * value / (10**scale)
def scale_value_back(value: float, step: float, scale: float) -> int:
"""Official scaling function from Tuya.
See https://support.tuya.com/en/help/_detail/Kadi66s463e2q
"""
return int(value * (10**scale) / step)
def parse_enum[T: StrEnum](enum_class: type[T], value: str | None) -> T | None:
"""Parse a string to an enum member.
+3 -1
View File
@@ -12,11 +12,13 @@ from homeassistant.components.tuya.tuya_device_handlers import (
TUYA_QUIRKS_REGISTRY,
QuirksRegistry,
TuyaDeviceQuirk,
register_tuya_quirks,
)
from homeassistant.components.tuya.tuya_device_handlers.base_quirk import (
BaseTuyaDefinition,
)
from homeassistant.components.tuya.tuya_device_handlers.devices import (
register_tuya_quirks,
)
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import translation