diff --git a/homeassistant/components/litterrobot/coordinator.py b/homeassistant/components/litterrobot/coordinator.py index c99d4794ff623..581257ab2dbb1 100644 --- a/homeassistant/components/litterrobot/coordinator.py +++ b/homeassistant/components/litterrobot/coordinator.py @@ -48,6 +48,9 @@ async def _async_update_data(self) -> None: """Update all device states from the Litter-Robot API.""" await self.account.refresh_robots() await self.account.load_pets() + for pet in self.account.pets: + # Need to fetch weight history for `get_visits_since` + await pet.fetch_weight_history() async def _async_setup(self) -> None: """Set up the coordinator.""" diff --git a/homeassistant/components/litterrobot/icons.json b/homeassistant/components/litterrobot/icons.json index 2e0cafe43d97b..86a95b59b1802 100644 --- a/homeassistant/components/litterrobot/icons.json +++ b/homeassistant/components/litterrobot/icons.json @@ -49,6 +49,9 @@ }, "total_cycles": { "default": "mdi:counter" + }, + "visits_today": { + "default": "mdi:counter" } }, "switch": { diff --git a/homeassistant/components/litterrobot/sensor.py b/homeassistant/components/litterrobot/sensor.py index b7ddf3c3249ba..aa7c3a451be15 100644 --- a/homeassistant/components/litterrobot/sensor.py +++ b/homeassistant/components/litterrobot/sensor.py @@ -18,6 +18,7 @@ from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfMass from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback +from homeassistant.util import dt as dt_util from .coordinator import LitterRobotConfigEntry from .entity import LitterRobotEntity, _WhiskerEntityT @@ -39,6 +40,7 @@ class RobotSensorEntityDescription(SensorEntityDescription, Generic[_WhiskerEnti """A class that describes robot sensor entities.""" icon_fn: Callable[[Any], str | None] = lambda _: None + last_reset_fn: Callable[[], datetime | None] = lambda: None value_fn: Callable[[_WhiskerEntityT], float | datetime | str | None] @@ -179,7 +181,14 @@ class RobotSensorEntityDescription(SensorEntityDescription, Generic[_WhiskerEnti native_unit_of_measurement=UnitOfMass.POUNDS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda pet: pet.weight, - ) + ), + RobotSensorEntityDescription[Pet]( + key="visits_today", + translation_key="visits_today", + state_class=SensorStateClass.TOTAL, + last_reset_fn=dt_util.start_of_local_day, + value_fn=lambda pet: pet.get_visits_since(dt_util.start_of_local_day()), + ), ] @@ -225,3 +234,8 @@ def icon(self) -> str | None: if (icon := self.entity_description.icon_fn(self.state)) is not None: return icon return super().icon + + @property + def last_reset(self) -> datetime | None: + """Return the time when the sensor was last reset, if any.""" + return self.entity_description.last_reset_fn() or super().last_reset diff --git a/homeassistant/components/litterrobot/strings.json b/homeassistant/components/litterrobot/strings.json index 160f5edb6a0dc..35aff0f91051c 100644 --- a/homeassistant/components/litterrobot/strings.json +++ b/homeassistant/components/litterrobot/strings.json @@ -122,6 +122,10 @@ "name": "Total cycles", "unit_of_measurement": "cycles" }, + "visits_today": { + "name": "Visits today", + "unit_of_measurement": "visits" + }, "waste_drawer": { "name": "Waste drawer" } diff --git a/homeassistant/components/miele/config_flow.py b/homeassistant/components/miele/config_flow.py index d3c7dbba12b71..191cd9a0454e0 100644 --- a/homeassistant/components/miele/config_flow.py +++ b/homeassistant/components/miele/config_flow.py @@ -26,14 +26,6 @@ def logger(self) -> logging.Logger: """Return logger.""" return logging.getLogger(__name__) - @property - def extra_authorize_data(self) -> dict: - """Extra data that needs to be appended to the authorize url.""" - # "vg" is mandatory but the value doesn't seem to matter - return { - "vg": "sv-SE", - } - async def async_step_reauth( self, entry_data: Mapping[str, Any] ) -> ConfigFlowResult: diff --git a/homeassistant/components/rainmachine/strings.json b/homeassistant/components/rainmachine/strings.json index aad61458e88a4..49731df5b6f1c 100644 --- a/homeassistant/components/rainmachine/strings.json +++ b/homeassistant/components/rainmachine/strings.json @@ -196,12 +196,12 @@ "description": "UNIX timestamp for the weather data. If omitted, the RainMachine device's local time at the time of the call is used." }, "mintemp": { - "name": "Min temp", - "description": "Minimum temperature (°C)." + "name": "Min temperature", + "description": "Minimum temperature in current period (°C)." }, "maxtemp": { - "name": "Max temp", - "description": "Maximum temperature (°C)." + "name": "Max temperature", + "description": "Maximum temperature in current period (°C)." }, "temperature": { "name": "Temperature", @@ -209,11 +209,11 @@ }, "wind": { "name": "Wind speed", - "description": "Wind speed (m/s)." + "description": "Current wind speed (m/s)." }, "solarrad": { "name": "Solar radiation", - "description": "Solar radiation (MJ/m²/h)." + "description": "Current solar radiation (MJ/m²/h)." }, "et": { "name": "Evapotranspiration", @@ -229,11 +229,11 @@ }, "minrh": { "name": "Min relative humidity", - "description": "Min relative humidity (%RH)." + "description": "Minimum relative humidity in current period (%RH)." }, "maxrh": { "name": "Max relative humidity", - "description": "Max relative humidity (%RH)." + "description": "Maximum relative humidity in current period (%RH)." }, "condition": { "name": "Weather condition code", @@ -241,11 +241,11 @@ }, "pressure": { "name": "Barametric pressure", - "description": "Barametric pressure (kPa)." + "description": "Current barametric pressure (kPa)." }, "dewpoint": { "name": "Dew point", - "description": "Dew point (°C)." + "description": "Current dew point (°C)." } } }, diff --git a/homeassistant/components/tuya/alarm_control_panel.py b/homeassistant/components/tuya/alarm_control_panel.py index 4972fe8833978..61985fb7622fb 100644 --- a/homeassistant/components/tuya/alarm_control_panel.py +++ b/homeassistant/components/tuya/alarm_control_panel.py @@ -20,7 +20,8 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import EnumTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import EnumTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/binary_sensor.py b/homeassistant/components/tuya/binary_sensor.py index a613661149fde..4fef11a733578 100644 --- a/homeassistant/components/tuya/binary_sensor.py +++ b/homeassistant/components/tuya/binary_sensor.py @@ -15,9 +15,10 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback +from homeassistant.util.json import json_loads from . import TuyaConfigEntry -from .const import TUYA_DISCOVERY_NEW, DPCode +from .const import TUYA_DISCOVERY_NEW, DPCode, DPType from .entity import TuyaEntity @@ -31,6 +32,9 @@ class TuyaBinarySensorEntityDescription(BinarySensorEntityDescription): # Value or values to consider binary sensor to be "on" on_value: bool | float | int | str | set[bool | float | int | str] = True + # For DPType.BITMAP, the bitmap_key is used to extract the bit mask + bitmap_key: str | None = None + # Commonly used sensors TAMPER_BINARY_SENSOR = TuyaBinarySensorEntityDescription( @@ -71,6 +75,34 @@ class TuyaBinarySensorEntityDescription(BinarySensorEntityDescription): ), TAMPER_BINARY_SENSOR, ), + # Dehumidifier + # https://developer.tuya.com/en/docs/iot/categorycs?id=Kaiuz1vcz4dha + "cs": ( + TuyaBinarySensorEntityDescription( + key="tankfull", + dpcode=DPCode.FAULT, + device_class=BinarySensorDeviceClass.PROBLEM, + entity_category=EntityCategory.DIAGNOSTIC, + bitmap_key="tankfull", + translation_key="tankfull", + ), + TuyaBinarySensorEntityDescription( + key="defrost", + dpcode=DPCode.FAULT, + device_class=BinarySensorDeviceClass.PROBLEM, + entity_category=EntityCategory.DIAGNOSTIC, + bitmap_key="defrost", + translation_key="defrost", + ), + TuyaBinarySensorEntityDescription( + key="wet", + dpcode=DPCode.FAULT, + device_class=BinarySensorDeviceClass.PROBLEM, + entity_category=EntityCategory.DIAGNOSTIC, + bitmap_key="wet", + translation_key="wet", + ), + ), # Smart Pet Feeder # https://developer.tuya.com/en/docs/iot/categorycwwsq?id=Kaiuz2b6vydld "cwwsq": ( @@ -343,6 +375,22 @@ class TuyaBinarySensorEntityDescription(BinarySensorEntityDescription): } +def _get_bitmap_bit_mask( + device: CustomerDevice, dpcode: str, bitmap_key: str | None +) -> int | None: + """Get the bit mask for a given bitmap description.""" + if ( + bitmap_key is None + or (status_range := device.status_range.get(dpcode)) is None + or status_range.type != DPType.BITMAP + or not isinstance(bitmap_values := json_loads(status_range.values), dict) + or not isinstance(bitmap_labels := bitmap_values.get("label"), list) + or bitmap_key not in bitmap_labels + ): + return None + return bitmap_labels.index(bitmap_key) + + async def async_setup_entry( hass: HomeAssistant, entry: TuyaConfigEntry, @@ -361,12 +409,23 @@ def async_discover_device(device_ids: list[str]) -> None: for description in descriptions: dpcode = description.dpcode or description.key if dpcode in device.status: - entities.append( - TuyaBinarySensorEntity( - device, hass_data.manager, description - ) + mask = _get_bitmap_bit_mask( + device, dpcode, description.bitmap_key ) + if ( + description.bitmap_key is None # Regular binary sensor + or mask is not None # Bitmap sensor with valid mask + ): + entities.append( + TuyaBinarySensorEntity( + device, + hass_data.manager, + description, + mask, + ) + ) + async_add_entities(entities) async_discover_device([*hass_data.manager.device_map]) @@ -386,11 +445,13 @@ def __init__( device: CustomerDevice, device_manager: Manager, description: TuyaBinarySensorEntityDescription, + bit_mask: int | None = None, ) -> None: """Init Tuya binary sensor.""" super().__init__(device, device_manager) self.entity_description = description self._attr_unique_id = f"{super().unique_id}{description.key}" + self._bit_mask = bit_mask @property def is_on(self) -> bool: @@ -399,6 +460,10 @@ def is_on(self) -> bool: if dpcode not in self.device.status: return False + if self._bit_mask is not None: + # For bitmap sensors, check the specific bit mask + return (self.device.status[dpcode] & (1 << self._bit_mask)) != 0 + if isinstance(self.entity_description.on_value, set): return self.device.status[dpcode] in self.entity_description.on_value diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 991c3589e120d..734f6ba7f7ac7 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -25,7 +25,8 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData TUYA_HVAC_TO_HA = { "auto": HVACMode.HEAT_COOL, diff --git a/homeassistant/components/tuya/const.py b/homeassistant/components/tuya/const.py index 922aaab193ba2..abf5223175ce1 100644 --- a/homeassistant/components/tuya/const.py +++ b/homeassistant/components/tuya/const.py @@ -82,6 +82,7 @@ class WorkMode(StrEnum): class DPType(StrEnum): """Data point types.""" + BITMAP = "Bitmap" BOOLEAN = "Boolean" ENUM = "Enum" INTEGER = "Integer" diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index 015daae42121c..a385a35d90386 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -21,7 +21,8 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/entity.py b/homeassistant/components/tuya/entity.py index cc258560067fd..fbddfb0ab83b2 100644 --- a/homeassistant/components/tuya/entity.py +++ b/homeassistant/components/tuya/entity.py @@ -2,11 +2,7 @@ from __future__ import annotations -import base64 -from dataclasses import dataclass -import json -import struct -from typing import Any, Literal, Self, overload +from typing import Any, Literal, overload from tuya_sharing import CustomerDevice, Manager @@ -15,11 +11,10 @@ from homeassistant.helpers.entity import Entity from .const import DOMAIN, LOGGER, TUYA_HA_SIGNAL_UPDATE_ENTITY, DPCode, DPType -from .util import remap_value +from .models import EnumTypeData, IntegerTypeData _DPTYPE_MAPPING: dict[str, DPType] = { - "Bitmap": DPType.RAW, - "bitmap": DPType.RAW, + "bitmap": DPType.BITMAP, "bool": DPType.BOOLEAN, "enum": DPType.ENUM, "json": DPType.JSON, @@ -29,118 +24,6 @@ } -@dataclass -class IntegerTypeData: - """Integer Type Data.""" - - dpcode: DPCode - min: int - max: int - scale: float - step: float - unit: str | None = None - type: str | None = None - - @property - def max_scaled(self) -> float: - """Return the max scaled.""" - return self.scale_value(self.max) - - @property - def min_scaled(self) -> float: - """Return the min scaled.""" - return self.scale_value(self.min) - - @property - def step_scaled(self) -> float: - """Return the step scaled.""" - return self.step / (10**self.scale) - - def scale_value(self, value: float) -> float: - """Scale a value.""" - return value / (10**self.scale) - - def scale_value_back(self, value: float) -> int: - """Return raw value for scaled.""" - return int(value * (10**self.scale)) - - def remap_value_to( - self, - value: float, - to_min: float = 0, - to_max: float = 255, - reverse: bool = False, - ) -> float: - """Remap a value from this range to a new range.""" - return remap_value(value, self.min, self.max, to_min, to_max, reverse) - - def remap_value_from( - self, - value: float, - from_min: float = 0, - from_max: float = 255, - reverse: bool = False, - ) -> float: - """Remap a value from its current range to this range.""" - return remap_value(value, from_min, from_max, self.min, self.max, reverse) - - @classmethod - def from_json(cls, dpcode: DPCode, data: str) -> IntegerTypeData | None: - """Load JSON string and return a IntegerTypeData object.""" - if not (parsed := json.loads(data)): - return None - - return cls( - dpcode, - min=int(parsed["min"]), - max=int(parsed["max"]), - scale=float(parsed["scale"]), - step=max(float(parsed["step"]), 1), - unit=parsed.get("unit"), - type=parsed.get("type"), - ) - - -@dataclass -class EnumTypeData: - """Enum Type Data.""" - - dpcode: DPCode - range: list[str] - - @classmethod - def from_json(cls, dpcode: DPCode, data: str) -> EnumTypeData | None: - """Load JSON string and return a EnumTypeData object.""" - if not (parsed := json.loads(data)): - return None - return cls(dpcode, **parsed) - - -@dataclass -class ElectricityTypeData: - """Electricity Type Data.""" - - electriccurrent: str | None = None - power: str | None = None - voltage: str | None = None - - @classmethod - def from_json(cls, data: str) -> Self: - """Load JSON string and return a ElectricityTypeData object.""" - return cls(**json.loads(data.lower())) - - @classmethod - def from_raw(cls, data: str) -> Self: - """Decode base64 string and return a ElectricityTypeData object.""" - raw = base64.b64decode(data) - voltage = struct.unpack(">H", raw[0:2])[0] / 10.0 - electriccurrent = struct.unpack(">L", b"\x00" + raw[2:5])[0] / 1000.0 - power = struct.unpack(">L", b"\x00" + raw[5:8])[0] / 1000.0 - return cls( - electriccurrent=str(electriccurrent), power=str(power), voltage=str(voltage) - ) - - class TuyaEntity(Entity): """Tuya base device.""" diff --git a/homeassistant/components/tuya/fan.py b/homeassistant/components/tuya/fan.py index f2d856b6d860b..f96ea2c0a65f2 100644 --- a/homeassistant/components/tuya/fan.py +++ b/homeassistant/components/tuya/fan.py @@ -22,7 +22,8 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import EnumTypeData, IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import EnumTypeData, IntegerTypeData TUYA_SUPPORT_TYPE = { "cs", # Dehumidifier diff --git a/homeassistant/components/tuya/humidifier.py b/homeassistant/components/tuya/humidifier.py index f8fd9237ffc31..6539d98e9d8f1 100644 --- a/homeassistant/components/tuya/humidifier.py +++ b/homeassistant/components/tuya/humidifier.py @@ -19,7 +19,8 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/light.py b/homeassistant/components/tuya/light.py index 37c79b952d46a..3f8fc7d0fb976 100644 --- a/homeassistant/components/tuya/light.py +++ b/homeassistant/components/tuya/light.py @@ -25,7 +25,8 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType, WorkMode -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData from .util import remap_value diff --git a/homeassistant/components/tuya/models.py b/homeassistant/components/tuya/models.py new file mode 100644 index 0000000000000..b4afca83a8549 --- /dev/null +++ b/homeassistant/components/tuya/models.py @@ -0,0 +1,124 @@ +"""Tuya Home Assistant Base Device Model.""" + +from __future__ import annotations + +import base64 +from dataclasses import dataclass +import json +import struct +from typing import Self + +from .const import DPCode +from .util import remap_value + + +@dataclass +class IntegerTypeData: + """Integer Type Data.""" + + dpcode: DPCode + min: int + max: int + scale: float + step: float + unit: str | None = None + type: str | None = None + + @property + def max_scaled(self) -> float: + """Return the max scaled.""" + return self.scale_value(self.max) + + @property + def min_scaled(self) -> float: + """Return the min scaled.""" + return self.scale_value(self.min) + + @property + def step_scaled(self) -> float: + """Return the step scaled.""" + return self.step / (10**self.scale) + + def scale_value(self, value: float) -> float: + """Scale a value.""" + return value / (10**self.scale) + + def scale_value_back(self, value: float) -> int: + """Return raw value for scaled.""" + return int(value * (10**self.scale)) + + def remap_value_to( + self, + value: float, + to_min: float = 0, + to_max: float = 255, + reverse: bool = False, + ) -> float: + """Remap a value from this range to a new range.""" + return remap_value(value, self.min, self.max, to_min, to_max, reverse) + + def remap_value_from( + self, + value: float, + from_min: float = 0, + from_max: float = 255, + reverse: bool = False, + ) -> float: + """Remap a value from its current range to this range.""" + return remap_value(value, from_min, from_max, self.min, self.max, reverse) + + @classmethod + def from_json(cls, dpcode: DPCode, data: str) -> IntegerTypeData | None: + """Load JSON string and return a IntegerTypeData object.""" + if not (parsed := json.loads(data)): + return None + + return cls( + dpcode, + min=int(parsed["min"]), + max=int(parsed["max"]), + scale=float(parsed["scale"]), + step=max(float(parsed["step"]), 1), + unit=parsed.get("unit"), + type=parsed.get("type"), + ) + + +@dataclass +class EnumTypeData: + """Enum Type Data.""" + + dpcode: DPCode + range: list[str] + + @classmethod + def from_json(cls, dpcode: DPCode, data: str) -> EnumTypeData | None: + """Load JSON string and return a EnumTypeData object.""" + if not (parsed := json.loads(data)): + return None + return cls(dpcode, **parsed) + + +@dataclass +class ElectricityTypeData: + """Electricity Type Data.""" + + electriccurrent: str | None = None + power: str | None = None + voltage: str | None = None + + @classmethod + def from_json(cls, data: str) -> Self: + """Load JSON string and return a ElectricityTypeData object.""" + return cls(**json.loads(data.lower())) + + @classmethod + def from_raw(cls, data: str) -> Self: + """Decode base64 string and return a ElectricityTypeData object.""" + raw = base64.b64decode(data) + voltage = struct.unpack(">H", raw[0:2])[0] / 10.0 + electriccurrent = struct.unpack(">L", b"\x00" + raw[2:5])[0] / 1000.0 + power = struct.unpack(">L", b"\x00" + raw[5:8])[0] / 1000.0 + return cls( + electriccurrent=str(electriccurrent), power=str(power), voltage=str(voltage) + ) diff --git a/homeassistant/components/tuya/number.py b/homeassistant/components/tuya/number.py index ddee46b8799e6..b5b8437ea8ba8 100644 --- a/homeassistant/components/tuya/number.py +++ b/homeassistant/components/tuya/number.py @@ -16,7 +16,8 @@ from . import TuyaConfigEntry from .const import DEVICE_CLASS_UNITS, DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import IntegerTypeData # All descriptions can be found here. Mostly the Integer data types in the # default instructions set of each category end up being a number. diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index 5151f39eb2623..b45b8214bff4f 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -35,7 +35,8 @@ DPType, UnitOfMeasurement, ) -from .entity import ElectricityTypeData, EnumTypeData, IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import ElectricityTypeData, EnumTypeData, IntegerTypeData @dataclass(frozen=True) diff --git a/homeassistant/components/tuya/strings.json b/homeassistant/components/tuya/strings.json index a96f805f2483d..5964be5ce346d 100644 --- a/homeassistant/components/tuya/strings.json +++ b/homeassistant/components/tuya/strings.json @@ -56,6 +56,15 @@ }, "tilt": { "name": "Tilt" + }, + "tankfull": { + "name": "Tank full" + }, + "defrost": { + "name": "Defrost" + }, + "wet": { + "name": "Wet" } }, "button": { diff --git a/homeassistant/components/tuya/vacuum.py b/homeassistant/components/tuya/vacuum.py index f722fd918ca81..d61a624f0270a 100644 --- a/homeassistant/components/tuya/vacuum.py +++ b/homeassistant/components/tuya/vacuum.py @@ -17,7 +17,8 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DPCode, DPType -from .entity import EnumTypeData, IntegerTypeData, TuyaEntity +from .entity import TuyaEntity +from .models import EnumTypeData, IntegerTypeData TUYA_MODE_RETURN_HOME = "chargego" TUYA_STATUS_TO_HA = { diff --git a/homeassistant/components/yolink/manifest.json b/homeassistant/components/yolink/manifest.json index 779b830637b54..89001f98c1646 100644 --- a/homeassistant/components/yolink/manifest.json +++ b/homeassistant/components/yolink/manifest.json @@ -6,5 +6,5 @@ "dependencies": ["auth", "application_credentials"], "documentation": "https://www.home-assistant.io/integrations/yolink", "iot_class": "cloud_push", - "requirements": ["yolink-api==0.5.5"] + "requirements": ["yolink-api==0.5.7"] } diff --git a/homeassistant/components/yolink/sensor.py b/homeassistant/components/yolink/sensor.py index bc32d0eea836f..2845f8ee53305 100644 --- a/homeassistant/components/yolink/sensor.py +++ b/homeassistant/components/yolink/sensor.py @@ -21,6 +21,7 @@ ATTR_DEVICE_POWER_FAILURE_ALARM, ATTR_DEVICE_SIREN, ATTR_DEVICE_SMART_REMOTER, + ATTR_DEVICE_SOIL_TH_SENSOR, ATTR_DEVICE_SWITCH, ATTR_DEVICE_TH_SENSOR, ATTR_DEVICE_THERMOSTAT, @@ -42,6 +43,7 @@ PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, EntityCategory, + UnitOfConductivity, UnitOfEnergy, UnitOfLength, UnitOfPower, @@ -103,6 +105,7 @@ class YoLinkSensorEntityDescription(SensorEntityDescription): ATTR_DEVICE_MANIPULATOR, ATTR_DEVICE_CO_SMOKE_SENSOR, ATTR_GARAGE_DOOR_CONTROLLER, + ATTR_DEVICE_SOIL_TH_SENSOR, ] BATTERY_POWER_SENSOR = [ @@ -122,6 +125,7 @@ class YoLinkSensorEntityDescription(SensorEntityDescription): ATTR_DEVICE_WATER_DEPTH_SENSOR, ATTR_DEVICE_WATER_METER_CONTROLLER, ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER, + ATTR_DEVICE_SOIL_TH_SENSOR, ] MCU_DEV_TEMPERATURE_SENSOR = [ @@ -182,7 +186,7 @@ def cvt_volume(val: int | None) -> str | None: native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, exists_fn=lambda device: ( - device.device_type in [ATTR_DEVICE_TH_SENSOR] + device.device_type in [ATTR_DEVICE_TH_SENSOR, ATTR_DEVICE_SOIL_TH_SENSOR] and device.device_model_name not in NONE_HUMIDITY_SENSOR_MODELS ), ), @@ -191,7 +195,8 @@ def cvt_volume(val: int | None) -> str | None: device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, - exists_fn=lambda device: device.device_type in [ATTR_DEVICE_TH_SENSOR], + exists_fn=lambda device: device.device_type + in [ATTR_DEVICE_TH_SENSOR, ATTR_DEVICE_SOIL_TH_SENSOR], ), # mcu temperature YoLinkSensorEntityDescription( @@ -206,7 +211,7 @@ def cvt_volume(val: int | None) -> str | None: key="loraInfo", device_class=SensorDeviceClass.SIGNAL_STRENGTH, native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - value=lambda value: value["signal"] if value is not None else None, + value=lambda value: value.get("signal") if value is not None else None, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, @@ -302,6 +307,14 @@ def cvt_volume(val: int | None) -> str | None: exists_fn=lambda device: device.device_model_name in POWER_SUPPORT_MODELS, value=lambda value: value / 100 if value is not None else None, ), + YoLinkSensorEntityDescription( + key="conductivity", + device_class=SensorDeviceClass.CONDUCTIVITY, + native_unit_of_measurement=UnitOfConductivity.MICROSIEMENS_PER_CM, + state_class=SensorStateClass.MEASUREMENT, + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_SOIL_TH_SENSOR], + should_update_entity=lambda value: value is not None, + ), ) diff --git a/requirements_all.txt b/requirements_all.txt index 3fae895338662..b4a53c7dba7e8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -3163,7 +3163,7 @@ yeelight==0.7.16 yeelightsunflower==0.0.10 # homeassistant.components.yolink -yolink-api==0.5.5 +yolink-api==0.5.7 # homeassistant.components.youless youless-api==2.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 827b088fdab24..3173b1443b6e1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2610,7 +2610,7 @@ yalexs==8.10.0 yeelight==0.7.16 # homeassistant.components.yolink -yolink-api==0.5.5 +yolink-api==0.5.7 # homeassistant.components.youless youless-api==2.2.0 diff --git a/tests/components/google_assistant_sdk/test_init.py b/tests/components/google_assistant_sdk/test_init.py index 9bb08c802c206..caddf9ba79729 100644 --- a/tests/components/google_assistant_sdk/test_init.py +++ b/tests/components/google_assistant_sdk/test_init.py @@ -116,6 +116,25 @@ async def test_expired_token_refresh_failure( assert entries[0].state is expected_state +@pytest.mark.parametrize("expires_at", [time.time() - 3600], ids=["expired"]) +async def test_setup_client_error( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test setup handling aiohttp.ClientError.""" + aioclient_mock.post( + "https://oauth2.googleapis.com/token", + exc=aiohttp.ClientError, + ) + + await setup_integration() + + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + assert entries[0].state is ConfigEntryState.SETUP_RETRY + + @pytest.mark.parametrize( ("configured_language_code", "expected_language_code"), [("", "en-US"), ("en-US", "en-US"), ("es-ES", "es-ES")], diff --git a/tests/components/litterrobot/common.py b/tests/components/litterrobot/common.py index d96ce06ca598f..19c0c3600ea6b 100644 --- a/tests/components/litterrobot/common.py +++ b/tests/components/litterrobot/common.py @@ -159,6 +159,15 @@ "gender": "FEMALE", "lastWeightReading": 9.1, "breeds": ["sphynx"], + "weightHistory": [ + {"weight": 6.48, "timestamp": "2025-06-13T16:12:36"}, + {"weight": 6.6, "timestamp": "2025-06-14T03:52:00"}, + {"weight": 6.59, "timestamp": "2025-06-14T17:20:32"}, + {"weight": 6.5, "timestamp": "2025-06-14T19:22:48"}, + {"weight": 6.35, "timestamp": "2025-06-15T03:12:15"}, + {"weight": 6.45, "timestamp": "2025-06-15T15:27:21"}, + {"weight": 6.25, "timestamp": "2025-06-15T15:29:26"}, + ], } VACUUM_ENTITY_ID = "vacuum.test_litter_box" diff --git a/tests/components/litterrobot/conftest.py b/tests/components/litterrobot/conftest.py index a6058c75bcab9..aa67db23d8966 100644 --- a/tests/components/litterrobot/conftest.py +++ b/tests/components/litterrobot/conftest.py @@ -52,6 +52,20 @@ def create_mock_robot( return robot +def create_mock_pet( + pet_data: dict | None, + account: Account, + side_effect: Any | None = None, +) -> Pet: + """Create a mock Pet.""" + if not pet_data: + pet_data = {} + + pet = Pet(data={**PET_DATA, **pet_data}, session=account.session) + pet.fetch_weight_history = AsyncMock(side_effect=side_effect) + return pet + + def create_mock_account( robot_data: dict | None = None, side_effect: Any | None = None, @@ -69,7 +83,7 @@ def create_mock_account( if skip_robots else [create_mock_robot(robot_data, account, v4, feeder, side_effect)] ) - account.pets = [Pet(PET_DATA, account.session)] if pet else [] + account.pets = [create_mock_pet(PET_DATA, account, side_effect)] if pet else [] return account diff --git a/tests/components/litterrobot/test_sensor.py b/tests/components/litterrobot/test_sensor.py index 76c567f54179c..d1101a4231d9a 100644 --- a/tests/components/litterrobot/test_sensor.py +++ b/tests/components/litterrobot/test_sensor.py @@ -124,6 +124,16 @@ async def test_pet_weight_sensor( assert sensor.attributes["unit_of_measurement"] == UnitOfMass.POUNDS +@pytest.mark.freeze_time("2025-06-15 12:00:00+00:00") +async def test_pet_visits_today_sensor( + hass: HomeAssistant, mock_account_with_pet: MagicMock +) -> None: + """Tests pet visits today sensors.""" + await setup_integration(hass, mock_account_with_pet, PLATFORM_DOMAIN) + sensor = hass.states.get("sensor.kitty_visits_today") + assert sensor.state == "2" + + async def test_litterhopper_sensor( hass: HomeAssistant, mock_account_with_litterhopper: MagicMock ) -> None: diff --git a/tests/components/miele/test_config_flow.py b/tests/components/miele/test_config_flow.py index bbe5844c1cd85..5ce129b255d9c 100644 --- a/tests/components/miele/test_config_flow.py +++ b/tests/components/miele/test_config_flow.py @@ -46,7 +46,6 @@ async def test_full_flow( f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}" f"&redirect_uri={REDIRECT_URL}" f"&state={state}" - "&vg=sv-SE" ) client = await hass_client_no_auth() @@ -118,7 +117,6 @@ async def test_flow_reauth_abort( f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}" f"&redirect_uri={REDIRECT_URL}" f"&state={state}" - "&vg=sv-SE" ) client = await hass_client_no_auth() @@ -187,7 +185,6 @@ async def test_flow_reconfigure_abort( f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}" f"&redirect_uri={REDIRECT_URL}" f"&state={state}" - "&vg=sv-SE" ) client = await hass_client_no_auth() @@ -247,7 +244,6 @@ async def test_zeroconf_flow( f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}" f"&redirect_uri={REDIRECT_URL}" f"&state={state}" - "&vg=sv-SE" ) client = await hass_client_no_auth() diff --git a/tests/components/tuya/__init__.py b/tests/components/tuya/__init__.py index b308df7e2f934..bf8af8835cf77 100644 --- a/tests/components/tuya/__init__.py +++ b/tests/components/tuya/__init__.py @@ -19,6 +19,7 @@ Platform.LIGHT, ], "cs_arete_two_12l_dehumidifier_air_purifier": [ + Platform.BINARY_SENSOR, Platform.FAN, Platform.HUMIDIFIER, Platform.SELECT, @@ -40,6 +41,15 @@ Platform.SENSOR, Platform.SWITCH, ], + "dlq_earu_electric_eawcpt": [ + # https://github.com/home-assistant/core/issues/102769 + Platform.SENSOR, + Platform.SWITCH, + ], + "dlq_metering_3pn_wifi": [ + # https://github.com/home-assistant/core/issues/143499 + Platform.SENSOR, + ], "kg_smart_valve": [ # https://github.com/home-assistant/core/issues/148347 Platform.SWITCH, @@ -74,6 +84,14 @@ Platform.CLIMATE, Platform.SWITCH, ], + "wsdcg_temperature_humidity": [ + # https://github.com/home-assistant/core/issues/102769 + Platform.SENSOR, + ], + "zndb_smart_meter": [ + # https://github.com/home-assistant/core/issues/138372 + Platform.SENSOR, + ], } diff --git a/tests/components/tuya/conftest.py b/tests/components/tuya/conftest.py index 9aa8e8ea1475a..3d89e1d6f9235 100644 --- a/tests/components/tuya/conftest.py +++ b/tests/components/tuya/conftest.py @@ -143,7 +143,7 @@ async def mock_device(hass: HomeAssistant, mock_device_code: str) -> CustomerDev hass, f"{mock_device_code}.json", DOMAIN ) device = MagicMock(spec=CustomerDevice) - device.id = details["id"] + device.id = details.get("id", "mocked_device_id") device.name = details["name"] device.category = details["category"] device.product_id = details["product_id"] diff --git a/tests/components/tuya/fixtures/dlq_earu_electric_eawcpt.json b/tests/components/tuya/fixtures/dlq_earu_electric_eawcpt.json new file mode 100644 index 0000000000000..32535964a7ead --- /dev/null +++ b/tests/components/tuya/fixtures/dlq_earu_electric_eawcpt.json @@ -0,0 +1,247 @@ +{ + "endpoint": "https://openapi.tuyaeu.com", + "auth_type": 0, + "country_code": "48", + "app_type": "tuyaSmart", + "mqtt_connected": null, + "disabled_by": null, + "disabled_polling": false, + "name": "\u4e00\u8def\u5e26\u8ba1\u91cf\u78c1\u4fdd\u6301\u901a\u65ad\u5668", + "model": "", + "category": "dlq", + "product_id": "0tnvg2xaisqdadcf", + "product_name": "\u4e00\u8def\u5e26\u8ba1\u91cf\u78c1\u4fdd\u6301\u901a\u65ad\u5668", + "online": true, + "sub": false, + "time_zone": "+01:00", + "active_time": "2023-11-25T21:50:37+00:00", + "create_time": "2023-11-25T21:49:06+00:00", + "update_time": "2023-11-28T16:32:28+00:00", + "function": { + "switch": { + "type": "Boolean", + "value": {} + }, + "countdown_1": { + "type": "Integer", + "value": { + "unit": "s", + "min": 0, + "max": 86400, + "scale": 0, + "step": 1 + } + }, + "relay_status": { + "type": "Enum", + "value": { + "range": ["power_off", "power_on", "last"] + } + }, + "light_mode": { + "type": "Enum", + "value": { + "range": ["relay", "pos", "none", "on"] + } + }, + "child_lock": { + "type": "Boolean", + "value": {} + }, + "cycle_time": { + "type": "String", + "value": { + "maxlen": 255 + } + }, + "alarm_set_1": { + "type": "Raw", + "value": {} + }, + "alarm_set_2": { + "type": "Raw", + "value": {} + } + }, + "status_range": { + "switch": { + "type": "Boolean", + "value": {} + }, + "countdown_1": { + "type": "Integer", + "value": { + "unit": "s", + "min": 0, + "max": 86400, + "scale": 0, + "step": 1 + } + }, + "add_ele": { + "type": "Integer", + "value": { + "min": 0, + "max": 50000, + "scale": 3, + "step": 100 + } + }, + "cur_current": { + "type": "Integer", + "value": { + "unit": "mA", + "min": 0, + "max": 100000, + "scale": 0, + "step": 1 + } + }, + "cur_power": { + "type": "Integer", + "value": { + "unit": "W", + "min": 0, + "max": 99999, + "scale": 1, + "step": 1 + } + }, + "cur_voltage": { + "type": "Integer", + "value": { + "unit": "V", + "min": 0, + "max": 5000, + "scale": 1, + "step": 1 + } + }, + "test_bit": { + "type": "Integer", + "value": { + "min": 0, + "max": 5, + "scale": 0, + "step": 1 + } + }, + "voltage_coe": { + "type": "Integer", + "value": { + "min": 0, + "max": 1000000, + "scale": 0, + "step": 1 + } + }, + "electric_coe": { + "type": "Integer", + "value": { + "min": 0, + "max": 1000000, + "scale": 0, + "step": 1 + } + }, + "power_coe": { + "type": "Integer", + "value": { + "min": 0, + "max": 1000000, + "scale": 0, + "step": 1 + } + }, + "electricity_coe": { + "type": "Integer", + "value": { + "min": 0, + "max": 1000000, + "scale": 0, + "step": 1 + } + }, + "fault": { + "type": "Bitmap", + "value": { + "label": [ + "ov_cr", + "ov_vol", + "ov_pwr", + "ls_cr", + "ls_vol", + "ls_pow", + "short_circuit_alarm", + "overload_alarm", + "leakagecurr_alarm", + "self_test_alarm", + "high_temp", + "unbalance_alarm", + "miss_phase_alarm" + ] + } + }, + "relay_status": { + "type": "Enum", + "value": { + "range": ["power_off", "power_on", "last"] + } + }, + "light_mode": { + "type": "Enum", + "value": { + "range": ["relay", "pos", "none", "on"] + } + }, + "child_lock": { + "type": "Boolean", + "value": {} + }, + "cycle_time": { + "type": "String", + "value": { + "maxlen": 255 + } + }, + "temp_value": { + "type": "Integer", + "value": { + "unit": "\u2103", + "min": 0, + "max": 999, + "scale": 0, + "step": 1 + } + }, + "alarm_set_1": { + "type": "Raw", + "value": {} + }, + "alarm_set_2": { + "type": "Raw", + "value": {} + } + }, + "status": { + "switch": true, + "countdown_1": 0, + "add_ele": 100, + "cur_current": 2198, + "cur_power": 4953, + "cur_voltage": 2314, + "test_bit": 2, + "voltage_coe": 0, + "electric_coe": 0, + "power_coe": 0, + "electricity_coe": 0, + "fault": 0, + "relay_status": "last", + "light_mode": "relay", + "child_lock": false, + "cycle_time": "", + "temp_value": 0, + "alarm_set_1": "", + "alarm_set_2": "AQAAAAMAAAAEAAAA" + } +} diff --git a/tests/components/tuya/fixtures/dlq_metering_3pn_wifi.json b/tests/components/tuya/fixtures/dlq_metering_3pn_wifi.json new file mode 100644 index 0000000000000..8e9a06cc9a9d4 --- /dev/null +++ b/tests/components/tuya/fixtures/dlq_metering_3pn_wifi.json @@ -0,0 +1,137 @@ +{ + "endpoint": "https://apigw.tuyaeu.com", + "terminal_id": "1733006572651YokbqV", + "mqtt_connected": null, + "disabled_by": null, + "disabled_polling": false, + "id": "bf5e5bde2c52cb5994cd27", + "name": "Metering_3PN_WiFi_stable", + "category": "dlq", + "product_id": "kxdr6su0c55p7bbo", + "product_name": "Metering_3PN_WiFi", + "online": true, + "sub": false, + "time_zone": "+03:00", + "active_time": "2024-12-08T17:37:45+00:00", + "create_time": "2024-12-08T17:37:45+00:00", + "update_time": "2024-12-08T17:37:45+00:00", + "function": { + "clear_energy": { + "type": "Boolean", + "value": {} + }, + "alarm_set_1": { + "type": "Raw", + "value": {} + }, + "alarm_set_2": { + "type": "Raw", + "value": {} + }, + "online_state": { + "type": "Enum", + "value": { + "range": ["online", "offline"] + } + } + }, + "status_range": { + "forward_energy_total": { + "type": "Integer", + "value": { + "unit": "kW.h", + "min": 0, + "max": 999999999, + "scale": 2, + "step": 1 + } + }, + "phase_a": { + "type": "Raw", + "value": {} + }, + "phase_b": { + "type": "Raw", + "value": {} + }, + "phase_c": { + "type": "Raw", + "value": {} + }, + "fault": { + "type": "Bitmap", + "value": { + "label": [ + "short_circuit_alarm", + "surge_alarm", + "overload_alarm", + "leakagecurr_alarm", + "temp_dif_fault", + "fire_alarm", + "high_power_alarm", + "self_test_alarm", + "ov_cr", + "unbalance_alarm", + "ov_vol", + "undervoltage_alarm", + "miss_phase_alarm", + "outage_alarm", + "magnetism_alarm", + "credit_alarm", + "no_balance_alarm" + ] + } + }, + "energy_reset": { + "type": "Enum", + "value": { + "range": ["empty"] + } + }, + "alarm_set_1": { + "type": "Raw", + "value": {} + }, + "alarm_set_2": { + "type": "Raw", + "value": {} + }, + "breaker_number": { + "type": "String", + "value": { + "maxlen": 255 + } + }, + "supply_frequency": { + "type": "Integer", + "value": { + "unit": "Hz", + "min": 0, + "max": 9999, + "scale": 2, + "step": 1 + } + }, + "online_state": { + "type": "Enum", + "value": { + "range": ["online", "offline"] + } + } + }, + "status": { + "forward_energy_total": 2435416, + "phase_a": "CKMAAn0AAGw=", + "phase_b": "CIsAK8MACWo=", + "phase_c": "CJwAA5EAAFw=", + "fault": 0, + "energy_reset": "", + "alarm_set_1": "BwEADQ==", + "alarm_set_2": "AQEAPAMBAP0EAQC0BQEAAAcBAAAIAQAeCQAAAA==", + "breaker_number": "SPM02_6588", + "supply_frequency": 5000, + "online_state": "online" + }, + "set_up": true, + "support_local": true +} diff --git a/tests/components/tuya/fixtures/wsdcg_temperature_humidity.json b/tests/components/tuya/fixtures/wsdcg_temperature_humidity.json new file mode 100644 index 0000000000000..06d07a4c506ce --- /dev/null +++ b/tests/components/tuya/fixtures/wsdcg_temperature_humidity.json @@ -0,0 +1,158 @@ +{ + "endpoint": "https://apigw.tuyaeu.com", + "terminal_id": "17150293164666xhFUk", + "mqtt_connected": true, + "disabled_by": null, + "disabled_polling": false, + + "id": "bf316b8707b061f044th18", + "name": "NP DownStairs North", + "category": "wsdcg", + "product_id": "g2y6z3p3ja2qhyav", + "product_name": "\u6e29\u6e7f\u5ea6\u4f20\u611f\u5668wifi", + "online": true, + "sub": false, + "time_zone": "+10:30", + "active_time": "2023-12-22T03:38:57+00:00", + "create_time": "2023-12-22T03:38:57+00:00", + "update_time": "2023-12-22T03:38:57+00:00", + "function": { + "maxtemp_set": { + "type": "Integer", + "value": { + "unit": "\u2103", + "min": -200, + "max": 600, + "scale": 1, + "step": 1 + } + }, + "minitemp_set": { + "type": "Integer", + "value": { + "unit": "\u2103", + "min": -200, + "max": 600, + "scale": 1, + "step": 1 + } + }, + "maxhum_set": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "minihum_set": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + } + }, + "status_range": { + "va_temperature": { + "type": "Integer", + "value": { + "unit": "\u2103", + "min": -200, + "max": 600, + "scale": 1, + "step": 1 + } + }, + "va_humidity": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "battery_percentage": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "maxtemp_set": { + "type": "Integer", + "value": { + "unit": "\u2103", + "min": -200, + "max": 600, + "scale": 1, + "step": 1 + } + }, + "minitemp_set": { + "type": "Integer", + "value": { + "unit": "\u2103", + "min": -200, + "max": 600, + "scale": 1, + "step": 1 + } + }, + "maxhum_set": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "minihum_set": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "temp_alarm": { + "type": "Enum", + "value": { + "range": ["loweralarm", "upperalarm", "cancel"] + } + }, + "hum_alarm": { + "type": "Enum", + "value": { + "range": ["loweralarm", "upperalarm", "cancel"] + } + } + }, + "status": { + "va_temperature": 185, + "va_humidity": 47, + "battery_percentage": 0, + "maxtemp_set": 600, + "minitemp_set": -100, + "maxhum_set": 100, + "minihum_set": 0, + "temp_alarm": "cancel", + "hum_alarm": "cancel" + }, + "set_up": true, + "support_local": true +} diff --git a/tests/components/tuya/fixtures/zndb_smart_meter.json b/tests/components/tuya/fixtures/zndb_smart_meter.json new file mode 100644 index 0000000000000..139cf81434781 --- /dev/null +++ b/tests/components/tuya/fixtures/zndb_smart_meter.json @@ -0,0 +1,79 @@ +{ + "endpoint": "https://apigw.tuyaeu.com", + "terminal_id": "1739198173271wpFacM", + "mqtt_connected": true, + "disabled_by": null, + "disabled_polling": false, + "id": "bfe33b4c74661f1f1bgacy", + "name": "Meter", + "category": "zndb", + "product_id": "ze8faryrxr0glqnn", + "product_name": "PJ2101A 1P WiFi Smart Meter ", + "online": true, + "sub": false, + "time_zone": "+02:00", + "active_time": "2024-08-24T11:22:33+00:00", + "create_time": "2024-08-24T11:22:33+00:00", + "update_time": "2024-08-24T11:22:33+00:00", + "function": { + "forward_energy_total": { + "type": "Integer", + "value": { + "unit": "kW\u00b7h", + "min": 0, + "max": 99999999, + "scale": 2, + "step": 1 + } + }, + "energy_month": { + "type": "raw", + "value": {} + }, + "energy_daily": { + "type": "raw", + "value": {} + } + }, + "status_range": { + "energy_month": { + "type": "raw", + "value": {} + }, + "energy_daily": { + "type": "raw", + "value": {} + }, + "phase_a": { + "type": "raw", + "value": {} + }, + "forward_energy_total": { + "type": "Integer", + "value": { + "unit": "kW\u00b7h", + "min": 0, + "max": 99999999, + "scale": 2, + "step": 1 + } + }, + "reverse_energy_total": { + "type": "Integer", + "value": { + "unit": "kW\u00b7h", + "min": 0, + "max": 99999999, + "scale": 2, + "step": 1 + } + } + }, + "status": { + "energy_month": "GAkYCQAAANQ=", + "energy_daily": "", + "phase_a": "CSIAFfQABKE=" + }, + "set_up": true, + "support_local": false +} diff --git a/tests/components/tuya/snapshots/test_binary_sensor.ambr b/tests/components/tuya/snapshots/test_binary_sensor.ambr index b269664a2d493..efd995b328043 100644 --- a/tests/components/tuya/snapshots/test_binary_sensor.ambr +++ b/tests/components/tuya/snapshots/test_binary_sensor.ambr @@ -1,4 +1,151 @@ # serializer version: 1 +# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][binary_sensor.dehumidifier_defrost-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.dehumidifier_defrost', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Defrost', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'defrost', + 'unique_id': 'tuya.bf3fce6af592f12df3gbgqdefrost', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][binary_sensor.dehumidifier_defrost-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Dehumidifier Defrost', + }), + 'context': , + 'entity_id': 'binary_sensor.dehumidifier_defrost', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][binary_sensor.dehumidifier_tank_full-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.dehumidifier_tank_full', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Tank full', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'tankfull', + 'unique_id': 'tuya.bf3fce6af592f12df3gbgqtankfull', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][binary_sensor.dehumidifier_tank_full-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Dehumidifier Tank full', + }), + 'context': , + 'entity_id': 'binary_sensor.dehumidifier_tank_full', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][binary_sensor.dehumidifier_wet-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.dehumidifier_wet', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wet', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'wet', + 'unique_id': 'tuya.bf3fce6af592f12df3gbgqwet', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][binary_sensor.dehumidifier_wet-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Dehumidifier Wet', + }), + 'context': , + 'entity_id': 'binary_sensor.dehumidifier_wet', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- # name: test_platform_setup_and_discovery[mcs_door_sensor][binary_sensor.door_garage_door-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/tuya/snapshots/test_sensor.ambr b/tests/components/tuya/snapshots/test_sensor.ambr index ac34dc615b70e..5e52c0e063c49 100644 --- a/tests/components/tuya/snapshots/test_sensor.ambr +++ b/tests/components/tuya/snapshots/test_sensor.ambr @@ -528,6 +528,678 @@ 'state': '121.7', }) # --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Current', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current', + 'unique_id': 'tuya.mocked_device_idcur_current', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': '一路带计量磁保持通断器 Current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2.198', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Power', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'power', + 'unique_id': 'tuya.mocked_device_idcur_power', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': '一路带计量磁保持通断器 Power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '495.3', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Voltage', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'voltage', + 'unique_id': 'tuya.mocked_device_idcur_voltage', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'voltage', + 'friendly_name': '一路带计量磁保持通断器 Voltage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '231.4', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_a_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_a_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase A current', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_a_current', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_aelectriccurrent', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_a_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase A current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_a_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.637', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_a_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_a_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase A power', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_a_power', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_apower', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_a_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase A power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_a_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.108', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_a_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_a_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase A voltage', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_a_voltage', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_avoltage', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_a_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'voltage', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase A voltage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_a_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '221.1', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_b_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_b_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase B current', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_b_current', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_belectriccurrent', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_b_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase B current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_b_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '11.203', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_b_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_b_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase B power', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_b_power', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_bpower', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_b_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase B power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_b_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2.41', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_b_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_b_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase B voltage', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_b_voltage', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_bvoltage', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_b_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'voltage', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase B voltage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_b_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '218.7', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_c_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_c_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase C current', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_c_current', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_celectriccurrent', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_c_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase C current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_c_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.913', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_c_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_c_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase C power', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_c_power', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_cpower', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_c_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase C power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_c_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.092', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_c_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_c_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase C voltage', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_c_voltage', + 'unique_id': 'tuya.bf5e5bde2c52cb5994cd27phase_cvoltage', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[dlq_metering_3pn_wifi][sensor.metering_3pn_wifi_stable_phase_c_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'voltage', + 'friendly_name': 'Metering_3PN_WiFi_stable Phase C voltage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.metering_3pn_wifi_stable_phase_c_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '220.4', + }) +# --- # name: test_platform_setup_and_discovery[mcs_door_sensor][sensor.door_garage_battery-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -543,7 +1215,112 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.door_garage_battery', + 'entity_id': 'sensor.door_garage_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery', + 'unique_id': 'tuya.bf5cccf9027080e2dbb9w3battery', + 'unit_of_measurement': '%', + }) +# --- +# name: test_platform_setup_and_discovery[mcs_door_sensor][sensor.door_garage_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Door Garage Battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.door_garage_battery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100.0', + }) +# --- +# name: test_platform_setup_and_discovery[rqbj_gas_sensor][sensor.gas_sensor_gas-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.gas_sensor_gas', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Gas', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'gas', + 'unique_id': 'tuya.ebb9d0eb5014f98cfboxbzgas_sensor_value', + 'unit_of_measurement': 'ppm', + }) +# --- +# name: test_platform_setup_and_discovery[rqbj_gas_sensor][sensor.gas_sensor_gas-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Gas sensor Gas', + 'state_class': , + 'unit_of_measurement': 'ppm', + }), + 'context': , + 'entity_id': 'sensor.gas_sensor_gas', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_platform_setup_and_discovery[wsdcg_temperature_humidity][sensor.np_downstairs_north_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.np_downstairs_north_battery', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -561,27 +1338,27 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'battery', - 'unique_id': 'tuya.bf5cccf9027080e2dbb9w3battery', + 'unique_id': 'tuya.bf316b8707b061f044th18battery_percentage', 'unit_of_measurement': '%', }) # --- -# name: test_platform_setup_and_discovery[mcs_door_sensor][sensor.door_garage_battery-state] +# name: test_platform_setup_and_discovery[wsdcg_temperature_humidity][sensor.np_downstairs_north_battery-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'battery', - 'friendly_name': 'Door Garage Battery', + 'friendly_name': 'NP DownStairs North Battery', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.door_garage_battery', + 'entity_id': 'sensor.np_downstairs_north_battery', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '100.0', + 'state': '0.0', }) # --- -# name: test_platform_setup_and_discovery[rqbj_gas_sensor][sensor.gas_sensor_gas-entry] +# name: test_platform_setup_and_discovery[wsdcg_temperature_humidity][sensor.np_downstairs_north_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -596,7 +1373,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.gas_sensor_gas', + 'entity_id': 'sensor.np_downstairs_north_humidity', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -606,30 +1383,255 @@ 'name': None, 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, - 'original_name': 'Gas', + 'original_name': 'Humidity', 'platform': 'tuya', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'gas', - 'unique_id': 'tuya.ebb9d0eb5014f98cfboxbzgas_sensor_value', - 'unit_of_measurement': 'ppm', + 'translation_key': 'humidity', + 'unique_id': 'tuya.bf316b8707b061f044th18va_humidity', + 'unit_of_measurement': '%', }) # --- -# name: test_platform_setup_and_discovery[rqbj_gas_sensor][sensor.gas_sensor_gas-state] +# name: test_platform_setup_and_discovery[wsdcg_temperature_humidity][sensor.np_downstairs_north_humidity-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'Gas sensor Gas', + 'device_class': 'humidity', + 'friendly_name': 'NP DownStairs North Humidity', 'state_class': , - 'unit_of_measurement': 'ppm', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.gas_sensor_gas', + 'entity_id': 'sensor.np_downstairs_north_humidity', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '0.0', + 'state': '47.0', + }) +# --- +# name: test_platform_setup_and_discovery[wsdcg_temperature_humidity][sensor.np_downstairs_north_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.np_downstairs_north_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'temperature', + 'unique_id': 'tuya.bf316b8707b061f044th18va_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[wsdcg_temperature_humidity][sensor.np_downstairs_north_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'NP DownStairs North Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.np_downstairs_north_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '18.5', + }) +# --- +# name: test_platform_setup_and_discovery[zndb_smart_meter][sensor.meter_phase_a_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.meter_phase_a_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase A current', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_a_current', + 'unique_id': 'tuya.bfe33b4c74661f1f1bgacyphase_aelectriccurrent', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[zndb_smart_meter][sensor.meter_phase_a_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'Meter Phase A current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.meter_phase_a_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '5.62', + }) +# --- +# name: test_platform_setup_and_discovery[zndb_smart_meter][sensor.meter_phase_a_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.meter_phase_a_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase A power', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_a_power', + 'unique_id': 'tuya.bfe33b4c74661f1f1bgacyphase_apower', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[zndb_smart_meter][sensor.meter_phase_a_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'Meter Phase A power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.meter_phase_a_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1.185', + }) +# --- +# name: test_platform_setup_and_discovery[zndb_smart_meter][sensor.meter_phase_a_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.meter_phase_a_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Phase A voltage', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'phase_a_voltage', + 'unique_id': 'tuya.bfe33b4c74661f1f1bgacyphase_avoltage', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[zndb_smart_meter][sensor.meter_phase_a_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'voltage', + 'friendly_name': 'Meter Phase A voltage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.meter_phase_a_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '233.8', }) # --- diff --git a/tests/components/tuya/snapshots/test_switch.ambr b/tests/components/tuya/snapshots/test_switch.ambr index 0f042cbce52a6..77943ccdd297c 100644 --- a/tests/components/tuya/snapshots/test_switch.ambr +++ b/tests/components/tuya/snapshots/test_switch.ambr @@ -386,6 +386,102 @@ 'state': 'on', }) # --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_child_lock-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_child_lock', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Child lock', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'child_lock', + 'unique_id': 'tuya.mocked_device_idchild_lock', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_child_lock-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': '一路带计量磁保持通断器 Child lock', + }), + 'context': , + 'entity_id': 'switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_child_lock', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_switch-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_switch', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Switch', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'switch', + 'unique_id': 'tuya.mocked_device_idswitch', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[dlq_earu_electric_eawcpt][switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_switch-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': '一路带计量磁保持通断器 Switch', + }), + 'context': , + 'entity_id': 'switch.yi_lu_dai_ji_liang_ci_bao_chi_tong_duan_qi_switch', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- # name: test_platform_setup_and_discovery[kg_smart_valve][switch.qt_switch_switch_1-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/tuya/test_binary_sensor.py b/tests/components/tuya/test_binary_sensor.py index c77be47fb2d98..f59e325b6cc11 100644 --- a/tests/components/tuya/test_binary_sensor.py +++ b/tests/components/tuya/test_binary_sensor.py @@ -56,3 +56,38 @@ async def test_platform_setup_no_discovery( assert not er.async_entries_for_config_entry( entity_registry, mock_config_entry.entry_id ) + + +@pytest.mark.parametrize( + "mock_device_code", + ["cs_arete_two_12l_dehumidifier_air_purifier"], +) +@pytest.mark.parametrize( + ("fault_value", "tankfull", "defrost", "wet"), + [ + (0, "off", "off", "off"), + (0x1, "on", "off", "off"), + (0x2, "off", "on", "off"), + (0x80, "off", "off", "on"), + (0x83, "on", "on", "on"), + ], +) +@patch("homeassistant.components.tuya.PLATFORMS", [Platform.BINARY_SENSOR]) +async def test_bitmap( + hass: HomeAssistant, + mock_manager: ManagerCompat, + mock_config_entry: MockConfigEntry, + mock_device: CustomerDevice, + fault_value: int, + tankfull: str, + defrost: str, + wet: str, +) -> None: + """Test BITMAP fault sensor on cs_arete_two_12l_dehumidifier_air_purifier.""" + mock_device.status["fault"] = fault_value + + await initialize_entry(hass, mock_manager, mock_config_entry, mock_device) + + assert hass.states.get("binary_sensor.dehumidifier_tank_full").state == tankfull + assert hass.states.get("binary_sensor.dehumidifier_defrost").state == defrost + assert hass.states.get("binary_sensor.dehumidifier_wet").state == wet