Delete Home Connect deprecated actions (#150929)

This commit is contained in:
J. Diego Rodríguez Royo
2025-08-22 14:16:01 +02:00
committed by GitHub
parent 158fb35c5b
commit dd270f54fc
5 changed files with 4 additions and 692 deletions
@@ -61,19 +61,12 @@ BSH_DOOR_STATE_LOCKED = "BSH.Common.EnumType.DoorState.Locked"
BSH_DOOR_STATE_OPEN = "BSH.Common.EnumType.DoorState.Open"
SERVICE_OPTION_ACTIVE = "set_option_active"
SERVICE_OPTION_SELECTED = "set_option_selected"
SERVICE_PAUSE_PROGRAM = "pause_program"
SERVICE_RESUME_PROGRAM = "resume_program"
SERVICE_SELECT_PROGRAM = "select_program"
SERVICE_SET_PROGRAM_AND_OPTIONS = "set_program_and_options"
SERVICE_SETTING = "change_setting"
SERVICE_START_PROGRAM = "start_program"
ATTR_AFFECTS_TO = "affects_to"
ATTR_KEY = "key"
ATTR_PROGRAM = "program"
ATTR_UNIT = "unit"
ATTR_VALUE = "value"
AFFECTS_TO_ACTIVE_PROGRAM = "active_program"
@@ -8,7 +8,6 @@ from typing import Any, cast
from aiohomeconnect.client import Client as HomeConnectClient
from aiohomeconnect.model import (
ArrayOfOptions,
CommandKey,
Option,
OptionKey,
ProgramKey,
@@ -21,7 +20,6 @@ from homeassistant.const import ATTR_DEVICE_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from .const import (
AFFECTS_TO_ACTIVE_PROGRAM,
@@ -29,18 +27,11 @@ from .const import (
ATTR_AFFECTS_TO,
ATTR_KEY,
ATTR_PROGRAM,
ATTR_UNIT,
ATTR_VALUE,
DOMAIN,
PROGRAM_ENUM_OPTIONS,
SERVICE_OPTION_ACTIVE,
SERVICE_OPTION_SELECTED,
SERVICE_PAUSE_PROGRAM,
SERVICE_RESUME_PROGRAM,
SERVICE_SELECT_PROGRAM,
SERVICE_SET_PROGRAM_AND_OPTIONS,
SERVICE_SETTING,
SERVICE_START_PROGRAM,
TRANSLATION_KEYS_PROGRAMS_MAP,
)
from .coordinator import HomeConnectConfigEntry
@@ -88,43 +79,6 @@ SERVICE_SETTING_SCHEMA = vol.Schema(
}
)
# DEPRECATED: Remove in 2025.9.0
SERVICE_OPTION_SCHEMA = vol.Schema(
{
vol.Required(ATTR_DEVICE_ID): str,
vol.Required(ATTR_KEY): vol.All(
vol.Coerce(OptionKey),
vol.NotIn([OptionKey.UNKNOWN]),
),
vol.Required(ATTR_VALUE): vol.Any(str, int, bool),
vol.Optional(ATTR_UNIT): str,
}
)
# DEPRECATED: Remove in 2025.9.0
SERVICE_PROGRAM_SCHEMA = vol.Any(
{
vol.Required(ATTR_DEVICE_ID): str,
vol.Required(ATTR_PROGRAM): vol.All(
vol.Coerce(ProgramKey),
vol.NotIn([ProgramKey.UNKNOWN]),
),
vol.Required(ATTR_KEY): vol.All(
vol.Coerce(OptionKey),
vol.NotIn([OptionKey.UNKNOWN]),
),
vol.Required(ATTR_VALUE): vol.Any(int, str),
vol.Optional(ATTR_UNIT): str,
},
{
vol.Required(ATTR_DEVICE_ID): str,
vol.Required(ATTR_PROGRAM): vol.All(
vol.Coerce(ProgramKey),
vol.NotIn([ProgramKey.UNKNOWN]),
),
},
)
def _require_program_or_at_least_one_option(data: dict) -> dict:
if ATTR_PROGRAM not in data and not any(
@@ -216,205 +170,6 @@ async def _get_client_and_ha_id(
return entry.runtime_data.client, ha_id
async def _async_service_program(call: ServiceCall, start: bool) -> None:
"""Execute calls to services taking a program."""
program = call.data[ATTR_PROGRAM]
client, ha_id = await _get_client_and_ha_id(call.hass, call.data[ATTR_DEVICE_ID])
option_key = call.data.get(ATTR_KEY)
options = (
[
Option(
option_key,
call.data[ATTR_VALUE],
unit=call.data.get(ATTR_UNIT),
)
]
if option_key is not None
else None
)
async_create_issue(
call.hass,
DOMAIN,
"deprecated_set_program_and_option_actions",
breaks_in_ha_version="2025.9.0",
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,
translation_key="deprecated_set_program_and_option_actions",
translation_placeholders={
"new_action_key": SERVICE_SET_PROGRAM_AND_OPTIONS,
"remove_release": "2025.9.0",
"deprecated_action_yaml": "\n".join(
[
"```yaml",
f"action: {DOMAIN}.{SERVICE_START_PROGRAM if start else SERVICE_SELECT_PROGRAM}",
"data:",
f" {ATTR_DEVICE_ID}: DEVICE_ID",
f" {ATTR_PROGRAM}: {program}",
*([f" {ATTR_KEY}: {options[0].key}"] if options else []),
*([f" {ATTR_VALUE}: {options[0].value}"] if options else []),
*(
[f" {ATTR_UNIT}: {options[0].unit}"]
if options and options[0].unit
else []
),
"```",
]
),
"new_action_yaml": "\n ".join(
[
"```yaml",
f"action: {DOMAIN}.{SERVICE_SET_PROGRAM_AND_OPTIONS}",
"data:",
f" {ATTR_DEVICE_ID}: DEVICE_ID",
f" {ATTR_AFFECTS_TO}: {AFFECTS_TO_ACTIVE_PROGRAM if start else AFFECTS_TO_SELECTED_PROGRAM}",
f" {ATTR_PROGRAM}: {bsh_key_to_translation_key(program.value)}",
*(
[
f" {bsh_key_to_translation_key(options[0].key)}: {options[0].value}"
]
if options
else []
),
"```",
]
),
"repo_link": "[aiohomeconnect](https://github.com/MartinHjelmare/aiohomeconnect)",
},
)
try:
if start:
await client.start_program(ha_id, program_key=program, options=options)
else:
await client.set_selected_program(
ha_id, program_key=program, options=options
)
except HomeConnectError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="start_program" if start else "select_program",
translation_placeholders={
**get_dict_from_home_connect_error(err),
"program": program,
},
) from err
async def _async_service_set_program_options(call: ServiceCall, active: bool) -> None:
"""Execute calls to services taking a program."""
option_key = call.data[ATTR_KEY]
value = call.data[ATTR_VALUE]
unit = call.data.get(ATTR_UNIT)
client, ha_id = await _get_client_and_ha_id(call.hass, call.data[ATTR_DEVICE_ID])
async_create_issue(
call.hass,
DOMAIN,
"deprecated_set_program_and_option_actions",
breaks_in_ha_version="2025.9.0",
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,
translation_key="deprecated_set_program_and_option_actions",
translation_placeholders={
"new_action_key": SERVICE_SET_PROGRAM_AND_OPTIONS,
"remove_release": "2025.9.0",
"deprecated_action_yaml": "\n".join(
[
"```yaml",
f"action: {DOMAIN}.{SERVICE_OPTION_ACTIVE if active else SERVICE_OPTION_SELECTED}",
"data:",
f" {ATTR_DEVICE_ID}: DEVICE_ID",
f" {ATTR_KEY}: {option_key}",
f" {ATTR_VALUE}: {value}",
*([f" {ATTR_UNIT}: {unit}"] if unit else []),
"```",
]
),
"new_action_yaml": "\n ".join(
[
"```yaml",
f"action: {DOMAIN}.{SERVICE_SET_PROGRAM_AND_OPTIONS}",
"data:",
f" {ATTR_DEVICE_ID}: DEVICE_ID",
f" {ATTR_AFFECTS_TO}: {AFFECTS_TO_ACTIVE_PROGRAM if active else AFFECTS_TO_SELECTED_PROGRAM}",
f" {bsh_key_to_translation_key(option_key)}: {value}",
"```",
]
),
"repo_link": "[aiohomeconnect](https://github.com/MartinHjelmare/aiohomeconnect)",
},
)
try:
if active:
await client.set_active_program_option(
ha_id,
option_key=option_key,
value=value,
unit=unit,
)
else:
await client.set_selected_program_option(
ha_id,
option_key=option_key,
value=value,
unit=unit,
)
except HomeConnectError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="set_options_active_program"
if active
else "set_options_selected_program",
translation_placeholders={
**get_dict_from_home_connect_error(err),
"key": option_key,
"value": str(value),
},
) from err
async def _async_service_command(call: ServiceCall, command_key: CommandKey) -> None:
"""Execute calls to services executing a command."""
client, ha_id = await _get_client_and_ha_id(call.hass, call.data[ATTR_DEVICE_ID])
async_create_issue(
call.hass,
DOMAIN,
"deprecated_command_actions",
breaks_in_ha_version="2025.9.0",
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,
translation_key="deprecated_command_actions",
)
try:
await client.put_command(ha_id, command_key=command_key, value=True)
except HomeConnectError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="execute_command",
translation_placeholders={
**get_dict_from_home_connect_error(err),
"command": command_key.value,
},
) from err
async def async_service_option_active(call: ServiceCall) -> None:
"""Service for setting an option for an active program."""
await _async_service_set_program_options(call, True)
async def async_service_option_selected(call: ServiceCall) -> None:
"""Service for setting an option for a selected program."""
await _async_service_set_program_options(call, False)
async def async_service_setting(call: ServiceCall) -> None:
"""Service for changing a setting."""
key = call.data[ATTR_KEY]
@@ -435,21 +190,6 @@ async def async_service_setting(call: ServiceCall) -> None:
) from err
async def async_service_pause_program(call: ServiceCall) -> None:
"""Service for pausing a program."""
await _async_service_command(call, CommandKey.BSH_COMMON_PAUSE_PROGRAM)
async def async_service_resume_program(call: ServiceCall) -> None:
"""Service for resuming a paused program."""
await _async_service_command(call, CommandKey.BSH_COMMON_RESUME_PROGRAM)
async def async_service_select_program(call: ServiceCall) -> None:
"""Service for selecting a program."""
await _async_service_program(call, False)
async def async_service_set_program_and_options(call: ServiceCall) -> None:
"""Service for setting a program and options."""
data = dict(call.data)
@@ -517,54 +257,13 @@ async def async_service_set_program_and_options(call: ServiceCall) -> None:
) from err
async def async_service_start_program(call: ServiceCall) -> None:
"""Service for starting a program."""
await _async_service_program(call, True)
@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register custom actions."""
hass.services.async_register(
DOMAIN,
SERVICE_OPTION_ACTIVE,
async_service_option_active,
schema=SERVICE_OPTION_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_OPTION_SELECTED,
async_service_option_selected,
schema=SERVICE_OPTION_SCHEMA,
)
hass.services.async_register(
DOMAIN, SERVICE_SETTING, async_service_setting, schema=SERVICE_SETTING_SCHEMA
)
hass.services.async_register(
DOMAIN,
SERVICE_PAUSE_PROGRAM,
async_service_pause_program,
schema=SERVICE_COMMAND_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_RESUME_PROGRAM,
async_service_resume_program,
schema=SERVICE_COMMAND_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_SELECT_PROGRAM,
async_service_select_program,
schema=SERVICE_PROGRAM_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_START_PROGRAM,
async_service_start_program,
schema=SERVICE_PROGRAM_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_SET_PROGRAM_AND_OPTIONS,
@@ -1,51 +1,3 @@
start_program:
fields:
device_id:
required: true
selector:
device:
integration: home_connect
program:
example: "Dishcare.Dishwasher.Program.Auto2"
required: true
selector:
text:
key:
example: "BSH.Common.Option.StartInRelative"
selector:
text:
value:
example: 1800
selector:
object:
unit:
example: "seconds"
selector:
text:
select_program:
fields:
device_id:
required: true
selector:
device:
integration: home_connect
program:
example: "Dishcare.Dishwasher.Program.Auto2"
required: true
selector:
text:
key:
example: "BSH.Common.Option.StartInRelative"
selector:
text:
value:
example: 1800
selector:
object:
unit:
example: "seconds"
selector:
text:
set_program_and_options:
fields:
device_id:
@@ -599,54 +551,6 @@ set_program_and_options:
- laundry_care_common_enum_type_vario_perfect_off
- laundry_care_common_enum_type_vario_perfect_eco_perfect
- laundry_care_common_enum_type_vario_perfect_speed_perfect
pause_program:
fields:
device_id:
required: true
selector:
device:
integration: home_connect
resume_program:
fields:
device_id:
required: true
selector:
device:
integration: home_connect
set_option_active:
fields:
device_id:
required: true
selector:
device:
integration: home_connect
key:
example: "LaundryCare.Dryer.Option.DryingTarget"
required: true
selector:
text:
value:
example: "LaundryCare.Dryer.EnumType.DryingTarget.IronDry"
required: true
selector:
object:
set_option_selected:
fields:
device_id:
required: true
selector:
device:
integration: home_connect
key:
example: "LaundryCare.Dryer.Option.DryingTarget"
required: true
selector:
text:
value:
example: "LaundryCare.Dryer.EnumType.DryingTarget.IronDry"
required: true
selector:
object:
change_setting:
fields:
device_id:
@@ -145,28 +145,6 @@
}
}
}
},
"deprecated_command_actions": {
"title": "The command related actions are deprecated in favor of the new buttons",
"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_set_program_and_option_actions": {
"title": "The executed action is deprecated",
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::home_connect::issues::deprecated_set_program_and_option_actions::title%]",
"description": "`start_program`, `select_program`, `set_option_active`, and `set_option_selected` actions are deprecated and will be removed in the {remove_release} release, please use the `{new_action_key}` action instead. For the executed action:\n{deprecated_action_yaml}\nyou can do the following transformation using the recognized options:\n {new_action_yaml}\nIf the option is not in the recognized options, please submit an issue or a pull request requesting the addition of the option at {repo_link}."
}
}
}
}
},
"selector": {
@@ -517,49 +495,6 @@
}
},
"services": {
"start_program": {
"name": "Start program",
"description": "Selects a program and starts it.",
"fields": {
"device_id": {
"name": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::name%]",
"description": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::description%]"
},
"program": { "name": "Program", "description": "Program to select." },
"key": { "name": "Option key", "description": "Key of the option." },
"value": {
"name": "Option value",
"description": "Value of the option."
},
"unit": { "name": "Option unit", "description": "Unit for the option." }
}
},
"select_program": {
"name": "Select program",
"description": "Selects a program without starting it.",
"fields": {
"device_id": {
"name": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::name%]",
"description": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::description%]"
},
"program": {
"name": "[%key:component::home_connect::services::start_program::fields::program::name%]",
"description": "[%key:component::home_connect::services::start_program::fields::program::description%]"
},
"key": {
"name": "[%key:component::home_connect::services::start_program::fields::key::name%]",
"description": "[%key:component::home_connect::services::start_program::fields::key::description%]"
},
"value": {
"name": "[%key:component::home_connect::services::start_program::fields::value::name%]",
"description": "[%key:component::home_connect::services::start_program::fields::value::description%]"
},
"unit": {
"name": "[%key:component::home_connect::services::start_program::fields::unit::name%]",
"description": "[%key:component::home_connect::services::start_program::fields::unit::description%]"
}
}
},
"set_program_and_options": {
"name": "Set program and options",
"description": "Starts or selects a program with options or sets the options for the active or the selected program.",
@@ -744,62 +679,6 @@
}
}
},
"pause_program": {
"name": "Pause program",
"description": "Pauses the current running program.",
"fields": {
"device_id": {
"name": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::name%]",
"description": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::description%]"
}
}
},
"resume_program": {
"name": "Resume program",
"description": "Resumes a paused program.",
"fields": {
"device_id": {
"name": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::name%]",
"description": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::description%]"
}
}
},
"set_option_active": {
"name": "Set active program option",
"description": "Sets an option for the active program.",
"fields": {
"device_id": {
"name": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::name%]",
"description": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::description%]"
},
"key": {
"name": "Key",
"description": "[%key:component::home_connect::services::start_program::fields::key::description%]"
},
"value": {
"name": "Value",
"description": "[%key:component::home_connect::services::start_program::fields::value::description%]"
}
}
},
"set_option_selected": {
"name": "Set selected program option",
"description": "Sets options for the selected program.",
"fields": {
"device_id": {
"name": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::name%]",
"description": "[%key:component::home_connect::services::set_program_and_options::fields::device_id::description%]"
},
"key": {
"name": "[%key:component::home_connect::services::start_program::fields::key::name%]",
"description": "[%key:component::home_connect::services::start_program::fields::key::description%]"
},
"value": {
"name": "[%key:component::home_connect::services::start_program::fields::value::name%]",
"description": "[%key:component::home_connect::services::start_program::fields::value::description%]"
}
}
},
"change_setting": {
"name": "Change setting",
"description": "Changes a setting.",
+4 -167
View File
@@ -1,11 +1,10 @@
"""Tests for the Home Connect actions."""
from collections.abc import Awaitable, Callable
from http import HTTPStatus
from typing import Any
from unittest.mock import MagicMock
from aiohomeconnect.model import HomeAppliance, OptionKey, ProgramKey, SettingKey
from aiohomeconnect.model import HomeAppliance, SettingKey
import pytest
from syrupy.assertion import SnapshotAssertion
@@ -14,37 +13,10 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import device_registry as dr
import homeassistant.helpers.issue_registry as ir
from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator
DEPRECATED_SERVICE_KV_CALL_PARAMS = [
{
"domain": DOMAIN,
"service": "set_option_active",
"service_data": {
"device_id": "DEVICE_ID",
"key": OptionKey.BSH_COMMON_FINISH_IN_RELATIVE.value,
"value": 43200,
"unit": "seconds",
},
"blocking": True,
},
{
"domain": DOMAIN,
"service": "set_option_selected",
"service_data": {
"device_id": "DEVICE_ID",
"key": OptionKey.LAUNDRY_CARE_WASHER_TEMPERATURE.value,
"value": "LaundryCare.Washer.EnumType.Temperature.GC40",
},
"blocking": True,
},
]
SERVICE_KV_CALL_PARAMS = [
*DEPRECATED_SERVICE_KV_CALL_PARAMS,
{
"domain": DOMAIN,
"service": "change_setting",
@@ -57,70 +29,13 @@ SERVICE_KV_CALL_PARAMS = [
},
]
SERVICE_COMMAND_CALL_PARAMS = [
{
"domain": DOMAIN,
"service": "pause_program",
"service_data": {
"device_id": "DEVICE_ID",
},
"blocking": True,
},
{
"domain": DOMAIN,
"service": "resume_program",
"service_data": {
"device_id": "DEVICE_ID",
},
"blocking": True,
},
]
SERVICE_PROGRAM_CALL_PARAMS = [
{
"domain": DOMAIN,
"service": "select_program",
"service_data": {
"device_id": "DEVICE_ID",
"program": ProgramKey.LAUNDRY_CARE_WASHER_COTTON.value,
"key": OptionKey.LAUNDRY_CARE_WASHER_TEMPERATURE.value,
"value": "LaundryCare.Washer.EnumType.Temperature.GC40",
},
"blocking": True,
},
{
"domain": DOMAIN,
"service": "start_program",
"service_data": {
"device_id": "DEVICE_ID",
"program": ProgramKey.LAUNDRY_CARE_WASHER_COTTON.value,
"key": OptionKey.BSH_COMMON_FINISH_IN_RELATIVE.value,
"value": 43200,
"unit": "seconds",
},
"blocking": True,
},
]
SERVICE_APPLIANCE_METHOD_MAPPING = {
"set_option_active": "set_active_program_option",
"set_option_selected": "set_selected_program_option",
"change_setting": "set_setting",
"pause_program": "put_command",
"resume_program": "put_command",
"select_program": "set_selected_program",
"start_program": "start_program",
}
SERVICE_VALIDATION_ERROR_MAPPING = {
"set_option_active": r"Error.*setting.*options.*active.*program.*",
"set_option_selected": r"Error.*setting.*options.*selected.*program.*",
"change_setting": r"Error.*assigning.*value.*setting.*",
"pause_program": r"Error.*executing.*command.*",
"resume_program": r"Error.*executing.*command.*",
"select_program": r"Error.*selecting.*program.*",
"start_program": r"Error.*starting.*program.*",
}
@@ -171,10 +86,7 @@ SERVICES_SET_PROGRAM_AND_OPTIONS = [
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
"service_call",
SERVICE_KV_CALL_PARAMS + SERVICE_COMMAND_CALL_PARAMS + SERVICE_PROGRAM_CALL_PARAMS,
)
@pytest.mark.parametrize("service_call", SERVICE_KV_CALL_PARAMS)
async def test_key_value_services(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
@@ -202,81 +114,6 @@ async def test_key_value_services(
)
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("service_call", "issue_id"),
[
*zip(
DEPRECATED_SERVICE_KV_CALL_PARAMS + SERVICE_PROGRAM_CALL_PARAMS,
["deprecated_set_program_and_option_actions"]
* (
len(DEPRECATED_SERVICE_KV_CALL_PARAMS)
+ len(SERVICE_PROGRAM_CALL_PARAMS)
),
strict=True,
),
*zip(
SERVICE_COMMAND_CALL_PARAMS,
["deprecated_command_actions"] * len(SERVICE_COMMAND_CALL_PARAMS),
strict=True,
),
],
)
async def test_programs_and_options_actions_deprecation(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
device_registry: dr.DeviceRegistry,
issue_registry: ir.IssueRegistry,
client: MagicMock,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
appliance: HomeAppliance,
service_call: dict[str, Any],
issue_id: str,
) -> None:
"""Test deprecated service keys."""
assert await integration_setup(client)
assert config_entry.state is ConfigEntryState.LOADED
device_entry = device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers={(DOMAIN, appliance.ha_id)},
)
service_call["service_data"]["device_id"] = device_entry.id
await hass.services.async_call(**service_call)
await hass.async_block_till_done()
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 not issue_registry.async_get_issue(DOMAIN, issue_id)
assert len(issue_registry.issues) == 0
await hass.services.async_call(**service_call)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1
assert issue_registry.async_get_issue(DOMAIN, 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 len(issue_registry.issues) == 0
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("service_call", "called_method"),
@@ -360,7 +197,7 @@ async def test_set_program_and_options_exceptions(
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
"service_call",
SERVICE_KV_CALL_PARAMS + SERVICE_COMMAND_CALL_PARAMS + SERVICE_PROGRAM_CALL_PARAMS,
SERVICE_KV_CALL_PARAMS,
)
async def test_services_exception_device_id(
hass: HomeAssistant,
@@ -430,7 +267,7 @@ async def test_services_appliance_not_found(
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
"service_call",
SERVICE_KV_CALL_PARAMS + SERVICE_COMMAND_CALL_PARAMS + SERVICE_PROGRAM_CALL_PARAMS,
SERVICE_KV_CALL_PARAMS,
)
async def test_services_exception(
hass: HomeAssistant,