core/homeassistant/components/smartthings/select.py

251 lines
8.8 KiB
Python

"""Support for select entities through the SmartThings cloud API."""
from __future__ import annotations
from dataclasses import dataclass
from pysmartthings import Attribute, Capability, Command, SmartThings
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity
LAMP_TO_HA = {
"extraHigh": "extra_high",
}
WASHER_SOIL_LEVEL_TO_HA = {
"none": "none",
"heavy": "heavy",
"normal": "normal",
"light": "light",
"extraLight": "extra_light",
"extraHeavy": "extra_heavy",
"up": "up",
"down": "down",
}
WASHER_SPIN_LEVEL_TO_HA = {
"none": "none",
"rinseHold": "rinse_hold",
"noSpin": "no_spin",
"low": "low",
"extraLow": "extra_low",
"delicate": "delicate",
"medium": "medium",
"high": "high",
"extraHigh": "extra_high",
"200": "200",
"400": "400",
"600": "600",
"800": "800",
"1000": "1000",
"1200": "1200",
"1400": "1400",
"1600": "1600",
}
@dataclass(frozen=True, kw_only=True)
class SmartThingsSelectDescription(SelectEntityDescription):
"""Class describing SmartThings select entities."""
key: Capability
requires_remote_control_status: bool = False
options_attribute: Attribute
status_attribute: Attribute
command: Command
options_map: dict[str, str] | None = None
default_options: list[str] | None = None
extra_components: list[str] | None = None
capability_ignore_list: list[Capability] | None = None
CAPABILITIES_TO_SELECT: dict[Capability | str, SmartThingsSelectDescription] = {
Capability.DISHWASHER_OPERATING_STATE: SmartThingsSelectDescription(
key=Capability.DISHWASHER_OPERATING_STATE,
name=None,
translation_key="operating_state",
requires_remote_control_status=True,
options_attribute=Attribute.SUPPORTED_MACHINE_STATES,
status_attribute=Attribute.MACHINE_STATE,
command=Command.SET_MACHINE_STATE,
),
Capability.DRYER_OPERATING_STATE: SmartThingsSelectDescription(
key=Capability.DRYER_OPERATING_STATE,
name=None,
translation_key="operating_state",
requires_remote_control_status=True,
options_attribute=Attribute.SUPPORTED_MACHINE_STATES,
status_attribute=Attribute.MACHINE_STATE,
command=Command.SET_MACHINE_STATE,
default_options=["run", "pause", "stop"],
),
Capability.WASHER_OPERATING_STATE: SmartThingsSelectDescription(
key=Capability.WASHER_OPERATING_STATE,
name=None,
translation_key="operating_state",
requires_remote_control_status=True,
options_attribute=Attribute.SUPPORTED_MACHINE_STATES,
status_attribute=Attribute.MACHINE_STATE,
command=Command.SET_MACHINE_STATE,
default_options=["run", "pause", "stop"],
),
Capability.SAMSUNG_CE_AUTO_DISPENSE_DETERGENT: SmartThingsSelectDescription(
key=Capability.SAMSUNG_CE_AUTO_DISPENSE_DETERGENT,
translation_key="detergent_amount",
options_attribute=Attribute.SUPPORTED_AMOUNT,
status_attribute=Attribute.AMOUNT,
command=Command.SET_AMOUNT,
entity_category=EntityCategory.CONFIG,
),
Capability.SAMSUNG_CE_FLEXIBLE_AUTO_DISPENSE_DETERGENT: SmartThingsSelectDescription(
key=Capability.SAMSUNG_CE_FLEXIBLE_AUTO_DISPENSE_DETERGENT,
translation_key="flexible_detergent_amount",
options_attribute=Attribute.SUPPORTED_AMOUNT,
status_attribute=Attribute.AMOUNT,
command=Command.SET_AMOUNT,
entity_category=EntityCategory.CONFIG,
),
Capability.SAMSUNG_CE_LAMP: SmartThingsSelectDescription(
key=Capability.SAMSUNG_CE_LAMP,
translation_key="lamp",
options_attribute=Attribute.SUPPORTED_BRIGHTNESS_LEVEL,
status_attribute=Attribute.BRIGHTNESS_LEVEL,
command=Command.SET_BRIGHTNESS_LEVEL,
options_map=LAMP_TO_HA,
entity_category=EntityCategory.CONFIG,
extra_components=["hood"],
capability_ignore_list=[Capability.SAMSUNG_CE_CONNECTION_STATE],
),
Capability.CUSTOM_WASHER_SPIN_LEVEL: SmartThingsSelectDescription(
key=Capability.CUSTOM_WASHER_SPIN_LEVEL,
translation_key="spin_level",
options_attribute=Attribute.SUPPORTED_WASHER_SPIN_LEVEL,
status_attribute=Attribute.WASHER_SPIN_LEVEL,
command=Command.SET_WASHER_SPIN_LEVEL,
options_map=WASHER_SPIN_LEVEL_TO_HA,
entity_category=EntityCategory.CONFIG,
),
Capability.CUSTOM_WASHER_SOIL_LEVEL: SmartThingsSelectDescription(
key=Capability.CUSTOM_WASHER_SOIL_LEVEL,
translation_key="soil_level",
options_attribute=Attribute.SUPPORTED_WASHER_SOIL_LEVEL,
status_attribute=Attribute.WASHER_SOIL_LEVEL,
command=Command.SET_WASHER_SOIL_LEVEL,
options_map=WASHER_SOIL_LEVEL_TO_HA,
entity_category=EntityCategory.CONFIG,
),
}
async def async_setup_entry(
hass: HomeAssistant,
entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Add select entities for a config entry."""
entry_data = entry.runtime_data
async_add_entities(
SmartThingsSelectEntity(entry_data.client, device, description, component)
for capability, description in CAPABILITIES_TO_SELECT.items()
for device in entry_data.devices.values()
for component in device.status
if capability in device.status[component]
and (
component == MAIN
or (
description.extra_components is not None
and component in description.extra_components
)
)
and (
description.capability_ignore_list is None
or any(
capability not in device.status[component]
for capability in description.capability_ignore_list
)
)
)
class SmartThingsSelectEntity(SmartThingsEntity, SelectEntity):
"""Define a SmartThings select."""
entity_description: SmartThingsSelectDescription
def __init__(
self,
client: SmartThings,
device: FullDevice,
entity_description: SmartThingsSelectDescription,
component: str,
) -> None:
"""Initialize the instance."""
capabilities = {entity_description.key}
if entity_description.requires_remote_control_status:
capabilities.add(Capability.REMOTE_CONTROL_STATUS)
super().__init__(client, device, capabilities, component=component)
self.entity_description = entity_description
self._attr_unique_id = f"{device.device.device_id}_{component}_{entity_description.key}_{entity_description.status_attribute}_{entity_description.status_attribute}"
@property
def options(self) -> list[str]:
"""Return the list of options."""
options: list[str] = (
self.get_attribute_value(
self.entity_description.key, self.entity_description.options_attribute
)
or self.entity_description.default_options
or []
)
if self.entity_description.options_map:
options = [
self.entity_description.options_map.get(option, option)
for option in options
]
return options
@property
def current_option(self) -> str | None:
"""Return the current option."""
option = self.get_attribute_value(
self.entity_description.key, self.entity_description.status_attribute
)
if self.entity_description.options_map:
option = self.entity_description.options_map.get(option)
return option
async def async_select_option(self, option: str) -> None:
"""Select an option."""
if (
self.entity_description.requires_remote_control_status
and self.get_attribute_value(
Capability.REMOTE_CONTROL_STATUS, Attribute.REMOTE_CONTROL_ENABLED
)
== "false"
):
raise ServiceValidationError(
"Can only be updated when remote control is enabled"
)
if self.entity_description.options_map:
option = next(
(
key
for key, value in self.entity_description.options_map.items()
if value == option
),
option,
)
await self.execute_device_command(
self.entity_description.key,
self.entity_description.command,
option,
)