Add Ecovacs station entities (#133876)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
pull/133900/head
Robert Resch 2024-12-23 15:54:40 +01:00 committed by GitHub
parent 5ef12c3993
commit 8e86c3c775
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 1290 additions and 7 deletions

View File

@ -2,7 +2,12 @@
from dataclasses import dataclass
from deebot_client.capabilities import CapabilityExecute, CapabilityLifeSpan
from deebot_client.capabilities import (
CapabilityExecute,
CapabilityExecuteTypes,
CapabilityLifeSpan,
)
from deebot_client.commands import StationAction
from deebot_client.events import LifeSpan
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
@ -11,7 +16,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import EcovacsConfigEntry
from .const import SUPPORTED_LIFESPANS
from .const import SUPPORTED_LIFESPANS, SUPPORTED_STATION_ACTIONS
from .entity import (
EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity,
@ -35,6 +40,13 @@ class EcovacsLifespanButtonEntityDescription(ButtonEntityDescription):
component: LifeSpan
@dataclass(kw_only=True, frozen=True)
class EcovacsStationActionButtonEntityDescription(ButtonEntityDescription):
"""Ecovacs station action button entity description."""
action: StationAction
ENTITY_DESCRIPTIONS: tuple[EcovacsButtonEntityDescription, ...] = (
EcovacsButtonEntityDescription(
capability_fn=lambda caps: caps.map.relocation if caps.map else None,
@ -44,6 +56,16 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsButtonEntityDescription, ...] = (
),
)
STATION_ENTITY_DESCRIPTIONS = tuple(
EcovacsStationActionButtonEntityDescription(
action=action,
key=f"station_action_{action.name.lower()}",
translation_key=f"station_action_{action.name.lower()}",
)
for action in SUPPORTED_STATION_ACTIONS
)
LIFESPAN_ENTITY_DESCRIPTIONS = tuple(
EcovacsLifespanButtonEntityDescription(
component=component,
@ -74,6 +96,15 @@ async def async_setup_entry(
for description in LIFESPAN_ENTITY_DESCRIPTIONS
if description.component in device.capabilities.life_span.types
)
entities.extend(
EcovacsStationActionButtonEntity(
device, device.capabilities.station.action, description
)
for device in controller.devices
if device.capabilities.station
for description in STATION_ENTITY_DESCRIPTIONS
if description.action in device.capabilities.station.action.types
)
async_add_entities(entities)
@ -103,3 +134,18 @@ class EcovacsResetLifespanButtonEntity(
await self._device.execute_command(
self._capability.reset(self.entity_description.component)
)
class EcovacsStationActionButtonEntity(
EcovacsDescriptionEntity[CapabilityExecuteTypes[StationAction]],
ButtonEntity,
):
"""Ecovacs station action button entity."""
entity_description: EcovacsStationActionButtonEntityDescription
async def async_press(self) -> None:
"""Press the button."""
await self._device.execute_command(
self._capability.execute(self.entity_description.action)
)

View File

@ -2,6 +2,7 @@
from enum import StrEnum
from deebot_client.commands import StationAction
from deebot_client.events import LifeSpan
DOMAIN = "ecovacs"
@ -19,8 +20,11 @@ SUPPORTED_LIFESPANS = (
LifeSpan.SIDE_BRUSH,
LifeSpan.UNIT_CARE,
LifeSpan.ROUND_MOP,
LifeSpan.STATION_FILTER,
)
SUPPORTED_STATION_ACTIONS = (StationAction.EMPTY_DUSTBIN,)
LEGACY_SUPPORTED_LIFESPANS = (
"main_brush",
"side_brush",

View File

@ -27,11 +27,17 @@
"reset_lifespan_side_brush": {
"default": "mdi:broom"
},
"reset_lifespan_station_filter": {
"default": "mdi:air-filter"
},
"reset_lifespan_unit_care": {
"default": "mdi:robot-vacuum"
},
"reset_lifespan_round_mop": {
"default": "mdi:broom"
},
"station_action_empty_dustbin": {
"default": "mdi:delete-restore"
}
},
"event": {
@ -72,6 +78,9 @@
"lifespan_side_brush": {
"default": "mdi:broom"
},
"lifespan_station_filter": {
"default": "mdi:air-filter"
},
"lifespan_unit_care": {
"default": "mdi:robot-vacuum"
},
@ -87,6 +96,9 @@
"network_ssid": {
"default": "mdi:wifi"
},
"station_state": {
"default": "mdi:home"
},
"stats_area": {
"default": "mdi:floor-plan"
},

View File

@ -16,6 +16,7 @@ from deebot_client.events import (
NetworkInfoEvent,
StatsEvent,
TotalStatsEvent,
station,
)
from sucks import VacBot
@ -46,7 +47,7 @@ from .entity import (
EcovacsLegacyEntity,
EventT,
)
from .util import get_supported_entitites
from .util import get_name_key, get_options, get_supported_entitites
@dataclass(kw_only=True, frozen=True)
@ -136,6 +137,15 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
# Station
EcovacsSensorEntityDescription[station.StationEvent](
capability_fn=lambda caps: caps.station.state if caps.station else None,
value_fn=lambda e: get_name_key(e.state),
key="station_state",
translation_key="station_state",
device_class=SensorDeviceClass.ENUM,
options=get_options(station.State),
),
)

View File

@ -46,6 +46,9 @@
"relocate": {
"name": "Relocate"
},
"reset_lifespan_base_station_filter": {
"name": "Reset station filter lifespan"
},
"reset_lifespan_blade": {
"name": "Reset blade lifespan"
},
@ -66,6 +69,9 @@
},
"reset_lifespan_side_brush": {
"name": "Reset side brush lifespan"
},
"station_action_empty_dustbin": {
"name": "Empty dustbin"
}
},
"event": {
@ -107,6 +113,9 @@
}
}
},
"lifespan_base_station_filter": {
"name": "Station filter lifespan"
},
"lifespan_blade": {
"name": "Blade lifespan"
},
@ -140,6 +149,13 @@
"network_ssid": {
"name": "Wi-Fi SSID"
},
"station_state": {
"name": "Station state",
"state": {
"idle": "[%key:common::state::idle%]",
"emptying_dustbin": "Emptying dustbin"
}
},
"stats_area": {
"name": "Area cleaned"
},

View File

@ -7,6 +7,8 @@ import random
import string
from typing import TYPE_CHECKING
from deebot_client.events.station import State
from homeassistant.core import HomeAssistant, callback
from homeassistant.util import slugify
@ -47,4 +49,13 @@ def get_supported_entitites(
@callback
def get_name_key(enum: Enum) -> str:
"""Return the lower case name of the enum."""
if enum is State.EMPTYING:
# Will be fixed in the next major release of deebot-client
return "emptying_dustbin"
return enum.name.lower()
@callback
def get_options(enum: type[Enum]) -> list[str]:
"""Return the options for the enum."""
return [get_name_key(option) for option in enum]

View File

@ -0,0 +1,29 @@
{
"did": "8516fbb1-17f1-4194-0000001",
"name": "E1234567890000000003",
"class": "qhe2o2",
"resource": "NHl5",
"company": "eco-ng",
"bindTs": 1734792100110,
"service": {
"jmq": "jmq-ngiot-eu.dc.ww.ecouser.net",
"mqs": "api-ngiot.dc-eu.ww.ecouser.net"
},
"deviceName": "DEEBOT N20 PRO PLUS",
"icon": "https: //portal-ww.ecouser.net/api/pim/file/get/0000001",
"ota": true,
"UILogicId": "y2_ww_h_y2h5",
"materialNo": "110-2406-0001",
"pid": "0000001",
"product_category": "DEEBOT",
"model": "Y2_AES_BLACK_INT",
"updateInfo": {
"needUpdate": false,
"changeLog": ""
},
"nick": "Dusty",
"homeId": "1234567890abcdef12345678",
"homeSort": 1,
"status": 1,
"otaUpgrade": {}
}

View File

@ -91,6 +91,328 @@
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[qhe2o2][button.dusty_empty_dustbin:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.dusty_empty_dustbin',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Empty dustbin',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'station_action_empty_dustbin',
'unique_id': '8516fbb1-17f1-4194-0000001_station_action_empty_dustbin',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[qhe2o2][button.dusty_empty_dustbin:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Empty dustbin',
}),
'context': <ANY>,
'entity_id': 'button.dusty_empty_dustbin',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[qhe2o2][button.dusty_relocate:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'button.dusty_relocate',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Relocate',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'relocate',
'unique_id': '8516fbb1-17f1-4194-0000001_relocate',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[qhe2o2][button.dusty_relocate:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Relocate',
}),
'context': <ANY>,
'entity_id': 'button.dusty_relocate',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_filter_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'button.dusty_reset_filter_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Reset filter lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'reset_lifespan_filter',
'unique_id': '8516fbb1-17f1-4194-0000001_reset_lifespan_filter',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_filter_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Reset filter lifespan',
}),
'context': <ANY>,
'entity_id': 'button.dusty_reset_filter_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_main_brush_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'button.dusty_reset_main_brush_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Reset main brush lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'reset_lifespan_brush',
'unique_id': '8516fbb1-17f1-4194-0000001_reset_lifespan_brush',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_main_brush_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Reset main brush lifespan',
}),
'context': <ANY>,
'entity_id': 'button.dusty_reset_main_brush_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_round_mop_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'button.dusty_reset_round_mop_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Reset round mop lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'reset_lifespan_round_mop',
'unique_id': '8516fbb1-17f1-4194-0000001_reset_lifespan_round_mop',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_round_mop_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Reset round mop lifespan',
}),
'context': <ANY>,
'entity_id': 'button.dusty_reset_round_mop_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_side_brush_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'button.dusty_reset_side_brush_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Reset side brush lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'reset_lifespan_side_brush',
'unique_id': '8516fbb1-17f1-4194-0000001_reset_lifespan_side_brush',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_side_brush_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Reset side brush lifespan',
}),
'context': <ANY>,
'entity_id': 'button.dusty_reset_side_brush_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_unit_care_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'button.dusty_reset_unit_care_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Reset unit care lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'reset_lifespan_unit_care',
'unique_id': '8516fbb1-17f1-4194-0000001_reset_lifespan_unit_care',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[qhe2o2][button.dusty_reset_unit_care_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Reset unit care lifespan',
}),
'context': <ANY>,
'entity_id': 'button.dusty_reset_unit_care_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2024-01-01T00:00:00+00:00',
})
# ---
# name: test_buttons[yna5x1][button.ozmo_950_relocate:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -725,6 +725,781 @@
'state': 'Testnetwork',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_area_cleaned:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dusty_area_cleaned',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Area cleaned',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'stats_area',
'unique_id': '8516fbb1-17f1-4194-0000001_stats_area',
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_area_cleaned:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Area cleaned',
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_area_cleaned',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '10',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_battery:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_battery',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
'original_icon': None,
'original_name': 'Battery',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '8516fbb1-17f1-4194-0000001_battery_level',
'unit_of_measurement': '%',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_battery:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Dusty Battery',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_battery',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '100',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_cleaning_duration:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dusty_cleaning_duration',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Cleaning duration',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'stats_time',
'unique_id': '8516fbb1-17f1-4194-0000001_stats_time',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_cleaning_duration:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Dusty Cleaning duration',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_cleaning_duration',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '5.0',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_error:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_error',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Error',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'error',
'unique_id': '8516fbb1-17f1-4194-0000001_error',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_error:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'description': 'NoError: Robot is operational',
'friendly_name': 'Dusty Error',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_error',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_filter_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_filter_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Filter lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lifespan_filter',
'unique_id': '8516fbb1-17f1-4194-0000001_lifespan_filter',
'unit_of_measurement': '%',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_filter_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Filter lifespan',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_filter_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '56',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_ip_address:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_ip_address',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'IP address',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'network_ip',
'unique_id': '8516fbb1-17f1-4194-0000001_network_ip',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_ip_address:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty IP address',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_ip_address',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '192.168.0.10',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_main_brush_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_main_brush_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Main brush lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lifespan_brush',
'unique_id': '8516fbb1-17f1-4194-0000001_lifespan_brush',
'unit_of_measurement': '%',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_main_brush_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Main brush lifespan',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_main_brush_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '80',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_round_mop_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_round_mop_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Round mop lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lifespan_round_mop',
'unique_id': '8516fbb1-17f1-4194-0000001_lifespan_round_mop',
'unit_of_measurement': '%',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_round_mop_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Round mop lifespan',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_round_mop_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_side_brush_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_side_brush_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Side brush lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lifespan_side_brush',
'unique_id': '8516fbb1-17f1-4194-0000001_lifespan_side_brush',
'unit_of_measurement': '%',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_side_brush_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Side brush lifespan',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_side_brush_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '40',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_station_state:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'idle',
'emptying_dustbin',
]),
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dusty_station_state',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Station state',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'station_state',
'unique_id': '8516fbb1-17f1-4194-0000001_station_state',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_station_state:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Dusty Station state',
'options': list([
'idle',
'emptying_dustbin',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_station_state',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'emptying_dustbin',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_total_area_cleaned:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dusty_total_area_cleaned',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Total area cleaned',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'total_stats_area',
'unique_id': '8516fbb1-17f1-4194-0000001_total_stats_area',
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_total_area_cleaned:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Total area cleaned',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_total_area_cleaned',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '60',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_total_cleaning_duration:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dusty_total_cleaning_duration',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Total cleaning duration',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'total_stats_time',
'unique_id': '8516fbb1-17f1-4194-0000001_total_stats_time',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_total_cleaning_duration:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Dusty Total cleaning duration',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_total_cleaning_duration',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '40.000',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_total_cleanings:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dusty_total_cleanings',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Total cleanings',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'total_stats_cleanings',
'unique_id': '8516fbb1-17f1-4194-0000001_total_stats_cleanings',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_total_cleanings:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Total cleanings',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_total_cleanings',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '123',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_unit_care_lifespan:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_unit_care_lifespan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Unit care lifespan',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'lifespan_unit_care',
'unique_id': '8516fbb1-17f1-4194-0000001_lifespan_unit_care',
'unit_of_measurement': '%',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_unit_care_lifespan:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Unit care lifespan',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_unit_care_lifespan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_wi_fi_rssi:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_wi_fi_rssi',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Wi-Fi RSSI',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'network_rssi',
'unique_id': '8516fbb1-17f1-4194-0000001_network_rssi',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_wi_fi_rssi:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Wi-Fi RSSI',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_wi_fi_rssi',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '-62',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_wi_fi_ssid:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.dusty_wi_fi_ssid',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Wi-Fi SSID',
'platform': 'ecovacs',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'network_ssid',
'unique_id': '8516fbb1-17f1-4194-0000001_network_ssid',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_wi_fi_ssid:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Wi-Fi SSID',
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_wi_fi_ssid',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'Testnetwork',
})
# ---
# name: test_sensors[yna5x1][sensor.ozmo_950_area_cleaned:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -1,7 +1,12 @@
"""Tests for Ecovacs sensors."""
from deebot_client.command import Command
from deebot_client.commands.json import ResetLifeSpan, SetRelocationState
from deebot_client.commands import StationAction
from deebot_client.commands.json import (
ResetLifeSpan,
SetRelocationState,
station_action,
)
from deebot_client.events import LifeSpan
import pytest
from syrupy import SnapshotAssertion
@ -60,8 +65,38 @@ def platforms() -> Platform | list[Platform]:
),
],
),
(
"qhe2o2",
[
("button.dusty_relocate", SetRelocationState()),
(
"button.dusty_reset_main_brush_lifespan",
ResetLifeSpan(LifeSpan.BRUSH),
),
(
"button.dusty_reset_filter_lifespan",
ResetLifeSpan(LifeSpan.FILTER),
),
(
"button.dusty_reset_side_brush_lifespan",
ResetLifeSpan(LifeSpan.SIDE_BRUSH),
),
(
"button.dusty_reset_unit_care_lifespan",
ResetLifeSpan(LifeSpan.UNIT_CARE),
),
(
"button.dusty_reset_round_mop_lifespan",
ResetLifeSpan(LifeSpan.ROUND_MOP),
),
(
"button.dusty_empty_dustbin",
station_action.StationAction(StationAction.EMPTY_DUSTBIN),
),
],
),
],
ids=["yna5x1", "5xu9h3"],
ids=["yna5x1", "5xu9h3", "qhe2o2"],
)
async def test_buttons(
hass: HomeAssistant,

View File

@ -11,6 +11,7 @@ from deebot_client.events import (
NetworkInfoEvent,
StatsEvent,
TotalStatsEvent,
station,
)
import pytest
from syrupy import SnapshotAssertion
@ -45,6 +46,7 @@ async def notify_events(hass: HomeAssistant, event_bus: EventBus):
event_bus.notify(LifeSpanEvent(LifeSpan.FILTER, 56, 40 * 60))
event_bus.notify(LifeSpanEvent(LifeSpan.SIDE_BRUSH, 40, 20 * 60))
event_bus.notify(ErrorEvent(0, "NoError: Robot is operational"))
event_bus.notify(station.StationEvent(station.State.EMPTYING))
await block_till_done(hass, event_bus)
@ -87,8 +89,29 @@ async def notify_events(hass: HomeAssistant, event_bus: EventBus):
"sensor.goat_g1_error",
],
),
(
"qhe2o2",
[
"sensor.dusty_area_cleaned",
"sensor.dusty_cleaning_duration",
"sensor.dusty_total_area_cleaned",
"sensor.dusty_total_cleaning_duration",
"sensor.dusty_total_cleanings",
"sensor.dusty_battery",
"sensor.dusty_ip_address",
"sensor.dusty_wi_fi_rssi",
"sensor.dusty_wi_fi_ssid",
"sensor.dusty_station_state",
"sensor.dusty_main_brush_lifespan",
"sensor.dusty_filter_lifespan",
"sensor.dusty_side_brush_lifespan",
"sensor.dusty_unit_care_lifespan",
"sensor.dusty_round_mop_lifespan",
"sensor.dusty_error",
],
),
],
ids=["yna5x1", "5xu9h3"],
ids=["yna5x1", "5xu9h3", "qhe2o2"],
)
async def test_sensors(
hass: HomeAssistant,
@ -99,7 +122,7 @@ async def test_sensors(
entity_ids: list[str],
) -> None:
"""Test that sensor entity snapshots match."""
assert entity_ids == hass.states.async_entity_ids()
assert hass.states.async_entity_ids() == entity_ids
for entity_id in entity_ids:
assert (state := hass.states.get(entity_id)), f"State of {entity_id} is missing"
assert state.state == STATE_UNKNOWN