Home Assistant Unofficial Reference 2024.12.1
cover.py
Go to the documentation of this file.
1 """Support for Brunt Blind Engine covers."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from aiohttp.client_exceptions import ClientResponseError
8 from brunt import Thing
9 
11  ATTR_POSITION,
12  CoverDeviceClass,
13  CoverEntity,
14  CoverEntityFeature,
15 )
16 from homeassistant.core import HomeAssistant, callback
17 from homeassistant.exceptions import HomeAssistantError
18 from homeassistant.helpers.device_registry import DeviceInfo
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 from homeassistant.helpers.update_coordinator import CoordinatorEntity
21 
22 from .const import (
23  ATTR_REQUEST_POSITION,
24  ATTRIBUTION,
25  CLOSED_POSITION,
26  DOMAIN,
27  FAST_INTERVAL,
28  OPEN_POSITION,
29  REGULAR_INTERVAL,
30 )
31 from .coordinator import BruntConfigEntry, BruntCoordinator
32 
33 
35  hass: HomeAssistant,
36  entry: BruntConfigEntry,
37  async_add_entities: AddEntitiesCallback,
38 ) -> None:
39  """Set up the brunt platform."""
40  coordinator = entry.runtime_data
41 
43  BruntDevice(coordinator, serial, thing, entry.entry_id)
44  for serial, thing in coordinator.data.items()
45  )
46 
47 
48 class BruntDevice(CoordinatorEntity[BruntCoordinator], CoverEntity):
49  """Representation of a Brunt cover device.
50 
51  Contains the common logic for all Brunt devices.
52  """
53 
54  _attr_has_entity_name = True
55  _attr_name = None
56  _attr_device_class = CoverDeviceClass.BLIND
57  _attr_attribution = ATTRIBUTION
58  _attr_supported_features = (
59  CoverEntityFeature.OPEN
60  | CoverEntityFeature.CLOSE
61  | CoverEntityFeature.SET_POSITION
62  )
63 
64  def __init__(
65  self,
66  coordinator: BruntCoordinator,
67  serial: str | None,
68  thing: Thing,
69  entry_id: str,
70  ) -> None:
71  """Init the Brunt device."""
72  super().__init__(coordinator)
73  self._attr_unique_id_attr_unique_id = serial
74  self._thing_thing = thing
75  self._entry_id_entry_id = entry_id
76 
77  self._remove_update_listener_remove_update_listener = None
78 
79  self._attr_device_info_attr_device_info = DeviceInfo(
80  identifiers={(DOMAIN, self._attr_unique_id_attr_unique_id)}, # type: ignore[arg-type]
81  name=self._thing_thing.name,
82  via_device=(DOMAIN, self._entry_id_entry_id),
83  manufacturer="Brunt",
84  sw_version=self._thing_thing.fw_version,
85  model=self._thing_thing.model,
86  )
87 
88  async def async_added_to_hass(self) -> None:
89  """When entity is added to hass."""
90  await super().async_added_to_hass()
91  self.async_on_removeasync_on_remove(
92  self.coordinator.async_add_listener(self._brunt_update_listener_brunt_update_listener)
93  )
94 
95  @property
96  def current_cover_position(self) -> int | None:
97  """Return current position of cover.
98 
99  None is unknown, 0 is closed, 100 is fully open.
100  """
101  return self.coordinator.data[self.unique_idunique_id].current_position
102 
103  @property
104  def request_cover_position(self) -> int | None:
105  """Return request position of cover.
106 
107  The request position is the position of the last request
108  to Brunt, at times there is a diff of 1 to current
109  None is unknown, 0 is closed, 100 is fully open.
110  """
111  return self.coordinator.data[self.unique_idunique_id].request_position
112 
113  @property
114  def move_state(self) -> int | None:
115  """Return current moving state of cover.
116 
117  None is unknown, 0 when stopped, 1 when opening, 2 when closing
118  """
119  return self.coordinator.data[self.unique_idunique_id].move_state
120 
121  @property
122  def is_opening(self) -> bool:
123  """Return if the cover is opening or not."""
124  return self.move_statemove_statemove_state == 1
125 
126  @property
127  def is_closing(self) -> bool:
128  """Return if the cover is closing or not."""
129  return self.move_statemove_statemove_state == 2
130 
131  @property
132  def extra_state_attributes(self) -> dict[str, Any]:
133  """Return the detailed device state attributes."""
134  return {
135  ATTR_REQUEST_POSITION: self.request_cover_positionrequest_cover_position,
136  }
137 
138  @property
139  def is_closed(self) -> bool:
140  """Return true if cover is closed, else False."""
142 
143  async def async_open_cover(self, **kwargs: Any) -> None:
144  """Set the cover to the open position."""
145  await self._async_update_cover_async_update_cover(OPEN_POSITION)
146 
147  async def async_close_cover(self, **kwargs: Any) -> None:
148  """Set the cover to the closed position."""
149  await self._async_update_cover_async_update_cover(CLOSED_POSITION)
150 
151  async def async_set_cover_position(self, **kwargs: Any) -> None:
152  """Set the cover to a specific position."""
153  await self._async_update_cover_async_update_cover(int(kwargs[ATTR_POSITION]))
154 
155  async def _async_update_cover(self, position: int) -> None:
156  """Set the cover to the new position and wait for the update to be reflected."""
157  try:
158  await self.coordinator.bapi.async_change_request_position(
159  position, thing_uri=self._thing_thing.thing_uri
160  )
161  except ClientResponseError as exc:
162  raise HomeAssistantError(
163  f"Unable to reposition {self._thing.name}"
164  ) from exc
165  self.coordinator.update_interval = FAST_INTERVAL
166  await self.coordinator.async_request_refresh()
167 
168  @callback
169  def _brunt_update_listener(self) -> None:
170  """Update the update interval after each refresh."""
171  if (
172  self.request_cover_positionrequest_cover_position
173  == self.coordinator.bapi.last_requested_positions[self._thing_thing.thing_uri]
174  and self.move_statemove_statemove_state == 0
175  ):
176  self.coordinator.update_interval = REGULAR_INTERVAL
177  else:
178  self.coordinator.update_interval = FAST_INTERVAL
None async_set_cover_position(self, **Any kwargs)
Definition: cover.py:151
None __init__(self, BruntCoordinator coordinator, str|None serial, Thing thing, str entry_id)
Definition: cover.py:70
None async_open_cover(self, **Any kwargs)
Definition: cover.py:143
None async_close_cover(self, **Any kwargs)
Definition: cover.py:147
None _async_update_cover(self, int position)
Definition: cover.py:155
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
Callable[[], None] async_add_listener(self, CALLBACK_TYPE update_callback, Any context=None)
None async_setup_entry(HomeAssistant hass, BruntConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: cover.py:38