Filter programs by execution type at select program entities at Home Connect (#136950)

* Filter programs by execution type at select program entities

* Suggestions and improvements

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Use function and translation key at select program entity description

* Fix select entity description docstring

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
pull/136951/head^2
J. Diego Rodríguez Royo 2025-02-01 12:06:39 +01:00 committed by GitHub
parent 889fe05a48
commit efcfd97d1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 80 additions and 22 deletions

View File

@ -1,9 +1,13 @@
"""Provides a select platform for Home Connect."""
from typing import cast
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from typing import Any, cast
from aiohomeconnect.client import Client as HomeConnectClient
from aiohomeconnect.model import EventKey, ProgramKey
from aiohomeconnect.model.error import HomeConnectError
from aiohomeconnect.model.program import Execution
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.core import HomeAssistant
@ -29,14 +33,38 @@ PROGRAMS_TRANSLATION_KEYS_MAP = {
value: key for key, value in TRANSLATION_KEYS_PROGRAMS_MAP.items()
}
@dataclass(frozen=True, kw_only=True)
class HomeConnectProgramSelectEntityDescription(
SelectEntityDescription,
):
"""Entity Description class for select entities for programs."""
allowed_executions: tuple[Execution, ...]
set_program_fn: Callable[
[HomeConnectClient, str, ProgramKey], Coroutine[Any, Any, None]
]
error_translation_key: str
PROGRAM_SELECT_ENTITY_DESCRIPTIONS = (
SelectEntityDescription(
HomeConnectProgramSelectEntityDescription(
key=EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM,
translation_key="active_program",
allowed_executions=(Execution.SELECT_AND_START, Execution.START_ONLY),
set_program_fn=lambda client, ha_id, program_key: client.start_program(
ha_id, program_key=program_key
),
error_translation_key="start_program",
),
SelectEntityDescription(
HomeConnectProgramSelectEntityDescription(
key=EventKey.BSH_COMMON_ROOT_SELECTED_PROGRAM,
translation_key="selected_program",
allowed_executions=(Execution.SELECT_AND_START, Execution.SELECT_ONLY),
set_program_fn=lambda client, ha_id, program_key: client.set_selected_program(
ha_id, program_key=program_key
),
error_translation_key="select_program",
),
)
@ -59,11 +87,13 @@ async def async_setup_entry(
class HomeConnectProgramSelectEntity(HomeConnectEntity, SelectEntity):
"""Select class for Home Connect programs."""
entity_description: HomeConnectProgramSelectEntityDescription
def __init__(
self,
coordinator: HomeConnectCoordinator,
appliance: HomeConnectApplianceData,
desc: SelectEntityDescription,
desc: HomeConnectProgramSelectEntityDescription,
) -> None:
"""Initialize the entity."""
super().__init__(
@ -75,8 +105,11 @@ class HomeConnectProgramSelectEntity(HomeConnectEntity, SelectEntity):
PROGRAMS_TRANSLATION_KEYS_MAP[program.key]
for program in appliance.programs
if program.key != ProgramKey.UNKNOWN
and (
program.constraints is None
or program.constraints.execution in desc.allowed_executions
)
]
self.start_on_select = desc.key == EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM
self._attr_current_option = None
def update_native_value(self) -> None:
@ -92,22 +125,15 @@ class HomeConnectProgramSelectEntity(HomeConnectEntity, SelectEntity):
"""Select new program."""
program_key = TRANSLATION_KEYS_PROGRAMS_MAP[option]
try:
if self.start_on_select:
await self.coordinator.client.start_program(
self.appliance.info.ha_id, program_key=program_key
)
else:
await self.coordinator.client.set_selected_program(
self.appliance.info.ha_id, program_key=program_key
)
await self.entity_description.set_program_fn(
self.coordinator.client,
self.appliance.info.ha_id,
program_key,
)
except HomeConnectError as err:
if self.start_on_select:
translation_key = "start_program"
else:
translation_key = "select_program"
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key=translation_key,
translation_key=self.entity_description.error_translation_key,
translation_placeholders={
**get_dict_from_home_connect_error(err),
SVE_TRANSLATION_PLACEHOLDER_PROGRAM: program_key.value,

View File

@ -13,7 +13,11 @@ from aiohomeconnect.model import (
ProgramKey,
)
from aiohomeconnect.model.error import HomeConnectError
from aiohomeconnect.model.program import EnumerateProgram
from aiohomeconnect.model.program import (
EnumerateProgram,
EnumerateProgramConstraints,
Execution,
)
import pytest
from homeassistant.components.select import (
@ -53,25 +57,42 @@ async def test_select(
assert config_entry.state is ConfigEntryState.LOADED
async def test_filter_unknown_programs(
async def test_filter_programs(
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
client: MagicMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test select that only known programs are shown."""
"""Test select that only right programs are shown."""
client.get_all_programs.side_effect = None
client.get_all_programs.return_value = ArrayOfPrograms(
[
EnumerateProgram(
key=ProgramKey.DISHCARE_DISHWASHER_ECO_50,
raw_key=ProgramKey.DISHCARE_DISHWASHER_ECO_50.value,
constraints=EnumerateProgramConstraints(
execution=Execution.SELECT_ONLY,
),
),
EnumerateProgram(
key=ProgramKey.UNKNOWN,
raw_key="an unknown program",
),
EnumerateProgram(
key=ProgramKey.DISHCARE_DISHWASHER_QUICK_45,
raw_key=ProgramKey.DISHCARE_DISHWASHER_QUICK_45.value,
constraints=EnumerateProgramConstraints(
execution=Execution.START_ONLY,
),
),
EnumerateProgram(
key=ProgramKey.DISHCARE_DISHWASHER_AUTO_1,
raw_key=ProgramKey.DISHCARE_DISHWASHER_AUTO_1.value,
constraints=EnumerateProgramConstraints(
execution=Execution.SELECT_AND_START,
),
),
]
)
@ -82,7 +103,18 @@ async def test_filter_unknown_programs(
entity = entity_registry.async_get("select.dishwasher_selected_program")
assert entity
assert entity.capabilities
assert entity.capabilities[ATTR_OPTIONS] == ["dishcare_dishwasher_program_eco_50"]
assert entity.capabilities[ATTR_OPTIONS] == [
"dishcare_dishwasher_program_eco_50",
"dishcare_dishwasher_program_auto_1",
]
entity = entity_registry.async_get("select.dishwasher_active_program")
assert entity
assert entity.capabilities
assert entity.capabilities[ATTR_OPTIONS] == [
"dishcare_dishwasher_program_quick_45",
"dishcare_dishwasher_program_auto_1",
]
@pytest.mark.parametrize(