Bump deebot-client to 6.0.2 (#111507)

This commit is contained in:
Robert Resch 2024-02-27 09:39:49 +01:00 committed by GitHub
parent d812507aeb
commit 3703698f77
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 155 additions and 77 deletions

View file

@ -3,7 +3,7 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Generic from typing import Generic
from deebot_client.capabilities import CapabilityEvent from deebot_client.capabilities import CapabilityEvent, VacuumCapabilities
from deebot_client.events.water_info import WaterInfoEvent from deebot_client.events.water_info import WaterInfoEvent
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
@ -17,7 +17,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .controller import EcovacsController from .controller import EcovacsController
from .entity import EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EventT from .entity import (
CapabilityDevice,
EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity,
EventT,
)
from .util import get_supported_entitites from .util import get_supported_entitites
@ -34,6 +39,7 @@ class EcovacsBinarySensorEntityDescription(
ENTITY_DESCRIPTIONS: tuple[EcovacsBinarySensorEntityDescription, ...] = ( ENTITY_DESCRIPTIONS: tuple[EcovacsBinarySensorEntityDescription, ...] = (
EcovacsBinarySensorEntityDescription[WaterInfoEvent]( EcovacsBinarySensorEntityDescription[WaterInfoEvent](
device_capabilities=VacuumCapabilities,
capability_fn=lambda caps: caps.water, capability_fn=lambda caps: caps.water,
value_fn=lambda e: e.mop_attached, value_fn=lambda e: e.mop_attached,
key="water_mop_attached", key="water_mop_attached",
@ -56,7 +62,7 @@ async def async_setup_entry(
class EcovacsBinarySensor( class EcovacsBinarySensor(
EcovacsDescriptionEntity[CapabilityEvent[EventT]], EcovacsDescriptionEntity[CapabilityDevice, CapabilityEvent[EventT]],
BinarySensorEntity, BinarySensorEntity,
): ):
"""Ecovacs binary sensor.""" """Ecovacs binary sensor."""

View file

@ -1,7 +1,12 @@
"""Ecovacs button module.""" """Ecovacs button module."""
from dataclasses import dataclass from dataclasses import dataclass
from deebot_client.capabilities import CapabilityExecute, CapabilityLifeSpan from deebot_client.capabilities import (
Capabilities,
CapabilityExecute,
CapabilityLifeSpan,
VacuumCapabilities,
)
from deebot_client.events import LifeSpan from deebot_client.events import LifeSpan
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
@ -13,6 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, SUPPORTED_LIFESPANS from .const import DOMAIN, SUPPORTED_LIFESPANS
from .controller import EcovacsController from .controller import EcovacsController
from .entity import ( from .entity import (
CapabilityDevice,
EcovacsCapabilityEntityDescription, EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity, EcovacsDescriptionEntity,
EcovacsEntity, EcovacsEntity,
@ -37,6 +43,7 @@ class EcovacsLifespanButtonEntityDescription(ButtonEntityDescription):
ENTITY_DESCRIPTIONS: tuple[EcovacsButtonEntityDescription, ...] = ( ENTITY_DESCRIPTIONS: tuple[EcovacsButtonEntityDescription, ...] = (
EcovacsButtonEntityDescription( EcovacsButtonEntityDescription(
device_capabilities=VacuumCapabilities,
capability_fn=lambda caps: caps.map.relocation if caps.map else None, capability_fn=lambda caps: caps.map.relocation if caps.map else None,
key="relocate", key="relocate",
translation_key="relocate", translation_key="relocate",
@ -66,7 +73,7 @@ async def async_setup_entry(
entities: list[EcovacsEntity] = get_supported_entitites( entities: list[EcovacsEntity] = get_supported_entitites(
controller, EcovacsButtonEntity, ENTITY_DESCRIPTIONS controller, EcovacsButtonEntity, ENTITY_DESCRIPTIONS
) )
for device in controller.devices: for device in controller.devices(Capabilities):
lifespan_capability = device.capabilities.life_span lifespan_capability = device.capabilities.life_span
for description in LIFESPAN_ENTITY_DESCRIPTIONS: for description in LIFESPAN_ENTITY_DESCRIPTIONS:
if description.component in lifespan_capability.types: if description.component in lifespan_capability.types:
@ -81,7 +88,7 @@ async def async_setup_entry(
class EcovacsButtonEntity( class EcovacsButtonEntity(
EcovacsDescriptionEntity[CapabilityExecute], EcovacsDescriptionEntity[CapabilityDevice, CapabilityExecute],
ButtonEntity, ButtonEntity,
): ):
"""Ecovacs button entity.""" """Ecovacs button entity."""
@ -94,7 +101,7 @@ class EcovacsButtonEntity(
class EcovacsResetLifespanButtonEntity( class EcovacsResetLifespanButtonEntity(
EcovacsDescriptionEntity[CapabilityLifeSpan], EcovacsDescriptionEntity[Capabilities, CapabilityLifeSpan],
ButtonEntity, ButtonEntity,
): ):
"""Ecovacs reset lifespan button entity.""" """Ecovacs reset lifespan button entity."""

View file

@ -1,13 +1,14 @@
"""Controller module.""" """Controller module."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping from collections.abc import Generator, Mapping
import logging import logging
import ssl import ssl
from typing import Any from typing import Any
from deebot_client.api_client import ApiClient from deebot_client.api_client import ApiClient
from deebot_client.authentication import Authenticator, create_rest_config 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.const import UNDEFINED, UndefinedType
from deebot_client.device import Device from deebot_client.device import Device
from deebot_client.exceptions import DeebotError, InvalidAuthenticationError from deebot_client.exceptions import DeebotError, InvalidAuthenticationError
@ -18,7 +19,7 @@ from deebot_client.util.continents import get_continent
from sucks import EcoVacsAPI, VacBot from sucks import EcoVacsAPI, VacBot
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client from homeassistant.helpers import aiohttp_client
from homeassistant.util.ssl import get_default_no_verify_context from homeassistant.util.ssl import get_default_no_verify_context
@ -39,7 +40,7 @@ class EcovacsController:
def __init__(self, hass: HomeAssistant, config: Mapping[str, Any]) -> None: def __init__(self, hass: HomeAssistant, config: Mapping[str, Any]) -> None:
"""Initialize controller.""" """Initialize controller."""
self._hass = hass self._hass = hass
self.devices: list[Device] = [] self._devices: list[Device] = []
self.legacy_devices: list[VacBot] = [] self.legacy_devices: list[VacBot] = []
self._device_id = get_client_device_id() self._device_id = get_client_device_id()
country = config[CONF_COUNTRY] country = config[CONF_COUNTRY]
@ -86,7 +87,7 @@ class EcovacsController:
mqtt_config_verfied = True mqtt_config_verfied = True
device = Device(device_config, self._authenticator) device = Device(device_config, self._authenticator)
await device.initialize(self._mqtt) await device.initialize(self._mqtt)
self.devices.append(device) self._devices.append(device)
else: else:
# Legacy device # Legacy device
bot = VacBot( bot = VacBot(
@ -108,9 +109,16 @@ class EcovacsController:
async def teardown(self) -> None: async def teardown(self) -> None:
"""Disconnect controller.""" """Disconnect controller."""
for device in self.devices: for device in self._devices:
await device.teardown() await device.teardown()
for legacy_device in self.legacy_devices: for legacy_device in self.legacy_devices:
await self._hass.async_add_executor_job(legacy_device.disconnect) await self._hass.async_add_executor_job(legacy_device.disconnect)
await self._mqtt.disconnect() await self._mqtt.disconnect()
await self._authenticator.teardown() await self._authenticator.teardown()
@callback
def devices(self, capability: type[Capabilities]) -> Generator[Device, None, None]:
"""Return generator for devices with a specific capability."""
for device in self._devices:
if isinstance(device.capabilities, capability):
yield device

View file

@ -3,6 +3,8 @@ from __future__ import annotations
from typing import Any from typing import Any
from deebot_client.capabilities import Capabilities
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME
@ -31,8 +33,8 @@ async def async_get_config_entry_diagnostics(
} }
diag["devices"] = [ diag["devices"] = [
async_redact_data(device.device_info.api_device_info, REDACT_DEVICE) async_redact_data(device.device_info, REDACT_DEVICE)
for device in controller.devices for device in controller.devices(Capabilities)
] ]
diag["legacy_devices"] = [ diag["legacy_devices"] = [
async_redact_data(device.vacuum, REDACT_DEVICE) async_redact_data(device.vacuum, REDACT_DEVICE)

View file

@ -16,11 +16,12 @@ from homeassistant.helpers.entity import Entity, EntityDescription
from .const import DOMAIN from .const import DOMAIN
CapabilityT = TypeVar("CapabilityT") CapabilityEntity = TypeVar("CapabilityEntity")
CapabilityDevice = TypeVar("CapabilityDevice", bound=Capabilities)
EventT = TypeVar("EventT", bound=Event) EventT = TypeVar("EventT", bound=Event)
class EcovacsEntity(Entity, Generic[CapabilityT]): class EcovacsEntity(Entity, Generic[CapabilityDevice, CapabilityEntity]):
"""Ecovacs entity.""" """Ecovacs entity."""
_attr_should_poll = False _attr_should_poll = False
@ -29,13 +30,15 @@ class EcovacsEntity(Entity, Generic[CapabilityT]):
def __init__( def __init__(
self, self,
device: Device, device: Device[CapabilityDevice],
capability: CapabilityT, capability: CapabilityEntity,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Initialize entity.""" """Initialize entity."""
super().__init__(**kwargs) super().__init__(**kwargs)
self._attr_unique_id = f"{device.device_info.did}_{self.entity_description.key}" self._attr_unique_id = (
f"{device.device_info['did']}_{self.entity_description.key}"
)
self._device = device self._device = device
self._capability = capability self._capability = capability
@ -46,16 +49,16 @@ class EcovacsEntity(Entity, Generic[CapabilityT]):
"""Return device specific attributes.""" """Return device specific attributes."""
device_info = self._device.device_info device_info = self._device.device_info
info = DeviceInfo( info = DeviceInfo(
identifiers={(DOMAIN, device_info.did)}, identifiers={(DOMAIN, device_info["did"])},
manufacturer="Ecovacs", manufacturer="Ecovacs",
sw_version=self._device.fw_version, sw_version=self._device.fw_version,
serial_number=device_info.name, serial_number=device_info["name"],
) )
if nick := device_info.api_device_info.get("nick"): if nick := device_info.get("nick"):
info["name"] = nick info["name"] = nick
if model := device_info.api_device_info.get("deviceName"): if model := device_info.get("deviceName"):
info["model"] = model info["model"] = model
if mac := self._device.mac: if mac := self._device.mac:
@ -93,13 +96,13 @@ class EcovacsEntity(Entity, Generic[CapabilityT]):
self._device.events.request_refresh(event_type) self._device.events.request_refresh(event_type)
class EcovacsDescriptionEntity(EcovacsEntity[CapabilityT]): class EcovacsDescriptionEntity(EcovacsEntity[CapabilityDevice, CapabilityEntity]):
"""Ecovacs entity.""" """Ecovacs entity."""
def __init__( def __init__(
self, self,
device: Device, device: Device[CapabilityDevice],
capability: CapabilityT, capability: CapabilityEntity,
entity_description: EntityDescription, entity_description: EntityDescription,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
@ -111,8 +114,9 @@ class EcovacsDescriptionEntity(EcovacsEntity[CapabilityT]):
@dataclass(kw_only=True, frozen=True) @dataclass(kw_only=True, frozen=True)
class EcovacsCapabilityEntityDescription( class EcovacsCapabilityEntityDescription(
EntityDescription, EntityDescription,
Generic[CapabilityT], Generic[CapabilityDevice, CapabilityEntity],
): ):
"""Ecovacs entity description.""" """Ecovacs entity description."""
capability_fn: Callable[[Capabilities], CapabilityT | None] device_capabilities: type[CapabilityDevice]
capability_fn: Callable[[CapabilityDevice], CapabilityEntity | None]

View file

@ -1,6 +1,6 @@
"""Ecovacs image entities.""" """Ecovacs image entities."""
from deebot_client.capabilities import CapabilityMap from deebot_client.capabilities import CapabilityMap, VacuumCapabilities
from deebot_client.device import Device from deebot_client.device import Device
from deebot_client.events.map import CachedMapInfoEvent, MapChangedEvent from deebot_client.events.map import CachedMapInfoEvent, MapChangedEvent
@ -23,8 +23,9 @@ async def async_setup_entry(
"""Add entities for passed config_entry in HA.""" """Add entities for passed config_entry in HA."""
controller: EcovacsController = hass.data[DOMAIN][config_entry.entry_id] controller: EcovacsController = hass.data[DOMAIN][config_entry.entry_id]
entities = [] entities = []
for device in controller.devices: for device in controller.devices(VacuumCapabilities):
if caps := device.capabilities.map: capabilities: VacuumCapabilities = device.capabilities
if caps := capabilities.map:
entities.append(EcovacsMap(device, caps, hass)) entities.append(EcovacsMap(device, caps, hass))
if entities: if entities:
@ -32,7 +33,7 @@ async def async_setup_entry(
class EcovacsMap( class EcovacsMap(
EcovacsEntity[CapabilityMap], EcovacsEntity[VacuumCapabilities, CapabilityMap],
ImageEntity, ImageEntity,
): ):
"""Ecovacs map.""" """Ecovacs map."""
@ -72,7 +73,7 @@ class EcovacsMap(
self._attr_image_last_updated = event.when self._attr_image_last_updated = event.when
self.async_write_ha_state() self.async_write_ha_state()
self._subscribe(self._capability.chached_info.event, on_info) self._subscribe(self._capability.cached_info.event, on_info)
self._subscribe(self._capability.changed.event, on_changed) self._subscribe(self._capability.changed.event, on_changed)
async def async_update(self) -> None: async def async_update(self) -> None:

View file

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs", "documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"], "loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.9", "deebot-client==5.2.2"] "requirements": ["py-sucks==0.9.9", "deebot-client==6.0.2"]
} }

View file

@ -5,7 +5,7 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Generic from typing import Generic
from deebot_client.capabilities import CapabilitySet from deebot_client.capabilities import Capabilities, CapabilitySet, VacuumCapabilities
from deebot_client.events import CleanCountEvent, VolumeEvent from deebot_client.events import CleanCountEvent, VolumeEvent
from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.components.number import NumberEntity, NumberEntityDescription
@ -17,6 +17,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .controller import EcovacsController from .controller import EcovacsController
from .entity import ( from .entity import (
CapabilityDevice,
EcovacsCapabilityEntityDescription, EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity, EcovacsDescriptionEntity,
EcovacsEntity, EcovacsEntity,
@ -39,6 +40,7 @@ class EcovacsNumberEntityDescription(
ENTITY_DESCRIPTIONS: tuple[EcovacsNumberEntityDescription, ...] = ( ENTITY_DESCRIPTIONS: tuple[EcovacsNumberEntityDescription, ...] = (
EcovacsNumberEntityDescription[VolumeEvent]( EcovacsNumberEntityDescription[VolumeEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.settings.volume, capability_fn=lambda caps: caps.settings.volume,
value_fn=lambda e: e.volume, value_fn=lambda e: e.volume,
native_max_value_fn=lambda e: e.maximum, native_max_value_fn=lambda e: e.maximum,
@ -51,6 +53,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsNumberEntityDescription, ...] = (
native_step=1.0, native_step=1.0,
), ),
EcovacsNumberEntityDescription[CleanCountEvent]( EcovacsNumberEntityDescription[CleanCountEvent](
device_capabilities=VacuumCapabilities,
capability_fn=lambda caps: caps.clean.count, capability_fn=lambda caps: caps.clean.count,
value_fn=lambda e: e.count, value_fn=lambda e: e.count,
key="clean_count", key="clean_count",
@ -79,7 +82,7 @@ async def async_setup_entry(
class EcovacsNumberEntity( class EcovacsNumberEntity(
EcovacsDescriptionEntity[CapabilitySet[EventT, int]], EcovacsDescriptionEntity[CapabilityDevice, CapabilitySet[EventT, int]],
NumberEntity, NumberEntity,
): ):
"""Ecovacs number entity.""" """Ecovacs number entity."""

View file

@ -3,7 +3,7 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Generic from typing import Any, Generic
from deebot_client.capabilities import CapabilitySetTypes from deebot_client.capabilities import CapabilitySetTypes, VacuumCapabilities
from deebot_client.device import Device from deebot_client.device import Device
from deebot_client.events import WaterInfoEvent, WorkModeEvent from deebot_client.events import WaterInfoEvent, WorkModeEvent
@ -15,7 +15,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .controller import EcovacsController from .controller import EcovacsController
from .entity import EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EventT from .entity import (
CapabilityDevice,
EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity,
EventT,
)
from .util import get_supported_entitites from .util import get_supported_entitites
@ -33,6 +38,7 @@ class EcovacsSelectEntityDescription(
ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = ( ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = (
EcovacsSelectEntityDescription[WaterInfoEvent]( EcovacsSelectEntityDescription[WaterInfoEvent](
device_capabilities=VacuumCapabilities,
capability_fn=lambda caps: caps.water, capability_fn=lambda caps: caps.water,
current_option_fn=lambda e: e.amount.display_name, current_option_fn=lambda e: e.amount.display_name,
options_fn=lambda water: [amount.display_name for amount in water.types], options_fn=lambda water: [amount.display_name for amount in water.types],
@ -41,6 +47,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = (
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
EcovacsSelectEntityDescription[WorkModeEvent]( EcovacsSelectEntityDescription[WorkModeEvent](
device_capabilities=VacuumCapabilities,
capability_fn=lambda caps: caps.clean.work_mode, capability_fn=lambda caps: caps.clean.work_mode,
current_option_fn=lambda e: e.mode.display_name, current_option_fn=lambda e: e.mode.display_name,
options_fn=lambda cap: [mode.display_name for mode in cap.types], options_fn=lambda cap: [mode.display_name for mode in cap.types],
@ -67,7 +74,7 @@ async def async_setup_entry(
class EcovacsSelectEntity( class EcovacsSelectEntity(
EcovacsDescriptionEntity[CapabilitySetTypes[EventT, str]], EcovacsDescriptionEntity[CapabilityDevice, CapabilitySetTypes[EventT, str]],
SelectEntity, SelectEntity,
): ):
"""Ecovacs select entity.""" """Ecovacs select entity."""

View file

@ -5,7 +5,7 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Generic from typing import Generic
from deebot_client.capabilities import CapabilityEvent, CapabilityLifeSpan from deebot_client.capabilities import Capabilities, CapabilityEvent, CapabilityLifeSpan
from deebot_client.events import ( from deebot_client.events import (
BatteryEvent, BatteryEvent,
ErrorEvent, ErrorEvent,
@ -39,6 +39,7 @@ from homeassistant.helpers.typing import StateType
from .const import DOMAIN, SUPPORTED_LIFESPANS from .const import DOMAIN, SUPPORTED_LIFESPANS
from .controller import EcovacsController from .controller import EcovacsController
from .entity import ( from .entity import (
CapabilityDevice,
EcovacsCapabilityEntityDescription, EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity, EcovacsDescriptionEntity,
EcovacsEntity, EcovacsEntity,
@ -62,6 +63,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
# Stats # Stats
EcovacsSensorEntityDescription[StatsEvent]( EcovacsSensorEntityDescription[StatsEvent](
key="stats_area", key="stats_area",
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.stats.clean, capability_fn=lambda caps: caps.stats.clean,
value_fn=lambda e: e.area, value_fn=lambda e: e.area,
translation_key="stats_area", translation_key="stats_area",
@ -69,6 +71,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
), ),
EcovacsSensorEntityDescription[StatsEvent]( EcovacsSensorEntityDescription[StatsEvent](
key="stats_time", key="stats_time",
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.stats.clean, capability_fn=lambda caps: caps.stats.clean,
value_fn=lambda e: e.time, value_fn=lambda e: e.time,
translation_key="stats_time", translation_key="stats_time",
@ -78,6 +81,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
), ),
# TotalStats # TotalStats
EcovacsSensorEntityDescription[TotalStatsEvent]( EcovacsSensorEntityDescription[TotalStatsEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.stats.total, capability_fn=lambda caps: caps.stats.total,
value_fn=lambda e: e.area, value_fn=lambda e: e.area,
key="total_stats_area", key="total_stats_area",
@ -86,6 +90,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
EcovacsSensorEntityDescription[TotalStatsEvent]( EcovacsSensorEntityDescription[TotalStatsEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.stats.total, capability_fn=lambda caps: caps.stats.total,
value_fn=lambda e: e.time, value_fn=lambda e: e.time,
key="total_stats_time", key="total_stats_time",
@ -96,6 +101,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
EcovacsSensorEntityDescription[TotalStatsEvent]( EcovacsSensorEntityDescription[TotalStatsEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.stats.total, capability_fn=lambda caps: caps.stats.total,
value_fn=lambda e: e.cleanings, value_fn=lambda e: e.cleanings,
key="total_stats_cleanings", key="total_stats_cleanings",
@ -103,6 +109,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
EcovacsSensorEntityDescription[BatteryEvent]( EcovacsSensorEntityDescription[BatteryEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.battery, capability_fn=lambda caps: caps.battery,
value_fn=lambda e: e.value, value_fn=lambda e: e.value,
key=ATTR_BATTERY_LEVEL, key=ATTR_BATTERY_LEVEL,
@ -111,6 +118,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
EcovacsSensorEntityDescription[NetworkInfoEvent]( EcovacsSensorEntityDescription[NetworkInfoEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.network, capability_fn=lambda caps: caps.network,
value_fn=lambda e: e.ip, value_fn=lambda e: e.ip,
key="network_ip", key="network_ip",
@ -119,6 +127,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
EcovacsSensorEntityDescription[NetworkInfoEvent]( EcovacsSensorEntityDescription[NetworkInfoEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.network, capability_fn=lambda caps: caps.network,
value_fn=lambda e: e.rssi, value_fn=lambda e: e.rssi,
key="network_rssi", key="network_rssi",
@ -127,6 +136,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
EcovacsSensorEntityDescription[NetworkInfoEvent]( EcovacsSensorEntityDescription[NetworkInfoEvent](
device_capabilities=Capabilities,
capability_fn=lambda caps: caps.network, capability_fn=lambda caps: caps.network,
value_fn=lambda e: e.ssid, value_fn=lambda e: e.ssid,
key="network_ssid", key="network_ssid",
@ -169,7 +179,7 @@ async def async_setup_entry(
entities: list[EcovacsEntity] = get_supported_entitites( entities: list[EcovacsEntity] = get_supported_entitites(
controller, EcovacsSensor, ENTITY_DESCRIPTIONS controller, EcovacsSensor, ENTITY_DESCRIPTIONS
) )
for device in controller.devices: for device in controller.devices(Capabilities):
lifespan_capability = device.capabilities.life_span lifespan_capability = device.capabilities.life_span
for description in LIFESPAN_ENTITY_DESCRIPTIONS: for description in LIFESPAN_ENTITY_DESCRIPTIONS:
if description.component in lifespan_capability.types: if description.component in lifespan_capability.types:
@ -184,7 +194,7 @@ async def async_setup_entry(
class EcovacsSensor( class EcovacsSensor(
EcovacsDescriptionEntity[CapabilityEvent], EcovacsDescriptionEntity[CapabilityDevice, CapabilityEvent],
SensorEntity, SensorEntity,
): ):
"""Ecovacs sensor.""" """Ecovacs sensor."""
@ -207,7 +217,7 @@ class EcovacsSensor(
class EcovacsLifespanSensor( class EcovacsLifespanSensor(
EcovacsDescriptionEntity[CapabilityLifeSpan], EcovacsDescriptionEntity[Capabilities, CapabilityLifeSpan],
SensorEntity, SensorEntity,
): ):
"""Lifespan sensor.""" """Lifespan sensor."""
@ -227,7 +237,7 @@ class EcovacsLifespanSensor(
class EcovacsErrorSensor( class EcovacsErrorSensor(
EcovacsEntity[CapabilityEvent[ErrorEvent]], EcovacsEntity[Capabilities, CapabilityEvent[ErrorEvent]],
SensorEntity, SensorEntity,
): ):
"""Error sensor.""" """Error sensor."""

View file

@ -2,7 +2,11 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
from deebot_client.capabilities import CapabilitySetEnable from deebot_client.capabilities import (
Capabilities,
CapabilitySetEnable,
VacuumCapabilities,
)
from deebot_client.events import EnableEvent from deebot_client.events import EnableEvent
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
@ -14,6 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .controller import EcovacsController from .controller import EcovacsController
from .entity import ( from .entity import (
CapabilityDevice,
EcovacsCapabilityEntityDescription, EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity, EcovacsDescriptionEntity,
EcovacsEntity, EcovacsEntity,
@ -24,41 +29,46 @@ from .util import get_supported_entitites
@dataclass(kw_only=True, frozen=True) @dataclass(kw_only=True, frozen=True)
class EcovacsSwitchEntityDescription( class EcovacsSwitchEntityDescription(
SwitchEntityDescription, SwitchEntityDescription,
EcovacsCapabilityEntityDescription, EcovacsCapabilityEntityDescription[CapabilityDevice, CapabilitySetEnable],
): ):
"""Ecovacs switch entity description.""" """Ecovacs switch entity description."""
ENTITY_DESCRIPTIONS: tuple[EcovacsSwitchEntityDescription, ...] = ( ENTITY_DESCRIPTIONS: tuple[EcovacsSwitchEntityDescription, ...] = (
EcovacsSwitchEntityDescription( EcovacsSwitchEntityDescription[Capabilities](
device_capabilities=Capabilities,
capability_fn=lambda c: c.settings.advanced_mode, capability_fn=lambda c: c.settings.advanced_mode,
key="advanced_mode", key="advanced_mode",
translation_key="advanced_mode", translation_key="advanced_mode",
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
EcovacsSwitchEntityDescription( EcovacsSwitchEntityDescription[VacuumCapabilities](
device_capabilities=VacuumCapabilities,
capability_fn=lambda c: c.clean.continuous, capability_fn=lambda c: c.clean.continuous,
key="continuous_cleaning", key="continuous_cleaning",
translation_key="continuous_cleaning", translation_key="continuous_cleaning",
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
EcovacsSwitchEntityDescription( EcovacsSwitchEntityDescription[VacuumCapabilities](
device_capabilities=VacuumCapabilities,
capability_fn=lambda c: c.settings.carpet_auto_fan_boost, capability_fn=lambda c: c.settings.carpet_auto_fan_boost,
key="carpet_auto_fan_boost", key="carpet_auto_fan_boost",
translation_key="carpet_auto_fan_boost", translation_key="carpet_auto_fan_boost",
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
EcovacsSwitchEntityDescription( EcovacsSwitchEntityDescription[VacuumCapabilities](
device_capabilities=VacuumCapabilities,
capability_fn=lambda c: c.clean.preference, capability_fn=lambda c: c.clean.preference,
key="clean_preference", key="clean_preference",
translation_key="clean_preference", translation_key="clean_preference",
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
EcovacsSwitchEntityDescription( EcovacsSwitchEntityDescription[Capabilities](
device_capabilities=Capabilities,
capability_fn=lambda c: c.settings.true_detect, capability_fn=lambda c: c.settings.true_detect,
key="true_detect", key="true_detect",
translation_key="true_detect", translation_key="true_detect",
@ -83,7 +93,7 @@ async def async_setup_entry(
class EcovacsSwitchEntity( class EcovacsSwitchEntity(
EcovacsDescriptionEntity[CapabilitySetEnable], EcovacsDescriptionEntity[CapabilityDevice, CapabilitySetEnable],
SwitchEntity, SwitchEntity,
): ):
"""Ecovacs switch entity.""" """Ecovacs switch entity."""

View file

@ -5,6 +5,8 @@ import random
import string import string
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from deebot_client.capabilities import Capabilities
from .entity import ( from .entity import (
EcovacsCapabilityEntityDescription, EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity, EcovacsDescriptionEntity,
@ -30,9 +32,11 @@ def get_supported_entitites(
"""Return all supported entities for all devices.""" """Return all supported entities for all devices."""
entities: list[EcovacsEntity] = [] entities: list[EcovacsEntity] = []
for device in controller.devices: for device in controller.devices(Capabilities):
for description in descriptions: for description in descriptions:
if capability := description.capability_fn(device.capabilities): if isinstance(device.capabilities, description.device_capabilities) and (
capability := description.capability_fn(device.capabilities)
):
entities.append(entity_class(device, capability, description)) entities.append(entity_class(device, capability, description))
return entities return entities

View file

@ -5,7 +5,7 @@ from collections.abc import Mapping
import logging import logging
from typing import Any from typing import Any
from deebot_client.capabilities import Capabilities from deebot_client.capabilities import VacuumCapabilities
from deebot_client.device import Device from deebot_client.device import Device
from deebot_client.events import BatteryEvent, FanSpeedEvent, RoomsEvent, StateEvent from deebot_client.events import BatteryEvent, FanSpeedEvent, RoomsEvent, StateEvent
from deebot_client.models import CleanAction, CleanMode, Room, State from deebot_client.models import CleanAction, CleanMode, Room, State
@ -50,7 +50,7 @@ async def async_setup_entry(
for device in controller.legacy_devices: for device in controller.legacy_devices:
await hass.async_add_executor_job(device.connect_and_wait_until_ready) await hass.async_add_executor_job(device.connect_and_wait_until_ready)
vacuums.append(EcovacsLegacyVacuum(device)) vacuums.append(EcovacsLegacyVacuum(device))
for device in controller.devices: for device in controller.devices(VacuumCapabilities):
vacuums.append(EcovacsVacuum(device)) vacuums.append(EcovacsVacuum(device))
_LOGGER.debug("Adding Ecovacs Vacuums to Home Assistant: %s", vacuums) _LOGGER.debug("Adding Ecovacs Vacuums to Home Assistant: %s", vacuums)
async_add_entities(vacuums) async_add_entities(vacuums)
@ -210,7 +210,7 @@ _ATTR_ROOMS = "rooms"
class EcovacsVacuum( class EcovacsVacuum(
EcovacsEntity[Capabilities], EcovacsEntity[VacuumCapabilities, VacuumCapabilities],
StateVacuumEntity, StateVacuumEntity,
): ):
"""Ecovacs vacuum.""" """Ecovacs vacuum."""
@ -233,7 +233,7 @@ class EcovacsVacuum(
key="vacuum", translation_key="vacuum", name=None key="vacuum", translation_key="vacuum", name=None
) )
def __init__(self, device: Device) -> None: def __init__(self, device: Device[VacuumCapabilities]) -> None:
"""Initialize the vacuum.""" """Initialize the vacuum."""
capabilities = device.capabilities capabilities = device.capabilities
super().__init__(device, capabilities) super().__init__(device, capabilities)
@ -349,6 +349,15 @@ class EcovacsVacuum(
translation_key="vacuum_send_command_params_required", translation_key="vacuum_send_command_params_required",
translation_placeholders={"command": command}, translation_placeholders={"command": command},
) )
if self._capability.clean.action.area is None:
info = self._device.device_info
name = info.get("nick", info["name"])
raise ServiceValidationError(
f"Vacuum {name} does not support area capability!",
translation_domain=DOMAIN,
translation_key="vacuum_send_command_area_not_supported",
translation_placeholders={"name": name},
)
if command in "spot_area": if command in "spot_area":
await self._device.execute_command( await self._device.execute_command(

View file

@ -699,7 +699,7 @@ debugpy==1.8.1
# decora==0.6 # decora==0.6
# homeassistant.components.ecovacs # homeassistant.components.ecovacs
deebot-client==5.2.2 deebot-client==6.0.2
# homeassistant.components.ihc # homeassistant.components.ihc
# homeassistant.components.namecheapdns # homeassistant.components.namecheapdns

View file

@ -574,7 +574,7 @@ dbus-fast==2.21.1
debugpy==1.8.1 debugpy==1.8.1
# homeassistant.components.ecovacs # homeassistant.components.ecovacs
deebot-client==5.2.2 deebot-client==6.0.2
# homeassistant.components.ihc # homeassistant.components.ihc
# homeassistant.components.namecheapdns # homeassistant.components.namecheapdns

View file

@ -1,5 +1,6 @@
"""Tests for Ecovacs binary sensors.""" """Tests for Ecovacs binary sensors."""
from deebot_client.capabilities import Capabilities
from deebot_client.events import WaterAmount, WaterInfoEvent from deebot_client.events import WaterAmount, WaterInfoEvent
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
@ -37,10 +38,10 @@ async def test_mop_attached(
assert entity_entry == snapshot(name=f"{entity_id}-entity_entry") assert entity_entry == snapshot(name=f"{entity_id}-entity_entry")
assert entity_entry.device_id assert entity_entry.device_id
device = controller.devices[0] device = next(controller.devices(Capabilities))
assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert device_entry.identifiers == {(DOMAIN, device.device_info.did)} assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])}
event_bus = device.events event_bus = device.events
await notify_and_wait( await notify_and_wait(

View file

@ -1,5 +1,6 @@
"""Tests for Ecovacs sensors.""" """Tests for Ecovacs sensors."""
from deebot_client.capabilities import Capabilities
from deebot_client.command import Command from deebot_client.command import Command
from deebot_client.commands.json import ResetLifeSpan, SetRelocationState from deebot_client.commands.json import ResetLifeSpan, SetRelocationState
from deebot_client.events import LifeSpan from deebot_client.events import LifeSpan
@ -60,7 +61,7 @@ async def test_buttons(
) -> None: ) -> None:
"""Test that sensor entity snapshots match.""" """Test that sensor entity snapshots match."""
assert hass.states.async_entity_ids() == [e[0] for e in entities] assert hass.states.async_entity_ids() == [e[0] for e in entities]
device = controller.devices[0] device = next(controller.devices(Capabilities))
for entity_id, command in entities: for entity_id, command in entities:
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"
assert state.state == STATE_UNKNOWN assert state.state == STATE_UNKNOWN
@ -83,7 +84,7 @@ async def test_buttons(
assert entity_entry.device_id assert entity_entry.device_id
assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert device_entry.identifiers == {(DOMAIN, device.device_info.did)} assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])}
@pytest.mark.parametrize( @pytest.mark.parametrize(

View file

@ -2,6 +2,7 @@
from typing import Any from typing import Any
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from deebot_client.capabilities import Capabilities
from deebot_client.exceptions import DeebotError, InvalidAuthenticationError from deebot_client.exceptions import DeebotError, InvalidAuthenticationError
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
@ -106,13 +107,13 @@ async def test_devices_in_dr(
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test all devices are in the device registry.""" """Test all devices are in the device registry."""
for device in controller.devices: for device in controller.devices(Capabilities):
assert ( assert (
device_entry := device_registry.async_get_device( device_entry := device_registry.async_get_device(
identifiers={(DOMAIN, device.device_info.did)} identifiers={(DOMAIN, device.device_info["did"])}
) )
) )
assert device_entry == snapshot(name=device.device_info.did) assert device_entry == snapshot(name=device.device_info["did"])
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration")

View file

@ -2,6 +2,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from deebot_client.capabilities import Capabilities
from deebot_client.command import Command from deebot_client.command import Command
from deebot_client.commands.json import SetVolume from deebot_client.commands.json import SetVolume
from deebot_client.events import Event, VolumeEvent from deebot_client.events import Event, VolumeEvent
@ -65,7 +66,7 @@ async def test_number_entities(
tests: list[NumberTestCase], tests: list[NumberTestCase],
) -> None: ) -> None:
"""Test that number entity snapshots match.""" """Test that number entity snapshots match."""
device = controller.devices[0] device = next(controller.devices(Capabilities))
event_bus = device.events event_bus = device.events
assert sorted(hass.states.async_entity_ids()) == sorted( assert sorted(hass.states.async_entity_ids()) == sorted(
@ -88,7 +89,7 @@ async def test_number_entities(
assert entity_entry.device_id assert entity_entry.device_id
assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert device_entry.identifiers == {(DOMAIN, device.device_info.did)} assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])}
device._execute_command.reset_mock() device._execute_command.reset_mock()
await hass.services.async_call( await hass.services.async_call(
@ -130,7 +131,7 @@ async def test_volume_maximum(
controller: EcovacsController, controller: EcovacsController,
) -> None: ) -> None:
"""Test volume maximum.""" """Test volume maximum."""
device = controller.devices[0] device = next(controller.devices(Capabilities))
event_bus = device.events event_bus = device.events
entity_id = "number.ozmo_950_volume" entity_id = "number.ozmo_950_volume"
assert (state := hass.states.get(entity_id)) assert (state := hass.states.get(entity_id))

View file

@ -1,5 +1,6 @@
"""Tests for Ecovacs select entities.""" """Tests for Ecovacs select entities."""
from deebot_client.capabilities import Capabilities
from deebot_client.command import Command from deebot_client.command import Command
from deebot_client.commands.json import SetWaterInfo from deebot_client.commands.json import SetWaterInfo
from deebot_client.event_bus import EventBus from deebot_client.event_bus import EventBus
@ -63,7 +64,7 @@ async def test_selects(
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"
assert state.state == STATE_UNKNOWN assert state.state == STATE_UNKNOWN
device = controller.devices[0] device = next(controller.devices(Capabilities))
await notify_events(hass, device.events) await notify_events(hass, device.events)
for entity_id in entity_ids: for entity_id in entity_ids:
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"
@ -74,7 +75,7 @@ async def test_selects(
assert entity_entry.device_id assert entity_entry.device_id
assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert device_entry.identifiers == {(DOMAIN, device.device_info.did)} assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])}
@pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.usefixtures("entity_registry_enabled_by_default")
@ -99,7 +100,7 @@ async def test_selects_change(
command: Command, command: Command,
) -> None: ) -> None:
"""Test that changing select entities works.""" """Test that changing select entities works."""
device = controller.devices[0] device = next(controller.devices(Capabilities))
await notify_events(hass, device.events) await notify_events(hass, device.events)
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"

View file

@ -1,5 +1,6 @@
"""Tests for Ecovacs sensors.""" """Tests for Ecovacs sensors."""
from deebot_client.capabilities import Capabilities
from deebot_client.event_bus import EventBus from deebot_client.event_bus import EventBus
from deebot_client.events import ( from deebot_client.events import (
BatteryEvent, BatteryEvent,
@ -84,7 +85,7 @@ async def test_sensors(
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"
assert state.state == STATE_UNKNOWN assert state.state == STATE_UNKNOWN
device = controller.devices[0] device = next(controller.devices(Capabilities))
await notify_events(hass, device.events) await notify_events(hass, device.events)
for entity_id in entity_ids: for entity_id in entity_ids:
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing" assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"
@ -95,7 +96,7 @@ async def test_sensors(
assert entity_entry.device_id assert entity_entry.device_id
assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert device_entry.identifiers == {(DOMAIN, device.device_info.did)} assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])}
@pytest.mark.parametrize( @pytest.mark.parametrize(

View file

@ -2,6 +2,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from deebot_client.capabilities import Capabilities
from deebot_client.command import Command from deebot_client.command import Command
from deebot_client.commands.json import ( from deebot_client.commands.json import (
SetAdvancedMode, SetAdvancedMode,
@ -87,7 +88,7 @@ async def test_switch_entities(
tests: list[SwitchTestCase], tests: list[SwitchTestCase],
) -> None: ) -> None:
"""Test switch entities.""" """Test switch entities."""
device = controller.devices[0] device = next(controller.devices(Capabilities))
event_bus = device.events event_bus = device.events
assert hass.states.async_entity_ids() == [test.entity_id for test in tests] assert hass.states.async_entity_ids() == [test.entity_id for test in tests]
@ -108,7 +109,7 @@ async def test_switch_entities(
assert entity_entry.device_id assert entity_entry.device_id
assert (device_entry := device_registry.async_get(entity_entry.device_id)) assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert device_entry.identifiers == {(DOMAIN, device.device_info.did)} assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])}
device._execute_command.reset_mock() device._execute_command.reset_mock()
await hass.services.async_call( await hass.services.async_call(