Skip to content

Commit

Permalink
Implement new state property for vacuum which is using an enum (home-…
Browse files Browse the repository at this point in the history
…assistant#126353)

* Implement new state property for vacuum which is using an enum

* Mod

* Mod init

* Mods

* Fix integrations

* Tests

* Fix state

* Add vacuum tests

* Fix last test

* Litterrobot tests

* Fixes

* Tests

* Fixes

* Fix VacuumEntity

* Mods

* Mods

* Mods

* Update demo

* LG

* Fix vacuum

* Fix Matter

* Fix deprecation version

* Mods

* Fixes

* Fix ruff

* Fix tests

* Fix roomba

* Fix breaking dates
  • Loading branch information
gjohansson-ST authored Dec 6, 2024
1 parent bd9aefd commit 2eaf206
Show file tree
Hide file tree
Showing 39 changed files with 844 additions and 509 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/alexa/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ def get_property(self, name: str) -> Any:
elif self.entity.domain == remote.DOMAIN:
is_on = self.entity.state not in (STATE_OFF, STATE_UNKNOWN)
elif self.entity.domain == vacuum.DOMAIN:
is_on = self.entity.state == vacuum.STATE_CLEANING
is_on = self.entity.state == vacuum.VacuumActivity.CLEANING
elif self.entity.domain == timer.DOMAIN:
is_on = self.entity.state != STATE_IDLE
elif self.entity.domain == water_heater.DOMAIN:
Expand Down
35 changes: 13 additions & 22 deletions homeassistant/components/demo/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@

from homeassistant.components.vacuum import (
ATTR_CLEANED_AREA,
STATE_CLEANING,
STATE_DOCKED,
STATE_IDLE,
STATE_PAUSED,
STATE_RETURNING,
StateVacuumEntity,
VacuumActivity,
VacuumEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
Expand Down Expand Up @@ -91,16 +87,11 @@ def __init__(self, name: str, supported_features: VacuumEntityFeature) -> None:
"""Initialize the vacuum."""
self._attr_name = name
self._attr_supported_features = supported_features
self._state = STATE_DOCKED
self._attr_activity = VacuumActivity.DOCKED
self._fan_speed = FAN_SPEEDS[1]
self._cleaned_area: float = 0
self._battery_level = 100

@property
def state(self) -> str:
"""Return the current state of the vacuum."""
return self._state

@property
def battery_level(self) -> int:
"""Return the current battery level of the vacuum."""
Expand All @@ -123,33 +114,33 @@ def extra_state_attributes(self) -> dict[str, Any]:

def start(self) -> None:
"""Start or resume the cleaning task."""
if self._state != STATE_CLEANING:
self._state = STATE_CLEANING
if self._attr_activity != VacuumActivity.CLEANING:
self._attr_activity = VacuumActivity.CLEANING
self._cleaned_area += 1.32
self._battery_level -= 1
self.schedule_update_ha_state()

def pause(self) -> None:
"""Pause the cleaning task."""
if self._state == STATE_CLEANING:
self._state = STATE_PAUSED
if self._attr_activity == VacuumActivity.CLEANING:
self._attr_activity = VacuumActivity.PAUSED
self.schedule_update_ha_state()

def stop(self, **kwargs: Any) -> None:
"""Stop the cleaning task, do not return to dock."""
self._state = STATE_IDLE
self._attr_activity = VacuumActivity.IDLE
self.schedule_update_ha_state()

def return_to_base(self, **kwargs: Any) -> None:
"""Return dock to charging base."""
self._state = STATE_RETURNING
self._attr_activity = VacuumActivity.RETURNING
self.schedule_update_ha_state()

event.call_later(self.hass, 30, self.__set_state_to_dock)

def clean_spot(self, **kwargs: Any) -> None:
"""Perform a spot clean-up."""
self._state = STATE_CLEANING
self._attr_activity = VacuumActivity.CLEANING
self._cleaned_area += 1.32
self._battery_level -= 1
self.schedule_update_ha_state()
Expand All @@ -167,12 +158,12 @@ async def async_locate(self, **kwargs: Any) -> None:
"persistent_notification",
service_data={"message": "I'm here!", "title": "Locate request"},
)
self._state = STATE_IDLE
self._attr_activity = VacuumActivity.IDLE
self.async_write_ha_state()

async def async_clean_spot(self, **kwargs: Any) -> None:
"""Locate the vacuum's position."""
self._state = STATE_CLEANING
self._attr_activity = VacuumActivity.CLEANING
self.async_write_ha_state()

async def async_send_command(
Expand All @@ -182,9 +173,9 @@ async def async_send_command(
**kwargs: Any,
) -> None:
"""Send a command to the vacuum."""
self._state = STATE_IDLE
self._attr_activity = VacuumActivity.IDLE
self.async_write_ha_state()

def __set_state_to_dock(self, _: datetime) -> None:
self._state = STATE_DOCKED
self._attr_activity = VacuumActivity.DOCKED
self.schedule_update_ha_state()
35 changes: 15 additions & 20 deletions homeassistant/components/ecovacs/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,9 @@
import sucks

from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_DOCKED,
STATE_ERROR,
STATE_IDLE,
STATE_PAUSED,
STATE_RETURNING,
StateVacuumEntity,
StateVacuumEntityDescription,
VacuumActivity,
VacuumEntityFeature,
)
from homeassistant.core import HomeAssistant, SupportsResponse
Expand Down Expand Up @@ -123,22 +118,22 @@ def on_error(self, error: str) -> None:
self.schedule_update_ha_state()

@property
def state(self) -> str | None:
def activity(self) -> VacuumActivity | None:
"""Return the state of the vacuum cleaner."""
if self.error is not None:
return STATE_ERROR
return VacuumActivity.ERROR

if self.device.is_cleaning:
return STATE_CLEANING
return VacuumActivity.CLEANING

if self.device.is_charging:
return STATE_DOCKED
return VacuumActivity.DOCKED

if self.device.vacuum_status == sucks.CLEAN_MODE_STOP:
return STATE_IDLE
return VacuumActivity.IDLE

if self.device.vacuum_status == sucks.CHARGE_MODE_RETURNING:
return STATE_RETURNING
return VacuumActivity.RETURNING

return None

Expand Down Expand Up @@ -202,7 +197,7 @@ def locate(self, **kwargs: Any) -> None:

def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
"""Set fan speed."""
if self.state == STATE_CLEANING:
if self.state == VacuumActivity.CLEANING:
self.device.run(sucks.Clean(mode=self.device.clean_status, speed=fan_speed))

def send_command(
Expand All @@ -225,12 +220,12 @@ async def async_raw_get_positions(


_STATE_TO_VACUUM_STATE = {
State.IDLE: STATE_IDLE,
State.CLEANING: STATE_CLEANING,
State.RETURNING: STATE_RETURNING,
State.DOCKED: STATE_DOCKED,
State.ERROR: STATE_ERROR,
State.PAUSED: STATE_PAUSED,
State.IDLE: VacuumActivity.IDLE,
State.CLEANING: VacuumActivity.CLEANING,
State.RETURNING: VacuumActivity.RETURNING,
State.DOCKED: VacuumActivity.DOCKED,
State.ERROR: VacuumActivity.ERROR,
State.PAUSED: VacuumActivity.PAUSED,
}

_ATTR_ROOMS = "rooms"
Expand Down Expand Up @@ -284,7 +279,7 @@ async def on_rooms(event: RoomsEvent) -> None:
self.async_write_ha_state()

async def on_status(event: StateEvent) -> None:
self._attr_state = _STATE_TO_VACUUM_STATE[event.state]
self._attr_activity = _STATE_TO_VACUUM_STATE[event.state]
self.async_write_ha_state()

self._subscribe(self._capability.battery.event, on_battery)
Expand Down
10 changes: 5 additions & 5 deletions homeassistant/components/google_assistant/trait.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ def sync_attributes(self) -> dict[str, Any]:

def query_attributes(self) -> dict[str, Any]:
"""Return dock query attributes."""
return {"isDocked": self.state.state == vacuum.STATE_DOCKED}
return {"isDocked": self.state.state == vacuum.VacuumActivity.DOCKED}

async def execute(self, command, data, params, challenge):
"""Execute a dock command."""
Expand Down Expand Up @@ -825,8 +825,8 @@ def query_attributes(self) -> dict[str, Any]:
"capacityUntilFull": [
{"rawValue": 100 - battery_level, "unit": "PERCENTAGE"}
],
"isCharging": self.state.state == vacuum.STATE_DOCKED,
"isPluggedIn": self.state.state == vacuum.STATE_DOCKED,
"isCharging": self.state.state == vacuum.VacuumActivity.DOCKED,
"isPluggedIn": self.state.state == vacuum.VacuumActivity.DOCKED,
}

async def execute(self, command, data, params, challenge):
Expand Down Expand Up @@ -882,8 +882,8 @@ def query_attributes(self) -> dict[str, Any]:

if domain == vacuum.DOMAIN:
return {
"isRunning": state == vacuum.STATE_CLEANING,
"isPaused": state == vacuum.STATE_PAUSED,
"isRunning": state == vacuum.VacuumActivity.CLEANING,
"isPaused": state == vacuum.VacuumActivity.PAUSED,
}

if domain in COVER_VALVE_DOMAINS:
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/group/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from homeassistant.components.alarm_control_panel import AlarmControlPanelState
from homeassistant.components.climate import HVACMode
from homeassistant.components.lock import LockState
from homeassistant.components.vacuum import STATE_CLEANING, STATE_ERROR, STATE_RETURNING
from homeassistant.components.vacuum import VacuumActivity
from homeassistant.components.water_heater import (
STATE_ECO,
STATE_ELECTRIC,
Expand Down Expand Up @@ -105,9 +105,9 @@
Platform.VACUUM: (
{
STATE_ON,
STATE_CLEANING,
STATE_RETURNING,
STATE_ERROR,
VacuumActivity.CLEANING,
VacuumActivity.RETURNING,
VacuumActivity.ERROR,
},
STATE_ON,
STATE_OFF,
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/homekit/type_switches.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
DOMAIN as VACUUM_DOMAIN,
SERVICE_RETURN_TO_BASE,
SERVICE_START,
STATE_CLEANING,
VacuumActivity,
VacuumEntityFeature,
)
from homeassistant.const import (
Expand Down Expand Up @@ -213,7 +213,7 @@ def set_state(self, value: bool) -> None:
@callback
def async_update_state(self, new_state: State) -> None:
"""Update switch state after state changed."""
current_state = new_state.state in (STATE_CLEANING, STATE_ON)
current_state = new_state.state in (VacuumActivity.CLEANING, STATE_ON)
_LOGGER.debug("%s: Set current state to %s", self.entity_id, current_state)
self.char_on.set_value(current_state)

Expand Down
40 changes: 18 additions & 22 deletions homeassistant/components/lg_thinq/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,11 @@
from thinqconnect.integration import ExtendedProperty

from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_DOCKED,
STATE_ERROR,
STATE_RETURNING,
StateVacuumEntity,
StateVacuumEntityDescription,
VacuumActivity,
VacuumEntityFeature,
)
from homeassistant.const import STATE_IDLE, STATE_PAUSED
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

Expand Down Expand Up @@ -46,21 +42,21 @@ class State(StrEnum):


ROBOT_STATUS_TO_HA = {
"charging": STATE_DOCKED,
"diagnosis": STATE_IDLE,
"homing": STATE_RETURNING,
"initializing": STATE_IDLE,
"macrosector": STATE_IDLE,
"monitoring_detecting": STATE_IDLE,
"monitoring_moving": STATE_IDLE,
"monitoring_positioning": STATE_IDLE,
"pause": STATE_PAUSED,
"reservation": STATE_IDLE,
"setdate": STATE_IDLE,
"sleep": STATE_IDLE,
"standby": STATE_IDLE,
"working": STATE_CLEANING,
"error": STATE_ERROR,
"charging": VacuumActivity.DOCKED,
"diagnosis": VacuumActivity.IDLE,
"homing": VacuumActivity.RETURNING,
"initializing": VacuumActivity.IDLE,
"macrosector": VacuumActivity.IDLE,
"monitoring_detecting": VacuumActivity.IDLE,
"monitoring_moving": VacuumActivity.IDLE,
"monitoring_positioning": VacuumActivity.IDLE,
"pause": VacuumActivity.PAUSED,
"reservation": VacuumActivity.IDLE,
"setdate": VacuumActivity.IDLE,
"sleep": VacuumActivity.IDLE,
"standby": VacuumActivity.IDLE,
"working": VacuumActivity.CLEANING,
"error": VacuumActivity.ERROR,
}
ROBOT_BATT_TO_HA = {
"moveless": 5,
Expand Down Expand Up @@ -114,7 +110,7 @@ def _update_status(self) -> None:
super()._update_status()

# Update state.
self._attr_state = ROBOT_STATUS_TO_HA[self.data.current_state]
self._attr_activity = ROBOT_STATUS_TO_HA[self.data.current_state]

# Update battery.
if (level := self.data.battery) is not None:
Expand All @@ -135,7 +131,7 @@ async def async_start(self, **kwargs) -> None:
"""Start the device."""
if self.data.current_state == State.SLEEP:
value = State.WAKE_UP
elif self._attr_state == STATE_PAUSED:
elif self._attr_activity == VacuumActivity.PAUSED:
value = State.RESUME
else:
value = State.START
Expand Down
29 changes: 13 additions & 16 deletions homeassistant/components/litterrobot/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
import voluptuous as vol

from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_DOCKED,
STATE_ERROR,
STATE_PAUSED,
StateVacuumEntity,
StateVacuumEntityDescription,
VacuumActivity,
VacuumEntityFeature,
)
from homeassistant.core import HomeAssistant
Expand All @@ -29,16 +26,16 @@
SERVICE_SET_SLEEP_MODE = "set_sleep_mode"

LITTER_BOX_STATUS_STATE_MAP = {
LitterBoxStatus.CLEAN_CYCLE: STATE_CLEANING,
LitterBoxStatus.EMPTY_CYCLE: STATE_CLEANING,
LitterBoxStatus.CLEAN_CYCLE_COMPLETE: STATE_DOCKED,
LitterBoxStatus.CAT_DETECTED: STATE_DOCKED,
LitterBoxStatus.CAT_SENSOR_TIMING: STATE_DOCKED,
LitterBoxStatus.DRAWER_FULL_1: STATE_DOCKED,
LitterBoxStatus.DRAWER_FULL_2: STATE_DOCKED,
LitterBoxStatus.READY: STATE_DOCKED,
LitterBoxStatus.CAT_SENSOR_INTERRUPTED: STATE_PAUSED,
LitterBoxStatus.OFF: STATE_DOCKED,
LitterBoxStatus.CLEAN_CYCLE: VacuumActivity.CLEANING,
LitterBoxStatus.EMPTY_CYCLE: VacuumActivity.CLEANING,
LitterBoxStatus.CLEAN_CYCLE_COMPLETE: VacuumActivity.DOCKED,
LitterBoxStatus.CAT_DETECTED: VacuumActivity.DOCKED,
LitterBoxStatus.CAT_SENSOR_TIMING: VacuumActivity.DOCKED,
LitterBoxStatus.DRAWER_FULL_1: VacuumActivity.DOCKED,
LitterBoxStatus.DRAWER_FULL_2: VacuumActivity.DOCKED,
LitterBoxStatus.READY: VacuumActivity.DOCKED,
LitterBoxStatus.CAT_SENSOR_INTERRUPTED: VacuumActivity.PAUSED,
LitterBoxStatus.OFF: VacuumActivity.DOCKED,
}

LITTER_BOX_ENTITY = StateVacuumEntityDescription(
Expand Down Expand Up @@ -78,9 +75,9 @@ class LitterRobotCleaner(LitterRobotEntity[LitterRobot], StateVacuumEntity):
)

@property
def state(self) -> str:
def activity(self) -> VacuumActivity:
"""Return the state of the cleaner."""
return LITTER_BOX_STATUS_STATE_MAP.get(self.robot.status, STATE_ERROR)
return LITTER_BOX_STATUS_STATE_MAP.get(self.robot.status, VacuumActivity.ERROR)

@property
def status(self) -> str:
Expand Down
Loading

0 comments on commit 2eaf206

Please sign in to comment.