Fetch current active and selected programs at Home Connect (#136948)

* Fetch current active and selected programs

* Intialize HomeConnectEntity first at SelectProgramEntity

* Use the right exception

* Use active/selected program from `get_all_programs`

This will allow us to reduce the number of requests that we need to perform to get all the data ready (only one requests vs. three requests)

* Remove no longer required mocks

* Fix
pull/136549/head^2
J. Diego Rodríguez Royo 2025-02-02 00:12:26 +01:00 committed by GitHub
parent bf6f790d09
commit 147b5f549f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 56 additions and 12 deletions

View File

@ -257,14 +257,9 @@ class HomeConnectCoordinator(
appliance_data = appliances_data[appliance.ha_id]
else:
appliances_data[appliance.ha_id] = appliance_data
if (
appliance.type in APPLIANCES_WITH_PROGRAMS
and not appliance_data.programs
):
if appliance.type in APPLIANCES_WITH_PROGRAMS:
try:
appliance_data.programs.extend(
(await self.client.get_all_programs(appliance.ha_id)).programs
)
all_programs = await self.client.get_all_programs(appliance.ha_id)
except HomeConnectError as error:
_LOGGER.debug(
"Error fetching programs for %s: %s",
@ -273,4 +268,30 @@ class HomeConnectCoordinator(
if isinstance(error, HomeConnectApiError)
else type(error).__name__,
)
else:
appliance_data.programs.extend(all_programs.programs)
for program, event_key in (
(
all_programs.active,
EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM,
),
(
all_programs.selected,
EventKey.BSH_COMMON_ROOT_SELECTED_PROGRAM,
),
):
if program and program.key:
appliance_data.events.update(
{
event_key: Event(
event_key,
event_key.value,
0,
"",
"",
program.key,
)
}
)
return appliances_data

View File

@ -110,7 +110,6 @@ class HomeConnectProgramSelectEntity(HomeConnectEntity, SelectEntity):
or program.constraints.execution in desc.allowed_executions
)
]
self._attr_current_option = None
def update_native_value(self) -> None:
"""Set the program value."""

View File

@ -192,6 +192,7 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
desc = " ".join(
["Program", program.key.split(".")[-3], program.key.split(".")[-1]]
)
self.program = program
super().__init__(
coordinator,
appliance,
@ -200,7 +201,6 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
self._attr_name = f"{appliance.info.name} {desc}"
self._attr_unique_id = f"{appliance.info.ha_id}-{desc}"
self._attr_has_entity_name = False
self.program = program
async def async_added_to_hass(self) -> None:
"""Call when entity is added to hass."""

View File

@ -19,8 +19,10 @@ from aiohomeconnect.model import (
EventMessage,
EventType,
Option,
Program,
)
from aiohomeconnect.model.error import HomeConnectApiError, HomeConnectError
from aiohomeconnect.model.program import EnumerateProgram
import pytest
from homeassistant.components.application_credentials import (
@ -227,7 +229,14 @@ async def _get_all_programs_side_effect(ha_id: str) -> ArrayOfPrograms:
if appliance_type not in MOCK_PROGRAMS:
raise HomeConnectApiError("error.key", "error description")
return ArrayOfPrograms.from_dict(MOCK_PROGRAMS[appliance_type]["data"])
return ArrayOfPrograms(
[
EnumerateProgram.from_dict(program)
for program in MOCK_PROGRAMS[appliance_type]["data"]["programs"]
],
Program.from_dict(MOCK_PROGRAMS[appliance_type]["data"]["programs"][0]),
Program.from_dict(MOCK_PROGRAMS[appliance_type]["data"]["programs"][0]),
)
async def _get_settings_side_effect(ha_id: str) -> ArrayOfSettings:

View File

@ -174,6 +174,7 @@ async def test_filter_programs(
(
"appliance_ha_id",
"entity_id",
"expected_initial_state",
"mock_method",
"program_key",
"program_to_set",
@ -183,6 +184,7 @@ async def test_filter_programs(
(
"Dishwasher",
"select.dishwasher_selected_program",
"dishcare_dishwasher_program_auto_1",
"set_selected_program",
ProgramKey.DISHCARE_DISHWASHER_ECO_50,
"dishcare_dishwasher_program_eco_50",
@ -191,6 +193,7 @@ async def test_filter_programs(
(
"Dishwasher",
"select.dishwasher_active_program",
"dishcare_dishwasher_program_auto_1",
"start_program",
ProgramKey.DISHCARE_DISHWASHER_ECO_50,
"dishcare_dishwasher_program_eco_50",
@ -202,6 +205,7 @@ async def test_filter_programs(
async def test_select_program_functionality(
appliance_ha_id: str,
entity_id: str,
expected_initial_state: str,
mock_method: str,
program_key: ProgramKey,
program_to_set: str,
@ -217,7 +221,7 @@ async def test_select_program_functionality(
assert await integration_setup(client)
assert config_entry.state is ConfigEntryState.LOADED
assert hass.states.is_state(entity_id, "unknown")
assert hass.states.is_state(entity_id, expected_initial_state)
await hass.services.async_call(
SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
@ -301,6 +305,8 @@ async def test_select_exception_handling(
assert await integration_setup(client_with_exception)
assert config_entry.state is ConfigEntryState.LOADED
assert hass.states.is_state(entity_id, STATE_UNKNOWN)
# Assert that an exception is called.
with pytest.raises(HomeConnectError):
await getattr(client_with_exception, mock_attr)()

View File

@ -178,11 +178,18 @@ async def test_switch_functionality(
@pytest.mark.parametrize(
("entity_id", "program_key", "appliance_ha_id"),
("entity_id", "program_key", "initial_state", "appliance_ha_id"),
[
(
"switch.dryer_program_mix",
ProgramKey.LAUNDRY_CARE_DRYER_MIX,
STATE_OFF,
"Dryer",
),
(
"switch.dryer_program_cotton",
ProgramKey.LAUNDRY_CARE_DRYER_COTTON,
STATE_ON,
"Dryer",
),
],
@ -191,6 +198,7 @@ async def test_switch_functionality(
async def test_program_switch_functionality(
entity_id: str,
program_key: ProgramKey,
initial_state: str,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -227,6 +235,7 @@ async def test_program_switch_functionality(
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
assert hass.states.is_state(entity_id, initial_state)
await hass.services.async_call(
SWITCH_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}