diff --git a/homeassistant/components/shelly/const.py b/homeassistant/components/shelly/const.py index 49e33dfd5e1..ea6b9320cb1 100644 --- a/homeassistant/components/shelly/const.py +++ b/homeassistant/components/shelly/const.py @@ -1,6 +1,7 @@ """Constants for the Shelly integration.""" from __future__ import annotations +import re from typing import Final COAP: Final = "coap" @@ -11,6 +12,22 @@ REST: Final = "rest" CONF_COAP_PORT: Final = "coap_port" DEFAULT_COAP_PORT: Final = 5683 +FIRMWARE_PATTERN: Final = re.compile(r"^(\d{8})") + +# Firmware 1.11.0 release date, this firmware supports light transition +LIGHT_TRANSITION_MIN_FIRMWARE_DATE: Final = 20210226 + +# max light transition time in milliseconds +MAX_TRANSITION_TIME: Final = 5000 + +MODELS_SUPPORTING_LIGHT_TRANSITION: Final = ( + "SHBDUO-1", + "SHCB-1", + "SHDM-1", + "SHDM-2", + "SHRGBW2", + "SHVIN-1", +) # Used in "_async_update_data" as timeout for polling data from devices. POLLING_TIMEOUT_SEC: Final = 18 diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index 047a105a30f..86624410708 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -14,12 +14,14 @@ from homeassistant.components.light import ( ATTR_EFFECT, ATTR_RGB_COLOR, ATTR_RGBW_COLOR, + ATTR_TRANSITION, COLOR_MODE_BRIGHTNESS, COLOR_MODE_COLOR_TEMP, COLOR_MODE_ONOFF, COLOR_MODE_RGB, COLOR_MODE_RGBW, SUPPORT_EFFECT, + SUPPORT_TRANSITION, LightEntity, brightness_supported, ) @@ -37,9 +39,13 @@ from .const import ( COAP, DATA_CONFIG_ENTRY, DOMAIN, + FIRMWARE_PATTERN, KELVIN_MAX_VALUE, KELVIN_MIN_VALUE_COLOR, KELVIN_MIN_VALUE_WHITE, + LIGHT_TRANSITION_MIN_FIRMWARE_DATE, + MAX_TRANSITION_TIME, + MODELS_SUPPORTING_LIGHT_TRANSITION, SHBLB_1_RGB_EFFECTS, STANDARD_RGB_EFFECTS, ) @@ -110,6 +116,14 @@ class ShellyLight(ShellyBlockEntity, LightEntity): if hasattr(block, "effect"): self._supported_features |= SUPPORT_EFFECT + if wrapper.model in MODELS_SUPPORTING_LIGHT_TRANSITION: + match = FIRMWARE_PATTERN.search(wrapper.device.settings.get("fw")) + if ( + match is not None + and int(match[0]) >= LIGHT_TRANSITION_MIN_FIRMWARE_DATE + ): + self._supported_features |= SUPPORT_TRANSITION + @property def supported_features(self) -> int: """Supported features.""" @@ -261,6 +275,11 @@ class ShellyLight(ShellyBlockEntity, LightEntity): supported_color_modes = self._supported_color_modes params: dict[str, Any] = {"turn": "on"} + if ATTR_TRANSITION in kwargs: + params["transition"] = min( + int(kwargs[ATTR_TRANSITION] * 1000), MAX_TRANSITION_TIME + ) + if ATTR_BRIGHTNESS in kwargs and brightness_supported(supported_color_modes): brightness_pct = int(100 * (kwargs[ATTR_BRIGHTNESS] + 1) / 255) if hasattr(self.block, "gain"): @@ -312,7 +331,15 @@ class ShellyLight(ShellyBlockEntity, LightEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn off light.""" - self.control_result = await self.set_state(turn="off") + params: dict[str, Any] = {"turn": "off"} + + if ATTR_TRANSITION in kwargs: + params["transition"] = min( + int(kwargs[ATTR_TRANSITION] * 1000), MAX_TRANSITION_TIME + ) + + self.control_result = await self.set_state(**params) + self.async_write_ha_state() async def set_light_mode(self, set_mode: str | None) -> bool: