"""Support for covers connected with WMS WebControl pro.""" from datetime import timedelta from typing import Any, override from wmspro.const import ( WMS_WebControl_pro_API_actionDescription as ACTION_DESC, WMS_WebControl_pro_API_actionType, WMS_WebControl_pro_API_responseType, ) from homeassistant.components.cover import ( ATTR_POSITION, ATTR_TILT_POSITION, CoverDeviceClass, CoverEntity, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util.percentage import ( percentage_to_ranged_value, ranged_value_to_percentage, ) from . import WebControlProConfigEntry from .entity import WebControlProGenericEntity SCAN_INTERVAL = timedelta(seconds=10) PARALLEL_UPDATES = 1 async def async_setup_entry( hass: HomeAssistant, config_entry: WebControlProConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the WMS based covers from a config entry.""" hub = config_entry.runtime_data entities: list[WebControlProGenericEntity] = [] for dest in hub.dests.values(): if dest.hasAction(ACTION_DESC.AwningDrive): entities.append(WebControlProAwning(hass, config_entry.entry_id, dest)) if dest.hasAction(ACTION_DESC.ValanceDrive): entities.append(WebControlProValance(hass, config_entry.entry_id, dest)) elif dest.hasAction(ACTION_DESC.RollerShutterBlindDrive): entities.append( WebControlProRollerShutter(hass, config_entry.entry_id, dest) ) elif dest.hasAction(ACTION_DESC.SlatDrive): if dest.hasAction(ACTION_DESC.SlatRotate): entities.append( WebControlProSlatRotate(hass, config_entry.entry_id, dest) ) else: entities.append(WebControlProSlat(hass, config_entry.entry_id, dest)) async_add_entities(entities) class WebControlProCover(WebControlProGenericEntity, CoverEntity): """Base representation of a WMS based cover.""" _drive_action_desc: ACTION_DESC _attr_name = None @property @override def current_cover_position(self) -> int | None: """Return current position of cover.""" action = self._dest.action(self._drive_action_desc) if action["percentage"] is None: return None return 100 - action["percentage"] @override async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" action = self._dest.action(self._drive_action_desc) await action(percentage=100 - kwargs[ATTR_POSITION]) @property @override def is_closed(self) -> bool | None: """Return if the cover is closed.""" return self.current_cover_position == 0 @override async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" action = self._dest.action(self._drive_action_desc) await action(percentage=0) @override async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover.""" action = self._dest.action(self._drive_action_desc) await action(percentage=100) @override async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the device if in motion.""" action = self._dest.action( ACTION_DESC.ManualCommand, WMS_WebControl_pro_API_actionType.Stop, ) await action(responseType=WMS_WebControl_pro_API_responseType.Detailed) class WebControlProAwning(WebControlProCover): """Representation of a WMS based awning.""" _attr_device_class = CoverDeviceClass.AWNING _drive_action_desc = ACTION_DESC.AwningDrive class WebControlProValance(WebControlProCover): """Representation of a WMS based valance.""" _attr_device_class = CoverDeviceClass.SHADE _attr_translation_key = "valance" _drive_action_desc = ACTION_DESC.ValanceDrive class WebControlProRollerShutter(WebControlProCover): """Representation of a WMS based roller shutter or blind.""" _attr_device_class = CoverDeviceClass.SHUTTER _drive_action_desc = ACTION_DESC.RollerShutterBlindDrive class WebControlProSlat(WebControlProCover): """Representation of a WMS based blind using a slat drive.""" _attr_device_class = CoverDeviceClass.BLIND _drive_action_desc = ACTION_DESC.SlatDrive class WebControlProSlatRotate(WebControlProSlat): """Representation of a WMS based blind which supports tilting.""" _tilt_action_desc = ACTION_DESC.SlatRotate @override async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover and tilt to minimum like the WMS WebControl pro.""" action_drive = self._dest.action(self._drive_action_desc) action_list = action_drive.prep(percentage=0) action_tilt = self._dest.action(self._tilt_action_desc) action_list += action_tilt.prep(rotation=action_tilt.minValue) await action_list() @override async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover and tilt to maximum like the WMS WebControl pro.""" action_drive = self._dest.action(self._drive_action_desc) action_list = action_drive.prep(percentage=100) action_tilt = self._dest.action(self._tilt_action_desc) action_list += action_tilt.prep(rotation=action_tilt.maxValue) await action_list() @override async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position and tilt for open/close.""" target_position = kwargs[ATTR_POSITION] if target_position == 0: await self.async_close_cover() elif target_position == 100: await self.async_open_cover() else: await super().async_set_cover_position(**kwargs) @property @override def current_cover_tilt_position(self) -> int | None: """Return current position of cover tilt.""" action = self._dest.action(self._tilt_action_desc) if action["rotation"] is None: return None return 100 - ranged_value_to_percentage( (action.minValue, action.maxValue), action["rotation"], ) @override async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: """Set the cover tilt position.""" action = self._dest.action(self._tilt_action_desc) rotation = percentage_to_ranged_value( (action.minValue, action.maxValue), 100 - kwargs[ATTR_TILT_POSITION], ) await action(rotation=rotation) @override async def async_open_cover_tilt(self, **kwargs: Any) -> None: """Open the cover tilt.""" action = self._dest.action(self._tilt_action_desc) # 0 is the open position for the tilt in WMS WebControl pro, # with the open position the slat is parallel to the ground. # This position will let the most light through and # is required to fully store the cover in the box. await action(rotation=0) @override async def async_close_cover_tilt(self, **kwargs: Any) -> None: """Close the cover tilt.""" action = self._dest.action(self._tilt_action_desc) # maxValue is the close position for the tilt in WMS WebControl pro, # with the close position the slat is perpendicular to the ground. # This position will block the light best. await action(rotation=action.maxValue) async def async_set_cover_position_and_tilt(self, **kwargs: Any) -> None: """Handle the service action call to set cover position and tilt.""" action_drive = self._dest.action(self._drive_action_desc) action_list = action_drive.prep(percentage=100 - kwargs[ATTR_POSITION]) action_tilt = self._dest.action(self._tilt_action_desc) rotation = percentage_to_ranged_value( (action_tilt.minValue, action_tilt.maxValue), 100 - kwargs[ATTR_TILT_POSITION], ) action_list += action_tilt.prep(rotation=rotation) await action_list()