From 87e41e807c284703977c4cc29a207a5aa0d86ffe Mon Sep 17 00:00:00 2001 From: Bram Gerritsen Date: Sat, 24 Jul 2021 15:52:14 +0200 Subject: [PATCH] Add support for Velux light devices (#49338) Co-authored-by: Franck Nijhof --- homeassistant/components/velux/__init__.py | 43 ++++++++++++++++- homeassistant/components/velux/cover.py | 38 +-------------- homeassistant/components/velux/light.py | 56 ++++++++++++++++++++++ 3 files changed, 100 insertions(+), 37 deletions(-) create mode 100644 homeassistant/components/velux/light.py diff --git a/homeassistant/components/velux/__init__.py b/homeassistant/components/velux/__init__.py index 5c1d8bfd370..6783c71b3cd 100644 --- a/homeassistant/components/velux/__init__.py +++ b/homeassistant/components/velux/__init__.py @@ -5,12 +5,14 @@ from pyvlx import PyVLX, PyVLXException import voluptuous as vol from homeassistant.const import CONF_HOST, CONF_PASSWORD, EVENT_HOMEASSISTANT_STOP +from homeassistant.core import callback from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity DOMAIN = "velux" DATA_VELUX = "data_velux" -PLATFORMS = ["cover", "scene"] +PLATFORMS = ["cover", "light", "scene"] _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( @@ -75,3 +77,42 @@ class VeluxModule: _LOGGER.debug("Velux interface started") await self.pyvlx.load_scenes() await self.pyvlx.load_nodes() + + +class VeluxEntity(Entity): + """Abstraction for al Velux entities.""" + + def __init__(self, node): + """Initialize the Velux device.""" + self.node = node + + @callback + def async_register_callbacks(self): + """Register callbacks to update hass after device was changed.""" + + async def after_update_callback(device): + """Call after device was updated.""" + self.async_write_ha_state() + + self.node.register_device_updated_cb(after_update_callback) + + async def async_added_to_hass(self): + """Store register state change callback.""" + self.async_register_callbacks() + + @property + def unique_id(self) -> str: + """Return the unique id base on the serial_id returned by Velux.""" + return self.node.serial_number + + @property + def name(self): + """Return the name of the Velux device.""" + if not self.node.name: + return "#" + str(self.node.node_id) + return self.node.name + + @property + def should_poll(self): + """No polling needed within Velux.""" + return False diff --git a/homeassistant/components/velux/cover.py b/homeassistant/components/velux/cover.py index 187c0d36178..3bb228dd425 100644 --- a/homeassistant/components/velux/cover.py +++ b/homeassistant/components/velux/cover.py @@ -21,9 +21,8 @@ from homeassistant.components.cover import ( SUPPORT_STOP_TILT, CoverEntity, ) -from homeassistant.core import callback -from . import DATA_VELUX +from . import DATA_VELUX, VeluxEntity async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): @@ -35,42 +34,9 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(entities) -class VeluxCover(CoverEntity): +class VeluxCover(VeluxEntity, CoverEntity): """Representation of a Velux cover.""" - def __init__(self, node): - """Initialize the cover.""" - self.node = node - - @callback - def async_register_callbacks(self): - """Register callbacks to update hass after device was changed.""" - - async def after_update_callback(device): - """Call after device was updated.""" - self.async_write_ha_state() - - self.node.register_device_updated_cb(after_update_callback) - - async def async_added_to_hass(self): - """Store register state change callback.""" - self.async_register_callbacks() - - @property - def unique_id(self): - """Return the unique ID of this cover.""" - return self.node.serial_number - - @property - def name(self): - """Return the name of the Velux device.""" - return self.node.name - - @property - def should_poll(self): - """No polling needed within Velux.""" - return False - @property def supported_features(self): """Flag supported features.""" diff --git a/homeassistant/components/velux/light.py b/homeassistant/components/velux/light.py new file mode 100644 index 00000000000..2e33b267e43 --- /dev/null +++ b/homeassistant/components/velux/light.py @@ -0,0 +1,56 @@ +"""Support for Velux lights.""" +from pyvlx import Intensity, LighteningDevice +from pyvlx.node import Node + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + COLOR_MODE_BRIGHTNESS, + LightEntity, +) + +from . import DATA_VELUX, VeluxEntity + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + """Set up light(s) for Velux platform.""" + async_add_entities( + VeluxLight(node) + for node in hass.data[DATA_VELUX].pyvlx.nodes + if isinstance(node, LighteningDevice) + ) + + +class VeluxLight(VeluxEntity, LightEntity): + """Representation of a Velux light.""" + + def __init__(self, node: Node) -> None: + """Initialize the Velux light.""" + super().__init__(node) + + self._attr_supported_color_modes = {COLOR_MODE_BRIGHTNESS} + self._attr_color_mode = COLOR_MODE_BRIGHTNESS + + @property + def brightness(self): + """Return the current brightness.""" + return int((100 - self.node.intensity.intensity_percent) * 255 / 100) + + @property + def is_on(self): + """Return true if light is on.""" + return not self.node.intensity.off and self.node.intensity.known + + async def async_turn_on(self, **kwargs): + """Instruct the light to turn on.""" + if ATTR_BRIGHTNESS in kwargs: + intensity_percent = int(100 - kwargs[ATTR_BRIGHTNESS] / 255 * 100) + await self.node.set_intensity( + Intensity(intensity_percent=intensity_percent), + wait_for_completion=True, + ) + else: + await self.node.turn_on(wait_for_completion=True) + + async def async_turn_off(self, **kwargs): + """Instruct the light to turn off.""" + await self.node.turn_off(wait_for_completion=True)