diff --git a/homeassistant/components/home_connect/coordinator.py b/homeassistant/components/home_connect/coordinator.py index 80ae8173d86..d9200b282c9 100644 --- a/homeassistant/components/home_connect/coordinator.py +++ b/homeassistant/components/home_connect/coordinator.py @@ -440,13 +440,27 @@ class HomeConnectCoordinator( self, ha_id: str, program_key: ProgramKey ) -> dict[OptionKey, ProgramDefinitionOption]: """Get options with constraints for appliance.""" - return { - option.key: option - for option in ( - await self.client.get_available_program(ha_id, program_key=program_key) - ).options - or [] - } + 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 + ) + ).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,8 +470,7 @@ 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)) + options.update(await self.get_options_definitions(ha_id, program_key)) for option in options.values(): option_value = option.constraints.default if option.constraints else None diff --git a/tests/components/home_connect/test_coordinator.py b/tests/components/home_connect/test_coordinator.py index 51f42a98f42..3dd9ffbe7c1 100644 --- a/tests/components/home_connect/test_coordinator.py +++ b/tests/components/home_connect/test_coordinator.py @@ -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( diff --git a/tests/components/home_connect/test_entity.py b/tests/components/home_connect/test_entity.py index f173cda0b0c..6ac9a2c1d90 100644 --- a/tests/components/home_connect/test_entity.py +++ b/tests/components/home_connect/test_entity.py @@ -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", [