diff --git a/homeassistant/components/ecovacs/binary_sensor.py b/homeassistant/components/ecovacs/binary_sensor.py index f6e3e34aaa4..d755d01a4ae 100644 --- a/homeassistant/components/ecovacs/binary_sensor.py +++ b/homeassistant/components/ecovacs/binary_sensor.py @@ -4,7 +4,7 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Generic -from deebot_client.capabilities import CapabilityEvent, VacuumCapabilities +from deebot_client.capabilities import CapabilityEvent from deebot_client.events.water_info import WaterInfoEvent from homeassistant.components.binary_sensor import ( @@ -16,12 +16,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import EcovacsConfigEntry -from .entity import ( - CapabilityDevice, - EcovacsCapabilityEntityDescription, - EcovacsDescriptionEntity, - EventT, -) +from .entity import EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EventT from .util import get_supported_entitites @@ -38,7 +33,6 @@ class EcovacsBinarySensorEntityDescription( ENTITY_DESCRIPTIONS: tuple[EcovacsBinarySensorEntityDescription, ...] = ( EcovacsBinarySensorEntityDescription[WaterInfoEvent]( - device_capabilities=VacuumCapabilities, capability_fn=lambda caps: caps.water, value_fn=lambda e: e.mop_attached, key="water_mop_attached", @@ -62,7 +56,7 @@ async def async_setup_entry( class EcovacsBinarySensor( - EcovacsDescriptionEntity[CapabilityDevice, CapabilityEvent[EventT]], + EcovacsDescriptionEntity[CapabilityEvent[EventT]], BinarySensorEntity, ): """Ecovacs binary sensor.""" diff --git a/homeassistant/components/ecovacs/button.py b/homeassistant/components/ecovacs/button.py index 14fd54df5a0..5d76b38bed8 100644 --- a/homeassistant/components/ecovacs/button.py +++ b/homeassistant/components/ecovacs/button.py @@ -2,12 +2,7 @@ from dataclasses import dataclass -from deebot_client.capabilities import ( - Capabilities, - CapabilityExecute, - CapabilityLifeSpan, - VacuumCapabilities, -) +from deebot_client.capabilities import CapabilityExecute, CapabilityLifeSpan from deebot_client.events import LifeSpan from homeassistant.components.button import ButtonEntity, ButtonEntityDescription @@ -18,7 +13,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import EcovacsConfigEntry from .const import SUPPORTED_LIFESPANS from .entity import ( - CapabilityDevice, EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EcovacsEntity, @@ -43,7 +37,6 @@ class EcovacsLifespanButtonEntityDescription(ButtonEntityDescription): ENTITY_DESCRIPTIONS: tuple[EcovacsButtonEntityDescription, ...] = ( EcovacsButtonEntityDescription( - device_capabilities=VacuumCapabilities, capability_fn=lambda caps: caps.map.relocation if caps.map else None, key="relocate", translation_key="relocate", @@ -77,7 +70,7 @@ async def async_setup_entry( EcovacsResetLifespanButtonEntity( device, device.capabilities.life_span, description ) - for device in controller.devices(Capabilities) + for device in controller.devices for description in LIFESPAN_ENTITY_DESCRIPTIONS if description.component in device.capabilities.life_span.types ) @@ -85,7 +78,7 @@ async def async_setup_entry( class EcovacsButtonEntity( - EcovacsDescriptionEntity[CapabilityDevice, CapabilityExecute], + EcovacsDescriptionEntity[CapabilityExecute], ButtonEntity, ): """Ecovacs button entity.""" @@ -98,7 +91,7 @@ class EcovacsButtonEntity( class EcovacsResetLifespanButtonEntity( - EcovacsDescriptionEntity[Capabilities, CapabilityLifeSpan], + EcovacsDescriptionEntity[CapabilityLifeSpan], ButtonEntity, ): """Ecovacs reset lifespan button entity.""" diff --git a/homeassistant/components/ecovacs/controller.py b/homeassistant/components/ecovacs/controller.py index 3e2d2ebdd9a..0bef2e8fdd7 100644 --- a/homeassistant/components/ecovacs/controller.py +++ b/homeassistant/components/ecovacs/controller.py @@ -9,7 +9,6 @@ from typing import Any from deebot_client.api_client import ApiClient from deebot_client.authentication import Authenticator, create_rest_config -from deebot_client.capabilities import Capabilities from deebot_client.const import UNDEFINED, UndefinedType from deebot_client.device import Device from deebot_client.exceptions import DeebotError, InvalidAuthenticationError @@ -18,10 +17,9 @@ from deebot_client.mqtt_client import MqttClient, create_mqtt_config from deebot_client.util import md5 from deebot_client.util.continents import get_continent from sucks import EcoVacsAPI, VacBot -from typing_extensions import Generator from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady from homeassistant.helpers import aiohttp_client from homeassistant.util.ssl import get_default_no_verify_context @@ -119,12 +117,10 @@ class EcovacsController: await self._mqtt.disconnect() await self._authenticator.teardown() - @callback - def devices(self, capability: type[Capabilities]) -> Generator[Device]: - """Return generator for devices with a specific capability.""" - for device in self._devices: - if isinstance(device.capabilities, capability): - yield device + @property + def devices(self) -> list[Device]: + """Return devices.""" + return self._devices @property def legacy_devices(self) -> list[VacBot]: diff --git a/homeassistant/components/ecovacs/diagnostics.py b/homeassistant/components/ecovacs/diagnostics.py index 50b59b90860..22a55d9c6ab 100644 --- a/homeassistant/components/ecovacs/diagnostics.py +++ b/homeassistant/components/ecovacs/diagnostics.py @@ -4,8 +4,6 @@ from __future__ import annotations from typing import Any -from deebot_client.capabilities import Capabilities - from homeassistant.components.diagnostics import async_redact_data from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant @@ -34,7 +32,7 @@ async def async_get_config_entry_diagnostics( diag["devices"] = [ async_redact_data(device.device_info, REDACT_DEVICE) - for device in controller.devices(Capabilities) + for device in controller.devices ] diag["legacy_devices"] = [ async_redact_data(device.vacuum, REDACT_DEVICE) diff --git a/homeassistant/components/ecovacs/entity.py b/homeassistant/components/ecovacs/entity.py index 4497f82d964..c038c54497a 100644 --- a/homeassistant/components/ecovacs/entity.py +++ b/homeassistant/components/ecovacs/entity.py @@ -18,11 +18,10 @@ from homeassistant.helpers.entity import Entity, EntityDescription from .const import DOMAIN CapabilityEntity = TypeVar("CapabilityEntity") -CapabilityDevice = TypeVar("CapabilityDevice", bound=Capabilities) EventT = TypeVar("EventT", bound=Event) -class EcovacsEntity(Entity, Generic[CapabilityDevice, CapabilityEntity]): +class EcovacsEntity(Entity, Generic[CapabilityEntity]): """Ecovacs entity.""" _attr_should_poll = False @@ -31,7 +30,7 @@ class EcovacsEntity(Entity, Generic[CapabilityDevice, CapabilityEntity]): def __init__( self, - device: Device[CapabilityDevice], + device: Device, capability: CapabilityEntity, **kwargs: Any, ) -> None: @@ -97,12 +96,12 @@ class EcovacsEntity(Entity, Generic[CapabilityDevice, CapabilityEntity]): self._device.events.request_refresh(event_type) -class EcovacsDescriptionEntity(EcovacsEntity[CapabilityDevice, CapabilityEntity]): +class EcovacsDescriptionEntity(EcovacsEntity[CapabilityEntity]): """Ecovacs entity.""" def __init__( self, - device: Device[CapabilityDevice], + device: Device, capability: CapabilityEntity, entity_description: EntityDescription, **kwargs: Any, @@ -115,9 +114,8 @@ class EcovacsDescriptionEntity(EcovacsEntity[CapabilityDevice, CapabilityEntity] @dataclass(kw_only=True, frozen=True) class EcovacsCapabilityEntityDescription( EntityDescription, - Generic[CapabilityDevice, CapabilityEntity], + Generic[CapabilityEntity], ): """Ecovacs entity description.""" - device_capabilities: type[CapabilityDevice] - capability_fn: Callable[[CapabilityDevice], CapabilityEntity | None] + capability_fn: Callable[[Capabilities], CapabilityEntity | None] diff --git a/homeassistant/components/ecovacs/event.py b/homeassistant/components/ecovacs/event.py index 9e4dde00b54..3249b466c77 100644 --- a/homeassistant/components/ecovacs/event.py +++ b/homeassistant/components/ecovacs/event.py @@ -1,6 +1,6 @@ """Event module.""" -from deebot_client.capabilities import Capabilities, CapabilityEvent +from deebot_client.capabilities import CapabilityEvent from deebot_client.device import Device from deebot_client.events import CleanJobStatus, ReportStatsEvent @@ -22,12 +22,12 @@ async def async_setup_entry( """Add entities for passed config_entry in HA.""" controller = config_entry.runtime_data async_add_entities( - EcovacsLastJobEventEntity(device) for device in controller.devices(Capabilities) + EcovacsLastJobEventEntity(device) for device in controller.devices ) class EcovacsLastJobEventEntity( - EcovacsEntity[Capabilities, CapabilityEvent[ReportStatsEvent]], + EcovacsEntity[CapabilityEvent[ReportStatsEvent]], EventEntity, ): """Ecovacs last job event entity.""" @@ -39,7 +39,7 @@ class EcovacsLastJobEventEntity( event_types=["finished", "finished_with_warnings", "manually_stopped"], ) - def __init__(self, device: Device[Capabilities]) -> None: + def __init__(self, device: Device) -> None: """Initialize entity.""" super().__init__(device, device.capabilities.stats.report) diff --git a/homeassistant/components/ecovacs/image.py b/homeassistant/components/ecovacs/image.py index 1e94dc856ee..d8b69084cec 100644 --- a/homeassistant/components/ecovacs/image.py +++ b/homeassistant/components/ecovacs/image.py @@ -1,6 +1,6 @@ """Ecovacs image entities.""" -from deebot_client.capabilities import CapabilityMap, VacuumCapabilities +from deebot_client.capabilities import CapabilityMap from deebot_client.device import Device from deebot_client.events.map import CachedMapInfoEvent, MapChangedEvent @@ -20,18 +20,18 @@ async def async_setup_entry( ) -> None: """Add entities for passed config_entry in HA.""" controller = config_entry.runtime_data - entities = [] - for device in controller.devices(VacuumCapabilities): - capabilities: VacuumCapabilities = device.capabilities - if caps := capabilities.map: - entities.append(EcovacsMap(device, caps, hass)) + entities = [ + EcovacsMap(device, caps, hass) + for device in controller.devices + if (caps := device.capabilities.map) + ] if entities: async_add_entities(entities) class EcovacsMap( - EcovacsEntity[VacuumCapabilities, CapabilityMap], + EcovacsEntity[CapabilityMap], ImageEntity, ): """Ecovacs map.""" diff --git a/homeassistant/components/ecovacs/lawn_mower.py b/homeassistant/components/ecovacs/lawn_mower.py index 2561fe22217..a1dc8acf3a2 100644 --- a/homeassistant/components/ecovacs/lawn_mower.py +++ b/homeassistant/components/ecovacs/lawn_mower.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging -from deebot_client.capabilities import MowerCapabilities +from deebot_client.capabilities import Capabilities, DeviceType from deebot_client.device import Device from deebot_client.events import StateEvent from deebot_client.models import CleanAction, State @@ -42,14 +42,16 @@ async def async_setup_entry( """Set up the Ecovacs mowers.""" controller = config_entry.runtime_data mowers: list[EcovacsMower] = [ - EcovacsMower(device) for device in controller.devices(MowerCapabilities) + EcovacsMower(device) + for device in controller.devices + if device.capabilities.device_type is DeviceType.MOWER ] _LOGGER.debug("Adding Ecovacs Mowers to Home Assistant: %s", mowers) async_add_entities(mowers) class EcovacsMower( - EcovacsEntity[MowerCapabilities, MowerCapabilities], + EcovacsEntity[Capabilities], LawnMowerEntity, ): """Ecovacs Mower.""" @@ -62,10 +64,9 @@ class EcovacsMower( entity_description = LawnMowerEntityEntityDescription(key="mower", name=None) - def __init__(self, device: Device[MowerCapabilities]) -> None: + def __init__(self, device: Device) -> None: """Initialize the mower.""" - capabilities = device.capabilities - super().__init__(device, capabilities) + super().__init__(device, device.capabilities) async def async_added_to_hass(self) -> None: """Set up the event listeners now that hass is ready.""" diff --git a/homeassistant/components/ecovacs/manifest.json b/homeassistant/components/ecovacs/manifest.json index 66dd07cf431..d14291576ff 100644 --- a/homeassistant/components/ecovacs/manifest.json +++ b/homeassistant/components/ecovacs/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/ecovacs", "iot_class": "cloud_push", "loggers": ["sleekxmppfs", "sucks", "deebot_client"], - "requirements": ["py-sucks==0.9.10", "deebot-client==7.3.0"] + "requirements": ["py-sucks==0.9.10", "deebot-client==8.0.0"] } diff --git a/homeassistant/components/ecovacs/number.py b/homeassistant/components/ecovacs/number.py index bd8ce50aadb..bfe840dad42 100644 --- a/homeassistant/components/ecovacs/number.py +++ b/homeassistant/components/ecovacs/number.py @@ -6,7 +6,7 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Generic -from deebot_client.capabilities import Capabilities, CapabilitySet, VacuumCapabilities +from deebot_client.capabilities import CapabilitySet from deebot_client.events import CleanCountEvent, VolumeEvent from homeassistant.components.number import NumberEntity, NumberEntityDescription @@ -16,7 +16,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import EcovacsConfigEntry from .entity import ( - CapabilityDevice, EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EcovacsEntity, @@ -39,7 +38,6 @@ class EcovacsNumberEntityDescription( ENTITY_DESCRIPTIONS: tuple[EcovacsNumberEntityDescription, ...] = ( EcovacsNumberEntityDescription[VolumeEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.settings.volume, value_fn=lambda e: e.volume, native_max_value_fn=lambda e: e.maximum, @@ -52,7 +50,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsNumberEntityDescription, ...] = ( native_step=1.0, ), EcovacsNumberEntityDescription[CleanCountEvent]( - device_capabilities=VacuumCapabilities, capability_fn=lambda caps: caps.clean.count, value_fn=lambda e: e.count, key="clean_count", @@ -81,7 +78,7 @@ async def async_setup_entry( class EcovacsNumberEntity( - EcovacsDescriptionEntity[CapabilityDevice, CapabilitySet[EventT, int]], + EcovacsDescriptionEntity[CapabilitySet[EventT, int]], NumberEntity, ): """Ecovacs number entity.""" diff --git a/homeassistant/components/ecovacs/select.py b/homeassistant/components/ecovacs/select.py index 4caa6327bb3..c8b01a0f83a 100644 --- a/homeassistant/components/ecovacs/select.py +++ b/homeassistant/components/ecovacs/select.py @@ -4,7 +4,7 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Any, Generic -from deebot_client.capabilities import CapabilitySetTypes, VacuumCapabilities +from deebot_client.capabilities import CapabilitySetTypes from deebot_client.device import Device from deebot_client.events import WaterInfoEvent, WorkModeEvent @@ -14,12 +14,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import EcovacsConfigEntry -from .entity import ( - CapabilityDevice, - EcovacsCapabilityEntityDescription, - EcovacsDescriptionEntity, - EventT, -) +from .entity import EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EventT from .util import get_name_key, get_supported_entitites @@ -37,7 +32,6 @@ class EcovacsSelectEntityDescription( ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = ( EcovacsSelectEntityDescription[WaterInfoEvent]( - device_capabilities=VacuumCapabilities, capability_fn=lambda caps: caps.water, current_option_fn=lambda e: get_name_key(e.amount), options_fn=lambda water: [get_name_key(amount) for amount in water.types], @@ -46,7 +40,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = ( entity_category=EntityCategory.CONFIG, ), EcovacsSelectEntityDescription[WorkModeEvent]( - device_capabilities=VacuumCapabilities, capability_fn=lambda caps: caps.clean.work_mode, current_option_fn=lambda e: get_name_key(e.mode), options_fn=lambda cap: [get_name_key(mode) for mode in cap.types], @@ -73,7 +66,7 @@ async def async_setup_entry( class EcovacsSelectEntity( - EcovacsDescriptionEntity[CapabilityDevice, CapabilitySetTypes[EventT, str]], + EcovacsDescriptionEntity[CapabilitySetTypes[EventT, str]], SelectEntity, ): """Ecovacs select entity.""" diff --git a/homeassistant/components/ecovacs/sensor.py b/homeassistant/components/ecovacs/sensor.py index e9229781827..256198693fb 100644 --- a/homeassistant/components/ecovacs/sensor.py +++ b/homeassistant/components/ecovacs/sensor.py @@ -6,7 +6,7 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Generic -from deebot_client.capabilities import Capabilities, CapabilityEvent, CapabilityLifeSpan +from deebot_client.capabilities import CapabilityEvent, CapabilityLifeSpan from deebot_client.events import ( BatteryEvent, ErrorEvent, @@ -39,7 +39,6 @@ from homeassistant.helpers.typing import StateType from . import EcovacsConfigEntry from .const import SUPPORTED_LIFESPANS from .entity import ( - CapabilityDevice, EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EcovacsEntity, @@ -63,7 +62,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( # Stats EcovacsSensorEntityDescription[StatsEvent]( key="stats_area", - device_capabilities=Capabilities, capability_fn=lambda caps: caps.stats.clean, value_fn=lambda e: e.area, translation_key="stats_area", @@ -71,7 +69,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( ), EcovacsSensorEntityDescription[StatsEvent]( key="stats_time", - device_capabilities=Capabilities, capability_fn=lambda caps: caps.stats.clean, value_fn=lambda e: e.time, translation_key="stats_time", @@ -81,7 +78,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( ), # TotalStats EcovacsSensorEntityDescription[TotalStatsEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.stats.total, value_fn=lambda e: e.area, key="total_stats_area", @@ -90,7 +86,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, ), EcovacsSensorEntityDescription[TotalStatsEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.stats.total, value_fn=lambda e: e.time, key="total_stats_time", @@ -101,7 +96,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, ), EcovacsSensorEntityDescription[TotalStatsEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.stats.total, value_fn=lambda e: e.cleanings, key="total_stats_cleanings", @@ -109,7 +103,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, ), EcovacsSensorEntityDescription[BatteryEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.battery, value_fn=lambda e: e.value, key=ATTR_BATTERY_LEVEL, @@ -118,7 +111,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, ), EcovacsSensorEntityDescription[NetworkInfoEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.network, value_fn=lambda e: e.ip, key="network_ip", @@ -127,7 +119,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, ), EcovacsSensorEntityDescription[NetworkInfoEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.network, value_fn=lambda e: e.rssi, key="network_rssi", @@ -136,7 +127,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, ), EcovacsSensorEntityDescription[NetworkInfoEvent]( - device_capabilities=Capabilities, capability_fn=lambda caps: caps.network, value_fn=lambda e: e.ssid, key="network_ssid", @@ -181,13 +171,13 @@ async def async_setup_entry( ) entities.extend( EcovacsLifespanSensor(device, device.capabilities.life_span, description) - for device in controller.devices(Capabilities) + for device in controller.devices for description in LIFESPAN_ENTITY_DESCRIPTIONS if description.component in device.capabilities.life_span.types ) entities.extend( EcovacsErrorSensor(device, capability) - for device in controller.devices(Capabilities) + for device in controller.devices if (capability := device.capabilities.error) ) @@ -195,7 +185,7 @@ async def async_setup_entry( class EcovacsSensor( - EcovacsDescriptionEntity[CapabilityDevice, CapabilityEvent], + EcovacsDescriptionEntity[CapabilityEvent], SensorEntity, ): """Ecovacs sensor.""" @@ -218,7 +208,7 @@ class EcovacsSensor( class EcovacsLifespanSensor( - EcovacsDescriptionEntity[Capabilities, CapabilityLifeSpan], + EcovacsDescriptionEntity[CapabilityLifeSpan], SensorEntity, ): """Lifespan sensor.""" @@ -238,7 +228,7 @@ class EcovacsLifespanSensor( class EcovacsErrorSensor( - EcovacsEntity[Capabilities, CapabilityEvent[ErrorEvent]], + EcovacsEntity[CapabilityEvent[ErrorEvent]], SensorEntity, ): """Error sensor.""" diff --git a/homeassistant/components/ecovacs/switch.py b/homeassistant/components/ecovacs/switch.py index 25ecb53e278..872981b5c28 100644 --- a/homeassistant/components/ecovacs/switch.py +++ b/homeassistant/components/ecovacs/switch.py @@ -3,11 +3,7 @@ from dataclasses import dataclass from typing import Any -from deebot_client.capabilities import ( - Capabilities, - CapabilitySetEnable, - VacuumCapabilities, -) +from deebot_client.capabilities import CapabilitySetEnable from deebot_client.events import EnableEvent from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription @@ -17,7 +13,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import EcovacsConfigEntry from .entity import ( - CapabilityDevice, EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EcovacsEntity, @@ -28,86 +23,76 @@ from .util import get_supported_entitites @dataclass(kw_only=True, frozen=True) class EcovacsSwitchEntityDescription( SwitchEntityDescription, - EcovacsCapabilityEntityDescription[CapabilityDevice, CapabilitySetEnable], + EcovacsCapabilityEntityDescription[CapabilitySetEnable], ): """Ecovacs switch entity description.""" ENTITY_DESCRIPTIONS: tuple[EcovacsSwitchEntityDescription, ...] = ( - EcovacsSwitchEntityDescription[Capabilities]( - device_capabilities=Capabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.advanced_mode, key="advanced_mode", translation_key="advanced_mode", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[VacuumCapabilities]( - device_capabilities=VacuumCapabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.clean.continuous, key="continuous_cleaning", translation_key="continuous_cleaning", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[VacuumCapabilities]( - device_capabilities=VacuumCapabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.carpet_auto_fan_boost, key="carpet_auto_fan_boost", translation_key="carpet_auto_fan_boost", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[VacuumCapabilities]( - device_capabilities=VacuumCapabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.clean.preference, key="clean_preference", translation_key="clean_preference", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[Capabilities]( - device_capabilities=Capabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.true_detect, key="true_detect", translation_key="true_detect", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[Capabilities]( - device_capabilities=Capabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.border_switch, key="border_switch", translation_key="border_switch", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[Capabilities]( - device_capabilities=Capabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.child_lock, key="child_lock", translation_key="child_lock", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[Capabilities]( - device_capabilities=Capabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.moveup_warning, key="move_up_warning", translation_key="move_up_warning", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[Capabilities]( - device_capabilities=Capabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.cross_map_border_warning, key="cross_map_border_warning", translation_key="cross_map_border_warning", entity_registry_enabled_default=False, entity_category=EntityCategory.CONFIG, ), - EcovacsSwitchEntityDescription[Capabilities]( - device_capabilities=Capabilities, + EcovacsSwitchEntityDescription( capability_fn=lambda c: c.settings.safe_protect, key="safe_protect", translation_key="safe_protect", @@ -132,7 +117,7 @@ async def async_setup_entry( class EcovacsSwitchEntity( - EcovacsDescriptionEntity[CapabilityDevice, CapabilitySetEnable], + EcovacsDescriptionEntity[CapabilitySetEnable], SwitchEntity, ): """Ecovacs switch entity.""" diff --git a/homeassistant/components/ecovacs/util.py b/homeassistant/components/ecovacs/util.py index 9d692bbbb8f..a4894de8968 100644 --- a/homeassistant/components/ecovacs/util.py +++ b/homeassistant/components/ecovacs/util.py @@ -7,8 +7,6 @@ import random import string from typing import TYPE_CHECKING -from deebot_client.capabilities import Capabilities - from homeassistant.core import HomeAssistant, callback from homeassistant.util import slugify @@ -40,9 +38,8 @@ def get_supported_entitites( """Return all supported entities for all devices.""" return [ entity_class(device, capability, description) - for device in controller.devices(Capabilities) + for device in controller.devices for description in descriptions - if isinstance(device.capabilities, description.device_capabilities) if (capability := description.capability_fn(device.capabilities)) ] diff --git a/homeassistant/components/ecovacs/vacuum.py b/homeassistant/components/ecovacs/vacuum.py index e637eb14fd6..401274609d8 100644 --- a/homeassistant/components/ecovacs/vacuum.py +++ b/homeassistant/components/ecovacs/vacuum.py @@ -4,9 +4,9 @@ from __future__ import annotations from collections.abc import Mapping import logging -from typing import Any +from typing import TYPE_CHECKING, Any -from deebot_client.capabilities import VacuumCapabilities +from deebot_client.capabilities import Capabilities, DeviceType from deebot_client.device import Device from deebot_client.events import BatteryEvent, FanSpeedEvent, RoomsEvent, StateEvent from deebot_client.models import CleanAction, CleanMode, Room, State @@ -52,7 +52,9 @@ async def async_setup_entry( controller = config_entry.runtime_data vacuums: list[EcovacsVacuum | EcovacsLegacyVacuum] = [ - EcovacsVacuum(device) for device in controller.devices(VacuumCapabilities) + EcovacsVacuum(device) + for device in controller.devices + if device.capabilities.device_type is DeviceType.VACUUM ] for device in controller.legacy_devices: await hass.async_add_executor_job(device.connect_and_wait_until_ready) @@ -232,7 +234,7 @@ _ATTR_ROOMS = "rooms" class EcovacsVacuum( - EcovacsEntity[VacuumCapabilities, VacuumCapabilities], + EcovacsEntity[Capabilities], StateVacuumEntity, ): """Ecovacs vacuum.""" @@ -243,7 +245,6 @@ class EcovacsVacuum( VacuumEntityFeature.PAUSE | VacuumEntityFeature.STOP | VacuumEntityFeature.RETURN_HOME - | VacuumEntityFeature.FAN_SPEED | VacuumEntityFeature.BATTERY | VacuumEntityFeature.SEND_COMMAND | VacuumEntityFeature.LOCATE @@ -255,16 +256,17 @@ class EcovacsVacuum( key="vacuum", translation_key="vacuum", name=None ) - def __init__(self, device: Device[VacuumCapabilities]) -> None: + def __init__(self, device: Device) -> None: """Initialize the vacuum.""" - capabilities = device.capabilities - super().__init__(device, capabilities) + super().__init__(device, device.capabilities) self._rooms: list[Room] = [] - self._attr_fan_speed_list = [ - get_name_key(level) for level in capabilities.fan_speed.types - ] + if fan_speed := self._capability.fan_speed: + self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED + self._attr_fan_speed_list = [ + get_name_key(level) for level in fan_speed.types + ] async def async_added_to_hass(self) -> None: """Set up the event listeners now that hass is ready.""" @@ -274,10 +276,6 @@ class EcovacsVacuum( self._attr_battery_level = event.value self.async_write_ha_state() - async def on_fan_speed(event: FanSpeedEvent) -> None: - self._attr_fan_speed = get_name_key(event.speed) - self.async_write_ha_state() - async def on_rooms(event: RoomsEvent) -> None: self._rooms = event.rooms self.async_write_ha_state() @@ -287,9 +285,16 @@ class EcovacsVacuum( self.async_write_ha_state() self._subscribe(self._capability.battery.event, on_battery) - self._subscribe(self._capability.fan_speed.event, on_fan_speed) self._subscribe(self._capability.state.event, on_status) + if self._capability.fan_speed: + + async def on_fan_speed(event: FanSpeedEvent) -> None: + self._attr_fan_speed = get_name_key(event.speed) + self.async_write_ha_state() + + self._subscribe(self._capability.fan_speed.event, on_fan_speed) + if map_caps := self._capability.map: self._subscribe(map_caps.rooms.event, on_rooms) @@ -319,6 +324,8 @@ class EcovacsVacuum( async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: """Set fan speed.""" + if TYPE_CHECKING: + assert self._capability.fan_speed await self._device.execute_command(self._capability.fan_speed.set(fan_speed)) async def async_return_to_base(self, **kwargs: Any) -> None: diff --git a/requirements_all.txt b/requirements_all.txt index 74df113ae97..88644b6b602 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -706,7 +706,7 @@ debugpy==1.8.1 # decora==0.6 # homeassistant.components.ecovacs -deebot-client==7.3.0 +deebot-client==8.0.0 # homeassistant.components.ihc # homeassistant.components.namecheapdns diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a508c8ff21e..b84951b56b9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -584,7 +584,7 @@ dbus-fast==2.21.3 debugpy==1.8.1 # homeassistant.components.ecovacs -deebot-client==7.3.0 +deebot-client==8.0.0 # homeassistant.components.ihc # homeassistant.components.namecheapdns diff --git a/tests/components/ecovacs/test_binary_sensor.py b/tests/components/ecovacs/test_binary_sensor.py index 697e57c6def..b57f67e948e 100644 --- a/tests/components/ecovacs/test_binary_sensor.py +++ b/tests/components/ecovacs/test_binary_sensor.py @@ -1,6 +1,5 @@ """Tests for Ecovacs binary sensors.""" -from deebot_client.capabilities import Capabilities from deebot_client.events import WaterAmount, WaterInfoEvent import pytest from syrupy import SnapshotAssertion @@ -38,7 +37,7 @@ async def test_mop_attached( assert entity_entry == snapshot(name=f"{entity_id}-entity_entry") assert entity_entry.device_id - device = next(controller.devices(Capabilities)) + device = controller.devices[0] assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])} diff --git a/tests/components/ecovacs/test_button.py b/tests/components/ecovacs/test_button.py index 82a75654b58..08d53f3e93d 100644 --- a/tests/components/ecovacs/test_button.py +++ b/tests/components/ecovacs/test_button.py @@ -1,6 +1,5 @@ """Tests for Ecovacs sensors.""" -from deebot_client.capabilities import Capabilities from deebot_client.command import Command from deebot_client.commands.json import ResetLifeSpan, SetRelocationState from deebot_client.events import LifeSpan @@ -74,7 +73,7 @@ async def test_buttons( ) -> None: """Test that sensor entity snapshots match.""" assert hass.states.async_entity_ids() == [e[0] for e in entities] - device = next(controller.devices(Capabilities)) + device = controller.devices[0] for entity_id, command in entities: assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert state.state == STATE_UNKNOWN diff --git a/tests/components/ecovacs/test_event.py b/tests/components/ecovacs/test_event.py index 1ee3efbf64d..03fb79e083f 100644 --- a/tests/components/ecovacs/test_event.py +++ b/tests/components/ecovacs/test_event.py @@ -2,7 +2,6 @@ from datetime import timedelta -from deebot_client.capabilities import Capabilities from deebot_client.events import CleanJobStatus, ReportStatsEvent from freezegun.api import FrozenDateTimeFactory import pytest @@ -44,7 +43,7 @@ async def test_last_job( assert entity_entry == snapshot(name=f"{entity_id}-entity_entry") assert entity_entry.device_id - device = next(controller.devices(Capabilities)) + device = controller.devices[0] assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])} diff --git a/tests/components/ecovacs/test_init.py b/tests/components/ecovacs/test_init.py index 752276015d3..27d00a2d023 100644 --- a/tests/components/ecovacs/test_init.py +++ b/tests/components/ecovacs/test_init.py @@ -3,7 +3,6 @@ from typing import Any from unittest.mock import AsyncMock, Mock, patch -from deebot_client.capabilities import Capabilities from deebot_client.exceptions import DeebotError, InvalidAuthenticationError import pytest from syrupy import SnapshotAssertion @@ -121,7 +120,7 @@ async def test_devices_in_dr( snapshot: SnapshotAssertion, ) -> None: """Test all devices are in the device registry.""" - for device in controller.devices(Capabilities): + for device in controller.devices: assert ( device_entry := device_registry.async_get_device( identifiers={(DOMAIN, device.device_info["did"])} diff --git a/tests/components/ecovacs/test_lawn_mower.py b/tests/components/ecovacs/test_lawn_mower.py index cd49374d4c2..2c0abd0a49e 100644 --- a/tests/components/ecovacs/test_lawn_mower.py +++ b/tests/components/ecovacs/test_lawn_mower.py @@ -2,7 +2,6 @@ from dataclasses import dataclass -from deebot_client.capabilities import MowerCapabilities from deebot_client.command import Command from deebot_client.commands.json import Charge, CleanV2 from deebot_client.events import StateEvent @@ -56,7 +55,7 @@ async def test_lawn_mower( assert entity_entry == snapshot(name=f"{entity_id}-entity_entry") assert entity_entry.device_id - device = next(controller.devices(MowerCapabilities)) + device = controller.devices[0] assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])} @@ -104,7 +103,7 @@ async def test_mover_services( tests: list[MowerTestCase], ) -> None: """Test mover services.""" - device = next(controller.devices(MowerCapabilities)) + device = controller.devices[0] for test in tests: device._execute_command.reset_mock() diff --git a/tests/components/ecovacs/test_number.py b/tests/components/ecovacs/test_number.py index 0b758fa6860..d444d6510a8 100644 --- a/tests/components/ecovacs/test_number.py +++ b/tests/components/ecovacs/test_number.py @@ -2,7 +2,6 @@ from dataclasses import dataclass -from deebot_client.capabilities import Capabilities from deebot_client.command import Command from deebot_client.commands.json import SetVolume from deebot_client.events import Event, VolumeEvent @@ -66,7 +65,7 @@ async def test_number_entities( tests: list[NumberTestCase], ) -> None: """Test that number entity snapshots match.""" - device = next(controller.devices(Capabilities)) + device = controller.devices[0] event_bus = device.events assert sorted(hass.states.async_entity_ids()) == sorted( @@ -131,7 +130,7 @@ async def test_volume_maximum( controller: EcovacsController, ) -> None: """Test volume maximum.""" - device = next(controller.devices(Capabilities)) + device = controller.devices[0] event_bus = device.events entity_id = "number.ozmo_950_volume" assert (state := hass.states.get(entity_id)) diff --git a/tests/components/ecovacs/test_select.py b/tests/components/ecovacs/test_select.py index b7e9435b416..02a6b5ebfa4 100644 --- a/tests/components/ecovacs/test_select.py +++ b/tests/components/ecovacs/test_select.py @@ -1,6 +1,5 @@ """Tests for Ecovacs select entities.""" -from deebot_client.capabilities import Capabilities from deebot_client.command import Command from deebot_client.commands.json import SetWaterInfo from deebot_client.event_bus import EventBus @@ -64,7 +63,7 @@ async def test_selects( assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert state.state == STATE_UNKNOWN - device = next(controller.devices(Capabilities)) + device = controller.devices[0] await notify_events(hass, device.events) for entity_id in entity_ids: assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" @@ -100,7 +99,7 @@ async def test_selects_change( command: Command, ) -> None: """Test that changing select entities works.""" - device = next(controller.devices(Capabilities)) + device = controller.devices[0] await notify_events(hass, device.events) assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" diff --git a/tests/components/ecovacs/test_sensor.py b/tests/components/ecovacs/test_sensor.py index 5b8bf18e1d8..005d10bffbd 100644 --- a/tests/components/ecovacs/test_sensor.py +++ b/tests/components/ecovacs/test_sensor.py @@ -1,6 +1,5 @@ """Tests for Ecovacs sensors.""" -from deebot_client.capabilities import Capabilities from deebot_client.event_bus import EventBus from deebot_client.events import ( BatteryEvent, @@ -103,7 +102,7 @@ async def test_sensors( assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert state.state == STATE_UNKNOWN - device = next(controller.devices(Capabilities)) + device = controller.devices[0] await notify_events(hass, device.events) for entity_id in entity_ids: assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" diff --git a/tests/components/ecovacs/test_switch.py b/tests/components/ecovacs/test_switch.py index 2e3feb36586..b14cafeaba4 100644 --- a/tests/components/ecovacs/test_switch.py +++ b/tests/components/ecovacs/test_switch.py @@ -2,7 +2,6 @@ from dataclasses import dataclass -from deebot_client.capabilities import Capabilities from deebot_client.command import Command from deebot_client.commands.json import ( SetAdvancedMode, @@ -140,7 +139,7 @@ async def test_switch_entities( tests: list[SwitchTestCase], ) -> None: """Test switch entities.""" - device = next(controller.devices(Capabilities)) + device = controller.devices[0] event_bus = device.events assert hass.states.async_entity_ids() == [test.entity_id for test in tests]