From 5b0eca7f8578c6e40154a00780d52613c1ffb453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Diego=20Rodr=C3=ADguez=20Royo?= Date: Sun, 23 Feb 2025 01:42:25 +0100 Subject: [PATCH] Add select setting entities to Home Connect (#138884) * Add select setting entities * Improvements --- .../components/home_connect/const.py | 4 +- .../components/home_connect/select.py | 225 +++++++++++++----- .../components/home_connect/strings.json | 26 ++ .../home_connect/fixtures/settings.json | 11 +- .../snapshots/test_diagnostics.ambr | 2 +- tests/components/home_connect/test_select.py | 130 ++++++++++ 6 files changed, 340 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/home_connect/const.py b/homeassistant/components/home_connect/const.py index 3a22297ebee..692a5e91851 100644 --- a/homeassistant/components/home_connect/const.py +++ b/homeassistant/components/home_connect/const.py @@ -87,7 +87,7 @@ PROGRAMS_TRANSLATION_KEYS_MAP = { value: key for key, value in TRANSLATION_KEYS_PROGRAMS_MAP.items() } -REFERENCE_MAP_ID_OPTIONS = { +AVAILABLE_MAPS_ENUM = { bsh_key_to_translation_key(option): option for option in ( "ConsumerProducts.CleaningRobot.EnumType.AvailableMaps.TempMap", @@ -305,7 +305,7 @@ PROGRAM_ENUM_OPTIONS = { for option_key, options in ( ( OptionKey.CONSUMER_PRODUCTS_CLEANING_ROBOT_REFERENCE_MAP_ID, - REFERENCE_MAP_ID_OPTIONS, + AVAILABLE_MAPS_ENUM, ), ( OptionKey.CONSUMER_PRODUCTS_CLEANING_ROBOT_CLEANING_MODE, diff --git a/homeassistant/components/home_connect/select.py b/homeassistant/components/home_connect/select.py index f5298056080..e4d50b0d5e9 100644 --- a/homeassistant/components/home_connect/select.py +++ b/homeassistant/components/home_connect/select.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import Any, cast from aiohomeconnect.client import Client as HomeConnectClient -from aiohomeconnect.model import EventKey, OptionKey, ProgramKey +from aiohomeconnect.model import EventKey, OptionKey, ProgramKey, SettingKey from aiohomeconnect.model.error import HomeConnectError from aiohomeconnect.model.program import Execution @@ -17,6 +17,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .common import setup_home_connect_entry from .const import ( APPLIANCES_WITH_PROGRAMS, + AVAILABLE_MAPS_ENUM, BEAN_AMOUNT_OPTIONS, BEAN_CONTAINER_OPTIONS, CLEANING_MODE_OPTIONS, @@ -28,9 +29,12 @@ from .const import ( HOT_WATER_TEMPERATURE_OPTIONS, INTENSIVE_LEVEL_OPTIONS, PROGRAMS_TRANSLATION_KEYS_MAP, - REFERENCE_MAP_ID_OPTIONS, SPIN_SPEED_OPTIONS, + SVE_TRANSLATION_KEY_SET_SETTING, + SVE_TRANSLATION_PLACEHOLDER_ENTITY_ID, + SVE_TRANSLATION_PLACEHOLDER_KEY, SVE_TRANSLATION_PLACEHOLDER_PROGRAM, + SVE_TRANSLATION_PLACEHOLDER_VALUE, TEMPERATURE_OPTIONS, TRANSLATION_KEYS_PROGRAMS_MAP, VARIO_PERFECT_OPTIONS, @@ -43,7 +47,30 @@ from .coordinator import ( HomeConnectCoordinator, ) from .entity import HomeConnectEntity, HomeConnectOptionEntity -from .utils import get_dict_from_home_connect_error +from .utils import bsh_key_to_translation_key, get_dict_from_home_connect_error + +FUNCTIONAL_LIGHT_COLOR_TEMPERATURE_ENUM = { + bsh_key_to_translation_key(option): option + for option in ( + "Cooking.Hood.EnumType.ColorTemperature.custom", + "Cooking.Hood.EnumType.ColorTemperature.warm", + "Cooking.Hood.EnumType.ColorTemperature.warmToNeutral", + "Cooking.Hood.EnumType.ColorTemperature.neutral", + "Cooking.Hood.EnumType.ColorTemperature.neutralToCold", + "Cooking.Hood.EnumType.ColorTemperature.cold", + ) +} + +AMBIENT_LIGHT_COLOR_TEMPERATURE_ENUM = { + **{ + bsh_key_to_translation_key(option): option + for option in ("BSH.Common.EnumType.AmbientLightColor.CustomColor",) + }, + **{ + str(option): f"BSH.Common.EnumType.AmbientLightColor.Color{option}" + for option in range(1, 100) + }, +} @dataclass(frozen=True, kw_only=True) @@ -60,10 +87,8 @@ class HomeConnectProgramSelectEntityDescription( @dataclass(frozen=True, kw_only=True) -class HomeConnectSelectOptionEntityDescription( - SelectEntityDescription, -): - """Entity Description class for options that have enumeration values.""" +class HomeConnectSelectEntityDescription(SelectEntityDescription): + """Entity Description class for settings and options that have enumeration values.""" translation_key_values: dict[str, str] values_translation_key: dict[str, str] @@ -90,151 +115,184 @@ PROGRAM_SELECT_ENTITY_DESCRIPTIONS = ( ), ) -PROGRAM_SELECT_OPTION_ENTITY_DESCRIPTIONS = ( - HomeConnectSelectOptionEntityDescription( - key=OptionKey.CONSUMER_PRODUCTS_CLEANING_ROBOT_REFERENCE_MAP_ID, - translation_key="reference_map_id", - options=list(REFERENCE_MAP_ID_OPTIONS.keys()), - translation_key_values=REFERENCE_MAP_ID_OPTIONS, +SELECT_ENTITY_DESCRIPTIONS = ( + HomeConnectSelectEntityDescription( + key=SettingKey.CONSUMER_PRODUCTS_CLEANING_ROBOT_CURRENT_MAP, + translation_key="current_map", + options=list(AVAILABLE_MAPS_ENUM), + translation_key_values=AVAILABLE_MAPS_ENUM, values_translation_key={ value: translation_key - for translation_key, value in REFERENCE_MAP_ID_OPTIONS.items() + for translation_key, value in AVAILABLE_MAPS_ENUM.items() }, ), - HomeConnectSelectOptionEntityDescription( - key=OptionKey.CONSUMER_PRODUCTS_CLEANING_ROBOT_CLEANING_MODE, + HomeConnectSelectEntityDescription( + key=SettingKey.COOKING_HOOD_COLOR_TEMPERATURE, + translation_key="functional_light_color_temperature", + options=list(FUNCTIONAL_LIGHT_COLOR_TEMPERATURE_ENUM), + translation_key_values=FUNCTIONAL_LIGHT_COLOR_TEMPERATURE_ENUM, + values_translation_key={ + value: translation_key + for translation_key, value in FUNCTIONAL_LIGHT_COLOR_TEMPERATURE_ENUM.items() + }, + ), + HomeConnectSelectEntityDescription( + key=SettingKey.BSH_COMMON_AMBIENT_LIGHT_COLOR, + translation_key="ambient_light_color", + options=list(AMBIENT_LIGHT_COLOR_TEMPERATURE_ENUM), + translation_key_values=AMBIENT_LIGHT_COLOR_TEMPERATURE_ENUM, + values_translation_key={ + value: translation_key + for translation_key, value in AMBIENT_LIGHT_COLOR_TEMPERATURE_ENUM.items() + }, + ), +) + +PROGRAM_SELECT_OPTION_ENTITY_DESCRIPTIONS = ( + HomeConnectSelectEntityDescription( + key=OptionKey.CONSUMER_PRODUCTS_CLEANING_ROBOT_REFERENCE_MAP_ID, translation_key="reference_map_id", - options=list(CLEANING_MODE_OPTIONS.keys()), + options=list(AVAILABLE_MAPS_ENUM), + translation_key_values=AVAILABLE_MAPS_ENUM, + values_translation_key={ + value: translation_key + for translation_key, value in AVAILABLE_MAPS_ENUM.items() + }, + ), + HomeConnectSelectEntityDescription( + key=OptionKey.CONSUMER_PRODUCTS_CLEANING_ROBOT_CLEANING_MODE, + translation_key="cleaning_mode", + options=list(CLEANING_MODE_OPTIONS), translation_key_values=CLEANING_MODE_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in CLEANING_MODE_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_BEAN_AMOUNT, translation_key="bean_amount", - options=list(BEAN_AMOUNT_OPTIONS.keys()), + options=list(BEAN_AMOUNT_OPTIONS), translation_key_values=BEAN_AMOUNT_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in BEAN_AMOUNT_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_COFFEE_TEMPERATURE, translation_key="coffee_temperature", - options=list(COFFEE_TEMPERATURE_OPTIONS.keys()), + options=list(COFFEE_TEMPERATURE_OPTIONS), translation_key_values=COFFEE_TEMPERATURE_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in COFFEE_TEMPERATURE_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_BEAN_CONTAINER_SELECTION, translation_key="bean_container", - options=list(BEAN_CONTAINER_OPTIONS.keys()), + options=list(BEAN_CONTAINER_OPTIONS), translation_key_values=BEAN_CONTAINER_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in BEAN_CONTAINER_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_FLOW_RATE, translation_key="flow_rate", - options=list(FLOW_RATE_OPTIONS.keys()), + options=list(FLOW_RATE_OPTIONS), translation_key_values=FLOW_RATE_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in FLOW_RATE_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_COFFEE_MILK_RATIO, translation_key="coffee_milk_ratio", - options=list(COFFEE_MILK_RATIO_OPTIONS.keys()), + options=list(COFFEE_MILK_RATIO_OPTIONS), translation_key_values=COFFEE_MILK_RATIO_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in FLOW_RATE_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_HOT_WATER_TEMPERATURE, translation_key="hot_water_temperature", - options=list(HOT_WATER_TEMPERATURE_OPTIONS.keys()), + options=list(HOT_WATER_TEMPERATURE_OPTIONS), translation_key_values=HOT_WATER_TEMPERATURE_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in HOT_WATER_TEMPERATURE_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.LAUNDRY_CARE_DRYER_DRYING_TARGET, translation_key="drying_target", - options=list(DRYING_TARGET_OPTIONS.keys()), + options=list(DRYING_TARGET_OPTIONS), translation_key_values=DRYING_TARGET_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in DRYING_TARGET_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.COOKING_COMMON_HOOD_VENTING_LEVEL, translation_key="venting_level", - options=list(VENTING_LEVEL_OPTIONS.keys()), + options=list(VENTING_LEVEL_OPTIONS), translation_key_values=VENTING_LEVEL_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in VENTING_LEVEL_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.COOKING_COMMON_HOOD_INTENSIVE_LEVEL, translation_key="intensive_level", - options=list(INTENSIVE_LEVEL_OPTIONS.keys()), + options=list(INTENSIVE_LEVEL_OPTIONS), translation_key_values=INTENSIVE_LEVEL_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in INTENSIVE_LEVEL_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.COOKING_OVEN_WARMING_LEVEL, translation_key="warming_level", - options=list(WARMING_LEVEL_OPTIONS.keys()), + options=list(WARMING_LEVEL_OPTIONS), translation_key_values=WARMING_LEVEL_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in WARMING_LEVEL_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_TEMPERATURE, translation_key="washer_temperature", - options=list(TEMPERATURE_OPTIONS.keys()), + options=list(TEMPERATURE_OPTIONS), translation_key_values=TEMPERATURE_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in TEMPERATURE_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_SPIN_SPEED, translation_key="spin_speed", - options=list(SPIN_SPEED_OPTIONS.keys()), + options=list(SPIN_SPEED_OPTIONS), translation_key_values=SPIN_SPEED_OPTIONS, values_translation_key={ value: translation_key for translation_key, value in SPIN_SPEED_OPTIONS.items() }, ), - HomeConnectSelectOptionEntityDescription( + HomeConnectSelectEntityDescription( key=OptionKey.LAUNDRY_CARE_COMMON_VARIO_PERFECT, translation_key="vario_perfect", - options=list(VARIO_PERFECT_OPTIONS.keys()), + options=list(VARIO_PERFECT_OPTIONS), translation_key_values=VARIO_PERFECT_OPTIONS, values_translation_key={ value: translation_key @@ -249,14 +307,21 @@ def _get_entities_for_appliance( appliance: HomeConnectApplianceData, ) -> list[HomeConnectEntity]: """Get a list of entities.""" - return ( - [ - HomeConnectProgramSelectEntity(entry.runtime_data, appliance, desc) - for desc in PROGRAM_SELECT_ENTITY_DESCRIPTIONS - ] - if appliance.info.type in APPLIANCES_WITH_PROGRAMS - else [] - ) + return [ + *( + [ + HomeConnectProgramSelectEntity(entry.runtime_data, appliance, desc) + for desc in PROGRAM_SELECT_ENTITY_DESCRIPTIONS + ] + if appliance.info.type in APPLIANCES_WITH_PROGRAMS + else [] + ), + *[ + HomeConnectSelectEntity(entry.runtime_data, appliance, desc) + for desc in SELECT_ENTITY_DESCRIPTIONS + if desc.key in appliance.settings + ], + ] def _get_option_entities_for_appliance( @@ -341,17 +406,71 @@ class HomeConnectProgramSelectEntity(HomeConnectEntity, SelectEntity): ) from err +class HomeConnectSelectEntity(HomeConnectEntity, SelectEntity): + """Select setting class for Home Connect.""" + + entity_description: HomeConnectSelectEntityDescription + + def __init__( + self, + coordinator: HomeConnectCoordinator, + appliance: HomeConnectApplianceData, + desc: HomeConnectSelectEntityDescription, + ) -> None: + """Initialize the entity.""" + super().__init__( + coordinator, + appliance, + desc, + ) + setting = appliance.settings.get(cast(SettingKey, desc.key)) + if setting and setting.constraints and setting.constraints.allowed_values: + self._attr_options = [ + desc.values_translation_key[option] + for option in setting.constraints.allowed_values + if option in desc.values_translation_key + ] + + async def async_select_option(self, option: str) -> None: + """Select new option.""" + value = self.entity_description.translation_key_values[option] + try: + await self.coordinator.client.set_setting( + self.appliance.info.ha_id, + setting_key=cast(SettingKey, self.bsh_key), + value=value, + ) + except HomeConnectError as err: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key=SVE_TRANSLATION_KEY_SET_SETTING, + translation_placeholders={ + **get_dict_from_home_connect_error(err), + SVE_TRANSLATION_PLACEHOLDER_ENTITY_ID: self.entity_id, + SVE_TRANSLATION_PLACEHOLDER_KEY: self.bsh_key, + SVE_TRANSLATION_PLACEHOLDER_VALUE: value, + }, + ) from err + + def update_native_value(self) -> None: + """Set the value of the entity.""" + data = self.appliance.settings[cast(SettingKey, self.bsh_key)] + self._attr_current_option = self.entity_description.values_translation_key.get( + data.value + ) + + class HomeConnectSelectOptionEntity(HomeConnectOptionEntity, SelectEntity): """Select option class for Home Connect.""" - entity_description: HomeConnectSelectOptionEntityDescription + entity_description: HomeConnectSelectEntityDescription _original_option_keys: set[str | None] def __init__( self, coordinator: HomeConnectCoordinator, appliance: HomeConnectApplianceData, - desc: HomeConnectSelectOptionEntityDescription, + desc: HomeConnectSelectEntityDescription, ) -> None: """Initialize the entity.""" self._original_option_keys = set(desc.values_translation_key.keys()) diff --git a/homeassistant/components/home_connect/strings.json b/homeassistant/components/home_connect/strings.json index db53e76fb95..dde002d1caa 100644 --- a/homeassistant/components/home_connect/strings.json +++ b/homeassistant/components/home_connect/strings.json @@ -1215,6 +1215,32 @@ "laundry_care_washer_dryer_program_wash_and_dry_90": "[%key:component::home_connect::selector::programs::options::laundry_care_washer_dryer_program_wash_and_dry_90%]" } }, + "current_map": { + "name": "Current map", + "state": { + "consumer_products_cleaning_robot_enum_type_available_maps_temp_map": "[%key:component::home_connect::selector::available_maps::options::consumer_products_cleaning_robot_enum_type_available_maps_temp_map%]", + "consumer_products_cleaning_robot_enum_type_available_maps_map1": "[%key:component::home_connect::selector::available_maps::options::consumer_products_cleaning_robot_enum_type_available_maps_map1%]", + "consumer_products_cleaning_robot_enum_type_available_maps_map2": "[%key:component::home_connect::selector::available_maps::options::consumer_products_cleaning_robot_enum_type_available_maps_map2%]", + "consumer_products_cleaning_robot_enum_type_available_maps_map3": "[%key:component::home_connect::selector::available_maps::options::consumer_products_cleaning_robot_enum_type_available_maps_map3%]" + } + }, + "functional_light_color_temperature": { + "name": "Functional light color temperature", + "state": { + "cooking_hood_enum_type_color_temperature_custom": "Custom", + "cooking_hood_enum_type_color_temperature_warm": "Warm", + "cooking_hood_enum_type_color_temperature_warm_to_neutral": "Warm to Neutral", + "cooking_hood_enum_type_color_temperature_neutral": "Neutral", + "cooking_hood_enum_type_color_temperature_neutral_to_cold": "Neutral to Cold", + "cooking_hood_enum_type_color_temperature_cold": "Cold" + } + }, + "ambient_light_color": { + "name": "Ambient light color", + "state": { + "b_s_h_common_enum_type_ambient_light_color_custom_color": "Custom" + } + }, "reference_map_id": { "name": "[%key:component::home_connect::services::set_program_and_options::fields::consumer_products_cleaning_robot_option_reference_map_id::name%]", "state": { diff --git a/tests/components/home_connect/fixtures/settings.json b/tests/components/home_connect/fixtures/settings.json index 8f649e5790b..bd1bea18365 100644 --- a/tests/components/home_connect/fixtures/settings.json +++ b/tests/components/home_connect/fixtures/settings.json @@ -68,9 +68,16 @@ "type": "Double" }, { - "key": "BSH.Common.Setting.ColorTemperature", + "key": "Cooking.Hood.Setting.ColorTemperature", "value": "Cooking.Hood.EnumType.ColorTemperature.warmToNeutral", - "type": "BSH.Common.EnumType.ColorTemperature" + "type": "BSH.Common.EnumType.ColorTemperature", + "constraints": { + "allowedvalues": [ + "Cooking.Hood.EnumType.ColorTemperature.warm", + "Cooking.Hood.EnumType.ColorTemperature.neutral", + "Cooking.Hood.EnumType.ColorTemperature.cold" + ] + } }, { "key": "BSH.Common.Setting.AmbientLightEnabled", diff --git a/tests/components/home_connect/snapshots/test_diagnostics.ambr b/tests/components/home_connect/snapshots/test_diagnostics.ambr index 512da8bd970..28f45ce97ba 100644 --- a/tests/components/home_connect/snapshots/test_diagnostics.ambr +++ b/tests/components/home_connect/snapshots/test_diagnostics.ambr @@ -98,8 +98,8 @@ 'BSH.Common.Setting.AmbientLightEnabled': True, 'Cooking.Common.Setting.Lighting': True, 'Cooking.Common.Setting.LightingBrightness': 70, + 'Cooking.Hood.Setting.ColorTemperature': 'Cooking.Hood.EnumType.ColorTemperature.warmToNeutral', 'Cooking.Hood.Setting.ColorTemperaturePercent': 70, - 'unknown': 'Cooking.Hood.EnumType.ColorTemperature.warmToNeutral', }), 'status': dict({ 'BSH.Common.Status.DoorState': 'BSH.Common.EnumType.DoorState.Closed', diff --git a/tests/components/home_connect/test_select.py b/tests/components/home_connect/test_select.py index 917c092136e..d98dbd8e5f6 100644 --- a/tests/components/home_connect/test_select.py +++ b/tests/components/home_connect/test_select.py @@ -6,13 +6,16 @@ from unittest.mock import AsyncMock, MagicMock from aiohomeconnect.model import ( ArrayOfEvents, ArrayOfPrograms, + ArrayOfSettings, Event, EventKey, EventMessage, EventType, + GetSetting, OptionKey, ProgramDefinition, ProgramKey, + SettingKey, ) from aiohomeconnect.model.error import ( ActiveProgramNotSetError, @@ -26,6 +29,7 @@ from aiohomeconnect.model.program import ( ProgramDefinitionConstraints, ProgramDefinitionOption, ) +from aiohomeconnect.model.setting import SettingConstraints import pytest from homeassistant.components.home_connect.const import DOMAIN @@ -434,6 +438,132 @@ async def test_select_exception_handling( assert getattr(client_with_exception, mock_attr).call_count == 2 +@pytest.mark.parametrize("appliance_ha_id", ["Hood"], indirect=True) +@pytest.mark.parametrize( + ( + "entity_id", + "setting_key", + "expected_options", + "value_to_set", + "expected_value_call_arg", + ), + [ + ( + "select.hood_functional_light_color_temperature", + SettingKey.COOKING_HOOD_COLOR_TEMPERATURE, + { + "cooking_hood_enum_type_color_temperature_warm", + "cooking_hood_enum_type_color_temperature_neutral", + "cooking_hood_enum_type_color_temperature_cold", + }, + "cooking_hood_enum_type_color_temperature_neutral", + "Cooking.Hood.EnumType.ColorTemperature.neutral", + ), + ( + "select.hood_ambient_light_color", + SettingKey.BSH_COMMON_AMBIENT_LIGHT_COLOR, + { + "b_s_h_common_enum_type_ambient_light_color_custom_color", + *[str(i) for i in range(1, 100)], + }, + "42", + "BSH.Common.EnumType.AmbientLightColor.Color42", + ), + ], +) +async def test_select_functionality( + appliance_ha_id: str, + entity_id: str, + setting_key: SettingKey, + expected_options: set[str], + value_to_set: str, + expected_value_call_arg: str, + hass: HomeAssistant, + config_entry: MockConfigEntry, + integration_setup: Callable[[MagicMock], Awaitable[bool]], + setup_credentials: None, + client: MagicMock, +) -> None: + """Test select functionality.""" + assert config_entry.state is ConfigEntryState.NOT_LOADED + assert await integration_setup(client) + assert config_entry.state is ConfigEntryState.LOADED + + entity_state = hass.states.get(entity_id) + assert entity_state + assert set(entity_state.attributes[ATTR_OPTIONS]) == expected_options + + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: entity_id, "option": value_to_set}, + ) + await hass.async_block_till_done() + + client.set_setting.assert_called_once() + assert client.set_setting.call_args.args == (appliance_ha_id,) + assert client.set_setting.call_args.kwargs == { + "setting_key": setting_key, + "value": expected_value_call_arg, + } + assert hass.states.is_state(entity_id, value_to_set) + + +@pytest.mark.parametrize( + ("entity_id", "setting_key", "allowed_value", "value_to_set", "mock_attr"), + [ + ( + "select.hood_functional_light_color_temperature", + SettingKey.COOKING_HOOD_COLOR_TEMPERATURE, + "Cooking.Hood.EnumType.ColorTemperature.neutral", + "cooking_hood_enum_type_color_temperature_neutral", + "set_setting", + ), + ], +) +async def test_select_entity_error( + entity_id: str, + setting_key: SettingKey, + allowed_value: str, + value_to_set: str, + mock_attr: str, + hass: HomeAssistant, + config_entry: MockConfigEntry, + integration_setup: Callable[[MagicMock], Awaitable[bool]], + setup_credentials: None, + client_with_exception: MagicMock, +) -> None: + """Test select entity error.""" + client_with_exception.get_settings.side_effect = None + client_with_exception.get_settings.return_value = ArrayOfSettings( + [ + GetSetting( + key=setting_key, + raw_key=setting_key.value, + value=value_to_set, + constraints=SettingConstraints(allowed_values=[allowed_value]), + ) + ] + ) + assert config_entry.state is ConfigEntryState.NOT_LOADED + assert await integration_setup(client_with_exception) + assert config_entry.state is ConfigEntryState.LOADED + + with pytest.raises(HomeConnectError): + await getattr(client_with_exception, mock_attr)() + + with pytest.raises( + HomeAssistantError, match=r"Error.*assign.*value.*to.*setting.*" + ): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: entity_id, "option": value_to_set}, + blocking=True, + ) + assert getattr(client_with_exception, mock_attr).call_count == 2 + + @pytest.mark.parametrize( ( "set_active_program_options_side_effect",