Bump deebot-client to 6.0.2 (#111507)
This commit is contained in:
parent
d812507aeb
commit
3703698f77
22 changed files with 155 additions and 77 deletions
|
@ -3,7 +3,7 @@ from collections.abc import Callable
|
|||
from dataclasses import dataclass
|
||||
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 homeassistant.components.binary_sensor import (
|
||||
|
@ -17,7 +17,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
|
||||
from .const import DOMAIN
|
||||
from .controller import EcovacsController
|
||||
from .entity import EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EventT
|
||||
from .entity import (
|
||||
CapabilityDevice,
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsDescriptionEntity,
|
||||
EventT,
|
||||
)
|
||||
from .util import get_supported_entitites
|
||||
|
||||
|
||||
|
@ -34,6 +39,7 @@ 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",
|
||||
|
@ -56,7 +62,7 @@ async def async_setup_entry(
|
|||
|
||||
|
||||
class EcovacsBinarySensor(
|
||||
EcovacsDescriptionEntity[CapabilityEvent[EventT]],
|
||||
EcovacsDescriptionEntity[CapabilityDevice, CapabilityEvent[EventT]],
|
||||
BinarySensorEntity,
|
||||
):
|
||||
"""Ecovacs binary sensor."""
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
"""Ecovacs button module."""
|
||||
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 homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
|
@ -13,6 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from .const import DOMAIN, SUPPORTED_LIFESPANS
|
||||
from .controller import EcovacsController
|
||||
from .entity import (
|
||||
CapabilityDevice,
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsDescriptionEntity,
|
||||
EcovacsEntity,
|
||||
|
@ -37,6 +43,7 @@ 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",
|
||||
|
@ -66,7 +73,7 @@ async def async_setup_entry(
|
|||
entities: list[EcovacsEntity] = get_supported_entitites(
|
||||
controller, EcovacsButtonEntity, ENTITY_DESCRIPTIONS
|
||||
)
|
||||
for device in controller.devices:
|
||||
for device in controller.devices(Capabilities):
|
||||
lifespan_capability = device.capabilities.life_span
|
||||
for description in LIFESPAN_ENTITY_DESCRIPTIONS:
|
||||
if description.component in lifespan_capability.types:
|
||||
|
@ -81,7 +88,7 @@ async def async_setup_entry(
|
|||
|
||||
|
||||
class EcovacsButtonEntity(
|
||||
EcovacsDescriptionEntity[CapabilityExecute],
|
||||
EcovacsDescriptionEntity[CapabilityDevice, CapabilityExecute],
|
||||
ButtonEntity,
|
||||
):
|
||||
"""Ecovacs button entity."""
|
||||
|
@ -94,7 +101,7 @@ class EcovacsButtonEntity(
|
|||
|
||||
|
||||
class EcovacsResetLifespanButtonEntity(
|
||||
EcovacsDescriptionEntity[CapabilityLifeSpan],
|
||||
EcovacsDescriptionEntity[Capabilities, CapabilityLifeSpan],
|
||||
ButtonEntity,
|
||||
):
|
||||
"""Ecovacs reset lifespan button entity."""
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
"""Controller module."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from collections.abc import Generator, Mapping
|
||||
import logging
|
||||
import ssl
|
||||
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,7 +19,7 @@ from deebot_client.util.continents import get_continent
|
|||
from sucks import EcoVacsAPI, VacBot
|
||||
|
||||
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.helpers import aiohttp_client
|
||||
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:
|
||||
"""Initialize controller."""
|
||||
self._hass = hass
|
||||
self.devices: list[Device] = []
|
||||
self._devices: list[Device] = []
|
||||
self.legacy_devices: list[VacBot] = []
|
||||
self._device_id = get_client_device_id()
|
||||
country = config[CONF_COUNTRY]
|
||||
|
@ -86,7 +87,7 @@ class EcovacsController:
|
|||
mqtt_config_verfied = True
|
||||
device = Device(device_config, self._authenticator)
|
||||
await device.initialize(self._mqtt)
|
||||
self.devices.append(device)
|
||||
self._devices.append(device)
|
||||
else:
|
||||
# Legacy device
|
||||
bot = VacBot(
|
||||
|
@ -108,9 +109,16 @@ class EcovacsController:
|
|||
|
||||
async def teardown(self) -> None:
|
||||
"""Disconnect controller."""
|
||||
for device in self.devices:
|
||||
for device in self._devices:
|
||||
await device.teardown()
|
||||
for legacy_device in self.legacy_devices:
|
||||
await self._hass.async_add_executor_job(legacy_device.disconnect)
|
||||
await self._mqtt.disconnect()
|
||||
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
|
||||
|
|
|
@ -3,6 +3,8 @@ from __future__ import annotations
|
|||
|
||||
from typing import Any
|
||||
|
||||
from deebot_client.capabilities import Capabilities
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME
|
||||
|
@ -31,8 +33,8 @@ async def async_get_config_entry_diagnostics(
|
|||
}
|
||||
|
||||
diag["devices"] = [
|
||||
async_redact_data(device.device_info.api_device_info, REDACT_DEVICE)
|
||||
for device in controller.devices
|
||||
async_redact_data(device.device_info, REDACT_DEVICE)
|
||||
for device in controller.devices(Capabilities)
|
||||
]
|
||||
diag["legacy_devices"] = [
|
||||
async_redact_data(device.vacuum, REDACT_DEVICE)
|
||||
|
|
|
@ -16,11 +16,12 @@ from homeassistant.helpers.entity import Entity, EntityDescription
|
|||
|
||||
from .const import DOMAIN
|
||||
|
||||
CapabilityT = TypeVar("CapabilityT")
|
||||
CapabilityEntity = TypeVar("CapabilityEntity")
|
||||
CapabilityDevice = TypeVar("CapabilityDevice", bound=Capabilities)
|
||||
EventT = TypeVar("EventT", bound=Event)
|
||||
|
||||
|
||||
class EcovacsEntity(Entity, Generic[CapabilityT]):
|
||||
class EcovacsEntity(Entity, Generic[CapabilityDevice, CapabilityEntity]):
|
||||
"""Ecovacs entity."""
|
||||
|
||||
_attr_should_poll = False
|
||||
|
@ -29,13 +30,15 @@ class EcovacsEntity(Entity, Generic[CapabilityT]):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
device: Device,
|
||||
capability: CapabilityT,
|
||||
device: Device[CapabilityDevice],
|
||||
capability: CapabilityEntity,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize entity."""
|
||||
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._capability = capability
|
||||
|
@ -46,16 +49,16 @@ class EcovacsEntity(Entity, Generic[CapabilityT]):
|
|||
"""Return device specific attributes."""
|
||||
device_info = self._device.device_info
|
||||
info = DeviceInfo(
|
||||
identifiers={(DOMAIN, device_info.did)},
|
||||
identifiers={(DOMAIN, device_info["did"])},
|
||||
manufacturer="Ecovacs",
|
||||
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
|
||||
|
||||
if model := device_info.api_device_info.get("deviceName"):
|
||||
if model := device_info.get("deviceName"):
|
||||
info["model"] = model
|
||||
|
||||
if mac := self._device.mac:
|
||||
|
@ -93,13 +96,13 @@ class EcovacsEntity(Entity, Generic[CapabilityT]):
|
|||
self._device.events.request_refresh(event_type)
|
||||
|
||||
|
||||
class EcovacsDescriptionEntity(EcovacsEntity[CapabilityT]):
|
||||
class EcovacsDescriptionEntity(EcovacsEntity[CapabilityDevice, CapabilityEntity]):
|
||||
"""Ecovacs entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
device: Device,
|
||||
capability: CapabilityT,
|
||||
device: Device[CapabilityDevice],
|
||||
capability: CapabilityEntity,
|
||||
entity_description: EntityDescription,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
|
@ -111,8 +114,9 @@ class EcovacsDescriptionEntity(EcovacsEntity[CapabilityT]):
|
|||
@dataclass(kw_only=True, frozen=True)
|
||||
class EcovacsCapabilityEntityDescription(
|
||||
EntityDescription,
|
||||
Generic[CapabilityT],
|
||||
Generic[CapabilityDevice, CapabilityEntity],
|
||||
):
|
||||
"""Ecovacs entity description."""
|
||||
|
||||
capability_fn: Callable[[Capabilities], CapabilityT | None]
|
||||
device_capabilities: type[CapabilityDevice]
|
||||
capability_fn: Callable[[CapabilityDevice], CapabilityEntity | None]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""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.events.map import CachedMapInfoEvent, MapChangedEvent
|
||||
|
||||
|
@ -23,8 +23,9 @@ async def async_setup_entry(
|
|||
"""Add entities for passed config_entry in HA."""
|
||||
controller: EcovacsController = hass.data[DOMAIN][config_entry.entry_id]
|
||||
entities = []
|
||||
for device in controller.devices:
|
||||
if caps := device.capabilities.map:
|
||||
for device in controller.devices(VacuumCapabilities):
|
||||
capabilities: VacuumCapabilities = device.capabilities
|
||||
if caps := capabilities.map:
|
||||
entities.append(EcovacsMap(device, caps, hass))
|
||||
|
||||
if entities:
|
||||
|
@ -32,7 +33,7 @@ async def async_setup_entry(
|
|||
|
||||
|
||||
class EcovacsMap(
|
||||
EcovacsEntity[CapabilityMap],
|
||||
EcovacsEntity[VacuumCapabilities, CapabilityMap],
|
||||
ImageEntity,
|
||||
):
|
||||
"""Ecovacs map."""
|
||||
|
@ -72,7 +73,7 @@ class EcovacsMap(
|
|||
self._attr_image_last_updated = event.when
|
||||
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)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
|
|
|
@ -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.9", "deebot-client==5.2.2"]
|
||||
"requirements": ["py-sucks==0.9.9", "deebot-client==6.0.2"]
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ from collections.abc import Callable
|
|||
from dataclasses import dataclass
|
||||
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 homeassistant.components.number import NumberEntity, NumberEntityDescription
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from .const import DOMAIN
|
||||
from .controller import EcovacsController
|
||||
from .entity import (
|
||||
CapabilityDevice,
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsDescriptionEntity,
|
||||
EcovacsEntity,
|
||||
|
@ -39,6 +40,7 @@ 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,
|
||||
|
@ -51,6 +53,7 @@ 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",
|
||||
|
@ -79,7 +82,7 @@ async def async_setup_entry(
|
|||
|
||||
|
||||
class EcovacsNumberEntity(
|
||||
EcovacsDescriptionEntity[CapabilitySet[EventT, int]],
|
||||
EcovacsDescriptionEntity[CapabilityDevice, CapabilitySet[EventT, int]],
|
||||
NumberEntity,
|
||||
):
|
||||
"""Ecovacs number entity."""
|
||||
|
|
|
@ -3,7 +3,7 @@ from collections.abc import Callable
|
|||
from dataclasses import dataclass
|
||||
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.events import WaterInfoEvent, WorkModeEvent
|
||||
|
||||
|
@ -15,7 +15,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
|
||||
from .const import DOMAIN
|
||||
from .controller import EcovacsController
|
||||
from .entity import EcovacsCapabilityEntityDescription, EcovacsDescriptionEntity, EventT
|
||||
from .entity import (
|
||||
CapabilityDevice,
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsDescriptionEntity,
|
||||
EventT,
|
||||
)
|
||||
from .util import get_supported_entitites
|
||||
|
||||
|
||||
|
@ -33,6 +38,7 @@ class EcovacsSelectEntityDescription(
|
|||
|
||||
ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = (
|
||||
EcovacsSelectEntityDescription[WaterInfoEvent](
|
||||
device_capabilities=VacuumCapabilities,
|
||||
capability_fn=lambda caps: caps.water,
|
||||
current_option_fn=lambda e: e.amount.display_name,
|
||||
options_fn=lambda water: [amount.display_name for amount in water.types],
|
||||
|
@ -41,6 +47,7 @@ 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: e.mode.display_name,
|
||||
options_fn=lambda cap: [mode.display_name for mode in cap.types],
|
||||
|
@ -67,7 +74,7 @@ async def async_setup_entry(
|
|||
|
||||
|
||||
class EcovacsSelectEntity(
|
||||
EcovacsDescriptionEntity[CapabilitySetTypes[EventT, str]],
|
||||
EcovacsDescriptionEntity[CapabilityDevice, CapabilitySetTypes[EventT, str]],
|
||||
SelectEntity,
|
||||
):
|
||||
"""Ecovacs select entity."""
|
||||
|
|
|
@ -5,7 +5,7 @@ from collections.abc import Callable
|
|||
from dataclasses import dataclass
|
||||
from typing import Generic
|
||||
|
||||
from deebot_client.capabilities import CapabilityEvent, CapabilityLifeSpan
|
||||
from deebot_client.capabilities import Capabilities, CapabilityEvent, CapabilityLifeSpan
|
||||
from deebot_client.events import (
|
||||
BatteryEvent,
|
||||
ErrorEvent,
|
||||
|
@ -39,6 +39,7 @@ from homeassistant.helpers.typing import StateType
|
|||
from .const import DOMAIN, SUPPORTED_LIFESPANS
|
||||
from .controller import EcovacsController
|
||||
from .entity import (
|
||||
CapabilityDevice,
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsDescriptionEntity,
|
||||
EcovacsEntity,
|
||||
|
@ -62,6 +63,7 @@ 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",
|
||||
|
@ -69,6 +71,7 @@ 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",
|
||||
|
@ -78,6 +81,7 @@ 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",
|
||||
|
@ -86,6 +90,7 @@ 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",
|
||||
|
@ -96,6 +101,7 @@ 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",
|
||||
|
@ -103,6 +109,7 @@ 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,
|
||||
|
@ -111,6 +118,7 @@ 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",
|
||||
|
@ -119,6 +127,7 @@ 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",
|
||||
|
@ -127,6 +136,7 @@ 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",
|
||||
|
@ -169,7 +179,7 @@ async def async_setup_entry(
|
|||
entities: list[EcovacsEntity] = get_supported_entitites(
|
||||
controller, EcovacsSensor, ENTITY_DESCRIPTIONS
|
||||
)
|
||||
for device in controller.devices:
|
||||
for device in controller.devices(Capabilities):
|
||||
lifespan_capability = device.capabilities.life_span
|
||||
for description in LIFESPAN_ENTITY_DESCRIPTIONS:
|
||||
if description.component in lifespan_capability.types:
|
||||
|
@ -184,7 +194,7 @@ async def async_setup_entry(
|
|||
|
||||
|
||||
class EcovacsSensor(
|
||||
EcovacsDescriptionEntity[CapabilityEvent],
|
||||
EcovacsDescriptionEntity[CapabilityDevice, CapabilityEvent],
|
||||
SensorEntity,
|
||||
):
|
||||
"""Ecovacs sensor."""
|
||||
|
@ -207,7 +217,7 @@ class EcovacsSensor(
|
|||
|
||||
|
||||
class EcovacsLifespanSensor(
|
||||
EcovacsDescriptionEntity[CapabilityLifeSpan],
|
||||
EcovacsDescriptionEntity[Capabilities, CapabilityLifeSpan],
|
||||
SensorEntity,
|
||||
):
|
||||
"""Lifespan sensor."""
|
||||
|
@ -227,7 +237,7 @@ class EcovacsLifespanSensor(
|
|||
|
||||
|
||||
class EcovacsErrorSensor(
|
||||
EcovacsEntity[CapabilityEvent[ErrorEvent]],
|
||||
EcovacsEntity[Capabilities, CapabilityEvent[ErrorEvent]],
|
||||
SensorEntity,
|
||||
):
|
||||
"""Error sensor."""
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
from dataclasses import dataclass
|
||||
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 homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
|
@ -14,6 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from .const import DOMAIN
|
||||
from .controller import EcovacsController
|
||||
from .entity import (
|
||||
CapabilityDevice,
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsDescriptionEntity,
|
||||
EcovacsEntity,
|
||||
|
@ -24,41 +29,46 @@ from .util import get_supported_entitites
|
|||
@dataclass(kw_only=True, frozen=True)
|
||||
class EcovacsSwitchEntityDescription(
|
||||
SwitchEntityDescription,
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsCapabilityEntityDescription[CapabilityDevice, CapabilitySetEnable],
|
||||
):
|
||||
"""Ecovacs switch entity description."""
|
||||
|
||||
|
||||
ENTITY_DESCRIPTIONS: tuple[EcovacsSwitchEntityDescription, ...] = (
|
||||
EcovacsSwitchEntityDescription(
|
||||
EcovacsSwitchEntityDescription[Capabilities](
|
||||
device_capabilities=Capabilities,
|
||||
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(
|
||||
EcovacsSwitchEntityDescription[VacuumCapabilities](
|
||||
device_capabilities=VacuumCapabilities,
|
||||
capability_fn=lambda c: c.clean.continuous,
|
||||
key="continuous_cleaning",
|
||||
translation_key="continuous_cleaning",
|
||||
entity_registry_enabled_default=False,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
),
|
||||
EcovacsSwitchEntityDescription(
|
||||
EcovacsSwitchEntityDescription[VacuumCapabilities](
|
||||
device_capabilities=VacuumCapabilities,
|
||||
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(
|
||||
EcovacsSwitchEntityDescription[VacuumCapabilities](
|
||||
device_capabilities=VacuumCapabilities,
|
||||
capability_fn=lambda c: c.clean.preference,
|
||||
key="clean_preference",
|
||||
translation_key="clean_preference",
|
||||
entity_registry_enabled_default=False,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
),
|
||||
EcovacsSwitchEntityDescription(
|
||||
EcovacsSwitchEntityDescription[Capabilities](
|
||||
device_capabilities=Capabilities,
|
||||
capability_fn=lambda c: c.settings.true_detect,
|
||||
key="true_detect",
|
||||
translation_key="true_detect",
|
||||
|
@ -83,7 +93,7 @@ async def async_setup_entry(
|
|||
|
||||
|
||||
class EcovacsSwitchEntity(
|
||||
EcovacsDescriptionEntity[CapabilitySetEnable],
|
||||
EcovacsDescriptionEntity[CapabilityDevice, CapabilitySetEnable],
|
||||
SwitchEntity,
|
||||
):
|
||||
"""Ecovacs switch entity."""
|
||||
|
|
|
@ -5,6 +5,8 @@ import random
|
|||
import string
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from deebot_client.capabilities import Capabilities
|
||||
|
||||
from .entity import (
|
||||
EcovacsCapabilityEntityDescription,
|
||||
EcovacsDescriptionEntity,
|
||||
|
@ -30,9 +32,11 @@ def get_supported_entitites(
|
|||
"""Return all supported entities for all devices."""
|
||||
entities: list[EcovacsEntity] = []
|
||||
|
||||
for device in controller.devices:
|
||||
for device in controller.devices(Capabilities):
|
||||
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))
|
||||
|
||||
return entities
|
||||
|
|
|
@ -5,7 +5,7 @@ from collections.abc import Mapping
|
|||
import logging
|
||||
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.events import BatteryEvent, FanSpeedEvent, RoomsEvent, StateEvent
|
||||
from deebot_client.models import CleanAction, CleanMode, Room, State
|
||||
|
@ -50,7 +50,7 @@ async def async_setup_entry(
|
|||
for device in controller.legacy_devices:
|
||||
await hass.async_add_executor_job(device.connect_and_wait_until_ready)
|
||||
vacuums.append(EcovacsLegacyVacuum(device))
|
||||
for device in controller.devices:
|
||||
for device in controller.devices(VacuumCapabilities):
|
||||
vacuums.append(EcovacsVacuum(device))
|
||||
_LOGGER.debug("Adding Ecovacs Vacuums to Home Assistant: %s", vacuums)
|
||||
async_add_entities(vacuums)
|
||||
|
@ -210,7 +210,7 @@ _ATTR_ROOMS = "rooms"
|
|||
|
||||
|
||||
class EcovacsVacuum(
|
||||
EcovacsEntity[Capabilities],
|
||||
EcovacsEntity[VacuumCapabilities, VacuumCapabilities],
|
||||
StateVacuumEntity,
|
||||
):
|
||||
"""Ecovacs vacuum."""
|
||||
|
@ -233,7 +233,7 @@ class EcovacsVacuum(
|
|||
key="vacuum", translation_key="vacuum", name=None
|
||||
)
|
||||
|
||||
def __init__(self, device: Device) -> None:
|
||||
def __init__(self, device: Device[VacuumCapabilities]) -> None:
|
||||
"""Initialize the vacuum."""
|
||||
capabilities = device.capabilities
|
||||
super().__init__(device, capabilities)
|
||||
|
@ -349,6 +349,15 @@ class EcovacsVacuum(
|
|||
translation_key="vacuum_send_command_params_required",
|
||||
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":
|
||||
await self._device.execute_command(
|
||||
|
|
|
@ -699,7 +699,7 @@ debugpy==1.8.1
|
|||
# decora==0.6
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==5.2.2
|
||||
deebot-client==6.0.2
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
|
|
|
@ -574,7 +574,7 @@ dbus-fast==2.21.1
|
|||
debugpy==1.8.1
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==5.2.2
|
||||
deebot-client==6.0.2
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Tests for Ecovacs binary sensors."""
|
||||
|
||||
from deebot_client.capabilities import Capabilities
|
||||
from deebot_client.events import WaterAmount, WaterInfoEvent
|
||||
import pytest
|
||||
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.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.identifiers == {(DOMAIN, device.device_info.did)}
|
||||
assert device_entry.identifiers == {(DOMAIN, device.device_info["did"])}
|
||||
|
||||
event_bus = device.events
|
||||
await notify_and_wait(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""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
|
||||
|
@ -60,7 +61,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 = controller.devices[0]
|
||||
device = next(controller.devices(Capabilities))
|
||||
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
|
||||
|
@ -83,7 +84,7 @@ async def test_buttons(
|
|||
|
||||
assert 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(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
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
|
||||
|
@ -106,13 +107,13 @@ async def test_devices_in_dr(
|
|||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test all devices are in the device registry."""
|
||||
for device in controller.devices:
|
||||
for device in controller.devices(Capabilities):
|
||||
assert (
|
||||
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")
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
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
|
||||
|
@ -65,7 +66,7 @@ async def test_number_entities(
|
|||
tests: list[NumberTestCase],
|
||||
) -> None:
|
||||
"""Test that number entity snapshots match."""
|
||||
device = controller.devices[0]
|
||||
device = next(controller.devices(Capabilities))
|
||||
event_bus = device.events
|
||||
|
||||
assert sorted(hass.states.async_entity_ids()) == sorted(
|
||||
|
@ -88,7 +89,7 @@ async def test_number_entities(
|
|||
|
||||
assert 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()
|
||||
await hass.services.async_call(
|
||||
|
@ -130,7 +131,7 @@ async def test_volume_maximum(
|
|||
controller: EcovacsController,
|
||||
) -> None:
|
||||
"""Test volume maximum."""
|
||||
device = controller.devices[0]
|
||||
device = next(controller.devices(Capabilities))
|
||||
event_bus = device.events
|
||||
entity_id = "number.ozmo_950_volume"
|
||||
assert (state := hass.states.get(entity_id))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""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
|
||||
|
@ -63,7 +64,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 = controller.devices[0]
|
||||
device = next(controller.devices(Capabilities))
|
||||
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"
|
||||
|
@ -74,7 +75,7 @@ async def test_selects(
|
|||
|
||||
assert 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")
|
||||
|
@ -99,7 +100,7 @@ async def test_selects_change(
|
|||
command: Command,
|
||||
) -> None:
|
||||
"""Test that changing select entities works."""
|
||||
device = controller.devices[0]
|
||||
device = next(controller.devices(Capabilities))
|
||||
await notify_events(hass, device.events)
|
||||
|
||||
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Tests for Ecovacs sensors."""
|
||||
|
||||
from deebot_client.capabilities import Capabilities
|
||||
from deebot_client.event_bus import EventBus
|
||||
from deebot_client.events import (
|
||||
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.state == STATE_UNKNOWN
|
||||
|
||||
device = controller.devices[0]
|
||||
device = next(controller.devices(Capabilities))
|
||||
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"
|
||||
|
@ -95,7 +96,7 @@ async def test_sensors(
|
|||
|
||||
assert 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(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from deebot_client.capabilities import Capabilities
|
||||
from deebot_client.command import Command
|
||||
from deebot_client.commands.json import (
|
||||
SetAdvancedMode,
|
||||
|
@ -87,7 +88,7 @@ async def test_switch_entities(
|
|||
tests: list[SwitchTestCase],
|
||||
) -> None:
|
||||
"""Test switch entities."""
|
||||
device = controller.devices[0]
|
||||
device = next(controller.devices(Capabilities))
|
||||
event_bus = device.events
|
||||
|
||||
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 (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()
|
||||
await hass.services.async_call(
|
||||
|
|
Loading…
Add table
Reference in a new issue