diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 5985bb5bcac..0fae52f2a9c 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -6,7 +6,7 @@ import asyncio from datetime import timedelta from functools import partial import logging -from typing import TYPE_CHECKING, Any, final +from typing import Any, final from propcache.api import cached_property import voluptuous as vol @@ -22,12 +22,6 @@ from homeassistant.const import ( # noqa: F401 # STATE_PAUSED/IDLE are API ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.deprecation import ( - DeprecatedConstantEnum, - all_with_deprecated_constants, - check_if_deprecated_constant, - dir_with_deprecated_constants, -) from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_platform import EntityPlatform @@ -36,16 +30,7 @@ from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass -from .const import ( # noqa: F401 - _DEPRECATED_STATE_CLEANING, - _DEPRECATED_STATE_DOCKED, - _DEPRECATED_STATE_ERROR, - _DEPRECATED_STATE_RETURNING, - DATA_COMPONENT, - DOMAIN, - VacuumActivity, - VacuumEntityFeature, -) +from .const import DATA_COMPONENT, DOMAIN, VacuumActivity, VacuumEntityFeature _LOGGER = logging.getLogger(__name__) @@ -73,11 +58,6 @@ SERVICE_STOP = "stop" DEFAULT_NAME = "Vacuum cleaner robot" -# These STATE_* constants are deprecated as of Home Assistant 2025.1. -# Please use the VacuumActivity enum instead. -_DEPRECATED_STATE_IDLE = DeprecatedConstantEnum(VacuumActivity.IDLE, "2026.1") -_DEPRECATED_STATE_PAUSED = DeprecatedConstantEnum(VacuumActivity.PAUSED, "2026.1") - _BATTERY_DEPRECATION_IGNORED_PLATFORMS = ( "mqtt", "template", @@ -196,7 +176,6 @@ class StateVacuumEntity( _attr_activity: VacuumActivity | None = None _attr_supported_features: VacuumEntityFeature = VacuumEntityFeature(0) - __vacuum_legacy_state: bool = False __vacuum_legacy_battery_level: bool = False __vacuum_legacy_battery_icon: bool = False __vacuum_legacy_battery_feature: bool = False @@ -204,10 +183,6 @@ class StateVacuumEntity( def __init_subclass__(cls, **kwargs: Any) -> None: """Post initialisation processing.""" super().__init_subclass__(**kwargs) - if any(method in cls.__dict__ for method in ("_attr_state", "state")): - # Integrations should use the 'activity' property instead of - # setting the state directly. - cls.__vacuum_legacy_state = True if any( method in cls.__dict__ for method in ("_attr_battery_level", "battery_level") @@ -223,11 +198,9 @@ class StateVacuumEntity( def __setattr__(self, name: str, value: Any) -> None: """Set attribute. - Deprecation warning if setting state, battery icon or battery level + Deprecation warning if setting battery icon or battery level attributes directly unless already reported. """ - if name == "_attr_state": - self._report_deprecated_activity_handling() if name in {"_attr_battery_level", "_attr_battery_icon"}: self._report_deprecated_battery_properties(name[6:]) return super().__setattr__(name, value) @@ -241,30 +214,11 @@ class StateVacuumEntity( ) -> None: """Start adding an entity to a platform.""" super().add_to_platform_start(hass, platform, parallel_updates) - if self.__vacuum_legacy_state: - self._report_deprecated_activity_handling() if self.__vacuum_legacy_battery_level: self._report_deprecated_battery_properties("battery_level") if self.__vacuum_legacy_battery_icon: self._report_deprecated_battery_properties("battery_icon") - @callback - def _report_deprecated_activity_handling(self) -> None: - """Report on deprecated handling of vacuum state. - - Integrations should implement activity instead of using state directly. - """ - report_usage( - "is setting state directly." - f" Entity {self.entity_id} ({type(self)}) should implement the 'activity'" - " property and return its state using the VacuumActivity enum", - core_integration_behavior=ReportBehavior.ERROR, - custom_integration_behavior=ReportBehavior.LOG, - breaks_in_ha_version="2026.1", - integration_domain=self.platform.platform_name if self.platform else None, - exclude_integrations={DOMAIN}, - ) - @callback def _report_deprecated_battery_properties(self, property: str) -> None: """Report on deprecated use of battery properties. @@ -368,12 +322,6 @@ class StateVacuumEntity( """Return the state of the vacuum cleaner.""" if (activity := self.activity) is not None: return activity - if self._attr_state is not None: - # Backwards compatibility for integrations that set state directly - # Should be removed in 2026.1 - if TYPE_CHECKING: - assert isinstance(self._attr_state, str) - return self._attr_state return None @cached_property @@ -491,13 +439,3 @@ class StateVacuumEntity( This method must be run in the event loop. """ await self.hass.async_add_executor_job(self.pause) - - -# As we import deprecated constants from the const module, we need to add these two functions -# otherwise this module will be logged for using deprecated constants and not the custom component -# These can be removed if no deprecated constant are in this module anymore -__getattr__ = partial(check_if_deprecated_constant, module_globals=globals()) -__dir__ = partial( - dir_with_deprecated_constants, module_globals_keys=[*globals().keys()] -) -__all__ = all_with_deprecated_constants(globals()) diff --git a/homeassistant/components/vacuum/const.py b/homeassistant/components/vacuum/const.py index d6b94e9a8bd..a6e8703a1b0 100644 --- a/homeassistant/components/vacuum/const.py +++ b/homeassistant/components/vacuum/const.py @@ -3,15 +3,8 @@ from __future__ import annotations from enum import IntFlag, StrEnum -from functools import partial from typing import TYPE_CHECKING -from homeassistant.helpers.deprecation import ( - DeprecatedConstantEnum, - all_with_deprecated_constants, - check_if_deprecated_constant, - dir_with_deprecated_constants, -) from homeassistant.helpers.entity_component import EntityComponent from homeassistant.util.hass_dict import HassKey @@ -51,19 +44,3 @@ class VacuumEntityFeature(IntFlag): MAP = 2048 STATE = 4096 # Must be set by vacuum platforms derived from StateVacuumEntity START = 8192 - - -# These STATE_* constants are deprecated as of Home Assistant 2025.1. -# Please use the VacuumActivity enum instead. -_DEPRECATED_STATE_CLEANING = DeprecatedConstantEnum(VacuumActivity.CLEANING, "2026.1") -_DEPRECATED_STATE_DOCKED = DeprecatedConstantEnum(VacuumActivity.DOCKED, "2026.1") -_DEPRECATED_STATE_RETURNING = DeprecatedConstantEnum(VacuumActivity.RETURNING, "2026.1") -_DEPRECATED_STATE_ERROR = DeprecatedConstantEnum(VacuumActivity.ERROR, "2026.1") - - -# These can be removed if no deprecated constant are in this module anymore -__getattr__ = partial(check_if_deprecated_constant, module_globals=globals()) -__dir__ = partial( - dir_with_deprecated_constants, module_globals_keys=[*globals().keys()] -) -__all__ = all_with_deprecated_constants(globals()) diff --git a/tests/components/vacuum/test_init.py b/tests/components/vacuum/test_init.py index 3fd7e525225..1607264d822 100644 --- a/tests/components/vacuum/test_init.py +++ b/tests/components/vacuum/test_init.py @@ -2,14 +2,11 @@ from __future__ import annotations -from enum import Enum import logging -from types import ModuleType from typing import Any import pytest -from homeassistant.components import vacuum from homeassistant.components.vacuum import ( DOMAIN, SERVICE_CLEAN_SPOT, @@ -31,47 +28,12 @@ from .common import async_start from tests.common import ( MockConfigEntry, - MockEntity, MockModule, - help_test_all, - import_and_test_deprecated_constant_enum, mock_integration, setup_test_component_platform, ) -def _create_tuples(enum: type[Enum], constant_prefix: str) -> list[tuple[Enum, str]]: - return [(enum_field, constant_prefix) for enum_field in enum if enum_field] - - -@pytest.mark.parametrize( - "module", - [vacuum], -) -def test_all(module: ModuleType) -> None: - """Test module.__all__ is correctly set.""" - help_test_all(module) - - -@pytest.mark.parametrize( - ("enum", "constant_prefix"), _create_tuples(vacuum.VacuumActivity, "STATE_") -) -@pytest.mark.parametrize( - "module", - [vacuum], -) -def test_deprecated_constants_for_state( - caplog: pytest.LogCaptureFixture, - enum: Enum, - constant_prefix: str, - module: ModuleType, -) -> None: - """Test deprecated constants.""" - import_and_test_deprecated_constant_enum( - caplog, module, enum, constant_prefix, "2026.1" - ) - - @pytest.mark.parametrize( ("service", "expected_state"), [ @@ -244,181 +206,6 @@ async def test_send_command(hass: HomeAssistant, config_flow_fixture: None) -> N assert "test" in strings -async def test_vacuum_not_log_deprecated_state_warning( - hass: HomeAssistant, - mock_vacuum_entity: MockVacuum, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test correctly using activity doesn't log issue or raise repair.""" - state = hass.states.get(mock_vacuum_entity.entity_id) - assert state is not None - assert ( - "should implement the 'activity' property and return its state using the VacuumActivity enum" - not in caplog.text - ) - - -@pytest.mark.usefixtures("mock_as_custom_component") -async def test_vacuum_log_deprecated_state_warning_using_state_prop( - hass: HomeAssistant, - config_flow_fixture: None, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test incorrectly using state property does log issue and raise repair.""" - - class MockLegacyVacuum(MockVacuum): - """Mocked vacuum entity.""" - - @property - def state(self) -> str: - """Return the state of the entity.""" - return VacuumActivity.CLEANING - - entity = MockLegacyVacuum( - name="Testing", - entity_id="vacuum.test", - ) - config_entry = MockConfigEntry(domain="test") - config_entry.add_to_hass(hass) - - mock_integration( - hass, - MockModule( - "test", - async_setup_entry=help_async_setup_entry_init, - async_unload_entry=help_async_unload_entry, - ), - built_in=False, - ) - setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True) - assert await hass.config_entries.async_setup(config_entry.entry_id) - - state = hass.states.get(entity.entity_id) - assert state is not None - - assert ( - "should implement the 'activity' property and return its state using the VacuumActivity enum" - in caplog.text - ) - - -@pytest.mark.usefixtures("mock_as_custom_component") -async def test_vacuum_log_deprecated_state_warning_using_attr_state_attr( - hass: HomeAssistant, - config_flow_fixture: None, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test incorrectly using _attr_state attribute does log issue and raise repair.""" - - class MockLegacyVacuum(MockVacuum): - """Mocked vacuum entity.""" - - def start(self) -> None: - """Start cleaning.""" - self._attr_state = VacuumActivity.CLEANING - - entity = MockLegacyVacuum( - name="Testing", - entity_id="vacuum.test", - ) - config_entry = MockConfigEntry(domain="test") - config_entry.add_to_hass(hass) - - mock_integration( - hass, - MockModule( - "test", - async_setup_entry=help_async_setup_entry_init, - async_unload_entry=help_async_unload_entry, - ), - built_in=False, - ) - setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True) - assert await hass.config_entries.async_setup(config_entry.entry_id) - - state = hass.states.get(entity.entity_id) - assert state is not None - - assert ( - "should implement the 'activity' property and return its state using the VacuumActivity enum" - not in caplog.text - ) - - await async_start(hass, entity.entity_id) - - assert ( - "should implement the 'activity' property and return its state using the VacuumActivity enum" - in caplog.text - ) - caplog.clear() - await async_start(hass, entity.entity_id) - # Test we only log once - assert ( - "should implement the 'activity' property and return its state using the VacuumActivity enum" - not in caplog.text - ) - - -@pytest.mark.usefixtures("mock_as_custom_component") -async def test_vacuum_deprecated_state_does_not_break_state( - hass: HomeAssistant, - config_flow_fixture: None, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test using _attr_state attribute does not break state.""" - - class MockLegacyVacuum(MockEntity, StateVacuumEntity): - """Mocked vacuum entity.""" - - _attr_supported_features = VacuumEntityFeature.STATE | VacuumEntityFeature.START - - def __init__(self, **values: Any) -> None: - """Initialize a mock vacuum entity.""" - super().__init__(**values) - self._attr_state = VacuumActivity.DOCKED - - def start(self) -> None: - """Start cleaning.""" - self._attr_state = VacuumActivity.CLEANING - - entity = MockLegacyVacuum( - name="Testing", - entity_id="vacuum.test", - ) - config_entry = MockConfigEntry(domain="test") - config_entry.add_to_hass(hass) - - mock_integration( - hass, - MockModule( - "test", - async_setup_entry=help_async_setup_entry_init, - async_unload_entry=help_async_unload_entry, - ), - built_in=False, - ) - setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True) - assert await hass.config_entries.async_setup(config_entry.entry_id) - - state = hass.states.get(entity.entity_id) - assert state is not None - assert state.state == "docked" - - await hass.services.async_call( - DOMAIN, - SERVICE_START, - { - "entity_id": entity.entity_id, - }, - blocking=True, - ) - await hass.async_block_till_done() - - state = hass.states.get(entity.entity_id) - assert state is not None - assert state.state == "cleaning" - - @pytest.mark.parametrize(("is_built_in", "log_warnings"), [(True, 0), (False, 3)]) async def test_vacuum_log_deprecated_battery_using_properties( hass: HomeAssistant,