"""Provides a switch for Home Connect.""" import logging from typing import Any, cast, override from aiohomeconnect.model import OptionKey, SettingKey from aiohomeconnect.model.error import HomeConnectError from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.typing import UNDEFINED, UndefinedType from .common import setup_home_connect_entry, should_add_option_entity from .const import BSH_POWER_OFF, BSH_POWER_ON, BSH_POWER_STANDBY, DOMAIN from .coordinator import HomeConnectApplianceCoordinator, HomeConnectConfigEntry from .entity import HomeConnectEntity, HomeConnectOptionEntity from .utils import get_dict_from_home_connect_error _LOGGER = logging.getLogger(__name__) PARALLEL_UPDATES = 1 SWITCHES = ( SwitchEntityDescription( key=SettingKey.BSH_COMMON_CHILD_LOCK, translation_key="child_lock", ), SwitchEntityDescription( key=SettingKey.CONSUMER_PRODUCTS_COFFEE_MAKER_CUP_WARMER, translation_key="cup_warmer", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_FRIDGE_FREEZER_SUPER_MODE_FREEZER, translation_key="freezer_super_mode", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_FRIDGE_FREEZER_SUPER_MODE_REFRIGERATOR, translation_key="refrigerator_super_mode", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_COMMON_ECO_MODE, translation_key="eco_mode", ), SwitchEntityDescription( key=SettingKey.COOKING_OVEN_SABBATH_MODE, translation_key="sabbath_mode", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_COMMON_SABBATH_MODE, translation_key="sabbath_mode", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_COMMON_VACATION_MODE, translation_key="vacation_mode", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_COMMON_FRESH_MODE, translation_key="fresh_mode", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_COMMON_DISPENSER_ENABLED, translation_key="dispenser_enabled", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_COMMON_DOOR_ASSISTANT_FRIDGE, translation_key="door_assistant_fridge", ), SwitchEntityDescription( key=SettingKey.REFRIGERATION_COMMON_DOOR_ASSISTANT_FREEZER, translation_key="door_assistant_freezer", ), ) POWER_SWITCH_DESCRIPTION = SwitchEntityDescription( key=SettingKey.BSH_COMMON_POWER_STATE, translation_key="power", ) SWITCH_OPTIONS = ( SwitchEntityDescription( key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_MULTIPLE_BEVERAGES, translation_key="multiple_beverages", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_INTENSIV_ZONE, translation_key="intensiv_zone", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_BRILLIANCE_DRY, translation_key="brilliance_dry", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_VARIO_SPEED_PLUS, translation_key="vario_speed_plus", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_SILENCE_ON_DEMAND, translation_key="silence_on_demand", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_HALF_LOAD, translation_key="half_load", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_EXTRA_DRY, translation_key="extra_dry", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_HYGIENE_PLUS, translation_key="hygiene_plus", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_ECO_DRY, translation_key="eco_dry", ), SwitchEntityDescription( key=OptionKey.DISHCARE_DISHWASHER_ZEOLITE_DRY, translation_key="zeolite_dry", ), SwitchEntityDescription( key=OptionKey.COOKING_OVEN_FAST_PRE_HEAT, translation_key="fast_pre_heat", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_COMMON_SILENT_MODE, translation_key="silent_mode", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_I_DOS_1_ACTIVE, translation_key="i_dos1_active", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_I_DOS_2_ACTIVE, translation_key="i_dos2_active", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_INTENSIVE_PLUS, translation_key="intensive_plus", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_LESS_IRONING, translation_key="less_ironing", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_MINI_LOAD, translation_key="mini_load", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_PREWASH, translation_key="prewash", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_RINSE_HOLD, translation_key="rinse_hold", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_SOAK, translation_key="soaking", ), SwitchEntityDescription( key=OptionKey.LAUNDRY_CARE_WASHER_WATER_PLUS, translation_key="water_plus", ), ) def _get_entities_for_appliance( appliance_coordinator: HomeConnectApplianceCoordinator, ) -> list[HomeConnectEntity]: """Get a list of entities.""" entities: list[HomeConnectEntity] = [] if SettingKey.BSH_COMMON_POWER_STATE in appliance_coordinator.data.settings: entities.append( HomeConnectPowerSwitch(appliance_coordinator, POWER_SWITCH_DESCRIPTION) ) entities.extend( HomeConnectSwitch(appliance_coordinator, description) for description in SWITCHES if description.key in appliance_coordinator.data.settings ) return entities def _get_option_entities_for_appliance( appliance_coordinator: HomeConnectApplianceCoordinator, entity_registry: er.EntityRegistry, ) -> list[HomeConnectEntity]: """Get a list of currently available option entities.""" return [ HomeConnectSwitchOptionEntity(appliance_coordinator, description) for description in SWITCH_OPTIONS if should_add_option_entity( description, appliance_coordinator.data, entity_registry, Platform.SWITCH ) ] async def async_setup_entry( hass: HomeAssistant, entry: HomeConnectConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the Home Connect switch.""" setup_home_connect_entry( hass, entry, _get_entities_for_appliance, async_add_entities, _get_option_entities_for_appliance, ) class HomeConnectSwitch(HomeConnectEntity, SwitchEntity): """Generic switch class for Home Connect Binary Settings.""" @override async def async_turn_on(self, **kwargs: Any) -> None: """Turn on setting.""" try: await self.coordinator.client.set_setting( self.appliance.info.ha_id, setting_key=SettingKey(self.bsh_key), value=True, ) except HomeConnectError as err: self._attr_available = False raise HomeAssistantError( translation_domain=DOMAIN, translation_key="turn_on", translation_placeholders={ **get_dict_from_home_connect_error(err), "entity_id": self.entity_id, "key": self.bsh_key, }, ) from err @override async def async_turn_off(self, **kwargs: Any) -> None: """Turn off setting.""" try: await self.coordinator.client.set_setting( self.appliance.info.ha_id, setting_key=SettingKey(self.bsh_key), value=False, ) except HomeConnectError as err: self._attr_available = False raise HomeAssistantError( translation_domain=DOMAIN, translation_key="turn_off", translation_placeholders={ **get_dict_from_home_connect_error(err), "entity_id": self.entity_id, "key": self.bsh_key, }, ) from err @override def update_native_value(self) -> None: """Update the switch's status.""" self._attr_is_on = self.appliance.settings[SettingKey(self.bsh_key)].value class HomeConnectPowerSwitch(HomeConnectEntity, SwitchEntity): """Power switch class for Home Connect.""" power_off_state: str | UndefinedType | None = UNDEFINED @override async def async_turn_on(self, **kwargs: Any) -> None: """Switch the device on.""" try: await self.coordinator.client.set_setting( self.appliance.info.ha_id, setting_key=SettingKey.BSH_COMMON_POWER_STATE, value=BSH_POWER_ON, ) except HomeConnectError as err: self._attr_is_on = False raise HomeAssistantError( translation_domain=DOMAIN, translation_key="power_on", translation_placeholders={ **get_dict_from_home_connect_error(err), "appliance_name": self.appliance.info.name, }, ) from err @override async def async_turn_off(self, **kwargs: Any) -> None: """Switch the device off.""" if self.power_off_state is UNDEFINED: await self.async_fetch_power_off_state() if self.power_off_state is UNDEFINED: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="unable_to_retrieve_turn_off", translation_placeholders={ "appliance_name": self.appliance.info.name }, ) if self.power_off_state is None: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="turn_off_not_supported", translation_placeholders={"appliance_name": self.appliance.info.name}, ) try: await self.coordinator.client.set_setting( self.appliance.info.ha_id, setting_key=SettingKey.BSH_COMMON_POWER_STATE, value=self.power_off_state, ) except HomeConnectError as err: self._attr_is_on = True raise HomeAssistantError( translation_domain=DOMAIN, translation_key="power_off", translation_placeholders={ **get_dict_from_home_connect_error(err), "appliance_name": self.appliance.info.name, "value": self.power_off_state, }, ) from err @override def update_native_value(self) -> None: """Set the value of the entity.""" power_state = self.appliance.settings[SettingKey.BSH_COMMON_POWER_STATE] value = cast(str, power_state.value) if value == BSH_POWER_ON: self._attr_is_on = True elif ( isinstance(self.power_off_state, str) and self.power_off_state and value == self.power_off_state ): self._attr_is_on = False elif self.power_off_state is UNDEFINED and value in [ BSH_POWER_OFF, BSH_POWER_STANDBY, ]: self.power_off_state = value self._attr_is_on = False else: self._attr_is_on = None async def async_fetch_power_off_state(self) -> None: """Fetch the power off state.""" data = self.appliance.settings[SettingKey.BSH_COMMON_POWER_STATE] if not data.constraints or not data.constraints.allowed_values: try: data = await self.coordinator.client.get_setting( self.appliance.info.ha_id, setting_key=SettingKey.BSH_COMMON_POWER_STATE, ) except HomeConnectError as err: _LOGGER.error("An error occurred fetching the power settings: %s", err) return if not data.constraints or not data.constraints.allowed_values: return if BSH_POWER_OFF in data.constraints.allowed_values: self.power_off_state = BSH_POWER_OFF elif BSH_POWER_STANDBY in data.constraints.allowed_values: self.power_off_state = BSH_POWER_STANDBY else: self.power_off_state = None class HomeConnectSwitchOptionEntity(HomeConnectOptionEntity, SwitchEntity): """Switch option class for Home Connect.""" @override async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the option.""" await self.async_set_option(True) @override async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the option.""" await self.async_set_option(False) @override def update_native_value(self) -> None: """Set the value of the entity.""" self._attr_is_on = cast(bool | None, self.option_value)