Fix fetch options error for Home connect (#139392)

* Handle errors when obtaining options definitions

* Don't fetch program options if the program key is unknown

* Test to ensure that available program endpoint is not called on unknown program
pull/139859/head
J. Diego Rodríguez Royo 2025-02-27 12:00:14 +01:00 committed by Bram Kragten
parent 585b950a46
commit fa6d7d5e3c
3 changed files with 113 additions and 13 deletions

View File

@ -440,13 +440,27 @@ class HomeConnectCoordinator(
self, ha_id: str, program_key: ProgramKey
) -> dict[OptionKey, ProgramDefinitionOption]:
"""Get options with constraints for appliance."""
if program_key is ProgramKey.UNKNOWN:
return {}
try:
return {
option.key: option
for option in (
await self.client.get_available_program(ha_id, program_key=program_key)
await self.client.get_available_program(
ha_id, program_key=program_key
)
).options
or []
}
except HomeConnectError as error:
_LOGGER.debug(
"Error fetching options for %s: %s",
ha_id,
error
if isinstance(error, HomeConnectApiError)
else type(error).__name__,
)
return {}
async def update_options(
self, ha_id: str, event_key: EventKey, program_key: ProgramKey
@ -456,7 +470,6 @@ class HomeConnectCoordinator(
events = self.data[ha_id].events
options_to_notify = options.copy()
options.clear()
if program_key is not ProgramKey.UNKNOWN:
options.update(await self.get_options_definitions(ha_id, program_key))
for option in options.values():

View File

@ -75,21 +75,35 @@ async def test_coordinator_update_failing_get_appliances(
assert config_entry.state == ConfigEntryState.SETUP_RETRY
async def test_coordinator_update_failing_get_settings_status(
@pytest.mark.parametrize(
"mock_method",
[
"get_settings",
"get_status",
"get_all_programs",
"get_available_commands",
"get_available_program",
],
)
async def test_coordinator_update_failing(
mock_method: str,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
client_with_exception: MagicMock,
client: MagicMock,
) -> None:
"""Test that although is not possible to get settings and status, the config entry is loaded.
This is for cases where some appliances are reachable and some are not in the same configuration entry.
"""
# Get home appliances does pass at client_with_exception.get_home_appliances mock, so no need to mock it again
setattr(client, mock_method, AsyncMock(side_effect=HomeConnectError()))
assert config_entry.state == ConfigEntryState.NOT_LOADED
await integration_setup(client_with_exception)
await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
getattr(client, mock_method).assert_called()
@pytest.mark.parametrize("appliance_ha_id", ["Dishwasher"], indirect=True)
@pytest.mark.parametrize(

View File

@ -23,6 +23,7 @@ from aiohomeconnect.model.error import (
SelectedProgramNotSetError,
)
from aiohomeconnect.model.program import (
EnumerateProgram,
ProgramDefinitionConstraints,
ProgramDefinitionOption,
)
@ -234,6 +235,78 @@ async def test_program_options_retrieval(
assert hass.states.is_state(entity_id, STATE_UNKNOWN)
@pytest.mark.parametrize(
("array_of_programs_program_arg", "event_key"),
[
(
"active",
EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM,
),
(
"selected",
EventKey.BSH_COMMON_ROOT_SELECTED_PROGRAM,
),
],
)
async def test_no_options_retrieval_on_unknown_program(
array_of_programs_program_arg: str,
event_key: EventKey,
appliance_ha_id: str,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
client: MagicMock,
) -> None:
"""Test that no options are retrieved when the program is unknown."""
async def get_all_programs_with_options_mock(ha_id: str) -> ArrayOfPrograms:
return ArrayOfPrograms(
**(
{
"programs": [
EnumerateProgram(ProgramKey.UNKNOWN, "unknown program")
],
array_of_programs_program_arg: Program(
ProgramKey.UNKNOWN, options=[]
),
}
)
)
client.get_all_programs = AsyncMock(side_effect=get_all_programs_with_options_mock)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
assert client.get_available_program.call_count == 0
await client.add_events(
[
EventMessage(
appliance_ha_id,
EventType.NOTIFY,
data=ArrayOfEvents(
[
Event(
key=event_key,
raw_key=event_key.value,
timestamp=0,
level="",
handling="",
value=ProgramKey.UNKNOWN,
)
]
),
)
]
)
await hass.async_block_till_done()
assert client.get_available_program.call_count == 0
@pytest.mark.parametrize(
"event_key",
[