diff --git a/homeassistant/components/tuya/__init__.py b/homeassistant/components/tuya/__init__.py index 22ea3220caf6..727587be6266 100644 --- a/homeassistant/components/tuya/__init__.py +++ b/homeassistant/components/tuya/__init__.py @@ -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) diff --git a/homeassistant/components/tuya/tuya_device_handlers/__init__.py b/homeassistant/components/tuya/tuya_device_handlers/__init__.py index 3630321f3aaf..087770dfa596 100644 --- a/homeassistant/components/tuya/tuya_device_handlers/__init__.py +++ b/homeassistant/components/tuya/tuya_device_handlers/__init__.py @@ -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" - ) diff --git a/homeassistant/components/tuya/tuya_device_handlers/base_quirk.py b/homeassistant/components/tuya/tuya_device_handlers/base_quirk.py index d8394522b692..d2be4916036e 100644 --- a/homeassistant/components/tuya/tuya_device_handlers/base_quirk.py +++ b/homeassistant/components/tuya/tuya_device_handlers/base_quirk.py @@ -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, ) diff --git a/homeassistant/components/tuya/tuya_device_handlers/const.py b/homeassistant/components/tuya/tuya_device_handlers/const.py index e8cdb7798912..8b5b99b3b4ab 100644 --- a/homeassistant/components/tuya/tuya_device_handlers/const.py +++ b/homeassistant/components/tuya/tuya_device_handlers/const.py @@ -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): diff --git a/homeassistant/components/tuya/tuya_device_handlers/conversion.py b/homeassistant/components/tuya/tuya_device_handlers/conversion.py index 0c439f07923a..5e0fa5f59612 100644 --- a/homeassistant/components/tuya/tuya_device_handlers/conversion.py +++ b/homeassistant/components/tuya/tuya_device_handlers/conversion.py @@ -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) diff --git a/homeassistant/components/tuya/tuya_device_handlers/devices/__init__.py b/homeassistant/components/tuya/tuya_device_handlers/devices/__init__.py index 7cd7ea00b35e..e9904a605ca1 100644 --- a/homeassistant/components/tuya/tuya_device_handlers/devices/__init__.py +++ b/homeassistant/components/tuya/tuya_device_handlers/devices/__init__.py @@ -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" + ) diff --git a/homeassistant/components/tuya/tuya_device_handlers/devices/wk/wk_IAYz2WK1th0cMLmL.py b/homeassistant/components/tuya/tuya_device_handlers/devices/wk/wk_IAYz2WK1th0cMLmL.py index 5b0b0070812c..baaae3e5b836 100644 --- a/homeassistant/components/tuya/tuya_device_handlers/devices/wk/wk_IAYz2WK1th0cMLmL.py +++ b/homeassistant/components/tuya/tuya_device_handlers/devices/wk/wk_IAYz2WK1th0cMLmL.py @@ -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, diff --git a/homeassistant/components/tuya/tuya_device_handlers/utils.py b/homeassistant/components/tuya/tuya_device_handlers/utils.py index 0f2dc9976b4a..d57f4689c1a2 100644 --- a/homeassistant/components/tuya/tuya_device_handlers/utils.py +++ b/homeassistant/components/tuya/tuya_device_handlers/utils.py @@ -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. diff --git a/tests/components/tuya/test_quirks.py b/tests/components/tuya/test_quirks.py index 8e805e0f3e9a..f1cdbff9af23 100644 --- a/tests/components/tuya/test_quirks.py +++ b/tests/components/tuya/test_quirks.py @@ -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