Home Assistant Unofficial Reference 2024.12.1
cover.py
Go to the documentation of this file.
1 """Cover entities for the Motionblinds Bluetooth integration."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 import logging
7 from typing import Any
8 
9 from motionblindsble.const import MotionBlindType, MotionRunningType
10 from motionblindsble.device import MotionDevice
11 
13  ATTR_POSITION,
14  ATTR_TILT_POSITION,
15  CoverDeviceClass,
16  CoverEntity,
17  CoverEntityDescription,
18  CoverEntityFeature,
19 )
20 from homeassistant.config_entries import ConfigEntry
21 from homeassistant.core import HomeAssistant, callback
22 from homeassistant.helpers.entity_platform import AddEntitiesCallback
23 
24 from .const import CONF_BLIND_TYPE, CONF_MAC_CODE, DOMAIN, ICON_VERTICAL_BLIND
25 from .entity import MotionblindsBLEEntity
26 
27 _LOGGER = logging.getLogger(__name__)
28 
29 
30 @dataclass(frozen=True, kw_only=True)
32  """Entity description of a cover entity with default values."""
33 
34  key: str = CoverDeviceClass.BLIND.value
35  translation_key: str = CoverDeviceClass.BLIND.value
36 
37 
38 SHADE_ENTITY_DESCRIPTION = MotionblindsBLECoverEntityDescription(
39  device_class=CoverDeviceClass.SHADE
40 )
41 BLIND_ENTITY_DESCRIPTION = MotionblindsBLECoverEntityDescription(
42  device_class=CoverDeviceClass.BLIND
43 )
44 CURTAIN_ENTITY_DESCRIPTION = MotionblindsBLECoverEntityDescription(
45  device_class=CoverDeviceClass.CURTAIN
46 )
47 VERTICAL_ENTITY_DESCRIPTION = MotionblindsBLECoverEntityDescription(
48  device_class=CoverDeviceClass.CURTAIN, icon=ICON_VERTICAL_BLIND
49 )
50 
51 BLIND_TYPE_TO_ENTITY_DESCRIPTION: dict[str, MotionblindsBLECoverEntityDescription] = {
52  MotionBlindType.HONEYCOMB.name: SHADE_ENTITY_DESCRIPTION,
53  MotionBlindType.ROMAN.name: SHADE_ENTITY_DESCRIPTION,
54  MotionBlindType.ROLLER.name: SHADE_ENTITY_DESCRIPTION,
55  MotionBlindType.DOUBLE_ROLLER.name: SHADE_ENTITY_DESCRIPTION,
56  MotionBlindType.VENETIAN.name: BLIND_ENTITY_DESCRIPTION,
57  MotionBlindType.VENETIAN_TILT_ONLY.name: BLIND_ENTITY_DESCRIPTION,
58  MotionBlindType.CURTAIN.name: CURTAIN_ENTITY_DESCRIPTION,
59  MotionBlindType.VERTICAL.name: VERTICAL_ENTITY_DESCRIPTION,
60 }
61 
62 
64  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
65 ) -> None:
66  """Set up cover entity based on a config entry."""
67 
68  cover_class: type[MotionblindsBLECoverEntity] = BLIND_TYPE_TO_CLASS[
69  entry.data[CONF_BLIND_TYPE].upper()
70  ]
71  device: MotionDevice = hass.data[DOMAIN][entry.entry_id]
72  entity_description: MotionblindsBLECoverEntityDescription = (
73  BLIND_TYPE_TO_ENTITY_DESCRIPTION[entry.data[CONF_BLIND_TYPE].upper()]
74  )
75  entity: MotionblindsBLECoverEntity = cover_class(device, entry, entity_description)
76 
77  async_add_entities([entity])
78 
79 
81  """Representation of a cover entity."""
82 
83  _attr_is_closed: bool | None = None
84  _attr_name = None
85 
86  async def async_added_to_hass(self) -> None:
87  """Register device callbacks."""
88  _LOGGER.debug(
89  "(%s) Added %s cover entity (%s)",
90  self.entryentry.data[CONF_MAC_CODE],
91  MotionBlindType[self.entryentry.data[CONF_BLIND_TYPE].upper()].value.lower(),
92  BLIND_TYPE_TO_CLASS[self.entryentry.data[CONF_BLIND_TYPE].upper()].__name__,
93  )
94  self.devicedevice.register_running_callback(self.async_update_runningasync_update_running)
95  self.devicedevice.register_position_callback(self.async_update_positionasync_update_position)
96 
97  async def async_stop_cover(self, **kwargs: Any) -> None:
98  """Stop moving the cover entity."""
99  _LOGGER.debug("(%s) Stopping", self.entryentry.data[CONF_MAC_CODE])
100  await self.devicedevice.stop()
101 
102  @callback
104  self, running_type: MotionRunningType | None, write_state: bool = True
105  ) -> None:
106  """Update the running type (e.g. opening/closing) of the cover entity."""
107  if running_type in {None, MotionRunningType.STILL, MotionRunningType.UNKNOWN}:
108  self._attr_is_opening_attr_is_opening = False
109  self._attr_is_closing_attr_is_closing = False
110  else:
111  self._attr_is_opening_attr_is_opening = running_type is MotionRunningType.OPENING
112  self._attr_is_closing_attr_is_closing = running_type is not MotionRunningType.OPENING
113  if running_type is not MotionRunningType.STILL:
114  self._attr_is_closed_attr_is_closed = None
115  if write_state:
116  self.async_write_ha_stateasync_write_ha_state()
117 
118  @callback
120  self,
121  position: int | None,
122  tilt: int | None,
123  ) -> None:
124  """Update the position of the cover entity."""
125  if position is None:
126  self._attr_current_cover_position_attr_current_cover_position = None
127  self._attr_is_closed_attr_is_closed = None
128  else:
129  self._attr_current_cover_position_attr_current_cover_position = 100 - position
130  self._attr_is_closed_attr_is_closed = self._attr_current_cover_position_attr_current_cover_position == 0
131  if tilt is None:
132  self._attr_current_cover_tilt_position_attr_current_cover_tilt_position = None
133  else:
134  self._attr_current_cover_tilt_position_attr_current_cover_tilt_position = 100 - round(100 * tilt / 180)
135  self.async_write_ha_stateasync_write_ha_state()
136 
137 
139  """Representation of a cover entity with position capability."""
140 
141  _attr_supported_features = (
142  CoverEntityFeature.OPEN
143  | CoverEntityFeature.CLOSE
144  | CoverEntityFeature.STOP
145  | CoverEntityFeature.SET_POSITION
146  )
147 
148  async def async_open_cover(self, **kwargs: Any) -> None:
149  """Open the cover entity."""
150  _LOGGER.debug("(%s) Opening", self.entryentry.data[CONF_MAC_CODE])
151  await self.devicedevice.open()
152 
153  async def async_close_cover(self, **kwargs: Any) -> None:
154  """Close the cover entity."""
155  _LOGGER.debug("(%s) Closing", self.entryentry.data[CONF_MAC_CODE])
156  await self.devicedevice.close()
157 
158  async def async_set_cover_position(self, **kwargs: Any) -> None:
159  """Move the cover entity to a specific position."""
160  new_position: int = 100 - int(kwargs[ATTR_POSITION])
161 
162  _LOGGER.debug(
163  "(%s) Setting position to %i",
164  self.entryentry.data[CONF_MAC_CODE],
165  new_position,
166  )
167  await self.devicedevice.position(new_position)
168 
169 
171  """Representation of a cover entity with tilt capability."""
172 
173  _attr_supported_features = (
174  CoverEntityFeature.OPEN_TILT
175  | CoverEntityFeature.CLOSE_TILT
176  | CoverEntityFeature.STOP_TILT
177  | CoverEntityFeature.SET_TILT_POSITION
178  )
179 
180  async def async_open_cover_tilt(self, **kwargs: Any) -> None:
181  """Tilt the cover entity open."""
182  _LOGGER.debug("(%s) Tilt opening", self.entryentry.data[CONF_MAC_CODE])
183  await self.devicedevice.open_tilt()
184 
185  async def async_close_cover_tilt(self, **kwargs: Any) -> None:
186  """Tilt the cover entity closed."""
187  _LOGGER.debug("(%s) Tilt closing", self.entryentry.data[CONF_MAC_CODE])
188  await self.devicedevice.close_tilt()
189 
190  async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
191  """Stop tilting the cover entity."""
192  await self.async_stop_coverasync_stop_coverasync_stop_cover(**kwargs)
193 
194  async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
195  """Tilt the cover entity to a specific position."""
196  new_tilt: int = 100 - int(kwargs[ATTR_TILT_POSITION])
197 
198  _LOGGER.debug(
199  "(%s) Setting tilt position to %i",
200  self.entryentry.data[CONF_MAC_CODE],
201  new_tilt,
202  )
203  await self.devicedevice.tilt(round(180 * new_tilt / 100))
204 
205 
207  """Representation of a cover entity with position & tilt capabilities."""
208 
209  _attr_supported_features = (
210  CoverEntityFeature.OPEN
211  | CoverEntityFeature.CLOSE
212  | CoverEntityFeature.STOP
213  | CoverEntityFeature.SET_POSITION
214  | CoverEntityFeature.OPEN_TILT
215  | CoverEntityFeature.CLOSE_TILT
216  | CoverEntityFeature.STOP_TILT
217  | CoverEntityFeature.SET_TILT_POSITION
218  )
219 
220 
221 BLIND_TYPE_TO_CLASS: dict[str, type[MotionblindsBLECoverEntity]] = {
222  MotionBlindType.ROLLER.name: PositionCover,
223  MotionBlindType.HONEYCOMB.name: PositionCover,
224  MotionBlindType.ROMAN.name: PositionCover,
225  MotionBlindType.VENETIAN.name: PositionTiltCover,
226  MotionBlindType.VENETIAN_TILT_ONLY.name: TiltCover,
227  MotionBlindType.DOUBLE_ROLLER.name: PositionTiltCover,
228  MotionBlindType.CURTAIN.name: PositionCover,
229  MotionBlindType.VERTICAL.name: PositionTiltCover,
230 }
None async_stop_cover(self, **Any kwargs)
Definition: __init__.py:438
None async_update_position(self, int|None position, int|None tilt)
Definition: cover.py:123
None async_update_running(self, MotionRunningType|None running_type, bool write_state=True)
Definition: cover.py:105
None async_set_cover_tilt_position(self, **Any kwargs)
Definition: cover.py:194
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: cover.py:65
None open(self, **Any kwargs)
Definition: lock.py:86