1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00

Use select entity for Ecovacs station auto empty settings (#155679)

This commit is contained in:
Robert Resch
2025-11-03 06:38:21 +01:00
committed by GitHub
parent 4e48c881aa
commit 84561cbc41
8 changed files with 159 additions and 91 deletions

View File

@@ -81,6 +81,9 @@
"active_map": {
"default": "mdi:floor-plan"
},
"auto_empty": {
"default": "mdi:delete-empty"
},
"water_amount": {
"default": "mdi:water"
},
@@ -89,9 +92,6 @@
}
},
"sensor": {
"auto_empty": {
"default": "mdi:delete-empty"
},
"error": {
"default": "mdi:alert-circle"
},

View File

@@ -5,8 +5,9 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING, Any
from deebot_client.capabilities import CapabilityMap, CapabilitySet, CapabilitySetTypes
from deebot_client.command import CommandWithMessageHandling
from deebot_client.device import Device
from deebot_client.events import WorkModeEvent
from deebot_client.events import WorkModeEvent, auto_empty
from deebot_client.events.base import Event
from deebot_client.events.map import CachedMapInfoEvent, MajorMapEvent
from deebot_client.events.water_info import WaterAmountEvent
@@ -34,6 +35,9 @@ class EcovacsSelectEntityDescription[EventT: Event](
current_option_fn: Callable[[EventT], str | None]
options_fn: Callable[[CapabilitySetTypes], list[str]]
set_option_fn: Callable[[CapabilitySetTypes, str], CommandWithMessageHandling] = (
lambda cap, option: cap.set(option)
)
ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = (
@@ -58,6 +62,14 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSelectEntityDescription, ...] = (
entity_registry_enabled_default=False,
entity_category=EntityCategory.CONFIG,
),
EcovacsSelectEntityDescription[auto_empty.AutoEmptyEvent](
capability_fn=lambda caps: caps.station.auto_empty if caps.station else None,
current_option_fn=lambda e: get_name_key(e.frequency) if e.frequency else None,
options_fn=lambda cap: [get_name_key(freq) for freq in cap.types],
set_option_fn=lambda cap, option: cap.set(None, option),
key="auto_empty",
translation_key="auto_empty",
),
)
@@ -106,14 +118,17 @@ class EcovacsSelectEntity[EventT: Event](
await super().async_added_to_hass()
async def on_event(event: EventT) -> None:
self._attr_current_option = self.entity_description.current_option_fn(event)
self.async_write_ha_state()
if (option := self.entity_description.current_option_fn(event)) is not None:
self._attr_current_option = option
self.async_write_ha_state()
self._subscribe(self._capability.event, on_event)
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self._device.execute_command(self._capability.set(option))
await self._device.execute_command(
self.entity_description.set_option_fn(self._capability, option)
)
class EcovacsActiveMapSelectEntity(

View File

@@ -17,7 +17,6 @@ from deebot_client.events import (
NetworkInfoEvent,
StatsEvent,
TotalStatsEvent,
auto_empty,
station,
)
from sucks import VacBot
@@ -159,14 +158,6 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.ENUM,
options=get_options(station.State),
),
EcovacsSensorEntityDescription[auto_empty.AutoEmptyEvent](
capability_fn=lambda caps: caps.station.auto_empty if caps.station else None,
value_fn=lambda e: get_name_key(e.frequency) if e.frequency else None,
key="auto_empty",
translation_key="auto_empty",
device_class=SensorDeviceClass.ENUM,
options=get_options(auto_empty.Frequency),
),
)

View File

@@ -129,6 +129,16 @@
"active_map": {
"name": "Active map"
},
"auto_empty": {
"name": "Auto-empty frequency",
"state": {
"auto": "Auto",
"min_10": "10 minutes",
"min_15": "15 minutes",
"min_25": "25 minutes",
"smart": "Smart"
}
},
"water_amount": {
"name": "[%key:component::ecovacs::entity::number::water_amount::name%]",
"state": {
@@ -149,13 +159,6 @@
}
},
"sensor": {
"auto_empty": {
"name": "Auto-empty frequency",
"state": {
"auto": "Auto",
"smart": "Smart"
}
},
"error": {
"name": "Error",
"state_attributes": {

View File

@@ -56,6 +56,63 @@
'state': 'Map 2',
})
# ---
# name: test_selects[n0vyif-entity_ids2][select.x8_pro_omni_auto_empty_frequency:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'auto',
'smart',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': None,
'entity_id': 'select.x8_pro_omni_auto_empty_frequency',
'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': 'Auto-empty frequency',
'platform': 'ecovacs',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'auto_empty',
'unique_id': 'E1234567890000000009_auto_empty',
'unit_of_measurement': None,
})
# ---
# name: test_selects[n0vyif-entity_ids2][select.x8_pro_omni_auto_empty_frequency:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'X8 PRO OMNI Auto-empty frequency',
'options': list([
'auto',
'smart',
]),
}),
'context': <ANY>,
'entity_id': 'select.x8_pro_omni_auto_empty_frequency',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'auto',
})
# ---
# name: test_selects[n0vyif-entity_ids2][select.x8_pro_omni_work_mode:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -174,6 +231,63 @@
'state': 'Map 2',
})
# ---
# name: test_selects[qhe2o2-entity_ids1][select.dusty_auto_empty_frequency:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'auto',
'smart',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': None,
'entity_id': 'select.dusty_auto_empty_frequency',
'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': 'Auto-empty frequency',
'platform': 'ecovacs',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'auto_empty',
'unique_id': '8516fbb1-17f1-4194-0000001_auto_empty',
'unit_of_measurement': None,
})
# ---
# name: test_selects[qhe2o2-entity_ids1][select.dusty_auto_empty_frequency:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dusty Auto-empty frequency',
'options': list([
'auto',
'smart',
]),
}),
'context': <ANY>,
'entity_id': 'select.dusty_auto_empty_frequency',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'auto',
})
# ---
# name: test_selects[qhe2o2-entity_ids1][select.dusty_water_flow_level:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@@ -1652,70 +1652,6 @@
'state': 'Testnetwork',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_auto_empty_frequency:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Dusty Auto-empty frequency',
'options': list([
'min_10',
'min_15',
'min_25',
'auto',
'smart',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.dusty_auto_empty_frequency',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'auto',
})
# ---
# name: test_sensors[qhe2o2][sensor.dusty_auto_empty_frequency:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'min_10',
'min_15',
'min_25',
'auto',
'smart',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dusty_auto_empty_frequency',
'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': 'Auto-empty frequency',
'platform': 'ecovacs',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'auto_empty',
'unique_id': '8516fbb1-17f1-4194-0000001_auto_empty',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[yna5x1][sensor.ozmo_950_area_cleaned:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@@ -2,7 +2,9 @@
from deebot_client.command import Command
from deebot_client.commands.json import SetWaterInfo
from deebot_client.commands.json.auto_empty import SetAutoEmpty
from deebot_client.event_bus import EventBus
from deebot_client.events import auto_empty
from deebot_client.events.map import CachedMapInfoEvent, MajorMapEvent, Map
from deebot_client.events.water_info import WaterAmount, WaterAmountEvent
from deebot_client.events.work_mode import WorkMode, WorkModeEvent
@@ -55,6 +57,7 @@ async def notify_events(hass: HomeAssistant, event_bus: EventBus):
)
)
event_bus.notify(MajorMapEvent("2", [], requested=False))
event_bus.notify(auto_empty.AutoEmptyEvent(True, auto_empty.Frequency.AUTO))
await block_till_done(hass, event_bus)
@@ -73,6 +76,7 @@ async def notify_events(hass: HomeAssistant, event_bus: EventBus):
"qhe2o2",
[
"select.dusty_water_flow_level",
"select.dusty_auto_empty_frequency",
"select.dusty_active_map",
],
),
@@ -80,6 +84,7 @@ async def notify_events(hass: HomeAssistant, event_bus: EventBus):
"n0vyif",
[
"select.x8_pro_omni_work_mode",
"select.x8_pro_omni_auto_empty_frequency",
"select.x8_pro_omni_active_map",
],
),
@@ -94,7 +99,7 @@ async def test_selects(
entity_ids: list[str],
) -> None:
"""Test that select 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
@@ -124,6 +129,13 @@ async def test_selects(
"low",
SetWaterInfo(WaterAmount.LOW),
),
(
"qhe2o2",
"select.dusty_auto_empty_frequency",
"auto",
"smart",
SetAutoEmpty(None, auto_empty.Frequency.SMART),
),
],
)
async def test_selects_change(

View File

@@ -11,7 +11,6 @@ from deebot_client.events import (
NetworkInfoEvent,
StatsEvent,
TotalStatsEvent,
auto_empty,
station,
)
import pytest
@@ -48,7 +47,6 @@ async def notify_events(hass: HomeAssistant, event_bus: EventBus):
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_DUSTBIN))
event_bus.notify(auto_empty.AutoEmptyEvent(True, auto_empty.Frequency.AUTO))
await block_till_done(hass, event_bus)
@@ -104,7 +102,6 @@ async def notify_events(hass: HomeAssistant, event_bus: EventBus):
"sensor.dusty_wi_fi_rssi",
"sensor.dusty_wi_fi_ssid",
"sensor.dusty_station_state",
"sensor.dusty_auto_empty_frequency",
"sensor.dusty_main_brush_lifespan",
"sensor.dusty_filter_lifespan",
"sensor.dusty_round_mop_lifespan",