mirror of
https://github.com/home-assistant/core.git
synced 2026-09-27 01:46:11 -04:00
Improve
This commit is contained in:
@@ -26,8 +26,9 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from . import TuyaConfigEntry
|
||||
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, DPType
|
||||
from .entity import TuyaEntity
|
||||
from .models import IntegerTypeData
|
||||
from .models import IntegerTypeData, StateConversionFunction
|
||||
from .util import get_dpcode
|
||||
from .xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY, TuyaClimateDefinition
|
||||
|
||||
TUYA_HVAC_TO_HA = {
|
||||
"auto": HVACMode.HEAT_COOL,
|
||||
@@ -46,6 +47,9 @@ class TuyaClimateEntityDescription(ClimateEntityDescription):
|
||||
"""Describe an Tuya climate entity."""
|
||||
|
||||
switch_only_hvac_mode: HVACMode
|
||||
current_temperature_state_conversion: StateConversionFunction | None = None
|
||||
target_temperature_state_conversion: StateConversionFunction | None = None
|
||||
target_temperature_command_conversion: StateConversionFunction | None = None
|
||||
|
||||
|
||||
CLIMATE_DESCRIPTIONS: dict[DeviceCategory, TuyaClimateEntityDescription] = {
|
||||
@@ -76,6 +80,18 @@ CLIMATE_DESCRIPTIONS: dict[DeviceCategory, TuyaClimateEntityDescription] = {
|
||||
}
|
||||
|
||||
|
||||
def _create_quirk_description(
|
||||
definition: TuyaClimateDefinition,
|
||||
) -> TuyaClimateEntityDescription:
|
||||
return TuyaClimateEntityDescription(
|
||||
key=definition.key,
|
||||
switch_only_hvac_mode=HVACMode(definition.switch_only_hvac_mode),
|
||||
current_temperature_state_conversion=definition.current_temperature_state_conversion,
|
||||
target_temperature_state_conversion=definition.target_temperature_state_conversion,
|
||||
target_temperature_command_conversion=definition.target_temperature_command_conversion,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: TuyaConfigEntry,
|
||||
@@ -90,7 +106,17 @@ async def async_setup_entry(
|
||||
entities: list[TuyaClimateEntity] = []
|
||||
for device_id in device_ids:
|
||||
device = manager.device_map[device_id]
|
||||
if device and device.category in CLIMATE_DESCRIPTIONS:
|
||||
if quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device):
|
||||
entities.extend(
|
||||
TuyaClimateEntity(
|
||||
device,
|
||||
manager,
|
||||
_create_quirk_description(definition),
|
||||
hass.config.units.temperature_unit,
|
||||
)
|
||||
for definition in quirk.climate_definitions
|
||||
)
|
||||
elif device.category in CLIMATE_DESCRIPTIONS:
|
||||
entities.append(
|
||||
TuyaClimateEntity(
|
||||
device,
|
||||
@@ -347,13 +373,20 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity):
|
||||
# guarded by ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
assert self._set_temperature is not None
|
||||
|
||||
if convert := self.entity_description.target_temperature_command_conversion:
|
||||
value = convert(
|
||||
self.device, self._current_temperature, kwargs[ATTR_TEMPERATURE]
|
||||
)
|
||||
else:
|
||||
value = round(
|
||||
self._set_temperature.scale_value_back(kwargs[ATTR_TEMPERATURE])
|
||||
)
|
||||
|
||||
self._send_command(
|
||||
[
|
||||
{
|
||||
"code": self._set_temperature.dpcode,
|
||||
"value": round(
|
||||
self._set_temperature.scale_value_back(kwargs[ATTR_TEMPERATURE])
|
||||
),
|
||||
"value": value,
|
||||
}
|
||||
]
|
||||
)
|
||||
@@ -368,6 +401,9 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity):
|
||||
if temperature is None:
|
||||
return None
|
||||
|
||||
if convert := self.entity_description.current_temperature_state_conversion:
|
||||
return convert(self.device, self._current_temperature, temperature)
|
||||
|
||||
if self._current_temperature.scale == 0 and self._current_temperature.step != 1:
|
||||
# The current temperature can have a scale of 0 or 1 and is used for
|
||||
# rounding, Home Assistant doesn't need to round but we will always
|
||||
@@ -399,6 +435,9 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity):
|
||||
if temperature is None:
|
||||
return None
|
||||
|
||||
if convert := self.entity_description.target_temperature_state_conversion:
|
||||
return convert(self.device, self._set_temperature, temperature)
|
||||
|
||||
return self._set_temperature.scale_value(temperature)
|
||||
|
||||
@property
|
||||
|
||||
@@ -3,14 +3,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import json
|
||||
import struct
|
||||
from typing import Self
|
||||
from typing import Any, Self
|
||||
|
||||
from tuya_sharing import CustomerDevice
|
||||
|
||||
from .const import DPCode
|
||||
from .util import remap_value
|
||||
|
||||
type StateConversionFunction = Callable[
|
||||
[CustomerDevice, EnumTypeData | IntegerTypeData | None, Any], Any
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class IntegerTypeData:
|
||||
|
||||
@@ -27,6 +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 .xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY, TuyaSwitchDefinition, parse_enum
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -921,6 +922,17 @@ SWITCHES[DeviceCategory.CZ] = SWITCHES[DeviceCategory.PC]
|
||||
SWITCHES[DeviceCategory.DGHSXJ] = SWITCHES[DeviceCategory.SP]
|
||||
|
||||
|
||||
def _create_quirk_description(
|
||||
definition: TuyaSwitchDefinition,
|
||||
) -> SwitchEntityDescription:
|
||||
return SwitchEntityDescription(
|
||||
key=DPCode(definition.key),
|
||||
translation_key=definition.translation_key,
|
||||
device_class=parse_enum(SwitchDeviceClass, definition.device_class),
|
||||
entity_category=parse_enum(EntityCategory, definition.entity_category),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: TuyaConfigEntry,
|
||||
@@ -936,7 +948,15 @@ async def async_setup_entry(
|
||||
entities: list[TuyaSwitchEntity] = []
|
||||
for device_id in device_ids:
|
||||
device = manager.device_map[device_id]
|
||||
if descriptions := SWITCHES.get(device.category):
|
||||
if quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device):
|
||||
entities.extend(
|
||||
TuyaSwitchEntity(
|
||||
device, manager, _create_quirk_description(definition)
|
||||
)
|
||||
for definition in quirk.switch_definitions
|
||||
if definition.key in device.status
|
||||
)
|
||||
elif descriptions := SWITCHES.get(device.category):
|
||||
entities.extend(
|
||||
TuyaSwitchEntity(device, manager, description)
|
||||
for description in descriptions
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ..xternal_tuya_quirks import (
|
||||
TUYA_QUIRKS_REGISTRY,
|
||||
TuyaCoverDeviceClass,
|
||||
TuyaDeviceQuirk,
|
||||
TuyaEntityCategory,
|
||||
)
|
||||
from ..const import DPCode
|
||||
from ..xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY, TuyaDeviceQuirk
|
||||
from ..xternal_tuya_quirks.homeassistant import TuyaCoverDeviceClass, TuyaEntityCategory
|
||||
|
||||
(
|
||||
# This model has percent_state and percent_control but percent_state never
|
||||
@@ -15,16 +12,16 @@ from ..xternal_tuya_quirks import (
|
||||
TuyaDeviceQuirk()
|
||||
.applies_to(category="cl", product_id="g1cp07dsqnbdbbki")
|
||||
.add_cover(
|
||||
key="control",
|
||||
key=DPCode.CONTROL,
|
||||
translation_key="curtain",
|
||||
translation_string="[%key:component::cover::entity_component::curtain::name%]",
|
||||
current_state_dp_code="control",
|
||||
current_position_dp_code="percent_control",
|
||||
set_position_dp_code="percent_control",
|
||||
current_state_dp_code=DPCode.CONTROL,
|
||||
current_position_dp_code=DPCode.PERCENT_CONTROL,
|
||||
set_position_dp_code=DPCode.PERCENT_CONTROL,
|
||||
device_class=TuyaCoverDeviceClass.CURTAIN,
|
||||
)
|
||||
.add_select(
|
||||
key="control_back_mode",
|
||||
key=DPCode.CONTROL_BACK_MODE,
|
||||
translation_key="curtain_motor_mode",
|
||||
translation_string="Motor mode",
|
||||
entity_category=TuyaEntityCategory.CONFIG,
|
||||
@@ -38,21 +35,21 @@ from ..xternal_tuya_quirks import (
|
||||
TuyaDeviceQuirk()
|
||||
.applies_to(category="cl", product_id="lfkr93x0ukp5gaia")
|
||||
.add_cover(
|
||||
key="control",
|
||||
key=DPCode.CONTROL,
|
||||
translation_key="curtain",
|
||||
translation_string="[%key:component::cover::entity_component::curtain::name%]",
|
||||
current_state_dp_code="control",
|
||||
current_state_dp_code=DPCode.CONTROL,
|
||||
device_class=TuyaCoverDeviceClass.CURTAIN,
|
||||
)
|
||||
.add_select(
|
||||
key="control_back_mode",
|
||||
key=DPCode.CONTROL_BACK_MODE,
|
||||
translation_key="curtain_motor_mode",
|
||||
translation_string="Motor mode",
|
||||
entity_category=TuyaEntityCategory.CONFIG,
|
||||
state_translations={"forward": "Forward", "back": "Back"},
|
||||
)
|
||||
.add_sensor(
|
||||
key="time_total",
|
||||
key=DPCode.TIME_TOTAL,
|
||||
translation_key="last_operation_duration",
|
||||
translation_string="Last operation duration",
|
||||
entity_category=TuyaEntityCategory.DIAGNOSTIC,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Quirks for Tuya."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from tuya_sharing import CustomerDevice
|
||||
|
||||
from ..const import DPCode
|
||||
from ..models import EnumTypeData, IntegerTypeData
|
||||
from ..xternal_tuya_quirks import TUYA_QUIRKS_REGISTRY, TuyaDeviceQuirk
|
||||
from ..xternal_tuya_quirks.homeassistant import TuyaClimateHVACMode, TuyaEntityCategory
|
||||
from ..xternal_tuya_quirks.utils import scale_value, scale_value_back
|
||||
|
||||
|
||||
def _scale_value_force_scale_1(
|
||||
_device: CustomerDevice, dptype: EnumTypeData | IntegerTypeData | None, value: Any
|
||||
) -> float:
|
||||
"""Scale value to scale 1."""
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(dptype, IntegerTypeData)
|
||||
assert isinstance(value, int)
|
||||
return scale_value(value, dptype.step, 1)
|
||||
|
||||
|
||||
def _scale_value_back_force_scale_1(
|
||||
_device: CustomerDevice, dptype: EnumTypeData | IntegerTypeData | None, value: Any
|
||||
) -> int:
|
||||
"""Unscale value to scale 1."""
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(dptype, IntegerTypeData)
|
||||
assert isinstance(value, float)
|
||||
return scale_value_back(value, dptype.step, 1)
|
||||
|
||||
|
||||
(
|
||||
TuyaDeviceQuirk()
|
||||
.applies_to(category="wk", product_id="IAYz2WK1th0cMLmL")
|
||||
.add_climate(
|
||||
key="wk",
|
||||
switch_only_hvac_mode=TuyaClimateHVACMode.HEAT_COOL,
|
||||
current_temperature_state_conversion=_scale_value_force_scale_1,
|
||||
target_temperature_state_conversion=_scale_value_force_scale_1,
|
||||
target_temperature_command_conversion=_scale_value_back_force_scale_1,
|
||||
)
|
||||
.add_switch(
|
||||
key=DPCode.CHILD_LOCK,
|
||||
translation_key="child_lock",
|
||||
translation_string="Child lock",
|
||||
entity_category=TuyaEntityCategory.CONFIG,
|
||||
)
|
||||
.register(TUYA_QUIRKS_REGISTRY)
|
||||
)
|
||||
@@ -3,29 +3,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .device_quirk import (
|
||||
TuyaClimateDefinition,
|
||||
TuyaCoverDefinition,
|
||||
TuyaDeviceQuirk,
|
||||
TuyaSelectDefinition,
|
||||
TuyaSensorDefinition,
|
||||
TuyaSwitchDefinition,
|
||||
)
|
||||
from .homeassistant import (
|
||||
TuyaCoverDeviceClass,
|
||||
TuyaEntityCategory,
|
||||
TuyaSensorDeviceClass,
|
||||
parse_enum,
|
||||
)
|
||||
from .homeassistant import parse_enum
|
||||
from .registry import QuirksRegistry
|
||||
|
||||
__all__ = [
|
||||
"TUYA_QUIRKS_REGISTRY",
|
||||
"QuirksRegistry",
|
||||
"TuyaClimateDefinition",
|
||||
"TuyaCoverDefinition",
|
||||
"TuyaCoverDeviceClass",
|
||||
"TuyaDeviceQuirk",
|
||||
"TuyaEntityCategory",
|
||||
"TuyaSelectDefinition",
|
||||
"TuyaSensorDefinition",
|
||||
"TuyaSensorDeviceClass",
|
||||
"TuyaSwitchDefinition",
|
||||
"parse_enum",
|
||||
]
|
||||
|
||||
|
||||
@@ -7,7 +7,10 @@ import inspect
|
||||
import pathlib
|
||||
from typing import TYPE_CHECKING, Self
|
||||
|
||||
from ..const import DPCode
|
||||
from ..models import StateConversionFunction
|
||||
from .homeassistant import (
|
||||
TuyaClimateHVACMode,
|
||||
TuyaCoverDeviceClass,
|
||||
TuyaEntityCategory,
|
||||
TuyaSensorDeviceClass,
|
||||
@@ -22,13 +25,24 @@ class BaseTuyaDefinition:
|
||||
"""Definition for a Tuya entity."""
|
||||
|
||||
key: str
|
||||
translation_key: str
|
||||
translation_string: str
|
||||
translation_key: str | None = None
|
||||
translation_string: str | None = None
|
||||
state_translations: dict[str, str] | None = None
|
||||
device_class: str | None = None
|
||||
entity_category: str | None = None
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class TuyaClimateDefinition(BaseTuyaDefinition):
|
||||
"""Definition for a climate entity."""
|
||||
|
||||
switch_only_hvac_mode: TuyaClimateHVACMode
|
||||
|
||||
current_temperature_state_conversion: StateConversionFunction | None = None
|
||||
target_temperature_state_conversion: StateConversionFunction | None = None
|
||||
target_temperature_command_conversion: StateConversionFunction | None = None
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class TuyaCoverDefinition(BaseTuyaDefinition):
|
||||
"""Definition for a cover entity."""
|
||||
@@ -52,20 +66,22 @@ class TuyaSensorDefinition(BaseTuyaDefinition):
|
||||
device_class: TuyaSensorDeviceClass | None = None
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class TuyaSwitchDefinition(BaseTuyaDefinition):
|
||||
"""Definition for a switch entity."""
|
||||
|
||||
|
||||
class TuyaDeviceQuirk:
|
||||
"""Quirk for Tuya device."""
|
||||
|
||||
_applies_to: list[tuple[str, str]]
|
||||
cover_definitions: list[TuyaCoverDefinition]
|
||||
select_definitions: list[TuyaSelectDefinition]
|
||||
sensor_definitions: list[TuyaSensorDefinition]
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the quirk."""
|
||||
self._applies_to = []
|
||||
self.cover_definitions = []
|
||||
self.select_definitions = []
|
||||
self.sensor_definitions = []
|
||||
self._applies_to: list[tuple[str, str]] = []
|
||||
self.cover_definitions: list[TuyaCoverDefinition] = []
|
||||
self.climate_definitions: list[TuyaClimateDefinition] = []
|
||||
self.select_definitions: list[TuyaSelectDefinition] = []
|
||||
self.sensor_definitions: list[TuyaSensorDefinition] = []
|
||||
self.switch_definitions: list[TuyaSwitchDefinition] = []
|
||||
|
||||
current_frame = inspect.currentframe()
|
||||
if TYPE_CHECKING:
|
||||
@@ -86,10 +102,32 @@ class TuyaDeviceQuirk:
|
||||
for category, product_id in self._applies_to:
|
||||
registry.register(category, product_id, self)
|
||||
|
||||
def add_cover(
|
||||
def add_climate(
|
||||
self,
|
||||
*,
|
||||
key: str,
|
||||
# Climate specific
|
||||
switch_only_hvac_mode: TuyaClimateHVACMode,
|
||||
current_temperature_state_conversion: StateConversionFunction | None = None,
|
||||
target_temperature_state_conversion: StateConversionFunction | None = None,
|
||||
target_temperature_command_conversion: StateConversionFunction | None = None,
|
||||
) -> Self:
|
||||
"""Add climate definition."""
|
||||
self.climate_definitions.append(
|
||||
TuyaClimateDefinition(
|
||||
key=key,
|
||||
switch_only_hvac_mode=switch_only_hvac_mode,
|
||||
current_temperature_state_conversion=current_temperature_state_conversion,
|
||||
target_temperature_state_conversion=target_temperature_state_conversion,
|
||||
target_temperature_command_conversion=target_temperature_command_conversion,
|
||||
)
|
||||
)
|
||||
return self
|
||||
|
||||
def add_cover(
|
||||
self,
|
||||
*,
|
||||
key: DPCode,
|
||||
translation_key: str,
|
||||
translation_string: str,
|
||||
device_class: TuyaCoverDeviceClass | None = None,
|
||||
@@ -115,7 +153,7 @@ class TuyaDeviceQuirk:
|
||||
def add_select(
|
||||
self,
|
||||
*,
|
||||
key: str,
|
||||
key: DPCode,
|
||||
translation_key: str,
|
||||
translation_string: str,
|
||||
entity_category: TuyaEntityCategory | None = None,
|
||||
@@ -137,7 +175,7 @@ class TuyaDeviceQuirk:
|
||||
def add_sensor(
|
||||
self,
|
||||
*,
|
||||
key: str,
|
||||
key: DPCode,
|
||||
translation_key: str,
|
||||
translation_string: str,
|
||||
device_class: TuyaSensorDeviceClass | None = None,
|
||||
@@ -155,3 +193,23 @@ class TuyaDeviceQuirk:
|
||||
)
|
||||
)
|
||||
return self
|
||||
|
||||
def add_switch(
|
||||
self,
|
||||
*,
|
||||
key: DPCode,
|
||||
translation_key: str,
|
||||
translation_string: str,
|
||||
entity_category: TuyaEntityCategory | None = None,
|
||||
# Switch specific
|
||||
) -> Self:
|
||||
"""Add switch definition."""
|
||||
self.switch_definitions.append(
|
||||
TuyaSwitchDefinition(
|
||||
key=key,
|
||||
translation_key=translation_key,
|
||||
translation_string=translation_string,
|
||||
entity_category=entity_category,
|
||||
)
|
||||
)
|
||||
return self
|
||||
|
||||
@@ -31,6 +31,32 @@ class TuyaEntityCategory(StrEnum):
|
||||
DIAGNOSTIC = "diagnostic"
|
||||
|
||||
|
||||
class TuyaClimateHVACMode(StrEnum):
|
||||
"""HVAC mode for climate devices."""
|
||||
|
||||
# All activity disabled / Device is off/standby
|
||||
OFF = "off"
|
||||
|
||||
# Heating
|
||||
HEAT = "heat"
|
||||
|
||||
# Cooling
|
||||
COOL = "cool"
|
||||
|
||||
# The device supports heating/cooling to a range
|
||||
HEAT_COOL = "heat_cool"
|
||||
|
||||
# The temperature is set based on a schedule, learned behavior, AI or some
|
||||
# other related mechanism. User is not able to adjust the temperature
|
||||
AUTO = "auto"
|
||||
|
||||
# Device is in Dry/Humidity mode
|
||||
DRY = "dry"
|
||||
|
||||
# Only the fan is on, not fan and another mode like cool
|
||||
FAN_ONLY = "fan_only"
|
||||
|
||||
|
||||
class TuyaCoverDeviceClass(StrEnum):
|
||||
"""Device class for cover."""
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Common utility functions for Tuya quirks."""
|
||||
|
||||
|
||||
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)
|
||||
@@ -419,7 +419,7 @@
|
||||
# name: test_platform_setup_and_discovery[climate.el_termostato_de_la_cocina-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_temperature': 4.5,
|
||||
'current_temperature': 22.5,
|
||||
'friendly_name': 'El termostato de la cocina',
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
@@ -429,7 +429,7 @@
|
||||
'min_temp': 1.0,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
'target_temp_step': 0.5,
|
||||
'temperature': 4.6,
|
||||
'temperature': 23.0,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'climate.el_termostato_de_la_cocina',
|
||||
|
||||
@@ -45,11 +45,12 @@ def _validate_quirk_platform(
|
||||
definitions: list[BaseTuyaDefinition] = getattr(quirk, f"{platform}_definitions")
|
||||
for definition in definitions:
|
||||
# Validate entity translations exist in strings.json.
|
||||
full_key = f"entity.{platform}.{definition.translation_key}.name"
|
||||
assert definition.translation_string == strings.get(full_key), (
|
||||
f"Incorrect or missing translation string for {full_key} in "
|
||||
"homeassistant/components/tuya/strings.json"
|
||||
)
|
||||
if definition.translation_key:
|
||||
full_key = f"entity.{platform}.{definition.translation_key}.name"
|
||||
assert definition.translation_string == strings.get(full_key), (
|
||||
f"Incorrect or missing translation string for {full_key} in "
|
||||
"homeassistant/components/tuya/strings.json"
|
||||
)
|
||||
# Validate entity state translations exist in strings.json.
|
||||
if state_translations := definition.state_translations:
|
||||
for state, state_translation in state_translations.items():
|
||||
|
||||
Reference in New Issue
Block a user