Remove legacy VacuumEntity base class support (#108189)
This commit is contained in:
parent
a27eea9b9f
commit
f704a1a05a
3 changed files with 101 additions and 395 deletions
|
@ -1,13 +1,12 @@
|
||||||
"""Support for vacuum cleaner robots (botvacs)."""
|
"""Support for vacuum cleaner robots (botvacs)."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from enum import IntFlag
|
from enum import IntFlag
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Any, final
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -22,28 +21,18 @@ from homeassistant.const import ( # noqa: F401 # STATE_PAUSED/IDLE are API
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
STATE_PAUSED,
|
STATE_PAUSED,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||||
PLATFORM_SCHEMA,
|
PLATFORM_SCHEMA,
|
||||||
PLATFORM_SCHEMA_BASE,
|
PLATFORM_SCHEMA_BASE,
|
||||||
make_entity_service_schema,
|
make_entity_service_schema,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import (
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||||
Entity,
|
|
||||||
EntityDescription,
|
|
||||||
ToggleEntity,
|
|
||||||
ToggleEntityDescription,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
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.icon import icon_for_battery_level
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.loader import (
|
from homeassistant.loader import bind_hass
|
||||||
async_get_issue_tracker,
|
|
||||||
async_suggest_report_issue,
|
|
||||||
bind_hass,
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
@ -131,38 +120,12 @@ def is_on(hass: HomeAssistant, entity_id: str) -> bool:
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up the vacuum component."""
|
"""Set up the vacuum component."""
|
||||||
component = hass.data[DOMAIN] = EntityComponent[_BaseVacuum](
|
component = hass.data[DOMAIN] = EntityComponent[StateVacuumEntity](
|
||||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
|
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
|
||||||
)
|
)
|
||||||
|
|
||||||
await component.async_setup(config)
|
await component.async_setup(config)
|
||||||
|
|
||||||
component.async_register_entity_service(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{},
|
|
||||||
"async_turn_on",
|
|
||||||
[VacuumEntityFeature.TURN_ON],
|
|
||||||
)
|
|
||||||
component.async_register_entity_service(
|
|
||||||
SERVICE_TURN_OFF,
|
|
||||||
{},
|
|
||||||
"async_turn_off",
|
|
||||||
[VacuumEntityFeature.TURN_OFF],
|
|
||||||
)
|
|
||||||
component.async_register_entity_service(
|
|
||||||
SERVICE_TOGGLE,
|
|
||||||
{},
|
|
||||||
"async_toggle",
|
|
||||||
[VacuumEntityFeature.TURN_OFF | VacuumEntityFeature.TURN_ON],
|
|
||||||
)
|
|
||||||
# start_pause is a legacy service, only supported by VacuumEntity, and only needs
|
|
||||||
# VacuumEntityFeature.PAUSE
|
|
||||||
component.async_register_entity_service(
|
|
||||||
SERVICE_START_PAUSE,
|
|
||||||
{},
|
|
||||||
"async_start_pause",
|
|
||||||
[VacuumEntityFeature.PAUSE],
|
|
||||||
)
|
|
||||||
component.async_register_entity_service(
|
component.async_register_entity_service(
|
||||||
SERVICE_START,
|
SERVICE_START,
|
||||||
{},
|
{},
|
||||||
|
@ -220,30 +183,36 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up a config entry."""
|
"""Set up a config entry."""
|
||||||
component: EntityComponent[_BaseVacuum] = hass.data[DOMAIN]
|
component: EntityComponent[StateVacuumEntity] = hass.data[DOMAIN]
|
||||||
return await component.async_setup_entry(entry)
|
return await component.async_setup_entry(entry)
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
component: EntityComponent[_BaseVacuum] = hass.data[DOMAIN]
|
component: EntityComponent[StateVacuumEntity] = hass.data[DOMAIN]
|
||||||
return await component.async_unload_entry(entry)
|
return await component.async_unload_entry(entry)
|
||||||
|
|
||||||
|
|
||||||
BASE_CACHED_PROPERTIES_WITH_ATTR_ = {
|
class StateVacuumEntityDescription(EntityDescription, frozen_or_thawed=True):
|
||||||
|
"""A class that describes vacuum entities."""
|
||||||
|
|
||||||
|
|
||||||
|
STATE_VACUUM_CACHED_PROPERTIES_WITH_ATTR_ = {
|
||||||
"supported_features",
|
"supported_features",
|
||||||
"battery_level",
|
"battery_level",
|
||||||
"battery_icon",
|
"battery_icon",
|
||||||
"fan_speed",
|
"fan_speed",
|
||||||
"fan_speed_list",
|
"fan_speed_list",
|
||||||
|
"state",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _BaseVacuum(Entity, cached_properties=BASE_CACHED_PROPERTIES_WITH_ATTR_):
|
class StateVacuumEntity(
|
||||||
"""Representation of a base vacuum.
|
Entity, cached_properties=STATE_VACUUM_CACHED_PROPERTIES_WITH_ATTR_
|
||||||
|
):
|
||||||
|
"""Representation of a vacuum cleaner robot that supports states."""
|
||||||
|
|
||||||
Contains common properties and functions for all vacuum devices.
|
entity_description: StateVacuumEntityDescription
|
||||||
"""
|
|
||||||
|
|
||||||
_entity_component_unrecorded_attributes = frozenset({ATTR_FAN_SPEED_LIST})
|
_entity_component_unrecorded_attributes = frozenset({ATTR_FAN_SPEED_LIST})
|
||||||
|
|
||||||
|
@ -251,8 +220,60 @@ class _BaseVacuum(Entity, cached_properties=BASE_CACHED_PROPERTIES_WITH_ATTR_):
|
||||||
_attr_battery_level: int | None = None
|
_attr_battery_level: int | None = None
|
||||||
_attr_fan_speed: str | None = None
|
_attr_fan_speed: str | None = None
|
||||||
_attr_fan_speed_list: list[str]
|
_attr_fan_speed_list: list[str]
|
||||||
|
_attr_state: str | None = None
|
||||||
_attr_supported_features: VacuumEntityFeature = VacuumEntityFeature(0)
|
_attr_supported_features: VacuumEntityFeature = VacuumEntityFeature(0)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def battery_level(self) -> int | None:
|
||||||
|
"""Return the battery level of the vacuum cleaner."""
|
||||||
|
return self._attr_battery_level
|
||||||
|
|
||||||
|
@property
|
||||||
|
def battery_icon(self) -> str:
|
||||||
|
"""Return the battery icon for the vacuum cleaner."""
|
||||||
|
charging = bool(self.state == STATE_DOCKED)
|
||||||
|
|
||||||
|
return icon_for_battery_level(
|
||||||
|
battery_level=self.battery_level, charging=charging
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def capability_attributes(self) -> Mapping[str, Any] | None:
|
||||||
|
"""Return capability attributes."""
|
||||||
|
if VacuumEntityFeature.FAN_SPEED in self.supported_features_compat:
|
||||||
|
return {ATTR_FAN_SPEED_LIST: self.fan_speed_list}
|
||||||
|
return None
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def fan_speed(self) -> str | None:
|
||||||
|
"""Return the fan speed of the vacuum cleaner."""
|
||||||
|
return self._attr_fan_speed
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def fan_speed_list(self) -> list[str]:
|
||||||
|
"""Get the list of available fan speed steps of the vacuum cleaner."""
|
||||||
|
return self._attr_fan_speed_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self) -> dict[str, Any]:
|
||||||
|
"""Return the state attributes of the vacuum cleaner."""
|
||||||
|
data: dict[str, Any] = {}
|
||||||
|
supported_features = self.supported_features_compat
|
||||||
|
|
||||||
|
if VacuumEntityFeature.BATTERY in supported_features:
|
||||||
|
data[ATTR_BATTERY_LEVEL] = self.battery_level
|
||||||
|
data[ATTR_BATTERY_ICON] = self.battery_icon
|
||||||
|
|
||||||
|
if VacuumEntityFeature.FAN_SPEED in supported_features:
|
||||||
|
data[ATTR_FAN_SPEED] = self.fan_speed
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def state(self) -> str | None:
|
||||||
|
"""Return the state of the vacuum cleaner."""
|
||||||
|
return self._attr_state
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def supported_features(self) -> VacuumEntityFeature:
|
def supported_features(self) -> VacuumEntityFeature:
|
||||||
"""Flag vacuum cleaner features that are supported."""
|
"""Flag vacuum cleaner features that are supported."""
|
||||||
|
@ -271,48 +292,6 @@ class _BaseVacuum(Entity, cached_properties=BASE_CACHED_PROPERTIES_WITH_ATTR_):
|
||||||
return new_features
|
return new_features
|
||||||
return features
|
return features
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def battery_level(self) -> int | None:
|
|
||||||
"""Return the battery level of the vacuum cleaner."""
|
|
||||||
return self._attr_battery_level
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def battery_icon(self) -> str:
|
|
||||||
"""Return the battery icon for the vacuum cleaner."""
|
|
||||||
return self._attr_battery_icon
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def fan_speed(self) -> str | None:
|
|
||||||
"""Return the fan speed of the vacuum cleaner."""
|
|
||||||
return self._attr_fan_speed
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def fan_speed_list(self) -> list[str]:
|
|
||||||
"""Get the list of available fan speed steps of the vacuum cleaner."""
|
|
||||||
return self._attr_fan_speed_list
|
|
||||||
|
|
||||||
@property
|
|
||||||
def capability_attributes(self) -> Mapping[str, Any] | None:
|
|
||||||
"""Return capability attributes."""
|
|
||||||
if VacuumEntityFeature.FAN_SPEED in self.supported_features_compat:
|
|
||||||
return {ATTR_FAN_SPEED_LIST: self.fan_speed_list}
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state_attributes(self) -> dict[str, Any]:
|
|
||||||
"""Return the state attributes of the vacuum cleaner."""
|
|
||||||
data: dict[str, Any] = {}
|
|
||||||
supported_features = self.supported_features_compat
|
|
||||||
|
|
||||||
if VacuumEntityFeature.BATTERY in supported_features:
|
|
||||||
data[ATTR_BATTERY_LEVEL] = self.battery_level
|
|
||||||
data[ATTR_BATTERY_ICON] = self.battery_icon
|
|
||||||
|
|
||||||
if VacuumEntityFeature.FAN_SPEED in supported_features:
|
|
||||||
data[ATTR_FAN_SPEED] = self.fan_speed
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def stop(self, **kwargs: Any) -> None:
|
def stop(self, **kwargs: Any) -> None:
|
||||||
"""Stop the vacuum cleaner."""
|
"""Stop the vacuum cleaner."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -393,163 +372,6 @@ class _BaseVacuum(Entity, cached_properties=BASE_CACHED_PROPERTIES_WITH_ATTR_):
|
||||||
partial(self.send_command, command, params=params, **kwargs)
|
partial(self.send_command, command, params=params, **kwargs)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VacuumEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
|
|
||||||
"""A class that describes vacuum entities."""
|
|
||||||
|
|
||||||
|
|
||||||
VACUUM_CACHED_PROPERTIES_WITH_ATTR_ = {
|
|
||||||
"status",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class VacuumEntity(
|
|
||||||
_BaseVacuum, ToggleEntity, cached_properties=VACUUM_CACHED_PROPERTIES_WITH_ATTR_
|
|
||||||
):
|
|
||||||
"""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)
|
|
||||||
translation_key = "deprecated_vacuum_base_class"
|
|
||||||
translation_placeholders = {"platform": self.platform.platform_name}
|
|
||||||
issue_tracker = async_get_issue_tracker(
|
|
||||||
hass,
|
|
||||||
integration_domain=self.platform.platform_name,
|
|
||||||
module=type(self).__module__,
|
|
||||||
)
|
|
||||||
if issue_tracker:
|
|
||||||
translation_placeholders["issue_tracker"] = issue_tracker
|
|
||||||
translation_key = "deprecated_vacuum_base_class_url"
|
|
||||||
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=translation_key,
|
|
||||||
translation_placeholders=translation_placeholders,
|
|
||||||
)
|
|
||||||
|
|
||||||
report_issue = async_suggest_report_issue(
|
|
||||||
hass,
|
|
||||||
integration_domain=self.platform.platform_name,
|
|
||||||
module=type(self).__module__,
|
|
||||||
)
|
|
||||||
_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 %s"
|
|
||||||
),
|
|
||||||
self.platform.platform_name,
|
|
||||||
self.__class__.__name__,
|
|
||||||
report_issue,
|
|
||||||
)
|
|
||||||
|
|
||||||
entity_description: VacuumEntityDescription
|
|
||||||
_attr_status: str | None = None
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def status(self) -> str | None:
|
|
||||||
"""Return the status of the vacuum cleaner."""
|
|
||||||
return self._attr_status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def battery_icon(self) -> str:
|
|
||||||
"""Return the battery icon for the vacuum cleaner."""
|
|
||||||
charging = False
|
|
||||||
if self.status is not None:
|
|
||||||
charging = "charg" in self.status.lower()
|
|
||||||
return icon_for_battery_level(
|
|
||||||
battery_level=self.battery_level, charging=charging
|
|
||||||
)
|
|
||||||
|
|
||||||
@final
|
|
||||||
@property
|
|
||||||
def state_attributes(self) -> dict[str, Any]:
|
|
||||||
"""Return the state attributes of the vacuum cleaner."""
|
|
||||||
data = super().state_attributes
|
|
||||||
|
|
||||||
if VacuumEntityFeature.STATUS in self.supported_features_compat:
|
|
||||||
data[ATTR_STATUS] = self.status
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs: Any) -> None:
|
|
||||||
"""Turn the vacuum on and start cleaning."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
||||||
"""Turn the vacuum on and start cleaning.
|
|
||||||
|
|
||||||
This method must be run in the event loop.
|
|
||||||
"""
|
|
||||||
await self.hass.async_add_executor_job(partial(self.turn_on, **kwargs))
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs: Any) -> None:
|
|
||||||
"""Turn the vacuum off stopping the cleaning and returning home."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
||||||
"""Turn the vacuum off stopping the cleaning and returning home.
|
|
||||||
|
|
||||||
This method must be run in the event loop.
|
|
||||||
"""
|
|
||||||
await self.hass.async_add_executor_job(partial(self.turn_off, **kwargs))
|
|
||||||
|
|
||||||
def start_pause(self, **kwargs: Any) -> None:
|
|
||||||
"""Start, pause or resume the cleaning task."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def async_start_pause(self, **kwargs: Any) -> None:
|
|
||||||
"""Start, pause or resume the cleaning task.
|
|
||||||
|
|
||||||
This method must be run in the event loop.
|
|
||||||
"""
|
|
||||||
await self.hass.async_add_executor_job(partial(self.start_pause, **kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class StateVacuumEntityDescription(EntityDescription, frozen_or_thawed=True):
|
|
||||||
"""A class that describes vacuum entities."""
|
|
||||||
|
|
||||||
|
|
||||||
STATE_VACUUM_CACHED_PROPERTIES_WITH_ATTR_ = {
|
|
||||||
"state",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class StateVacuumEntity(
|
|
||||||
_BaseVacuum, cached_properties=STATE_VACUUM_CACHED_PROPERTIES_WITH_ATTR_
|
|
||||||
):
|
|
||||||
"""Representation of a vacuum cleaner robot that supports states."""
|
|
||||||
|
|
||||||
entity_description: StateVacuumEntityDescription
|
|
||||||
_attr_state: str | None = None
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def state(self) -> str | None:
|
|
||||||
"""Return the state of the vacuum cleaner."""
|
|
||||||
return self._attr_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def battery_icon(self) -> str:
|
|
||||||
"""Return the battery icon for the vacuum cleaner."""
|
|
||||||
charging = bool(self.state == STATE_DOCKED)
|
|
||||||
|
|
||||||
return icon_for_battery_level(
|
|
||||||
battery_level=self.battery_level, charging=charging
|
|
||||||
)
|
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
"""Start or resume the cleaning task."""
|
"""Start or resume the cleaning task."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
@ -29,16 +29,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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."
|
|
||||||
},
|
|
||||||
"deprecated_vacuum_base_class_url": {
|
|
||||||
"title": "[%key:component::vacuum::issues::deprecated_vacuum_base_class::title%]",
|
|
||||||
"description": "The custom integration `{platform}` is extending the deprecated base class `VacuumEntity` instead of `StateVacuumEntity`.\n\nPlease create a bug report at {issue_tracker}.\n\nOnce an updated version of `{platform}` is available, install it and restart Home Assistant to fix this issue."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"services": {
|
"services": {
|
||||||
"turn_on": {
|
"turn_on": {
|
||||||
"name": "[%key:common::action::turn_on%]",
|
"name": "[%key:common::action::turn_on%]",
|
||||||
|
|
|
@ -1,147 +1,41 @@
|
||||||
"""The tests for the Vacuum entity integration."""
|
"""The tests for the Vacuum entity integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Generator
|
from homeassistant.components.vacuum import StateVacuumEntity, VacuumEntityFeature
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.vacuum import (
|
|
||||||
DOMAIN as VACUUM_DOMAIN,
|
|
||||||
VacuumEntity,
|
|
||||||
VacuumEntityFeature,
|
|
||||||
)
|
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
|
||||||
from homeassistant.core import HomeAssistant
|
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):
|
async def test_supported_features_compat(hass: HomeAssistant) -> None:
|
||||||
"""Test flow."""
|
"""Test StateVacuumEntity using deprecated feature constants features."""
|
||||||
|
|
||||||
|
features = (
|
||||||
@pytest.fixture(autouse=True)
|
VacuumEntityFeature.BATTERY
|
||||||
def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]:
|
| VacuumEntityFeature.FAN_SPEED
|
||||||
"""Mock config flow."""
|
| VacuumEntityFeature.START
|
||||||
mock_platform(hass, f"{TEST_DOMAIN}.config_flow")
|
| VacuumEntityFeature.STOP
|
||||||
|
| VacuumEntityFeature.PAUSE
|
||||||
with mock_config_flow(TEST_DOMAIN, MockFlow):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
ISSUE_TRACKER = "https://blablabla.com"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
("manifest_extra", "translation_key", "translation_placeholders_extra"),
|
|
||||||
[
|
|
||||||
(
|
|
||||||
{},
|
|
||||||
"deprecated_vacuum_base_class",
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
{"issue_tracker": ISSUE_TRACKER},
|
|
||||||
"deprecated_vacuum_base_class_url",
|
|
||||||
{"issue_tracker": ISSUE_TRACKER},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_deprecated_base_class(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
caplog: pytest.LogCaptureFixture,
|
|
||||||
manifest_extra: dict[str, str],
|
|
||||||
translation_key: str,
|
|
||||||
translation_placeholders_extra: dict[str, str],
|
|
||||||
) -> 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,
|
|
||||||
partial_manifest=manifest_extra,
|
|
||||||
),
|
|
||||||
built_in=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
entity1 = VacuumEntity()
|
class _LegacyConstantsStateVacuum(StateVacuumEntity):
|
||||||
entity1.entity_id = "vacuum.test1"
|
_attr_supported_features = int(features)
|
||||||
|
_attr_fan_speed_list = ["silent", "normal", "pet hair"]
|
||||||
|
|
||||||
async def async_setup_entry_platform(
|
entity = _LegacyConstantsStateVacuum()
|
||||||
hass: HomeAssistant,
|
assert isinstance(entity.supported_features, int)
|
||||||
config_entry: ConfigEntry,
|
assert entity.supported_features == int(features)
|
||||||
async_add_entities: AddEntitiesCallback,
|
assert entity.supported_features_compat is (
|
||||||
) -> None:
|
VacuumEntityFeature.BATTERY
|
||||||
"""Set up test vacuum platform via config entry."""
|
| VacuumEntityFeature.FAN_SPEED
|
||||||
async_add_entities([entity1])
|
| VacuumEntityFeature.START
|
||||||
|
| VacuumEntityFeature.STOP
|
||||||
mock_platform(
|
| VacuumEntityFeature.PAUSE
|
||||||
hass,
|
|
||||||
f"{TEST_DOMAIN}.{VACUUM_DOMAIN}",
|
|
||||||
MockPlatform(async_setup_entry=async_setup_entry_platform),
|
|
||||||
)
|
)
|
||||||
|
assert entity.state_attributes == {
|
||||||
config_entry = MockConfigEntry(domain=TEST_DOMAIN)
|
"battery_level": None,
|
||||||
config_entry.add_to_hass(hass)
|
"battery_icon": "mdi:battery-unknown",
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
"fan_speed": None,
|
||||||
await hass.async_block_till_done()
|
}
|
||||||
|
assert entity.capability_attributes == {
|
||||||
assert hass.states.get(entity1.entity_id)
|
"fan_speed_list": ["silent", "normal", "pet hair"]
|
||||||
|
}
|
||||||
assert (
|
assert entity._deprecated_supported_features_reported
|
||||||
"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
|
|
||||||
assert issue.issue_id == f"deprecated_vacuum_base_class_{TEST_DOMAIN}"
|
|
||||||
assert issue.translation_key == translation_key
|
|
||||||
assert (
|
|
||||||
issue.translation_placeholders
|
|
||||||
== {"platform": "test"} | translation_placeholders_extra
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
|
|
||||||
"""Test deprecated supported features ints."""
|
|
||||||
|
|
||||||
class MockVacuumEntity(VacuumEntity):
|
|
||||||
@property
|
|
||||||
def supported_features(self) -> int:
|
|
||||||
"""Return supported features."""
|
|
||||||
return 1
|
|
||||||
|
|
||||||
entity = MockVacuumEntity()
|
|
||||||
assert entity.supported_features_compat is VacuumEntityFeature(1)
|
|
||||||
assert "MockVacuumEntity" in caplog.text
|
|
||||||
assert "is using deprecated supported features values" in caplog.text
|
|
||||||
assert "Instead it should use" in caplog.text
|
|
||||||
assert "VacuumEntityFeature.TURN_ON" in caplog.text
|
|
||||||
caplog.clear()
|
|
||||||
assert entity.supported_features_compat is VacuumEntityFeature(1)
|
|
||||||
assert "is using deprecated supported features values" not in caplog.text
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue