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 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."""
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Add table
Reference in a new issue