Improve some Home Connect deprecations (#141508)

This commit is contained in:
J. Diego Rodríguez Royo 2025-03-27 10:18:30 +01:00 committed by Franck Nijhof
parent f4c0eb4189
commit e7ff0a3f8b
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
6 changed files with 271 additions and 21 deletions

View File

@ -244,6 +244,7 @@ class HomeConnectDoorBinarySensor(HomeConnectBinarySensor):
BSH_DOOR_STATE_LOCKED: False,
BSH_DOOR_STATE_OPEN: True,
},
entity_registry_enabled_default=False,
),
)
self._attr_unique_id = f"{appliance.info.ha_id}-Door"
@ -283,7 +284,8 @@ class HomeConnectDoorBinarySensor(HomeConnectBinarySensor):
DOMAIN,
f"deprecated_binary_common_door_sensor_{self.entity_id}",
breaks_in_ha_version="2025.5.0",
is_fixable=False,
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,
translation_key="deprecated_binary_common_door_sensor",
translation_placeholders={

View File

@ -134,15 +134,47 @@
},
"deprecated_binary_common_door_sensor": {
"title": "Deprecated binary door sensor detected in some automations or scripts",
"description": "The binary door sensor `{entity}`, which is deprecated, is used in the following automations or scripts:\n{items}\n\nA sensor entity with additional possible states is available and should be used going forward; Please use it on the above automations or scripts to fix this issue."
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::home_connect::issues::deprecated_binary_common_door_sensor::title%]",
"description": "The binary door sensor `{entity}`, which is deprecated, is used in the following automations or scripts:\n{items}\n\nA sensor entity with additional possible states is available and should be used going forward; Please use it on the above automations or scripts to fix this issue."
}
}
}
},
"deprecated_command_actions": {
"title": "The command related actions are deprecated in favor of the new buttons",
"description": "The `pause_program` and `resume_program` actions have been deprecated in favor of new button entities, if the command is available for your appliance. Please update your automations, scripts and panels that use this action to use the button entities instead, and press on submit to fix the issue."
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::home_connect::issues::deprecated_command_actions::title%]",
"description": "The `pause_program` and `resume_program` actions have been deprecated in favor of new button entities, if the command is available for your appliance. Please update your automations, scripts and panels that use this action to use the button entities instead, and press on submit to fix the issue."
}
}
}
},
"deprecated_program_switch_in_automations_scripts": {
"title": "Deprecated program switch detected in some automations or scripts",
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::home_connect::issues::deprecated_program_switch_in_automations_scripts::title%]",
"description": "Program switches are deprecated and {entity_id} is used in the following automations or scripts:\n{items}\n\nYou can use the active program select entity to run the program without any additional options and get the current running program on the above automations or scripts to fix this issue."
}
}
}
},
"deprecated_program_switch": {
"title": "Deprecated program switch detected in some automations or scripts",
"description": "Program switches are deprecated and {entity_id} is used in the following automations or scripts:\n{items}\n\nYou can use the active program select entity to run the program without any additional options and get the current running program on the above automations or scripts to fix this issue."
"title": "Deprecated program switch entities",
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::home_connect::issues::deprecated_program_switch::title%]",
"description": "The switch entity `{entity_id}` and all the other program switches are deprecated.\n\nPlease use the active program select entity instead."
}
}
}
},
"deprecated_set_program_and_option_actions": {
"title": "The executed action is deprecated",

View File

@ -266,7 +266,10 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
super().__init__(
coordinator,
appliance,
SwitchEntityDescription(key=EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM),
SwitchEntityDescription(
key=EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM,
entity_registry_enabled_default=False,
),
)
self._attr_name = f"{appliance.info.name} {desc}"
self._attr_unique_id = f"{appliance.info.ha_id}-{desc}"
@ -304,11 +307,12 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
async_create_issue(
self.hass,
DOMAIN,
f"deprecated_program_switch_{self.entity_id}",
f"deprecated_program_switch_in_automations_scripts_{self.entity_id}",
breaks_in_ha_version="2025.6.0",
is_fixable=False,
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,
translation_key="deprecated_program_switch",
translation_key="deprecated_program_switch_in_automations_scripts",
translation_placeholders={
"entity_id": self.entity_id,
"items": "\n".join(items_list),
@ -317,12 +321,34 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
async def async_will_remove_from_hass(self) -> None:
"""Call when entity will be removed from hass."""
async_delete_issue(
self.hass,
DOMAIN,
f"deprecated_program_switch_in_automations_scripts_{self.entity_id}",
)
async_delete_issue(
self.hass, DOMAIN, f"deprecated_program_switch_{self.entity_id}"
)
def create_action_handler_issue(self) -> None:
"""Create deprecation issue."""
async_create_issue(
self.hass,
DOMAIN,
f"deprecated_program_switch_{self.entity_id}",
breaks_in_ha_version="2025.6.0",
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,
translation_key="deprecated_program_switch",
translation_placeholders={
"entity_id": self.entity_id,
},
)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Start the program."""
self.create_action_handler_issue()
try:
await self.coordinator.client.start_program(
self.appliance.info.ha_id, program_key=self.program.key
@ -339,6 +365,7 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Stop the program."""
self.create_action_handler_issue()
try:
await self.coordinator.client.stop_program(self.appliance.info.ha_id)
except HomeConnectError as err:

View File

@ -1,6 +1,7 @@
"""Tests for home_connect binary_sensor entities."""
from collections.abc import Awaitable, Callable
from http import HTTPStatus
from unittest.mock import AsyncMock, MagicMock
from aiohomeconnect.model import (
@ -39,6 +40,7 @@ import homeassistant.helpers.issue_registry as ir
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator
@pytest.fixture
@ -165,6 +167,7 @@ async def test_connected_devices(
assert len(new_entity_entries) > len(entity_entries)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
async def test_binary_sensors_entity_availability(
hass: HomeAssistant,
@ -219,6 +222,7 @@ async def test_binary_sensors_entity_availability(
assert state.state != STATE_UNAVAILABLE
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("value", "expected"),
@ -402,7 +406,7 @@ async def test_connected_sensor_functionality(
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_create_issue(
async def test_create_door_binary_sensor_deprecation_issue(
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -410,7 +414,7 @@ async def test_create_issue(
client: MagicMock,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test we create an issue when an automation or script is using a deprecated entity."""
"""Test that we create an issue when an automation or script is using a door binary sensor entity."""
entity_id = "binary_sensor.washer_door"
issue_id = f"deprecated_binary_common_door_sensor_{entity_id}"
@ -464,3 +468,76 @@ async def test_create_issue(
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(DOMAIN, issue_id)
assert len(issue_registry.issues) == 0
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_door_binary_sensor_deprecation_issue_fix(
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
client: MagicMock,
issue_registry: ir.IssueRegistry,
hass_client: ClientSessionGenerator,
) -> None:
"""Test that we create an issue when an automation or script is using a door binary sensor entity."""
entity_id = "binary_sensor.washer_door"
issue_id = f"deprecated_binary_common_door_sensor_{entity_id}"
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"alias": "test",
"trigger": {"platform": "state", "entity_id": entity_id},
"action": {
"action": "automation.turn_on",
"target": {
"entity_id": "automation.test",
},
},
}
},
)
assert await async_setup_component(
hass,
script.DOMAIN,
{
script.DOMAIN: {
"test": {
"sequence": [
{
"condition": "state",
"entity_id": entity_id,
"state": "on",
},
],
}
}
},
)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
assert automations_with_entity(hass, entity_id)[0] == "automation.test"
assert scripts_with_entity(hass, entity_id)[0] == "script.test"
assert len(issue_registry.issues) == 1
issue = issue_registry.async_get_issue(DOMAIN, issue_id)
assert issue
_client = await hass_client()
resp = await _client.post(
"/api/repairs/issues/fix",
json={"handler": DOMAIN, "issue_id": issue.issue_id},
)
assert resp.status == HTTPStatus.OK
flow_id = (await resp.json())["flow_id"]
resp = await _client.post(f"/api/repairs/issues/fix/{flow_id}")
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(DOMAIN, issue_id)
assert len(issue_registry.issues) == 0

View File

@ -1,6 +1,7 @@
"""Tests for home_connect sensor entities."""
from collections.abc import Awaitable, Callable
from http import HTTPStatus
from typing import Any
from unittest.mock import AsyncMock, MagicMock
@ -59,6 +60,7 @@ from homeassistant.helpers import (
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator
@pytest.fixture
@ -209,6 +211,7 @@ async def test_connected_devices(
assert len(new_entity_entries) > len(entity_entries)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize("appliance", ["Dishwasher"], indirect=True)
async def test_switch_entity_availability(
hass: HomeAssistant,
@ -320,6 +323,7 @@ async def test_switch_functionality(
assert hass.states.is_state(entity_id, state)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("entity_id", "program_key", "initial_state", "appliance"),
[
@ -397,6 +401,7 @@ async def test_program_switch_functionality(
client.stop_program.assert_awaited_once_with(appliance.ha_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
(
"entity_id",
@ -801,18 +806,24 @@ async def test_power_switch_service_validation_errors(
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_create_issue(
@pytest.mark.parametrize(
"service",
[SERVICE_TURN_ON, SERVICE_TURN_OFF],
)
async def test_create_program_switch_deprecation_issue(
hass: HomeAssistant,
appliance: HomeAppliance,
service: str,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
client: MagicMock,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test we create an issue when an automation or script is using a deprecated entity."""
"""Test that we create an issue when an automation or script is using a program switch entity or the entity is used by the user."""
entity_id = "switch.washer_program_mix"
issue_id = f"deprecated_program_switch_{entity_id}"
automation_script_issue_id = f"deprecated_program_switch_{entity_id}"
action_handler_issue_id = f"deprecated_program_switch_{entity_id}"
assert await async_setup_component(
hass,
@ -851,17 +862,118 @@ async def test_create_issue(
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{
ATTR_ENTITY_ID: entity_id,
},
blocking=True,
)
assert automations_with_entity(hass, entity_id)[0] == "automation.test"
assert scripts_with_entity(hass, entity_id)[0] == "script.test"
assert len(issue_registry.issues) == 1
assert issue_registry.async_get_issue(DOMAIN, issue_id)
assert len(issue_registry.issues) == 2
assert issue_registry.async_get_issue(DOMAIN, automation_script_issue_id)
assert issue_registry.async_get_issue(DOMAIN, action_handler_issue_id)
await hass.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done()
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(DOMAIN, issue_id)
assert not issue_registry.async_get_issue(DOMAIN, automation_script_issue_id)
assert not issue_registry.async_get_issue(DOMAIN, action_handler_issue_id)
assert len(issue_registry.issues) == 0
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
"service",
[SERVICE_TURN_ON, SERVICE_TURN_OFF],
)
async def test_program_switch_deprecation_issue_fix(
hass: HomeAssistant,
appliance: HomeAppliance,
service: str,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
client: MagicMock,
issue_registry: ir.IssueRegistry,
hass_client: ClientSessionGenerator,
) -> None:
"""Test we can fix the issues created when a program switch entity is in an automation or in a script or when is used."""
entity_id = "switch.washer_program_mix"
automation_script_issue_id = f"deprecated_program_switch_{entity_id}"
action_handler_issue_id = f"deprecated_program_switch_{entity_id}"
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"alias": "test",
"trigger": {"platform": "state", "entity_id": entity_id},
"action": {
"action": "automation.turn_on",
"target": {
"entity_id": "automation.test",
},
},
}
},
)
assert await async_setup_component(
hass,
script.DOMAIN,
{
script.DOMAIN: {
"test": {
"sequence": [
{
"action": "switch.turn_on",
"entity_id": entity_id,
},
],
}
}
},
)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{
ATTR_ENTITY_ID: entity_id,
},
blocking=True,
)
assert automations_with_entity(hass, entity_id)[0] == "automation.test"
assert scripts_with_entity(hass, entity_id)[0] == "script.test"
assert len(issue_registry.issues) == 2
assert issue_registry.async_get_issue(DOMAIN, automation_script_issue_id)
assert issue_registry.async_get_issue(DOMAIN, action_handler_issue_id)
for issue in issue_registry.issues.copy().values():
_client = await hass_client()
resp = await _client.post(
"/api/repairs/issues/fix",
json={"handler": DOMAIN, "issue_id": issue.issue_id},
)
assert resp.status == HTTPStatus.OK
flow_id = (await resp.json())["flow_id"]
resp = await _client.post(f"/api/repairs/issues/fix/{flow_id}")
# Assert the issue is no longer present
assert not issue_registry.async_get_issue(DOMAIN, automation_script_issue_id)
assert not issue_registry.async_get_issue(DOMAIN, action_handler_issue_id)
assert len(issue_registry.issues) == 0

View File

@ -320,7 +320,7 @@ async def test_time_entity_error(
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize("appliance", ["Oven"], indirect=True)
async def test_create_issue(
async def test_create_alarm_clock_deprecation_issue(
hass: HomeAssistant,
appliance: HomeAppliance,
config_entry: MockConfigEntry,
@ -329,7 +329,7 @@ async def test_create_issue(
client: MagicMock,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test we create an issue when an automation or script is using a deprecated entity."""
"""Test that we create an issue when an automation or script is using a alarm clock time entity or the entity is used by the user."""
entity_id = f"{TIME_DOMAIN}.oven_alarm_clock"
automation_script_issue_id = (
f"deprecated_time_alarm_clock_in_automations_scripts_{entity_id}"
@ -401,7 +401,7 @@ async def test_create_issue(
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize("appliance", ["Oven"], indirect=True)
async def test_issue_fix(
async def test_alarm_clock_deprecation_issue_fix(
hass: HomeAssistant,
appliance: HomeAppliance,
config_entry: MockConfigEntry,
@ -411,7 +411,7 @@ async def test_issue_fix(
issue_registry: ir.IssueRegistry,
hass_client: ClientSessionGenerator,
) -> None:
"""Test we create an issue when an automation or script is using a deprecated entity."""
"""Test we can fix the issues created when a alarm clock time entity is in an automation or in a script or when is used."""
entity_id = f"{TIME_DOMAIN}.oven_alarm_clock"
automation_script_issue_id = (
f"deprecated_time_alarm_clock_in_automations_scripts_{entity_id}"