core/homeassistant/components/miele/switch.py

225 lines
7.3 KiB
Python

"""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
import aiohttp
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 aiohttp.ClientError as 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 aiohttp.ClientError as 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
)