Deprecate MQTT vacuum battery feature and remove it as default feature (#149877)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Jan Bouwhuis
2025-08-06 11:51:31 +02:00
committed by GitHub
parent 0aeff366bd
commit 1302b6744e
3 changed files with 103 additions and 11 deletions

View File

@@ -1,5 +1,9 @@
{ {
"issues": { "issues": {
"deprecated_vacuum_battery_feature": {
"title": "Deprecated battery feature used",
"description": "Vacuum entity {entity_id} implements the battery feature which is deprecated. This will stop working in Home Assistant 2026.2. Implement a separate entity for the battery state instead. To fix the issue, remove the `battery` feature from the configured supported features, and restart Home Assistant."
},
"invalid_platform_config": { "invalid_platform_config": {
"title": "Invalid config found for MQTT {domain} item", "title": "Invalid config found for MQTT {domain} item",
"description": "Home Assistant detected an invalid config for a manually configured item.\n\nPlatform domain: **{domain}**\nConfiguration file: **{config_file}**\nNear line: **{line}**\nConfiguration found:\n```yaml\n{config}\n```\nError: **{error}**.\n\nMake sure the configuration is valid and [reload](/developer-tools/yaml) the manually configured MQTT items or restart Home Assistant to fix this issue." "description": "Home Assistant detected an invalid config for a manually configured item.\n\nPlatform domain: **{domain}**\nConfiguration file: **{config_file}**\nNear line: **{line}**\nConfiguration found:\n```yaml\n{config}\n```\nError: **{error}**.\n\nMake sure the configuration is valid and [reload](/developer-tools/yaml) the manually configured MQTT items or restart Home Assistant to fix this issue."

View File

@@ -17,7 +17,7 @@ from homeassistant.components.vacuum import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_SUPPORTED_FEATURES, CONF_NAME from homeassistant.const import ATTR_SUPPORTED_FEATURES, CONF_NAME
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv, issue_registry as ir
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.json import json_dumps from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, VolSchemaType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, VolSchemaType
@@ -25,11 +25,11 @@ from homeassistant.util.json import json_loads_object
from . import subscription from . import subscription
from .config import MQTT_BASE_SCHEMA from .config import MQTT_BASE_SCHEMA
from .const import CONF_COMMAND_TOPIC, CONF_RETAIN, CONF_STATE_TOPIC from .const import CONF_COMMAND_TOPIC, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .entity import MqttEntity, async_setup_entity_entry_helper from .entity import IssueSeverity, MqttEntity, async_setup_entity_entry_helper
from .models import ReceiveMessage from .models import ReceiveMessage
from .schemas import MQTT_ENTITY_COMMON_SCHEMA from .schemas import MQTT_ENTITY_COMMON_SCHEMA
from .util import valid_publish_topic from .util import learn_more_url, valid_publish_topic
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
@@ -84,6 +84,8 @@ SERVICE_TO_STRING: dict[VacuumEntityFeature, str] = {
VacuumEntityFeature.STOP: "stop", VacuumEntityFeature.STOP: "stop",
VacuumEntityFeature.RETURN_HOME: "return_home", VacuumEntityFeature.RETURN_HOME: "return_home",
VacuumEntityFeature.FAN_SPEED: "fan_speed", VacuumEntityFeature.FAN_SPEED: "fan_speed",
# Use of the battery feature was deprecated in HA Core 2025.8
# and will be removed with HA Core 2026.2
VacuumEntityFeature.BATTERY: "battery", VacuumEntityFeature.BATTERY: "battery",
VacuumEntityFeature.STATUS: "status", VacuumEntityFeature.STATUS: "status",
VacuumEntityFeature.SEND_COMMAND: "send_command", VacuumEntityFeature.SEND_COMMAND: "send_command",
@@ -96,7 +98,6 @@ DEFAULT_SERVICES = (
VacuumEntityFeature.START VacuumEntityFeature.START
| VacuumEntityFeature.STOP | VacuumEntityFeature.STOP
| VacuumEntityFeature.RETURN_HOME | VacuumEntityFeature.RETURN_HOME
| VacuumEntityFeature.BATTERY
| VacuumEntityFeature.CLEAN_SPOT | VacuumEntityFeature.CLEAN_SPOT
) )
ALL_SERVICES = ( ALL_SERVICES = (
@@ -251,10 +252,35 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
) )
} }
async def mqtt_async_added_to_hass(self) -> None:
"""Check for use of deprecated battery features."""
if self.supported_features & VacuumEntityFeature.BATTERY:
ir.async_create_issue(
self.hass,
DOMAIN,
f"deprecated_vacuum_battery_feature_{self.entity_id}",
issue_domain=vacuum.DOMAIN,
breaks_in_ha_version="2026.2",
is_fixable=False,
severity=IssueSeverity.WARNING,
learn_more_url=learn_more_url(vacuum.DOMAIN),
translation_placeholders={"entity_id": self.entity_id},
translation_key="deprecated_vacuum_battery_feature",
)
_LOGGER.warning(
"MQTT vacuum entity %s implements the battery feature "
"which is deprecated. This will stop working "
"in Home Assistant 2026.2. Implement a separate entity "
"for the battery status instead",
self.entity_id,
)
def _update_state_attributes(self, payload: dict[str, Any]) -> None: def _update_state_attributes(self, payload: dict[str, Any]) -> None:
"""Update the entity state attributes.""" """Update the entity state attributes."""
self._state_attrs.update(payload) self._state_attrs.update(payload)
self._attr_fan_speed = self._state_attrs.get(FAN_SPEED, 0) self._attr_fan_speed = self._state_attrs.get(FAN_SPEED, 0)
# Use of the battery feature was deprecated in HA Core 2025.8
# and will be removed with HA Core 2026.2
self._attr_battery_level = max(0, min(100, self._state_attrs.get(BATTERY, 0))) self._attr_battery_level = max(0, min(100, self._state_attrs.get(BATTERY, 0)))
@callback @callback

View File

@@ -32,6 +32,7 @@ from homeassistant.components.vacuum import (
from homeassistant.const import CONF_NAME, ENTITY_MATCH_ALL, STATE_UNKNOWN from homeassistant.const import CONF_NAME, ENTITY_MATCH_ALL, STATE_UNKNOWN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import issue_registry as ir
from .common import ( from .common import (
help_custom_config, help_custom_config,
@@ -108,7 +109,7 @@ async def test_default_supported_features(
entity = hass.states.get("vacuum.mqtttest") entity = hass.states.get("vacuum.mqtttest")
entity_features = entity.attributes.get(mqttvacuum.CONF_SUPPORTED_FEATURES, 0) entity_features = entity.attributes.get(mqttvacuum.CONF_SUPPORTED_FEATURES, 0)
assert sorted(services_to_strings(entity_features, SERVICE_TO_STRING)) == sorted( assert sorted(services_to_strings(entity_features, SERVICE_TO_STRING)) == sorted(
["start", "stop", "return_home", "battery", "clean_spot"] ["start", "stop", "return_home", "clean_spot"]
) )
@@ -313,8 +314,6 @@ async def test_status(
async_fire_mqtt_message(hass, "vacuum/state", message) async_fire_mqtt_message(hass, "vacuum/state", message)
state = hass.states.get("vacuum.mqtttest") state = hass.states.get("vacuum.mqtttest")
assert state.state == VacuumActivity.CLEANING assert state.state == VacuumActivity.CLEANING
assert state.attributes.get(ATTR_BATTERY_LEVEL) == 54
assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-50"
assert state.attributes.get(ATTR_FAN_SPEED) == "max" assert state.attributes.get(ATTR_FAN_SPEED) == "max"
message = """{ message = """{
@@ -326,8 +325,6 @@ async def test_status(
async_fire_mqtt_message(hass, "vacuum/state", message) async_fire_mqtt_message(hass, "vacuum/state", message)
state = hass.states.get("vacuum.mqtttest") state = hass.states.get("vacuum.mqtttest")
assert state.state == VacuumActivity.DOCKED assert state.state == VacuumActivity.DOCKED
assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-charging-60"
assert state.attributes.get(ATTR_BATTERY_LEVEL) == 61
assert state.attributes.get(ATTR_FAN_SPEED) == "min" assert state.attributes.get(ATTR_FAN_SPEED) == "min"
assert state.attributes.get(ATTR_FAN_SPEED_LIST) == ["min", "medium", "high", "max"] assert state.attributes.get(ATTR_FAN_SPEED_LIST) == ["min", "medium", "high", "max"]
@@ -337,6 +334,69 @@ async def test_status(
assert state.state == STATE_UNKNOWN assert state.state == STATE_UNKNOWN
# Use of the battery feature was deprecated in HA Core 2025.8
# and will be removed with HA Core 2026.2
@pytest.mark.parametrize(
"hass_config",
[
help_custom_config(
vacuum.DOMAIN,
DEFAULT_CONFIG,
({mqttvacuum.CONF_SUPPORTED_FEATURES: ["battery"]},),
)
],
)
async def test_status_with_deprecated_battery_feature(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test status updates from the vacuum with deprecated battery feature."""
await mqtt_mock_entry()
state = hass.states.get("vacuum.mqtttest")
assert state.state == STATE_UNKNOWN
message = """{
"battery_level": 54,
"state": "cleaning"
}"""
async_fire_mqtt_message(hass, "vacuum/state", message)
state = hass.states.get("vacuum.mqtttest")
assert state.state == VacuumActivity.CLEANING
assert state.attributes.get(ATTR_BATTERY_LEVEL) == 54
assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-50"
message = """{
"battery_level": 61,
"state": "docked"
}"""
async_fire_mqtt_message(hass, "vacuum/state", message)
state = hass.states.get("vacuum.mqtttest")
assert state.state == VacuumActivity.DOCKED
assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-charging-60"
assert state.attributes.get(ATTR_BATTERY_LEVEL) == 61
message = '{"state":null}'
async_fire_mqtt_message(hass, "vacuum/state", message)
state = hass.states.get("vacuum.mqtttest")
assert state.state == STATE_UNKNOWN
assert (
"MQTT vacuum entity vacuum.mqtttest implements "
"the battery feature which is deprecated." in caplog.text
)
# assert a repair issue was created for the entity
issue_registry = ir.async_get(hass)
issue = issue_registry.async_get_issue(
mqtt.DOMAIN, "deprecated_vacuum_battery_feature_vacuum.mqtttest"
)
assert issue is not None
assert issue.issue_domain == "vacuum"
assert issue.translation_key == "deprecated_vacuum_battery_feature"
assert issue.translation_placeholders == {"entity_id": "vacuum.mqtttest"}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"hass_config", "hass_config",
[ [
@@ -346,7 +406,9 @@ async def test_status(
( (
{ {
mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings( mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings(
mqttvacuum.DEFAULT_SERVICES, SERVICE_TO_STRING mqttvacuum.DEFAULT_SERVICES
| vacuum.VacuumEntityFeature.BATTERY,
SERVICE_TO_STRING,
) )
}, },
), ),