From 137377b50a6756a7392d2a5a989b96a14308870b Mon Sep 17 00:00:00 2001 From: wollew Date: Sat, 14 Feb 2026 12:16:14 +0100 Subject: [PATCH] Refactor Velux cover class (#162984) --- homeassistant/components/velux/cover.py | 101 +++++++++++++----------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/velux/cover.py b/homeassistant/components/velux/cover.py index e287e795390..e56fc2e54d2 100644 --- a/homeassistant/components/velux/cover.py +++ b/homeassistant/components/velux/cover.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, cast +from typing import Any from pyvlx import ( Awning, @@ -12,6 +12,7 @@ from pyvlx import ( OpeningDevice, Position, RollerShutter, + Window, ) from homeassistant.components.cover import ( @@ -37,64 +38,53 @@ async def async_setup_entry( ) -> None: """Set up cover(s) for Velux platform.""" pyvlx = config_entry.runtime_data - async_add_entities( - VeluxCover(node, config_entry.entry_id) - for node in pyvlx.nodes - if isinstance(node, OpeningDevice) - ) + + entities: list[VeluxCover] = [] + for node in pyvlx.nodes: + if isinstance(node, Blind): + entities.append(VeluxBlind(node, config_entry.entry_id)) + elif isinstance(node, OpeningDevice): + entities.append(VeluxCover(node, config_entry.entry_id)) + + async_add_entities(entities) class VeluxCover(VeluxEntity, CoverEntity): """Representation of a Velux cover.""" - _is_blind = False node: OpeningDevice # Do not name the "main" feature of the device (position control) _attr_name = None + # Features common to all covers + _attr_supported_features = ( + CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.SET_POSITION + | CoverEntityFeature.STOP + ) + def __init__(self, node: OpeningDevice, config_entry_id: str) -> None: """Initialize VeluxCover.""" super().__init__(node, config_entry_id) - # Features common to all covers - self._attr_supported_features = ( - CoverEntityFeature.OPEN - | CoverEntityFeature.CLOSE - | CoverEntityFeature.SET_POSITION - | CoverEntityFeature.STOP - ) - # Window is the default device class for covers - self._attr_device_class = CoverDeviceClass.WINDOW - if isinstance(node, Awning): - self._attr_device_class = CoverDeviceClass.AWNING - if isinstance(node, GarageDoor): - self._attr_device_class = CoverDeviceClass.GARAGE - if isinstance(node, Gate): - self._attr_device_class = CoverDeviceClass.GATE - if isinstance(node, RollerShutter): - self._attr_device_class = CoverDeviceClass.SHUTTER - if isinstance(node, Blind): - self._attr_device_class = CoverDeviceClass.BLIND - self._is_blind = True - self._attr_supported_features |= ( - CoverEntityFeature.OPEN_TILT - | CoverEntityFeature.CLOSE_TILT - | CoverEntityFeature.SET_TILT_POSITION - | CoverEntityFeature.STOP_TILT - ) + match node: + case Window(): + self._attr_device_class = CoverDeviceClass.WINDOW + case Awning(): + self._attr_device_class = CoverDeviceClass.AWNING + case GarageDoor(): + self._attr_device_class = CoverDeviceClass.GARAGE + case Gate(): + self._attr_device_class = CoverDeviceClass.GATE + case RollerShutter(): + self._attr_device_class = CoverDeviceClass.SHUTTER @property def current_cover_position(self) -> int: """Return the current position of the cover.""" return 100 - self.node.position.position_percent - @property - def current_cover_tilt_position(self) -> int | None: - """Return the current position of the cover.""" - if self._is_blind: - return 100 - cast(Blind, self.node).orientation.position_percent - return None - @property def is_closed(self) -> bool: """Return if the cover is closed.""" @@ -134,26 +124,49 @@ class VeluxCover(VeluxEntity, CoverEntity): """Stop the cover.""" await self.node.stop(wait_for_completion=False) + +class VeluxBlind(VeluxCover): + """Representation of a Velux blind cover.""" + + node: Blind + _attr_device_class = CoverDeviceClass.BLIND + + def __init__(self, node: Blind, config_entry_id: str) -> None: + """Initialize VeluxBlind.""" + super().__init__(node, config_entry_id) + + self._attr_supported_features |= ( + CoverEntityFeature.OPEN_TILT + | CoverEntityFeature.CLOSE_TILT + | CoverEntityFeature.SET_TILT_POSITION + | CoverEntityFeature.STOP_TILT + ) + + @property + def current_cover_tilt_position(self) -> int | None: + """Return the current tilt position of the cover.""" + return 100 - self.node.orientation.position_percent + @wrap_pyvlx_call_exceptions async def async_close_cover_tilt(self, **kwargs: Any) -> None: """Close cover tilt.""" - await cast(Blind, self.node).close_orientation(wait_for_completion=False) + await self.node.close_orientation(wait_for_completion=False) @wrap_pyvlx_call_exceptions async def async_open_cover_tilt(self, **kwargs: Any) -> None: """Open cover tilt.""" - await cast(Blind, self.node).open_orientation(wait_for_completion=False) + await self.node.open_orientation(wait_for_completion=False) @wrap_pyvlx_call_exceptions async def async_stop_cover_tilt(self, **kwargs: Any) -> None: """Stop cover tilt.""" - await cast(Blind, self.node).stop_orientation(wait_for_completion=False) + await self.node.stop_orientation(wait_for_completion=False) @wrap_pyvlx_call_exceptions async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: """Move cover tilt to a specific position.""" position_percent = 100 - kwargs[ATTR_TILT_POSITION] orientation = Position(position_percent=position_percent) - await cast(Blind, self.node).set_orientation( + await self.node.set_orientation( orientation=orientation, wait_for_completion=False )