From bc26377c167debb9446552f9120b157aedbde4ca Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 4 Jan 2024 10:31:09 -1000 Subject: [PATCH] Cache homekit_controller supported features (#106702) --- .../components/homekit_controller/climate.py | 57 +- .../components/homekit_controller/cover.py | 16 +- .../components/homekit_controller/entity.py | 11 + .../components/homekit_controller/fan.py | 34 +- .../homekit_controller/humidifier.py | 222 +- .../components/homekit_controller/light.py | 21 +- .../home_assistant_bridge_basic_cover.json | 323 +++ ..._assistant_bridge_basic_heater_cooler.json | 229 ++ .../home_assistant_bridge_basic_light.json | 183 ++ .../fixtures/home_assistant_bridge_cover.json | 330 +++ .../home_assistant_bridge_heater_cooler.json | 237 ++ .../home_assistant_bridge_humidifier.json | 173 ++ ...assistant_bridge_humidifier_new_range.json | 173 ++ .../fixtures/home_assistant_bridge_light.json | 205 ++ .../snapshots/test_init.ambr | 2378 +++++++++++++++++ .../test_cover_that_changes_features.py | 54 + ...est_heater_cooler_that_changes_features.py | 48 + ...est_humidifier_that_changes_value_range.py | 44 + .../test_light_that_changes_features.py | 42 + 19 files changed, 4588 insertions(+), 192 deletions(-) create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_cover.json create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_heater_cooler.json create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_light.json create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_cover.json create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_heater_cooler.json create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier.json create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier_new_range.json create mode 100644 tests/components/homekit_controller/fixtures/home_assistant_bridge_light.json create mode 100644 tests/components/homekit_controller/specific_devices/test_cover_that_changes_features.py create mode 100644 tests/components/homekit_controller/specific_devices/test_heater_cooler_that_changes_features.py create mode 100644 tests/components/homekit_controller/specific_devices/test_humidifier_that_changes_value_range.py create mode 100644 tests/components/homekit_controller/specific_devices/test_light_that_changes_features.py diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index d3e9a0f13a6..1548c23a543 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -2,7 +2,7 @@ from __future__ import annotations import logging -from typing import Any, Final +from typing import TYPE_CHECKING, Any, Final from aiohomekit.model.characteristics import ( ActivationStateValues, @@ -48,6 +48,12 @@ from . import KNOWN_DEVICES from .connection import HKDevice from .entity import HomeKitEntity +if TYPE_CHECKING: + from functools import cached_property +else: + from homeassistant.backports.functools import cached_property + + _LOGGER = logging.getLogger(__name__) # Map of Homekit operation modes to hass modes @@ -134,6 +140,12 @@ class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity): _attr_temperature_unit = UnitOfTemperature.CELSIUS + @callback + def _async_reconfigure(self) -> None: + """Reconfigure entity.""" + self._async_clear_property_cache(("supported_features", "fan_modes")) + super()._async_reconfigure() + def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" return [ @@ -146,7 +158,7 @@ class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity): """Return the current temperature.""" return self.service.value(CharacteristicsTypes.TEMPERATURE_CURRENT) - @property + @cached_property def fan_modes(self) -> list[str] | None: """Return the available fan modes.""" if self.service.has(CharacteristicsTypes.FAN_STATE_TARGET): @@ -165,7 +177,7 @@ class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity): {CharacteristicsTypes.FAN_STATE_TARGET: int(fan_mode == FAN_AUTO)} ) - @property + @cached_property def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" features = ClimateEntityFeature(0) @@ -179,6 +191,12 @@ class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity): class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): """Representation of a Homekit climate device.""" + @callback + def _async_reconfigure(self) -> None: + """Reconfigure entity.""" + self._async_clear_property_cache(("hvac_modes", "swing_modes")) + super()._async_reconfigure() + def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" return super().get_characteristic_types() + [ @@ -197,7 +215,7 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): rotation_speed.maxValue or 100 ) - @property + @cached_property def fan_modes(self) -> list[str]: """Return the available fan modes.""" return [FAN_OFF, FAN_LOW, FAN_MEDIUM, FAN_HIGH] @@ -388,7 +406,7 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): value = self.service.value(CharacteristicsTypes.TARGET_HEATER_COOLER_STATE) return TARGET_HEATER_COOLER_STATE_HOMEKIT_TO_HASS[value] - @property + @cached_property def hvac_modes(self) -> list[HVACMode]: """Return the list of available hvac operation modes.""" valid_values = clamp_enum_to_char( @@ -410,7 +428,7 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): value = self.service.value(CharacteristicsTypes.SWING_MODE) return SWING_MODE_HOMEKIT_TO_HASS[value] - @property + @cached_property def swing_modes(self) -> list[str]: """Return the list of available swing modes. @@ -428,7 +446,7 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): {CharacteristicsTypes.SWING_MODE: SWING_MODE_HASS_TO_HOMEKIT[swing_mode]} ) - @property + @cached_property def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" features = super().supported_features @@ -451,6 +469,12 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): class HomeKitClimateEntity(HomeKitBaseClimateEntity): """Representation of a Homekit climate device.""" + @callback + def _async_reconfigure(self) -> None: + """Reconfigure entity.""" + self._async_clear_property_cache(("hvac_modes",)) + super()._async_reconfigure() + def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" return super().get_characteristic_types() + [ @@ -483,7 +507,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): if ( (mode == HVACMode.HEAT_COOL) and ( - ClimateEntityFeature.TARGET_TEMPERATURE_RANGE & self.supported_features + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE in self.supported_features ) and heat_temp and cool_temp @@ -524,9 +548,8 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) if (MODE_HOMEKIT_TO_HASS.get(value) in {HVACMode.HEAT, HVACMode.COOL}) or ( (MODE_HOMEKIT_TO_HASS.get(value) in {HVACMode.HEAT_COOL}) - and not ( - ClimateEntityFeature.TARGET_TEMPERATURE_RANGE & self.supported_features - ) + and ClimateEntityFeature.TARGET_TEMPERATURE_RANGE + not in self.supported_features ): return self.service.value(CharacteristicsTypes.TEMPERATURE_TARGET) return None @@ -536,7 +559,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): """Return the highbound target temperature we try to reach.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) if (MODE_HOMEKIT_TO_HASS.get(value) in {HVACMode.HEAT_COOL}) and ( - ClimateEntityFeature.TARGET_TEMPERATURE_RANGE & self.supported_features + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE in self.supported_features ): return self.service.value( CharacteristicsTypes.TEMPERATURE_COOLING_THRESHOLD @@ -548,7 +571,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): """Return the lowbound target temperature we try to reach.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) if (MODE_HOMEKIT_TO_HASS.get(value) in {HVACMode.HEAT_COOL}) and ( - ClimateEntityFeature.TARGET_TEMPERATURE_RANGE & self.supported_features + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE in self.supported_features ): return self.service.value( CharacteristicsTypes.TEMPERATURE_HEATING_THRESHOLD @@ -560,7 +583,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): """Return the minimum target temp.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) if (MODE_HOMEKIT_TO_HASS.get(value) in {HVACMode.HEAT_COOL}) and ( - ClimateEntityFeature.TARGET_TEMPERATURE_RANGE & self.supported_features + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE in self.supported_features ): min_temp = self.service[ CharacteristicsTypes.TEMPERATURE_HEATING_THRESHOLD @@ -582,7 +605,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): """Return the maximum target temp.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) if (MODE_HOMEKIT_TO_HASS.get(value) in {HVACMode.HEAT_COOL}) and ( - ClimateEntityFeature.TARGET_TEMPERATURE_RANGE & self.supported_features + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE in self.supported_features ): max_temp = self.service[ CharacteristicsTypes.TEMPERATURE_COOLING_THRESHOLD @@ -656,7 +679,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) return MODE_HOMEKIT_TO_HASS[value] - @property + @cached_property def hvac_modes(self) -> list[HVACMode]: """Return the list of available hvac operation modes.""" valid_values = clamp_enum_to_char( @@ -665,7 +688,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): ) return [MODE_HOMEKIT_TO_HASS[mode] for mode in valid_values] - @property + @cached_property def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" features = super().supported_features diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index f94e1145627..f99563843c7 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -1,7 +1,7 @@ """Support for Homekit covers.""" from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.services import Service, ServicesTypes @@ -28,6 +28,12 @@ from . import KNOWN_DEVICES from .connection import HKDevice from .entity import HomeKitEntity +if TYPE_CHECKING: + from functools import cached_property +else: + from homeassistant.backports.functools import cached_property + + STATE_STOPPED = "stopped" CURRENT_GARAGE_STATE_MAP = { @@ -128,6 +134,12 @@ class HomeKitGarageDoorCover(HomeKitEntity, CoverEntity): class HomeKitWindowCover(HomeKitEntity, CoverEntity): """Representation of a HomeKit Window or Window Covering.""" + @callback + def _async_reconfigure(self) -> None: + """Reconfigure entity.""" + self._async_clear_property_cache(("supported_features",)) + super()._async_reconfigure() + def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" return [ @@ -142,7 +154,7 @@ class HomeKitWindowCover(HomeKitEntity, CoverEntity): CharacteristicsTypes.OBSTRUCTION_DETECTED, ] - @property + @cached_property def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" features = ( diff --git a/homeassistant/components/homekit_controller/entity.py b/homeassistant/components/homekit_controller/entity.py index ceb1505518b..ba0cad8d666 100644 --- a/homeassistant/components/homekit_controller/entity.py +++ b/homeassistant/components/homekit_controller/entity.py @@ -1,6 +1,7 @@ """Homekit Controller entities.""" from __future__ import annotations +import contextlib from typing import Any from aiohomekit.model.characteristics import ( @@ -74,6 +75,16 @@ class HomeKitEntity(Entity): if not self._async_remove_entity_if_accessory_or_service_disappeared(): self._async_reconfigure() + @callback + def _async_clear_property_cache(self, properties: tuple[str, ...]) -> None: + """Clear the cache of properties.""" + for prop in properties: + # suppress is slower than try-except-pass, but + # we do not expect to have many properties to clear + # or this to be called often. + with contextlib.suppress(AttributeError): + delattr(self, prop) + @callback def _async_reconfigure(self) -> None: """Reconfigure the entity.""" diff --git a/homeassistant/components/homekit_controller/fan.py b/homeassistant/components/homekit_controller/fan.py index 550f86ddbe4..d87b6ab3e39 100644 --- a/homeassistant/components/homekit_controller/fan.py +++ b/homeassistant/components/homekit_controller/fan.py @@ -1,7 +1,7 @@ """Support for Homekit fans.""" from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.services import Service, ServicesTypes @@ -25,6 +25,12 @@ from . import KNOWN_DEVICES from .connection import HKDevice from .entity import HomeKitEntity +if TYPE_CHECKING: + from functools import cached_property +else: + from homeassistant.backports.functools import cached_property + + # 0 is clockwise, 1 is counter-clockwise. The match to forward and reverse is so that # its consistent with homeassistant.components.homekit. DIRECTION_TO_HK = { @@ -41,6 +47,20 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity): # that controls whether the fan is on or off. on_characteristic: str + @callback + def _async_reconfigure(self) -> None: + """Reconfigure entity.""" + self._async_clear_property_cache( + ( + "_speed_range", + "_min_speed", + "_max_speed", + "speed_count", + "supported_features", + ) + ) + super()._async_reconfigure() + def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" return [ @@ -55,19 +75,19 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity): """Return true if device is on.""" return self.service.value(self.on_characteristic) == 1 - @property + @cached_property def _speed_range(self) -> tuple[int, int]: """Return the speed range.""" return (self._min_speed, self._max_speed) - @property + @cached_property def _min_speed(self) -> int: """Return the minimum speed.""" return ( round(self.service[CharacteristicsTypes.ROTATION_SPEED].minValue or 0) + 1 ) - @property + @cached_property def _max_speed(self) -> int: """Return the minimum speed.""" return round(self.service[CharacteristicsTypes.ROTATION_SPEED].maxValue or 100) @@ -94,7 +114,7 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity): oscillating = self.service.value(CharacteristicsTypes.SWING_MODE) return oscillating == 1 - @property + @cached_property def supported_features(self) -> FanEntityFeature: """Flag supported features.""" features = FanEntityFeature(0) @@ -110,7 +130,7 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity): return features - @property + @cached_property def speed_count(self) -> int: """Speed count for the fan.""" return round( @@ -157,7 +177,7 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity): if ( percentage is not None - and self.supported_features & FanEntityFeature.SET_SPEED + and FanEntityFeature.SET_SPEED in self.supported_features ): characteristics[CharacteristicsTypes.ROTATION_SPEED] = round( percentage_to_ranged_value(self._speed_range, percentage) diff --git a/homeassistant/components/homekit_controller/humidifier.py b/homeassistant/components/homekit_controller/humidifier.py index 57e4e7e73d8..b5e67e7f1a4 100644 --- a/homeassistant/components/homekit_controller/humidifier.py +++ b/homeassistant/components/homekit_controller/humidifier.py @@ -1,7 +1,7 @@ """Support for HomeKit Controller humidifier.""" from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.services import Service, ServicesTypes @@ -25,6 +25,12 @@ from . import KNOWN_DEVICES from .connection import HKDevice from .entity import HomeKitEntity +if TYPE_CHECKING: + from functools import cached_property +else: + from homeassistant.backports.functools import cached_property + + HK_MODE_TO_HA = { 0: "off", 1: MODE_AUTO, @@ -39,46 +45,25 @@ HA_MODE_TO_HK = { } -class HomeKitHumidifier(HomeKitEntity, HumidifierEntity): +class HomeKitBaseHumidifier(HomeKitEntity, HumidifierEntity): """Representation of a HomeKit Controller Humidifier.""" - _attr_device_class = HumidifierDeviceClass.HUMIDIFIER _attr_supported_features = HumidifierEntityFeature.MODES + _attr_available_modes = [MODE_NORMAL, MODE_AUTO] + _humidity_char = CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD + _on_mode_value = 1 - def get_characteristic_types(self) -> list[str]: - """Define the homekit characteristics the entity cares about.""" - return [ - CharacteristicsTypes.ACTIVE, - CharacteristicsTypes.CURRENT_HUMIDIFIER_DEHUMIDIFIER_STATE, - CharacteristicsTypes.TARGET_HUMIDIFIER_DEHUMIDIFIER_STATE, - CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD, - ] + @callback + def _async_reconfigure(self) -> None: + """Reconfigure entity.""" + self._async_clear_property_cache(("max_humidity", "min_humidity")) + super()._async_reconfigure() @property def is_on(self) -> bool: """Return true if device is on.""" return self.service.value(CharacteristicsTypes.ACTIVE) - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn the specified valve on.""" - await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: True}) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn the specified valve off.""" - await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: False}) - - @property - def target_humidity(self) -> int | None: - """Return the humidity we try to reach.""" - return self.service.value( - CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD - ) - - @property - def current_humidity(self) -> int | None: - """Return the current humidity.""" - return self.service.value(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT) - @property def mode(self) -> str | None: """Return the current mode, e.g., home, auto, baby. @@ -91,23 +76,36 @@ class HomeKitHumidifier(HomeKitEntity, HumidifierEntity): return MODE_AUTO if mode == 1 else MODE_NORMAL @property - def available_modes(self) -> list[str] | None: - """Return a list of available modes. + def current_humidity(self) -> int | None: + """Return the current humidity.""" + return self.service.value(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT) - Requires HumidifierEntityFeature.MODES. - """ - available_modes = [ - MODE_NORMAL, - MODE_AUTO, - ] + @property + def target_humidity(self) -> int | None: + """Return the humidity we try to reach.""" + return self.service.value(self._humidity_char) - return available_modes + @cached_property + def min_humidity(self) -> int: + """Return the minimum humidity.""" + return int(self.service[self._humidity_char].minValue or DEFAULT_MIN_HUMIDITY) + + @cached_property + def max_humidity(self) -> int: + """Return the maximum humidity.""" + return int(self.service[self._humidity_char].maxValue or DEFAULT_MAX_HUMIDITY) async def async_set_humidity(self, humidity: int) -> None: """Set new target humidity.""" - await self.async_put_characteristics( - {CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD: humidity} - ) + await self.async_put_characteristics({self._humidity_char: humidity}) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the specified valve on.""" + await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: True}) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the specified valve off.""" + await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: False}) async def async_set_mode(self, mode: str) -> None: """Set new mode.""" @@ -121,37 +119,33 @@ class HomeKitHumidifier(HomeKitEntity, HumidifierEntity): else: await self.async_put_characteristics( { - CharacteristicsTypes.TARGET_HUMIDIFIER_DEHUMIDIFIER_STATE: 1, + CharacteristicsTypes.TARGET_HUMIDIFIER_DEHUMIDIFIER_STATE: self._on_mode_value, CharacteristicsTypes.ACTIVE: True, } ) - @property - def min_humidity(self) -> int: - """Return the minimum humidity.""" - return int( - self.service[ - CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD - ].minValue - or DEFAULT_MIN_HUMIDITY - ) - - @property - def max_humidity(self) -> int: - """Return the maximum humidity.""" - return int( - self.service[ - CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD - ].maxValue - or DEFAULT_MAX_HUMIDITY - ) + def get_characteristic_types(self) -> list[str]: + """Define the homekit characteristics the entity cares about.""" + return [ + CharacteristicsTypes.ACTIVE, + CharacteristicsTypes.CURRENT_HUMIDIFIER_DEHUMIDIFIER_STATE, + CharacteristicsTypes.TARGET_HUMIDIFIER_DEHUMIDIFIER_STATE, + CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD, + ] -class HomeKitDehumidifier(HomeKitEntity, HumidifierEntity): +class HomeKitHumidifier(HomeKitBaseHumidifier): + """Representation of a HomeKit Controller Humidifier.""" + + _attr_device_class = HumidifierDeviceClass.HUMIDIFIER + + +class HomeKitDehumidifier(HomeKitBaseHumidifier): """Representation of a HomeKit Controller Humidifier.""" _attr_device_class = HumidifierDeviceClass.DEHUMIDIFIER - _attr_supported_features = HumidifierEntityFeature.MODES + _humidity_char = CharacteristicsTypes.RELATIVE_HUMIDITY_DEHUMIDIFIER_THRESHOLD + _on_mode_value = 2 def __init__(self, accessory: HKDevice, devinfo: ConfigType) -> None: """Initialise the dehumidifier.""" @@ -160,106 +154,10 @@ class HomeKitDehumidifier(HomeKitEntity, HumidifierEntity): def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" - return [ - CharacteristicsTypes.ACTIVE, - CharacteristicsTypes.CURRENT_HUMIDIFIER_DEHUMIDIFIER_STATE, - CharacteristicsTypes.TARGET_HUMIDIFIER_DEHUMIDIFIER_STATE, - CharacteristicsTypes.RELATIVE_HUMIDITY_HUMIDIFIER_THRESHOLD, - CharacteristicsTypes.RELATIVE_HUMIDITY_DEHUMIDIFIER_THRESHOLD, - ] - - @property - def is_on(self) -> bool: - """Return true if device is on.""" - return self.service.value(CharacteristicsTypes.ACTIVE) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn the specified valve on.""" - await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: True}) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn the specified valve off.""" - await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: False}) - - @property - def target_humidity(self) -> int | None: - """Return the humidity we try to reach.""" - return self.service.value( + return super().get_characteristic_types() + [ CharacteristicsTypes.RELATIVE_HUMIDITY_DEHUMIDIFIER_THRESHOLD - ) - - @property - def current_humidity(self) -> int | None: - """Return the current humidity.""" - return self.service.value(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT) - - @property - def mode(self) -> str | None: - """Return the current mode, e.g., home, auto, baby. - - Requires HumidifierEntityFeature.MODES. - """ - mode = self.service.value( - CharacteristicsTypes.CURRENT_HUMIDIFIER_DEHUMIDIFIER_STATE - ) - return MODE_AUTO if mode == 1 else MODE_NORMAL - - @property - def available_modes(self) -> list[str] | None: - """Return a list of available modes. - - Requires HumidifierEntityFeature.MODES. - """ - available_modes = [ - MODE_NORMAL, - MODE_AUTO, ] - return available_modes - - async def async_set_humidity(self, humidity: int) -> None: - """Set new target humidity.""" - await self.async_put_characteristics( - {CharacteristicsTypes.RELATIVE_HUMIDITY_DEHUMIDIFIER_THRESHOLD: humidity} - ) - - async def async_set_mode(self, mode: str) -> None: - """Set new mode.""" - if mode == MODE_AUTO: - await self.async_put_characteristics( - { - CharacteristicsTypes.TARGET_HUMIDIFIER_DEHUMIDIFIER_STATE: 0, - CharacteristicsTypes.ACTIVE: True, - } - ) - else: - await self.async_put_characteristics( - { - CharacteristicsTypes.TARGET_HUMIDIFIER_DEHUMIDIFIER_STATE: 2, - CharacteristicsTypes.ACTIVE: True, - } - ) - - @property - def min_humidity(self) -> int: - """Return the minimum humidity.""" - return int( - self.service[ - CharacteristicsTypes.RELATIVE_HUMIDITY_DEHUMIDIFIER_THRESHOLD - ].minValue - or DEFAULT_MIN_HUMIDITY - ) - - @property - def max_humidity(self) -> int: - """Return the maximum humidity.""" - return int( - self.service[ - CharacteristicsTypes.RELATIVE_HUMIDITY_DEHUMIDIFIER_THRESHOLD - ].maxValue - or DEFAULT_MAX_HUMIDITY - ) - @property def old_unique_id(self) -> str: """Return the old ID of this device.""" diff --git a/homeassistant/components/homekit_controller/light.py b/homeassistant/components/homekit_controller/light.py index 5bf810a89db..f1d36c02933 100644 --- a/homeassistant/components/homekit_controller/light.py +++ b/homeassistant/components/homekit_controller/light.py @@ -1,7 +1,7 @@ """Support for Homekit lights.""" from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.services import Service, ServicesTypes @@ -22,6 +22,11 @@ from . import KNOWN_DEVICES from .connection import HKDevice from .entity import HomeKitEntity +if TYPE_CHECKING: + from functools import cached_property +else: + from homeassistant.backports.functools import cached_property + async def async_setup_entry( hass: HomeAssistant, @@ -50,6 +55,14 @@ async def async_setup_entry( class HomeKitLight(HomeKitEntity, LightEntity): """Representation of a Homekit light.""" + @callback + def _async_reconfigure(self) -> None: + """Reconfigure entity.""" + self._async_clear_property_cache( + ("supported_features", "min_mireds", "max_mireds", "supported_color_modes") + ) + super()._async_reconfigure() + def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" return [ @@ -78,13 +91,13 @@ class HomeKitLight(HomeKitEntity, LightEntity): self.service.value(CharacteristicsTypes.SATURATION), ) - @property + @cached_property def min_mireds(self) -> int: """Return minimum supported color temperature.""" min_value = self.service[CharacteristicsTypes.COLOR_TEMPERATURE].minValue return int(min_value) if min_value else super().min_mireds - @property + @cached_property def max_mireds(self) -> int: """Return the maximum color temperature.""" max_value = self.service[CharacteristicsTypes.COLOR_TEMPERATURE].maxValue @@ -113,7 +126,7 @@ class HomeKitLight(HomeKitEntity, LightEntity): return ColorMode.ONOFF - @property + @cached_property def supported_color_modes(self) -> set[ColorMode]: """Flag supported color modes.""" color_modes: set[ColorMode] = set() diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_cover.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_cover.json new file mode 100644 index 00000000000..cfb94b104b0 --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_cover.json @@ -0,0 +1,323 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 878448248, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "RYSE Inc." + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "RYSE Shade" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Kitchen Window" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "cover.kitchen_window" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "3.6.2" + }, + { + "iid": 8, + "type": "53", + "perms": ["pr"], + "format": "string", + "value": "1.0.0" + } + ] + }, + { + "iid": 9, + "type": "96", + "characteristics": [ + { + "iid": 10, + "type": "68", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 11, + "type": "8F", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + }, + { + "iid": 12, + "type": "79", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + } + ] + }, + { + "iid": 13, + "type": "8C", + "characteristics": [ + { + "iid": 14, + "type": "6D", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 15, + "type": "7C", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 16, + "type": "72", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + } + ] + } + ] + }, + { + "aid": 123016423, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 155, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 156, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "RYSE Inc." + }, + { + "iid": 157, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "RYSE Shade" + }, + { + "iid": 158, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Family Room North" + }, + { + "iid": 159, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "cover.family_door_north" + }, + { + "iid": 160, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "3.6.2" + }, + { + "iid": 161, + "type": "53", + "perms": ["pr"], + "format": "string", + "value": "1.0.0" + } + ] + }, + { + "iid": 162, + "type": "96", + "characteristics": [ + { + "iid": 163, + "type": "68", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 164, + "type": "8F", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + }, + { + "iid": 165, + "type": "79", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + } + ] + }, + { + "iid": 166, + "type": "8C", + "characteristics": [ + { + "iid": 167, + "type": "6D", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 98 + }, + { + "iid": 168, + "type": "7C", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 98 + }, + { + "iid": 169, + "type": "72", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_heater_cooler.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_heater_cooler.json new file mode 100644 index 00000000000..4526179b4da --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_heater_cooler.json @@ -0,0 +1,229 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 1233851541, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 163, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 164, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Lookin" + }, + { + "iid": 165, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Climate Control" + }, + { + "iid": 166, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "89 Living Room" + }, + { + "iid": 167, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "climate.89_living_room" + }, + { + "iid": 168, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ], + "primary": false + }, + { + "iid": 169, + "type": "BC", + "characteristics": [ + { + "iid": 170, + "type": "B2", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 0 + }, + { + "iid": 171, + "type": "B1", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2, 3], + "value": 2 + }, + { + "iid": 172, + "type": "11", + "perms": ["pr", "ev"], + "format": "float", + "unit": "celsius", + "minStep": 0.1, + "maxValue": 1000, + "minValue": -273.1, + "value": 22.8 + }, + { + "iid": 173, + "type": "35", + "perms": ["pr", "pw", "ev"], + "format": "float", + "unit": "celsius", + "minStep": 0.1, + "maxValue": 30.0, + "minValue": 16.0, + "value": 20.0 + }, + { + "iid": 174, + "type": "36", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 1 + }, + { + "iid": 180, + "type": "10", + "perms": ["pr", "ev"], + "format": "float", + "unit": "percentage", + "minStep": 1, + "maxValue": 100, + "minValue": 0, + "value": 60 + } + ], + "primary": true, + "linked": [175] + }, + { + "iid": 175, + "type": "B7", + "characteristics": [ + { + "iid": 176, + "type": "B0", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 1 + }, + { + "iid": 177, + "type": "29", + "perms": ["pr", "pw", "ev"], + "format": "float", + "description": "Fan Mode", + "unit": "percentage", + "minStep": 33.333333333333336, + "maxValue": 100, + "minValue": 0, + "value": 33.33333333333334 + }, + { + "iid": 178, + "type": "BF", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "description": "Fan Auto", + "valid-values": [0, 1], + "value": 0 + }, + { + "iid": 179, + "type": "B6", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "description": "Swing Mode", + "valid-values": [0, 1], + "value": 0 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_light.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_light.json new file mode 100644 index 00000000000..2e5c8719876 --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_basic_light.json @@ -0,0 +1,183 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 3982136094, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 597, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 598, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "FirstAlert" + }, + { + "iid": 599, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "1039102" + }, + { + "iid": 600, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Laundry Smoke ED78" + }, + { + "iid": 601, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "light.laundry_smoke_ed78" + }, + { + "iid": 602, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "1.4.84" + }, + { + "iid": 603, + "type": "53", + "perms": ["pr"], + "format": "string", + "value": "9.0.0" + } + ] + }, + { + "iid": 604, + "type": "96", + "characteristics": [ + { + "iid": 605, + "type": "68", + "perms": ["pr", "ev"], + "format": "uint8", + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "value": 100 + }, + { + "iid": 606, + "type": "8F", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + }, + { + "iid": 607, + "type": "79", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + } + ] + }, + { + "iid": 608, + "type": "43", + "characteristics": [ + { + "iid": 609, + "type": "25", + "perms": ["pr", "pw", "ev"], + "format": "bool", + "value": false + }, + { + "iid": 610, + "type": "8", + "perms": ["pr", "pw", "ev"], + "format": "int", + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "value": 100 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_cover.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_cover.json new file mode 100644 index 00000000000..d58de1d2b98 --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_cover.json @@ -0,0 +1,330 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 878448248, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "RYSE Inc." + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "RYSE Shade" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Kitchen Window" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "cover.kitchen_window" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "3.6.2" + }, + { + "iid": 8, + "type": "53", + "perms": ["pr"], + "format": "string", + "value": "1.0.0" + } + ] + }, + { + "iid": 9, + "type": "96", + "characteristics": [ + { + "iid": 10, + "type": "68", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 11, + "type": "8F", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + }, + { + "iid": 12, + "type": "79", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + } + ] + }, + { + "iid": 13, + "type": "8C", + "characteristics": [ + { + "iid": 14, + "type": "6D", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 15, + "type": "7C", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 16, + "type": "72", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + } + ] + } + ] + }, + { + "aid": 123016423, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 155, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 156, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "RYSE Inc." + }, + { + "iid": 157, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "RYSE Shade" + }, + { + "iid": 158, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Family Room North" + }, + { + "iid": 159, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "cover.family_door_north" + }, + { + "iid": 160, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "3.6.2" + }, + { + "iid": 161, + "type": "53", + "perms": ["pr"], + "format": "string", + "value": "1.0.0" + } + ] + }, + { + "iid": 162, + "type": "96", + "characteristics": [ + { + "iid": 163, + "type": "68", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 100 + }, + { + "iid": 164, + "type": "8F", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + }, + { + "iid": 165, + "type": "79", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + } + ] + }, + { + "iid": 166, + "type": "8C", + "characteristics": [ + { + "iid": 167, + "type": "6D", + "perms": ["pr", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 98 + }, + { + "iid": 168, + "type": "7C", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "maxValue": 100, + "value": 98 + }, + { + "iid": 169, + "type": "72", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + }, + { + "iid": 170, + "type": "6F", + "perms": ["pw", "pr", "ev"], + "format": "bool", + "value": false + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_heater_cooler.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_heater_cooler.json new file mode 100644 index 00000000000..f96d168fc5f --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_heater_cooler.json @@ -0,0 +1,237 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 1233851541, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 163, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 164, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Lookin" + }, + { + "iid": 165, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Climate Control" + }, + { + "iid": 166, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "89 Living Room" + }, + { + "iid": 167, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "climate.89_living_room" + }, + { + "iid": 168, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ], + "primary": false + }, + { + "iid": 169, + "type": "BC", + "characteristics": [ + { + "iid": 170, + "type": "B2", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 0 + }, + { + "iid": 171, + "type": "B1", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2, 3], + "value": 2 + }, + { + "iid": 172, + "type": "11", + "perms": ["pr", "ev"], + "format": "float", + "unit": "celsius", + "minStep": 0.1, + "maxValue": 1000, + "minValue": -273.1, + "value": 22.8 + }, + { + "iid": 173, + "type": "35", + "perms": ["pr", "pw", "ev"], + "format": "float", + "unit": "celsius", + "minStep": 0.1, + "maxValue": 30.0, + "minValue": 16.0, + "value": 20.0 + }, + { + "iid": 174, + "type": "36", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 1 + }, + { + "iid": 180, + "type": "10", + "perms": ["pr", "ev"], + "format": "float", + "unit": "percentage", + "minStep": 1, + "maxValue": 100, + "minValue": 0, + "value": 60 + }, + { + "iid": 290, + "type": "B6", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 1 + } + ], + "primary": true, + "linked": [175] + }, + { + "iid": 175, + "type": "B7", + "characteristics": [ + { + "iid": 176, + "type": "B0", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 1 + }, + { + "iid": 177, + "type": "29", + "perms": ["pr", "pw", "ev"], + "format": "float", + "description": "Fan Mode", + "unit": "percentage", + "minStep": 33.333333333333336, + "maxValue": 100, + "minValue": 0, + "value": 33.33333333333334 + }, + { + "iid": 178, + "type": "BF", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "description": "Fan Auto", + "valid-values": [0, 1], + "value": 0 + }, + { + "iid": 179, + "type": "B6", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "description": "Swing Mode", + "valid-values": [0, 1], + "value": 0 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier.json new file mode 100644 index 00000000000..8dd33639190 --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier.json @@ -0,0 +1,173 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 293334836, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "switchbot" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "WoHumi" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Humidifier 182A" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "humidifier.humidifier_182a" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "BD", + "characteristics": [ + { + "iid": 9, + "type": "10", + "perms": ["pr", "ev"], + "format": "float", + "unit": "percentage", + "minStep": 1, + "maxValue": 100, + "minValue": 0, + "value": 0 + }, + { + "iid": 10, + "type": "B3", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 0 + }, + { + "iid": 11, + "type": "B4", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "maxValue": 1, + "minValue": 1, + "valid-values": [1], + "value": 1 + }, + { + "iid": 12, + "type": "B0", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + }, + { + "iid": 13, + "type": "CA", + "perms": ["pr", "pw", "ev"], + "format": "float", + "unit": "percentage", + "minStep": 1, + "maxValue": 100, + "minValue": 0, + "value": 45 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier_new_range.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier_new_range.json new file mode 100644 index 00000000000..28ef6c91d25 --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_humidifier_new_range.json @@ -0,0 +1,173 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 293334836, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "switchbot" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "WoHumi" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Humidifier 182A" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "humidifier.humidifier_182a" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "BD", + "characteristics": [ + { + "iid": 9, + "type": "10", + "perms": ["pr", "ev"], + "format": "float", + "unit": "percentage", + "minStep": 1, + "maxValue": 100, + "minValue": 0, + "value": 0 + }, + { + "iid": 10, + "type": "B3", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 0 + }, + { + "iid": 11, + "type": "B4", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "maxValue": 1, + "minValue": 1, + "valid-values": [1], + "value": 1 + }, + { + "iid": 12, + "type": "B0", + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + }, + { + "iid": 13, + "type": "CA", + "perms": ["pr", "pw", "ev"], + "format": "float", + "unit": "percentage", + "minStep": 1, + "maxValue": 80, + "minValue": 20, + "value": 45 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/fixtures/home_assistant_bridge_light.json b/tests/components/homekit_controller/fixtures/home_assistant_bridge_light.json new file mode 100644 index 00000000000..b5614184fae --- /dev/null +++ b/tests/components/homekit_controller/fixtures/home_assistant_bridge_light.json @@ -0,0 +1,205 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 2, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 3, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "Home Assistant" + }, + { + "iid": 4, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "Bridge" + }, + { + "iid": 5, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "HASS Bridge S6" + }, + { + "iid": 6, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "homekit.bridge" + }, + { + "iid": 7, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "2024.2.0" + } + ] + }, + { + "iid": 8, + "type": "A2", + "characteristics": [ + { + "iid": 9, + "type": "37", + "perms": ["pr", "ev"], + "format": "string", + "value": "01.01.00" + } + ] + } + ] + }, + { + "aid": 3982136094, + "services": [ + { + "iid": 1, + "type": "3E", + "characteristics": [ + { + "iid": 597, + "type": "14", + "perms": ["pw"], + "format": "bool" + }, + { + "iid": 598, + "type": "20", + "perms": ["pr"], + "format": "string", + "value": "FirstAlert" + }, + { + "iid": 599, + "type": "21", + "perms": ["pr"], + "format": "string", + "value": "1039102" + }, + { + "iid": 600, + "type": "23", + "perms": ["pr"], + "format": "string", + "value": "Laundry Smoke ED78" + }, + { + "iid": 601, + "type": "30", + "perms": ["pr"], + "format": "string", + "value": "light.laundry_smoke_ed78" + }, + { + "iid": 602, + "type": "52", + "perms": ["pr"], + "format": "string", + "value": "1.4.84" + }, + { + "iid": 603, + "type": "53", + "perms": ["pr"], + "format": "string", + "value": "9.0.0" + } + ] + }, + { + "iid": 604, + "type": "96", + "characteristics": [ + { + "iid": 605, + "type": "68", + "perms": ["pr", "ev"], + "format": "uint8", + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "value": 100 + }, + { + "iid": 606, + "type": "8F", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1, 2], + "value": 2 + }, + { + "iid": 607, + "type": "79", + "perms": ["pr", "ev"], + "format": "uint8", + "valid-values": [0, 1], + "value": 0 + } + ] + }, + { + "iid": 608, + "type": "43", + "characteristics": [ + { + "iid": 609, + "type": "25", + "perms": ["pr", "pw", "ev"], + "format": "bool", + "value": false + }, + { + "iid": 610, + "type": "8", + "perms": ["pr", "pw", "ev"], + "format": "int", + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "value": 100 + }, + { + "iid": 611, + "type": "13", + "perms": ["pr", "pw", "ev"], + "format": "float", + "maxValue": 360, + "minStep": 1, + "minValue": 0, + "unit": "arcdegrees", + "value": 0 + }, + { + "iid": 612, + "type": "2F", + "perms": ["pr", "pw", "ev"], + "format": "float", + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "unit": "percentage", + "value": 75 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/snapshots/test_init.ambr b/tests/components/homekit_controller/snapshots/test_init.ambr index a0c6fd00ee6..4b4ffeb9aa3 100644 --- a/tests/components/homekit_controller/snapshots/test_init.ambr +++ b/tests/components/homekit_controller/snapshots/test_init.ambr @@ -6230,6 +6230,368 @@ }), ]) # --- +# name: test_snapshots[home_assistant_bridge_basic_cover] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '1.0.0', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:123016423', + ]), + ]), + 'is_new': False, + 'manufacturer': 'RYSE Inc.', + 'model': 'RYSE Shade', + 'name': 'Family Room North', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '3.6.2', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.family_room_north_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Family Room North Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_123016423_1_155', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Family Room North Identify', + }), + 'entity_id': 'button.family_room_north_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.family_room_north', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Family Room North', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_123016423_166', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'current_position': 98, + 'friendly_name': 'Family Room North', + 'supported_features': , + }), + 'entity_id': 'cover.family_room_north', + 'state': 'open', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.family_room_north_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Family Room North Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_123016423_162', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Family Room North Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.family_room_north_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '1.0.0', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:878448248', + ]), + ]), + 'is_new': False, + 'manufacturer': 'RYSE Inc.', + 'model': 'RYSE Shade', + 'name': 'Kitchen Window', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '3.6.2', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.kitchen_window_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Kitchen Window Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_878448248_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Kitchen Window Identify', + }), + 'entity_id': 'button.kitchen_window_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.kitchen_window', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Kitchen Window', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_878448248_13', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'current_position': 100, + 'friendly_name': 'Kitchen Window', + 'supported_features': , + }), + 'entity_id': 'cover.kitchen_window', + 'state': 'open', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.kitchen_window_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Kitchen Window Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_878448248_9', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Kitchen Window Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.kitchen_window_battery', + 'state': '100', + }), + }), + ]), + }), + ]) +# --- # name: test_snapshots[home_assistant_bridge_basic_fan] list([ dict({ @@ -6519,6 +6881,958 @@ }), ]) # --- +# name: test_snapshots[home_assistant_bridge_basic_heater_cooler] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1233851541', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Lookin', + 'model': 'Climate Control', + 'name': '89 Living Room', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.89_living_room_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': '89 Living Room Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_1_163', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': '89 Living Room Identify', + }), + 'entity_id': 'button.89_living_room_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 35, + 'min_temp': 7, + 'target_temp_step': 1.0, + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'climate', + 'entity_category': None, + 'entity_id': 'climate.89_living_room', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': '89 Living Room', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_169', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'current_temperature': 22.8, + 'friendly_name': '89 Living Room', + 'hvac_action': , + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 35, + 'min_temp': 7, + 'supported_features': , + 'target_temp_step': 1.0, + }), + 'entity_id': 'climate.89_living_room', + 'state': 'heat_cool', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'preset_modes': None, + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'fan', + 'entity_category': None, + 'entity_id': 'fan.89_living_room', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': '89 Living Room', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_175', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': '89 Living Room', + 'oscillating': False, + 'percentage': 33, + 'percentage_step': 33.333333333333336, + 'preset_mode': None, + 'preset_modes': None, + 'supported_features': , + }), + 'entity_id': 'fan.89_living_room', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'celsius', + 'fahrenheit', + ]), + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.89_living_room_temperature_display_units', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:thermometer', + 'original_name': '89 Living Room Temperature Display Units', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'temperature_display_units', + 'unique_id': '00:00:00:00:00:00_1233851541_169_174', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': '89 Living Room Temperature Display Units', + 'icon': 'mdi:thermometer', + 'options': list([ + 'celsius', + 'fahrenheit', + ]), + }), + 'entity_id': 'select.89_living_room_temperature_display_units', + 'state': 'fahrenheit', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.89_living_room_current_humidity', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': '89 Living Room Current Humidity', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_169_180', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'humidity', + 'friendly_name': '89 Living Room Current Humidity', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.89_living_room_current_humidity', + 'state': '60', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.89_living_room_current_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': '89 Living Room Current Temperature', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_169_172', + 'unit_of_measurement': , + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': '89 Living Room Current Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'entity_id': 'sensor.89_living_room_current_temperature', + 'state': '22.8', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + ]) +# --- +# name: test_snapshots[home_assistant_bridge_basic_light] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '9.0.0', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:3982136094', + ]), + ]), + 'is_new': False, + 'manufacturer': 'FirstAlert', + 'model': '1039102', + 'name': 'Laundry Smoke ED78', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '1.4.84', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.laundry_smoke_ed78_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Laundry Smoke ED78 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_3982136094_1_597', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Laundry Smoke ED78 Identify', + }), + 'entity_id': 'button.laundry_smoke_ed78_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'supported_color_modes': list([ + , + ]), + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'light', + 'entity_category': None, + 'entity_id': 'light.laundry_smoke_ed78', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Laundry Smoke ED78', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_3982136094_608', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'brightness': None, + 'color_mode': None, + 'friendly_name': 'Laundry Smoke ED78', + 'supported_color_modes': list([ + , + ]), + 'supported_features': , + }), + 'entity_id': 'light.laundry_smoke_ed78', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.laundry_smoke_ed78_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Laundry Smoke ED78 Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_3982136094_604', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Laundry Smoke ED78 Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.laundry_smoke_ed78_battery', + 'state': '100', + }), + }), + ]), + }), + ]) +# --- +# name: test_snapshots[home_assistant_bridge_cover] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '1.0.0', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:123016423', + ]), + ]), + 'is_new': False, + 'manufacturer': 'RYSE Inc.', + 'model': 'RYSE Shade', + 'name': 'Family Room North', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '3.6.2', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.family_room_north_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Family Room North Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_123016423_1_155', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Family Room North Identify', + }), + 'entity_id': 'button.family_room_north_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.family_room_north', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Family Room North', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_123016423_166', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'current_position': 98, + 'friendly_name': 'Family Room North', + 'supported_features': , + }), + 'entity_id': 'cover.family_room_north', + 'state': 'open', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.family_room_north_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Family Room North Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_123016423_162', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Family Room North Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.family_room_north_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '1.0.0', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:878448248', + ]), + ]), + 'is_new': False, + 'manufacturer': 'RYSE Inc.', + 'model': 'RYSE Shade', + 'name': 'Kitchen Window', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '3.6.2', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.kitchen_window_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Kitchen Window Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_878448248_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Kitchen Window Identify', + }), + 'entity_id': 'button.kitchen_window_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.kitchen_window', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Kitchen Window', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_878448248_13', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'current_position': 100, + 'friendly_name': 'Kitchen Window', + 'supported_features': , + }), + 'entity_id': 'cover.kitchen_window', + 'state': 'open', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.kitchen_window_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Kitchen Window Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_878448248_9', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Kitchen Window Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.kitchen_window_battery', + 'state': '100', + }), + }), + ]), + }), + ]) +# --- # name: test_snapshots[home_assistant_bridge_fan] list([ dict({ @@ -6990,6 +8304,1070 @@ }), ]) # --- +# name: test_snapshots[home_assistant_bridge_heater_cooler] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1233851541', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Lookin', + 'model': 'Climate Control', + 'name': '89 Living Room', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.89_living_room_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': '89 Living Room Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_1_163', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': '89 Living Room Identify', + }), + 'entity_id': 'button.89_living_room_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 35, + 'min_temp': 7, + 'swing_modes': list([ + 'off', + 'vertical', + ]), + 'target_temp_step': 1.0, + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'climate', + 'entity_category': None, + 'entity_id': 'climate.89_living_room', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': '89 Living Room', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_169', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'current_temperature': 22.8, + 'friendly_name': '89 Living Room', + 'hvac_action': , + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 35, + 'min_temp': 7, + 'supported_features': , + 'swing_mode': 'vertical', + 'swing_modes': list([ + 'off', + 'vertical', + ]), + 'target_temp_step': 1.0, + }), + 'entity_id': 'climate.89_living_room', + 'state': 'heat_cool', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'preset_modes': None, + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'fan', + 'entity_category': None, + 'entity_id': 'fan.89_living_room', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': '89 Living Room', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_175', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': '89 Living Room', + 'oscillating': False, + 'percentage': 33, + 'percentage_step': 33.333333333333336, + 'preset_mode': None, + 'preset_modes': None, + 'supported_features': , + }), + 'entity_id': 'fan.89_living_room', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'celsius', + 'fahrenheit', + ]), + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.89_living_room_temperature_display_units', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:thermometer', + 'original_name': '89 Living Room Temperature Display Units', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'temperature_display_units', + 'unique_id': '00:00:00:00:00:00_1233851541_169_174', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': '89 Living Room Temperature Display Units', + 'icon': 'mdi:thermometer', + 'options': list([ + 'celsius', + 'fahrenheit', + ]), + }), + 'entity_id': 'select.89_living_room_temperature_display_units', + 'state': 'fahrenheit', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.89_living_room_current_humidity', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': '89 Living Room Current Humidity', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_169_180', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'humidity', + 'friendly_name': '89 Living Room Current Humidity', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.89_living_room_current_humidity', + 'state': '60', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.89_living_room_current_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': '89 Living Room Current Temperature', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1233851541_169_172', + 'unit_of_measurement': , + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': '89 Living Room Current Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'entity_id': 'sensor.89_living_room_current_temperature', + 'state': '22.8', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + ]) +# --- +# name: test_snapshots[home_assistant_bridge_humidifier] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:293334836', + ]), + ]), + 'is_new': False, + 'manufacturer': 'switchbot', + 'model': 'WoHumi', + 'name': 'Humidifier 182A', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.humidifier_182a_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Humidifier 182A Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_293334836_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Humidifier 182A Identify', + }), + 'entity_id': 'button.humidifier_182a_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'max_humidity': 100, + 'min_humidity': 0, + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'humidifier', + 'entity_category': None, + 'entity_id': 'humidifier.humidifier_182a', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Humidifier 182A', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_293334836_8', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'current_humidity': 0, + 'device_class': 'humidifier', + 'friendly_name': 'Humidifier 182A', + 'humidity': 45, + 'max_humidity': 100, + 'min_humidity': 0, + 'mode': 'normal', + 'supported_features': , + }), + 'entity_id': 'humidifier.humidifier_182a', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.humidifier_182a_current_humidity', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Humidifier 182A Current Humidity', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_293334836_8_9', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'humidity', + 'friendly_name': 'Humidifier 182A Current Humidity', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.humidifier_182a_current_humidity', + 'state': '0', + }), + }), + ]), + }), + ]) +# --- +# name: test_snapshots[home_assistant_bridge_humidifier_new_range] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:293334836', + ]), + ]), + 'is_new': False, + 'manufacturer': 'switchbot', + 'model': 'WoHumi', + 'name': 'Humidifier 182A', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.humidifier_182a_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Humidifier 182A Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_293334836_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Humidifier 182A Identify', + }), + 'entity_id': 'button.humidifier_182a_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'max_humidity': 80, + 'min_humidity': 20, + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'humidifier', + 'entity_category': None, + 'entity_id': 'humidifier.humidifier_182a', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Humidifier 182A', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_293334836_8', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'current_humidity': 0, + 'device_class': 'humidifier', + 'friendly_name': 'Humidifier 182A', + 'humidity': 45, + 'max_humidity': 80, + 'min_humidity': 20, + 'mode': 'normal', + 'supported_features': , + }), + 'entity_id': 'humidifier.humidifier_182a', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.humidifier_182a_current_humidity', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Humidifier 182A Current Humidity', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_293334836_8_9', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'humidity', + 'friendly_name': 'Humidifier 182A Current Humidity', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.humidifier_182a_current_humidity', + 'state': '0', + }), + }), + ]), + }), + ]) +# --- +# name: test_snapshots[home_assistant_bridge_light] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'manufacturer': 'Home Assistant', + 'model': 'Bridge', + 'name': 'HASS Bridge S6', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '2024.2.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hass_bridge_s6_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HASS Bridge S6 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_2', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'HASS Bridge S6 Identify', + }), + 'entity_id': 'button.hass_bridge_s6_identify', + 'state': 'unknown', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '9.0.0', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:3982136094', + ]), + ]), + 'is_new': False, + 'manufacturer': 'FirstAlert', + 'model': '1039102', + 'name': 'Laundry Smoke ED78', + 'name_by_user': None, + 'serial_number': None, + 'suggested_area': None, + 'sw_version': '1.4.84', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.laundry_smoke_ed78_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Laundry Smoke ED78 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_3982136094_1_597', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Laundry Smoke ED78 Identify', + }), + 'entity_id': 'button.laundry_smoke_ed78_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'supported_color_modes': list([ + , + ]), + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'light', + 'entity_category': None, + 'entity_id': 'light.laundry_smoke_ed78', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Laundry Smoke ED78', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_3982136094_608', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'brightness': None, + 'color_mode': None, + 'friendly_name': 'Laundry Smoke ED78', + 'hs_color': None, + 'rgb_color': None, + 'supported_color_modes': list([ + , + ]), + 'supported_features': , + 'xy_color': None, + }), + 'entity_id': 'light.laundry_smoke_ed78', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': 'TestData', + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.laundry_smoke_ed78_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Laundry Smoke ED78 Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_3982136094_604', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Laundry Smoke ED78 Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.laundry_smoke_ed78_battery', + 'state': '100', + }), + }), + ]), + }), + ]) +# --- # name: test_snapshots[homespan_daikin_bridge] list([ dict({ diff --git a/tests/components/homekit_controller/specific_devices/test_cover_that_changes_features.py b/tests/components/homekit_controller/specific_devices/test_cover_that_changes_features.py new file mode 100644 index 00000000000..87948c92214 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_cover_that_changes_features.py @@ -0,0 +1,54 @@ +"""Test for a Home Assistant bridge that changes cover features at runtime.""" + + +from homeassistant.components.cover import CoverEntityFeature +from homeassistant.const import ATTR_SUPPORTED_FEATURES +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from ..common import ( + device_config_changed, + setup_accessories_from_file, + setup_test_accessories, +) + + +async def test_cover_add_feature_at_runtime( + hass: HomeAssistant, entity_registry: er.EntityRegistry +) -> None: + """Test that new features can be added at runtime.""" + + # Set up a basic cover that does not support position + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_cover.json" + ) + await setup_test_accessories(hass, accessories) + + cover = entity_registry.async_get("cover.family_room_north") + assert cover.unique_id == "00:00:00:00:00:00_123016423_166" + + cover_state = hass.states.get("cover.family_room_north") + assert ( + cover_state.attributes[ATTR_SUPPORTED_FEATURES] + is CoverEntityFeature.OPEN + | CoverEntityFeature.STOP + | CoverEntityFeature.CLOSE + | CoverEntityFeature.SET_POSITION + ) + + cover = entity_registry.async_get("cover.family_room_north") + assert cover.unique_id == "00:00:00:00:00:00_123016423_166" + + # Now change the config to remove stop + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_basic_cover.json" + ) + await device_config_changed(hass, accessories) + + cover_state = hass.states.get("cover.family_room_north") + assert ( + cover_state.attributes[ATTR_SUPPORTED_FEATURES] + is CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.SET_POSITION + ) diff --git a/tests/components/homekit_controller/specific_devices/test_heater_cooler_that_changes_features.py b/tests/components/homekit_controller/specific_devices/test_heater_cooler_that_changes_features.py new file mode 100644 index 00000000000..79b07512c67 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_heater_cooler_that_changes_features.py @@ -0,0 +1,48 @@ +"""Test for a Home Assistant bridge that changes climate features at runtime.""" + + +from homeassistant.components.climate import ATTR_SWING_MODES, ClimateEntityFeature +from homeassistant.const import ATTR_SUPPORTED_FEATURES +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from ..common import ( + device_config_changed, + setup_accessories_from_file, + setup_test_accessories, +) + + +async def test_cover_add_feature_at_runtime( + hass: HomeAssistant, entity_registry: er.EntityRegistry +) -> None: + """Test that new features can be added at runtime.""" + + # Set up a basic heater cooler that does not support swing mode + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_basic_heater_cooler.json" + ) + await setup_test_accessories(hass, accessories) + + climate = entity_registry.async_get("climate.89_living_room") + assert climate.unique_id == "00:00:00:00:00:00_1233851541_169" + + climate_state = hass.states.get("climate.89_living_room") + assert climate_state.attributes[ATTR_SUPPORTED_FEATURES] is ClimateEntityFeature(0) + assert ATTR_SWING_MODES not in climate_state.attributes + + climate = entity_registry.async_get("climate.89_living_room") + assert climate.unique_id == "00:00:00:00:00:00_1233851541_169" + + # Now change the config to add swing mode + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_heater_cooler.json" + ) + await device_config_changed(hass, accessories) + + climate_state = hass.states.get("climate.89_living_room") + assert ( + climate_state.attributes[ATTR_SUPPORTED_FEATURES] + is ClimateEntityFeature.SWING_MODE + ) + assert climate_state.attributes[ATTR_SWING_MODES] == ["off", "vertical"] diff --git a/tests/components/homekit_controller/specific_devices/test_humidifier_that_changes_value_range.py b/tests/components/homekit_controller/specific_devices/test_humidifier_that_changes_value_range.py new file mode 100644 index 00000000000..518bcbbef38 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_humidifier_that_changes_value_range.py @@ -0,0 +1,44 @@ +"""Test for a Home Assistant bridge that changes humidifier min/max at runtime.""" + + +from homeassistant.components.humidifier import ATTR_MAX_HUMIDITY, ATTR_MIN_HUMIDITY +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from ..common import ( + device_config_changed, + setup_accessories_from_file, + setup_test_accessories, +) + + +async def test_humidifier_change_range_at_runtime( + hass: HomeAssistant, entity_registry: er.EntityRegistry +) -> None: + """Test that min max can be changed at runtime.""" + + # Set up a basic humidifier + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_humidifier.json" + ) + await setup_test_accessories(hass, accessories) + + humidifier = entity_registry.async_get("humidifier.humidifier_182a") + assert humidifier.unique_id == "00:00:00:00:00:00_293334836_8" + + humidifier_state = hass.states.get("humidifier.humidifier_182a") + assert humidifier_state.attributes[ATTR_MIN_HUMIDITY] == 0 + assert humidifier_state.attributes[ATTR_MAX_HUMIDITY] == 100 + + cover = entity_registry.async_get("humidifier.humidifier_182a") + assert cover.unique_id == "00:00:00:00:00:00_293334836_8" + + # Now change min/max values + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_humidifier_new_range.json" + ) + await device_config_changed(hass, accessories) + + humidifier_state = hass.states.get("humidifier.humidifier_182a") + assert humidifier_state.attributes[ATTR_MIN_HUMIDITY] == 20 + assert humidifier_state.attributes[ATTR_MAX_HUMIDITY] == 80 diff --git a/tests/components/homekit_controller/specific_devices/test_light_that_changes_features.py b/tests/components/homekit_controller/specific_devices/test_light_that_changes_features.py new file mode 100644 index 00000000000..54dc900c130 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_light_that_changes_features.py @@ -0,0 +1,42 @@ +"""Test for a Home Assistant bridge that changes light features at runtime.""" + + +from homeassistant.components.light import ATTR_SUPPORTED_COLOR_MODES, ColorMode +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from ..common import ( + device_config_changed, + setup_accessories_from_file, + setup_test_accessories, +) + + +async def test_light_add_feature_at_runtime( + hass: HomeAssistant, entity_registry: er.EntityRegistry +) -> None: + """Test that new features can be added at runtime.""" + + # Set up a basic light that does not support color + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_basic_light.json" + ) + await setup_test_accessories(hass, accessories) + + light = entity_registry.async_get("light.laundry_smoke_ed78") + assert light.unique_id == "00:00:00:00:00:00_3982136094_608" + + light_state = hass.states.get("light.laundry_smoke_ed78") + assert light_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + + light = entity_registry.async_get("light.laundry_smoke_ed78") + assert light.unique_id == "00:00:00:00:00:00_3982136094_608" + + # Now add hue and saturation + accessories = await setup_accessories_from_file( + hass, "home_assistant_bridge_light.json" + ) + await device_config_changed(hass, accessories) + + light_state = hass.states.get("light.laundry_smoke_ed78") + assert light_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.HS]