"""Support for Elgato lights.""" from __future__ import annotations from datetime import timedelta import logging from typing import Any from elgato import Elgato, ElgatoError, Info, Settings, State from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, COLOR_MODE_COLOR_TEMP, COLOR_MODE_HS, LightEntity, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_IDENTIFIERS, ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME, ATTR_SW_VERSION, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import ( AddEntitiesCallback, async_get_current_platform, ) from .const import DATA_ELGATO_CLIENT, DOMAIN, SERVICE_IDENTIFY _LOGGER = logging.getLogger(__name__) PARALLEL_UPDATES = 1 SCAN_INTERVAL = timedelta(seconds=10) async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Elgato Light based on a config entry.""" elgato: Elgato = hass.data[DOMAIN][entry.entry_id][DATA_ELGATO_CLIENT] info = await elgato.info() settings = await elgato.settings() async_add_entities([ElgatoLight(elgato, info, settings)], True) platform = async_get_current_platform() platform.async_register_entity_service( SERVICE_IDENTIFY, {}, ElgatoLight.async_identify.__name__, ) class ElgatoLight(LightEntity): """Defines an Elgato Light.""" def __init__(self, elgato: Elgato, info: Info, settings: Settings) -> None: """Initialize Elgato Light.""" self._info = info self._settings = settings self._state: State | None = None self.elgato = elgato min_mired = 143 max_mired = 344 supported_color_modes = {COLOR_MODE_COLOR_TEMP} # Elgato Light supporting color, have a different temperature range if settings.power_on_hue is not None: supported_color_modes = {COLOR_MODE_COLOR_TEMP, COLOR_MODE_HS} min_mired = 153 max_mired = 285 self._attr_max_mireds = max_mired self._attr_min_mireds = min_mired self._attr_name = info.display_name or info.product_name self._attr_supported_color_modes = supported_color_modes self._attr_unique_id = info.serial_number @property def available(self) -> bool: """Return True if entity is available.""" return self._state is not None @property def brightness(self) -> int | None: """Return the brightness of this light between 1..255.""" assert self._state is not None return round((self._state.brightness * 255) / 100) @property def color_temp(self) -> int | None: """Return the CT color value in mireds.""" assert self._state is not None return self._state.temperature @property def color_mode(self) -> str | None: """Return the color mode of the light.""" if self._state and self._state.hue is not None: return COLOR_MODE_HS return COLOR_MODE_COLOR_TEMP @property def hs_color(self) -> tuple[float, float] | None: """Return the hue and saturation color value [float, float].""" if ( self._state is None or self._state.hue is None or self._state.saturation is None ): return None return (self._state.hue, self._state.saturation) @property def is_on(self) -> bool: """Return the state of the light.""" assert self._state is not None return self._state.on async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the light.""" try: await self.elgato.light(on=False) except ElgatoError: _LOGGER.error("An error occurred while updating the Elgato Light") self._state = None async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the light.""" temperature = kwargs.get(ATTR_COLOR_TEMP) hue = None saturation = None if ATTR_HS_COLOR in kwargs: hue, saturation = kwargs[ATTR_HS_COLOR] brightness = None if ATTR_BRIGHTNESS in kwargs: brightness = round((kwargs[ATTR_BRIGHTNESS] / 255) * 100) # For Elgato lights supporting color mode, but in temperature mode; # adjusting only brightness make them jump back to color mode. # Resending temperature prevents that. if ( brightness and ATTR_HS_COLOR not in kwargs and ATTR_COLOR_TEMP not in kwargs and self.supported_color_modes and COLOR_MODE_HS in self.supported_color_modes and self.color_mode == COLOR_MODE_COLOR_TEMP ): temperature = self.color_temp try: await self.elgato.light( on=True, brightness=brightness, hue=hue, saturation=saturation, temperature=temperature, ) except ElgatoError: _LOGGER.error("An error occurred while updating the Elgato Light") self._state = None async def async_update(self) -> None: """Update Elgato entity.""" restoring = self._state is None try: self._state = await self.elgato.state() if restoring: _LOGGER.info("Connection restored") except ElgatoError as err: meth = _LOGGER.error if self._state else _LOGGER.debug meth("An error occurred while updating the Elgato Light: %s", err) self._state = None @property def device_info(self) -> DeviceInfo: """Return device information about this Elgato Light.""" return { ATTR_IDENTIFIERS: {(DOMAIN, self._info.serial_number)}, ATTR_NAME: self._info.product_name, ATTR_MANUFACTURER: "Elgato", ATTR_MODEL: self._info.product_name, ATTR_SW_VERSION: f"{self._info.firmware_version} ({self._info.firmware_build_number})", } async def async_identify(self) -> None: """Identify the light, will make it blink.""" try: await self.elgato.identify() except ElgatoError: _LOGGER.exception("An error occurred while identifying the Elgato Light") self._state = None