cdnninja 82efa0893f
Vesync Display Switch Feature (#137493)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-04-14 17:26:21 +02:00

134 lines
4.4 KiB
Python

"""Support for VeSync switches."""
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import Any, Final
from pyvesync.vesyncbasedevice import VeSyncBaseDevice
from homeassistant.components.switch import (
SwitchDeviceClass,
SwitchEntity,
SwitchEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .common import is_outlet, is_wall_switch, rgetattr
from .const import DOMAIN, VS_COORDINATOR, VS_DEVICES, VS_DISCOVERY
from .coordinator import VeSyncDataCoordinator
from .entity import VeSyncBaseEntity
_LOGGER = logging.getLogger(__name__)
@dataclass(frozen=True, kw_only=True)
class VeSyncSwitchEntityDescription(SwitchEntityDescription):
"""A class that describes custom switch entities."""
is_on: Callable[[VeSyncBaseDevice], bool]
exists_fn: Callable[[VeSyncBaseDevice], bool]
on_fn: Callable[[VeSyncBaseDevice], bool]
off_fn: Callable[[VeSyncBaseDevice], bool]
SENSOR_DESCRIPTIONS: Final[tuple[VeSyncSwitchEntityDescription, ...]] = (
VeSyncSwitchEntityDescription(
key="device_status",
is_on=lambda device: device.device_status == "on",
# Other types of wall switches support dimming. Those use light.py platform.
exists_fn=lambda device: is_wall_switch(device) or is_outlet(device),
name=None,
on_fn=lambda device: device.turn_on(),
off_fn=lambda device: device.turn_off(),
),
VeSyncSwitchEntityDescription(
key="display",
is_on=lambda device: device.display_state,
exists_fn=lambda device: rgetattr(device, "display_state") is not None,
translation_key="display",
on_fn=lambda device: device.turn_on_display(),
off_fn=lambda device: device.turn_off_display(),
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up switch platform."""
coordinator = hass.data[DOMAIN][VS_COORDINATOR]
@callback
def discover(devices):
"""Add new devices to platform."""
_setup_entities(devices, async_add_entities, coordinator)
config_entry.async_on_unload(
async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_DEVICES), discover)
)
_setup_entities(hass.data[DOMAIN][VS_DEVICES], async_add_entities, coordinator)
@callback
def _setup_entities(
devices: list[VeSyncBaseDevice],
async_add_entities,
coordinator: VeSyncDataCoordinator,
):
"""Check if device is online and add entity."""
async_add_entities(
VeSyncSwitchEntity(dev, description, coordinator)
for dev in devices
for description in SENSOR_DESCRIPTIONS
if description.exists_fn(dev)
)
class VeSyncSwitchEntity(SwitchEntity, VeSyncBaseEntity):
"""VeSync switch entity class."""
entity_description: VeSyncSwitchEntityDescription
def __init__(
self,
device: VeSyncBaseDevice,
description: VeSyncSwitchEntityDescription,
coordinator: VeSyncDataCoordinator,
) -> None:
"""Initialize the sensor."""
super().__init__(device, coordinator)
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}-{description.key}"
if is_outlet(self.device):
self._attr_device_class = SwitchDeviceClass.OUTLET
elif is_wall_switch(self.device):
self._attr_device_class = SwitchDeviceClass.SWITCH
@property
def is_on(self) -> bool | None:
"""Return the entity value to represent the entity state."""
return self.entity_description.is_on(self.device)
def turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
if not self.entity_description.off_fn(self.device):
raise HomeAssistantError("An error occurred while turning off.")
self.schedule_update_ha_state()
def turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
if not self.entity_description.on_fn(self.device):
raise HomeAssistantError("An error occurred while turning on.")
self.schedule_update_ha_state()