diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 2399e5d9b3b..8285e1d76d1 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -1,6 +1,7 @@ """Support for vacuum cleaner robots (botvacs).""" from __future__ import annotations +import asyncio from collections.abc import Mapping from dataclasses import dataclass from datetime import timedelta @@ -22,8 +23,8 @@ from homeassistant.const import ( # noqa: F401 # STATE_PAUSED/IDLE are API STATE_ON, STATE_PAUSED, ) -from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import config_validation as cv, issue_registry as ir from homeassistant.helpers.config_validation import ( # noqa: F401 PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE, @@ -36,6 +37,7 @@ from homeassistant.helpers.entity import ( ToggleEntityDescription, ) from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity_platform import EntityPlatform from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass @@ -367,6 +369,45 @@ class VacuumEntityDescription(ToggleEntityDescription): class VacuumEntity(_BaseVacuum, ToggleEntity): """Representation of a vacuum cleaner robot.""" + @callback + def add_to_platform_start( + self, + hass: HomeAssistant, + platform: EntityPlatform, + parallel_updates: asyncio.Semaphore | None, + ) -> None: + """Start adding an entity to a platform.""" + super().add_to_platform_start(hass, platform, parallel_updates) + # Don't report core integrations known to still use the deprecated base class; + # we don't worry about demo and mqtt has it's own deprecation warnings. + if self.platform.platform_name in ("demo", "mqtt"): + return + ir.async_create_issue( + hass, + DOMAIN, + f"deprecated_vacuum_base_class_{self.platform.platform_name}", + breaks_in_ha_version="2024.2.0", + is_fixable=False, + is_persistent=False, + issue_domain=self.platform.platform_name, + severity=ir.IssueSeverity.WARNING, + translation_key="deprecated_vacuum_base_class", + translation_placeholders={ + "platform": self.platform.platform_name, + }, + ) + _LOGGER.warning( + ( + "%s::%s is extending the deprecated base class VacuumEntity instead of " + "StateVacuumEntity, this is not valid and will be unsupported " + "from Home Assistant 2024.2. Please report it to the author of the '%s'" + " custom integration" + ), + self.platform.platform_name, + self.__class__.__name__, + self.platform.platform_name, + ) + entity_description: VacuumEntityDescription _attr_status: str | None = None diff --git a/homeassistant/components/vacuum/strings.json b/homeassistant/components/vacuum/strings.json index a27a60bba4f..93ef1e8584c 100644 --- a/homeassistant/components/vacuum/strings.json +++ b/homeassistant/components/vacuum/strings.json @@ -28,5 +28,11 @@ "returning": "Returning to dock" } } + }, + "issues": { + "deprecated_vacuum_base_class": { + "title": "The {platform} custom integration is using deprecated vacuum feature", + "description": "The custom integration `{platform}` is extending the deprecated base class `VacuumEntity` instead of `StateVacuumEntity`.\n\nPlease report it to the author of the `{platform}` custom integration.\n\nOnce an updated version of `{platform}` is available, install it and restart Home Assistant to fix this issue." + } } } diff --git a/tests/components/vacuum/test_init.py b/tests/components/vacuum/test_init.py new file mode 100644 index 00000000000..eaa39bceaec --- /dev/null +++ b/tests/components/vacuum/test_init.py @@ -0,0 +1,93 @@ +"""The tests for the Vacuum entity integration.""" +from __future__ import annotations + +from collections.abc import Generator + +import pytest + +from homeassistant.components.vacuum import DOMAIN as VACUUM_DOMAIN, VacuumEntity +from homeassistant.config_entries import ConfigEntry, ConfigFlow +from homeassistant.core import HomeAssistant +from homeassistant.helpers import issue_registry as ir +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from tests.common import ( + MockConfigEntry, + MockModule, + MockPlatform, + mock_config_flow, + mock_integration, + mock_platform, +) + +TEST_DOMAIN = "test" + + +class MockFlow(ConfigFlow): + """Test flow.""" + + +@pytest.fixture(autouse=True) +def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]: + """Mock config flow.""" + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + + with mock_config_flow(TEST_DOMAIN, MockFlow): + yield + + +async def test_deprecated_base_class( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test warnings when adding VacuumEntity to the state machine.""" + + async def async_setup_entry_init( + hass: HomeAssistant, config_entry: ConfigEntry + ) -> bool: + """Set up test config entry.""" + await hass.config_entries.async_forward_entry_setup(config_entry, VACUUM_DOMAIN) + return True + + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + mock_integration( + hass, + MockModule( + TEST_DOMAIN, + async_setup_entry=async_setup_entry_init, + ), + ) + + entity1 = VacuumEntity() + entity1.entity_id = "vacuum.test1" + + async def async_setup_entry_platform( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, + ) -> None: + """Set up test stt platform via config entry.""" + async_add_entities([entity1]) + + mock_platform( + hass, + f"{TEST_DOMAIN}.{VACUUM_DOMAIN}", + MockPlatform(async_setup_entry=async_setup_entry_platform), + ) + + config_entry = MockConfigEntry(domain=TEST_DOMAIN) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity1.entity_id) + + assert ( + "test::VacuumEntity is extending the deprecated base class VacuumEntity" + in caplog.text + ) + + issue_registry = ir.async_get(hass) + issue = issue_registry.async_get_issue( + VACUUM_DOMAIN, f"deprecated_vacuum_base_class_{TEST_DOMAIN}" + ) + assert issue.issue_domain == TEST_DOMAIN