"""Switch platform for Miele switch integration.""" from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass import logging from typing import Any, Final, cast from aiohttp import ClientResponseError from pymiele import MieleDevice from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.typing import StateType from .const import ( DOMAIN, POWER_OFF, POWER_ON, PROCESS_ACTION, MieleActions, MieleAppliance, StateStatus, ) from .coordinator import MieleConfigEntry from .entity import MieleEntity PARALLEL_UPDATES = 1 _LOGGER = logging.getLogger(__name__) @dataclass(frozen=True, kw_only=True) class MieleSwitchDescription(SwitchEntityDescription): """Class describing Miele switch entities.""" value_fn: Callable[[MieleDevice], StateType] on_value: int = 0 off_value: int = 0 on_cmd_data: dict[str, str | int | bool] off_cmd_data: dict[str, str | int | bool] @dataclass class MieleSwitchDefinition: """Class for defining switch entities.""" types: tuple[MieleAppliance, ...] description: MieleSwitchDescription SWITCH_TYPES: Final[tuple[MieleSwitchDefinition, ...]] = ( MieleSwitchDefinition( types=(MieleAppliance.FRIDGE, MieleAppliance.FRIDGE_FREEZER), description=MieleSwitchDescription( key="supercooling", value_fn=lambda value: value.state_status, on_value=StateStatus.supercooling, translation_key="supercooling", on_cmd_data={PROCESS_ACTION: MieleActions.START_SUPERCOOL}, off_cmd_data={PROCESS_ACTION: MieleActions.STOP_SUPERCOOL}, ), ), MieleSwitchDefinition( types=( MieleAppliance.FREEZER, MieleAppliance.FRIDGE_FREEZER, MieleAppliance.WINE_CABINET_FREEZER, ), description=MieleSwitchDescription( key="superfreezing", value_fn=lambda value: value.state_status, on_value=StateStatus.superfreezing, translation_key="superfreezing", on_cmd_data={PROCESS_ACTION: MieleActions.START_SUPERFREEZE}, off_cmd_data={PROCESS_ACTION: MieleActions.STOP_SUPERFREEZE}, ), ), MieleSwitchDefinition( types=( MieleAppliance.WASHING_MACHINE, MieleAppliance.WASHING_MACHINE_SEMI_PROFESSIONAL, MieleAppliance.TUMBLE_DRYER, MieleAppliance.TUMBLE_DRYER_SEMI_PROFESSIONAL, MieleAppliance.DISHWASHER, MieleAppliance.DISH_WARMER, MieleAppliance.OVEN, MieleAppliance.OVEN_MICROWAVE, MieleAppliance.STEAM_OVEN, MieleAppliance.MICROWAVE, MieleAppliance.COFFEE_SYSTEM, MieleAppliance.HOOD, MieleAppliance.WASHER_DRYER, MieleAppliance.STEAM_OVEN_COMBI, MieleAppliance.STEAM_OVEN_MICRO, MieleAppliance.DIALOG_OVEN, MieleAppliance.STEAM_OVEN_MK2, ), description=MieleSwitchDescription( key="poweronoff", value_fn=lambda value: value.state_status, off_value=1, translation_key="power", on_cmd_data={POWER_ON: True}, off_cmd_data={POWER_OFF: True}, ), ), ) async def async_setup_entry( hass: HomeAssistant, config_entry: MieleConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the switch platform.""" coordinator = config_entry.runtime_data added_devices: set[str] = set() def _async_add_new_devices() -> None: nonlocal added_devices new_devices_set, current_devices = coordinator.async_add_devices(added_devices) added_devices = current_devices entities = [] for device_id, device in coordinator.data.devices.items(): for definition in SWITCH_TYPES: if ( device_id in new_devices_set and device.device_type in definition.types ): entity_class: type[MieleSwitch] = MieleSwitch match definition.description.key: case "poweronoff": entity_class = MielePowerSwitch case "supercooling" | "superfreezing": entity_class = MieleSuperSwitch entities.append( entity_class(coordinator, device_id, definition.description) ) async_add_entities(entities) config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices)) _async_add_new_devices() class MieleSwitch(MieleEntity, SwitchEntity): """Representation of a Switch.""" entity_description: MieleSwitchDescription async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the device.""" await self.async_turn_switch(self.entity_description.on_cmd_data) async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the device.""" await self.async_turn_switch(self.entity_description.off_cmd_data) async def async_turn_switch(self, mode: dict[str, str | int | bool]) -> None: """Set switch to mode.""" try: await self.api.send_action(self._device_id, mode) except ClientResponseError as err: _LOGGER.debug("Error setting switch state for %s: %s", self.entity_id, err) raise HomeAssistantError( translation_domain=DOMAIN, translation_key="set_state_error", translation_placeholders={ "entity": self.entity_id, }, ) from err class MielePowerSwitch(MieleSwitch): """Representation of a power switch.""" entity_description: MieleSwitchDescription @property def is_on(self) -> bool | None: """Return the state of the switch.""" return self.action.power_off_enabled @property def available(self) -> bool: """Return the availability of the entity.""" return ( self.action.power_off_enabled or self.action.power_on_enabled ) and super().available async def async_turn_switch(self, mode: dict[str, str | int | bool]) -> None: """Set switch to mode.""" try: await self.api.send_action(self._device_id, mode) except ClientResponseError as err: _LOGGER.debug("Error setting switch state for %s: %s", self.entity_id, err) raise HomeAssistantError( translation_domain=DOMAIN, translation_key="set_state_error", translation_placeholders={ "entity": self.entity_id, }, ) from err self.action.power_on_enabled = cast(bool, mode) self.action.power_off_enabled = not cast(bool, mode) self.async_write_ha_state() class MieleSuperSwitch(MieleSwitch): """Representation of a supercool/superfreeze switch.""" entity_description: MieleSwitchDescription @property def is_on(self) -> bool | None: """Return the state of the switch.""" return ( self.entity_description.value_fn(self.device) == self.entity_description.on_value )