fix: Fix the HA warning in the logs related to vacuum state setting (#694)
Some checks failed
Tests / check-rule-format (push) Failing after 12s
Validate / validate-hassfest (push) Failing after 8s
Validate / validate-hacs (push) Failing after 3m6s
Validate / validate-lint (push) Failing after 11s
Validate / validate-setup (push) Failing after 9s

This commit is contained in:
ted
2025-07-08 13:48:17 +08:00
committed by GitHub
parent 9fbbb26d33
commit e5165f34da

View File

@ -47,21 +47,18 @@ Vacuum entities for Xiaomi Home.
""" """
from __future__ import annotations from __future__ import annotations
from typing import Any, Optional from typing import Any, Optional
import re
import logging import logging
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.vacuum import ( from homeassistant.components.vacuum import (StateVacuumEntity,
StateVacuumEntity, VacuumEntityFeature)
VacuumEntityFeature
)
from .miot.const import DOMAIN from .miot.const import DOMAIN
from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData
from .miot.miot_spec import ( from .miot.miot_spec import (MIoTSpecAction, MIoTSpecProperty)
MIoTSpecAction,
MIoTSpecProperty)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -99,10 +96,12 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
_status_map: Optional[dict[int, str]] _status_map: Optional[dict[int, str]]
_fan_level_map: Optional[dict[int, str]] _fan_level_map: Optional[dict[int, str]]
def __init__( _device_name: str
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
) -> None: def __init__(self, miot_device: MIoTDevice,
entity_data: MIoTEntityData) -> None:
super().__init__(miot_device=miot_device, entity_data=entity_data) super().__init__(miot_device=miot_device, entity_data=entity_data)
self._device_name = miot_device.name
self._attr_supported_features = VacuumEntityFeature(0) self._attr_supported_features = VacuumEntityFeature(0)
self._prop_status = None self._prop_status = None
@ -121,21 +120,21 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
for prop in entity_data.props: for prop in entity_data.props:
if prop.name == 'status': if prop.name == 'status':
if not prop.value_list: if not prop.value_list:
_LOGGER.error( _LOGGER.error('invalid status value_list, %s',
'invalid status value_list, %s', self.entity_id) self.entity_id)
continue continue
self._status_map = prop.value_list.to_map() self._status_map = prop.value_list.to_map()
self._attr_supported_features |= VacuumEntityFeature.STATE
self._prop_status = prop self._prop_status = prop
elif prop.name == 'fan-level': elif prop.name == 'fan-level':
if not prop.value_list: if not prop.value_list:
_LOGGER.error( _LOGGER.error('invalid fan-level value_list, %s',
'invalid fan-level value_list, %s', self.entity_id) self.entity_id)
continue continue
self._fan_level_map = prop.value_list.to_map() self._fan_level_map = prop.value_list.to_map()
self._attr_fan_speed_list = list(self._fan_level_map.values()) self._attr_fan_speed_list = list(self._fan_level_map.values())
self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED
self._prop_fan_level = prop self._prop_fan_level = prop
elif prop.name == 'battery-level': elif prop.name == 'battery-level':
self._attr_supported_features |= VacuumEntityFeature.BATTERY self._attr_supported_features |= VacuumEntityFeature.BATTERY
self._prop_battery_level = prop self._prop_battery_level = prop
@ -155,14 +154,22 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
elif action.name == 'stop-and-gocharge': elif action.name == 'stop-and-gocharge':
self._attr_supported_features |= VacuumEntityFeature.RETURN_HOME self._attr_supported_features |= VacuumEntityFeature.RETURN_HOME
self._action_stop_and_gocharge = action self._action_stop_and_gocharge = action
elif action.name == 'identify': elif action.name == 'identify':
self._attr_supported_features |= VacuumEntityFeature.LOCATE self._attr_supported_features |= VacuumEntityFeature.LOCATE
self._action_identify = action self._action_identify = action
async def async_start(self) -> None: async def async_start(self) -> None:
"""Start or resume the cleaning task.""" """Start or resume the cleaning task."""
if self.state.lower() in ['paused', '暂停中']: try: # VacuumActivity is introduced in HA core 2025.1.0
# pylint: disable=import-outside-toplevel
from homeassistant.components.vacuum import VacuumActivity
if (self.activity
== VacuumActivity.PAUSED) and self._action_continue_sweep:
await self.action_async(action=self._action_continue_sweep)
return
except ImportError:
if self.state and (self.state in {'paused', 'pause'
}) and self._action_continue_sweep:
await self.action_async(action=self._action_continue_sweep) await self.action_async(action=self._action_continue_sweep)
return return
await self.action_async(action=self._action_start_sweep) await self.action_async(action=self._action_start_sweep)
@ -179,31 +186,92 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
"""Set the vacuum cleaner to return to the dock.""" """Set the vacuum cleaner to return to the dock."""
await self.action_async(action=self._action_stop_and_gocharge) await self.action_async(action=self._action_stop_and_gocharge)
async def async_clean_spot(self, **kwargs: Any) -> None:
"""Perform a spot clean-up."""
async def async_locate(self, **kwargs: Any) -> None: async def async_locate(self, **kwargs: Any) -> None:
"""Locate the vacuum cleaner.""" """Locate the vacuum cleaner."""
await self.action_async(action=self._action_identify) await self.action_async(action=self._action_identify)
async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
"""Set fan speed.""" """Set fan speed."""
fan_level_value = self.get_map_key(map_=self._fan_level_map,
value=fan_speed)
await self.set_property_async(prop=self._prop_fan_level,
value=fan_level_value)
@property
def name(self) -> Optional[str]:
"""Name of the vacuum entity."""
return self._device_name
@property @property
def state(self) -> Optional[str]: def state(self) -> Optional[str]:
"""Return the current state of the vacuum cleaner.""" """Return the current state of the vacuum cleaner.
return self.get_map_value(
map_=self._status_map, To fix the HA warning below:
key=self.get_prop_value(prop=self._prop_status)) Detected that custom integration 'xiaomi_home' is setting state
directly.Entity XXX(<class 'custom_components.xiaomi_home.vacuum
.Vacuum'>)should implement the 'activity' property and return
its state using the VacuumActivity enum.This will stop working in
Home Assistant 2026.1.
Refer to
https://developers.home-assistant.io/blog/2024/12/08/new-vacuum-state-property
There are only 6 states in VacuumActivity enum. To be compatible with
more constants, try get matching VacuumActivity enum first, return state
string as before if there is no match. In Home Assistant 2026.1, every
state should map to a VacuumActivity enum.
"""
return self.activity
@property
def activity(self) -> Optional[str]:
"""The current vacuum activity."""
status = self.get_prop_value(prop=self._prop_status)
if status is None:
return None
status_value = self.get_map_value(map_=self._status_map, key=status)
if status_value is None:
return None
try:
# pylint: disable=import-outside-toplevel
from homeassistant.components.vacuum import VacuumActivity
status_value = status_value.lower()
status_str = re.sub(r'[^a-z]', '', status_value)
if status_str in {
'charging', 'charged', 'chargingcompleted', 'fullcharge',
'fullpower', 'findchargerpause', 'drying', 'washing',
'wash', 'inthewash', 'inthedry', 'stationworking',
'dustcollecting', 'upgrade', 'upgrading', 'updating'
}:
return VacuumActivity.DOCKED
if status_str in {'paused', 'pause'}:
return VacuumActivity.PAUSED
if status_str in {
'gocharging', 'cleancompletegocharging', 'findchargewash',
'backtowashmop', 'gowash', 'gowashing', 'summon'
}:
return VacuumActivity.RETURNING
if (status_str.find('sweeping')
!= -1) or (status_str.find('mopping')
!= -1) or (status_str in {
'cleaning', 'remoteclean', 'continuesweep',
'busy', 'building', 'buildingmap', 'mapping'
}):
return VacuumActivity.CLEANING
if status_str in {'error', 'breakcharging', 'gochargebreak'}:
return VacuumActivity.ERROR
return VacuumActivity.IDLE
except ImportError:
return status_value
@property @property
def battery_level(self) -> Optional[int]: def battery_level(self) -> Optional[int]:
"""Return the current battery level of the vacuum cleaner.""" """The current battery level of the vacuum cleaner."""
return self.get_prop_value(prop=self._prop_battery_level) return self.get_prop_value(prop=self._prop_battery_level)
@property @property
def fan_speed(self) -> Optional[str]: def fan_speed(self) -> Optional[str]:
"""Return the current fan speed of the vacuum cleaner.""" """The current fan speed of the vacuum cleaner."""
return self.get_map_value( return self.get_map_value(
map_=self._fan_level_map, map_=self._fan_level_map,
key=self.get_prop_value(prop=self._prop_fan_level)) key=self.get_prop_value(prop=self._prop_fan_level))