Home Assistant Unofficial Reference 2024.12.1
binary_sensor.py
Go to the documentation of this file.
1 """Support for Tado sensors for each zone."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 import logging
8 from typing import Any
9 
11  BinarySensorDeviceClass,
12  BinarySensorEntity,
13  BinarySensorEntityDescription,
14 )
15 from homeassistant.core import HomeAssistant, callback
16 from homeassistant.helpers.dispatcher import async_dispatcher_connect
17 from homeassistant.helpers.entity_platform import AddEntitiesCallback
18 from homeassistant.helpers.typing import StateType
19 
20 from . import TadoConfigEntry
21 from .const import (
22  SIGNAL_TADO_UPDATE_RECEIVED,
23  TYPE_AIR_CONDITIONING,
24  TYPE_BATTERY,
25  TYPE_HEATING,
26  TYPE_HOT_WATER,
27  TYPE_POWER,
28 )
29 from .entity import TadoDeviceEntity, TadoZoneEntity
30 from .tado_connector import TadoConnector
31 
32 _LOGGER = logging.getLogger(__name__)
33 
34 
35 @dataclass(frozen=True, kw_only=True)
37  """Describes Tado binary sensor entity."""
38 
39  state_fn: Callable[[Any], bool]
40 
41  attributes_fn: Callable[[Any], dict[Any, StateType]] | None = None
42 
43 
44 BATTERY_STATE_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
45  key="battery state",
46  state_fn=lambda data: data["batteryState"] == "LOW",
47  device_class=BinarySensorDeviceClass.BATTERY,
48 )
49 CONNECTION_STATE_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
50  key="connection state",
51  translation_key="connection_state",
52  state_fn=lambda data: data.get("connectionState", {}).get("value", False),
53  device_class=BinarySensorDeviceClass.CONNECTIVITY,
54 )
55 POWER_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
56  key="power",
57  state_fn=lambda data: data.power == "ON",
58  device_class=BinarySensorDeviceClass.POWER,
59 )
60 LINK_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
61  key="link",
62  state_fn=lambda data: data.link == "ONLINE",
63  device_class=BinarySensorDeviceClass.CONNECTIVITY,
64 )
65 OVERLAY_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
66  key="overlay",
67  translation_key="overlay",
68  state_fn=lambda data: data.overlay_active,
69  attributes_fn=lambda data: (
70  {"termination": data.overlay_termination_type} if data.overlay_active else {}
71  ),
72  device_class=BinarySensorDeviceClass.POWER,
73 )
74 OPEN_WINDOW_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
75  key="open window",
76  state_fn=lambda data: bool(data.open_window or data.open_window_detected),
77  attributes_fn=lambda data: data.open_window_attr,
78  device_class=BinarySensorDeviceClass.WINDOW,
79 )
80 EARLY_START_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
81  key="early start",
82  translation_key="early_start",
83  state_fn=lambda data: data.preparation,
84  device_class=BinarySensorDeviceClass.POWER,
85 )
86 
87 DEVICE_SENSORS = {
88  TYPE_BATTERY: [
89  BATTERY_STATE_ENTITY_DESCRIPTION,
90  CONNECTION_STATE_ENTITY_DESCRIPTION,
91  ],
92  TYPE_POWER: [
93  CONNECTION_STATE_ENTITY_DESCRIPTION,
94  ],
95 }
96 
97 ZONE_SENSORS = {
98  TYPE_HEATING: [
99  POWER_ENTITY_DESCRIPTION,
100  LINK_ENTITY_DESCRIPTION,
101  OVERLAY_ENTITY_DESCRIPTION,
102  OPEN_WINDOW_ENTITY_DESCRIPTION,
103  EARLY_START_ENTITY_DESCRIPTION,
104  ],
105  TYPE_AIR_CONDITIONING: [
106  POWER_ENTITY_DESCRIPTION,
107  LINK_ENTITY_DESCRIPTION,
108  OVERLAY_ENTITY_DESCRIPTION,
109  OPEN_WINDOW_ENTITY_DESCRIPTION,
110  ],
111  TYPE_HOT_WATER: [
112  POWER_ENTITY_DESCRIPTION,
113  LINK_ENTITY_DESCRIPTION,
114  OVERLAY_ENTITY_DESCRIPTION,
115  ],
116 }
117 
118 
120  hass: HomeAssistant, entry: TadoConfigEntry, async_add_entities: AddEntitiesCallback
121 ) -> None:
122  """Set up the Tado sensor platform."""
123 
124  tado = entry.runtime_data
125  devices = tado.devices
126  zones = tado.zones
127  entities: list[BinarySensorEntity] = []
128 
129  # Create device sensors
130  for device in devices:
131  if "batteryState" in device:
132  device_type = TYPE_BATTERY
133  else:
134  device_type = TYPE_POWER
135 
136  entities.extend(
137  [
138  TadoDeviceBinarySensor(tado, device, entity_description)
139  for entity_description in DEVICE_SENSORS[device_type]
140  ]
141  )
142 
143  # Create zone sensors
144  for zone in zones:
145  zone_type = zone["type"]
146  if zone_type not in ZONE_SENSORS:
147  _LOGGER.warning("Unknown zone type skipped: %s", zone_type)
148  continue
149 
150  entities.extend(
151  [
152  TadoZoneBinarySensor(tado, zone["name"], zone["id"], entity_description)
153  for entity_description in ZONE_SENSORS[zone_type]
154  ]
155  )
156 
157  async_add_entities(entities, True)
158 
159 
161  """Representation of a tado Sensor."""
162 
163  entity_description: TadoBinarySensorEntityDescription
164 
165  def __init__(
166  self,
167  tado: TadoConnector,
168  device_info: dict[str, Any],
169  entity_description: TadoBinarySensorEntityDescription,
170  ) -> None:
171  """Initialize of the Tado Sensor."""
172  self.entity_descriptionentity_description = entity_description
173  self._tado_tado = tado
174  super().__init__(device_info)
175 
176  self._attr_unique_id_attr_unique_id = (
177  f"{entity_description.key} {self.device_id} {tado.home_id}"
178  )
179 
180  async def async_added_to_hass(self) -> None:
181  """Register for sensor updates."""
182  self.async_on_removeasync_on_remove(
184  self.hasshass,
185  SIGNAL_TADO_UPDATE_RECEIVED.format(
186  self._tado_tado.home_id, "device", self.device_iddevice_id
187  ),
188  self._async_update_callback_async_update_callback,
189  )
190  )
191  self._async_update_device_data_async_update_device_data()
192 
193  @callback
194  def _async_update_callback(self) -> None:
195  """Update and write state."""
196  self._async_update_device_data_async_update_device_data()
197  self.async_write_ha_stateasync_write_ha_state()
198 
199  @callback
200  def _async_update_device_data(self) -> None:
201  """Handle update callbacks."""
202  try:
203  self._device_info_device_info_device_info = self._tado_tado.data["device"][self.device_iddevice_id]
204  except KeyError:
205  return
206 
207  self._attr_is_on_attr_is_on = self.entity_descriptionentity_description.state_fn(self._device_info_device_info_device_info)
208  if self.entity_descriptionentity_description.attributes_fn is not None:
209  self._attr_extra_state_attributes_attr_extra_state_attributes = self.entity_descriptionentity_description.attributes_fn(
210  self._device_info_device_info_device_info
211  )
212 
213 
215  """Representation of a tado Sensor."""
216 
217  entity_description: TadoBinarySensorEntityDescription
218 
219  def __init__(
220  self,
221  tado: TadoConnector,
222  zone_name: str,
223  zone_id: int,
224  entity_description: TadoBinarySensorEntityDescription,
225  ) -> None:
226  """Initialize of the Tado Sensor."""
227  self.entity_descriptionentity_description = entity_description
228  self._tado_tado = tado
229  super().__init__(zone_name, tado.home_id, zone_id)
230 
231  self._attr_unique_id_attr_unique_id = f"{entity_description.key} {zone_id} {tado.home_id}"
232 
233  async def async_added_to_hass(self) -> None:
234  """Register for sensor updates."""
235  self.async_on_removeasync_on_remove(
237  self.hasshass,
238  SIGNAL_TADO_UPDATE_RECEIVED.format(
239  self._tado_tado.home_id, "zone", self.zone_idzone_id
240  ),
241  self._async_update_callback_async_update_callback,
242  )
243  )
244  self._async_update_zone_data_async_update_zone_data()
245 
246  @callback
247  def _async_update_callback(self) -> None:
248  """Update and write state."""
249  self._async_update_zone_data_async_update_zone_data()
250  self.async_write_ha_stateasync_write_ha_state()
251 
252  @callback
253  def _async_update_zone_data(self) -> None:
254  """Handle update callbacks."""
255  try:
256  tado_zone_data = self._tado_tado.data["zone"][self.zone_idzone_id]
257  except KeyError:
258  return
259 
260  self._attr_is_on_attr_is_on = self.entity_descriptionentity_description.state_fn(tado_zone_data)
261  if self.entity_descriptionentity_description.attributes_fn is not None:
262  self._attr_extra_state_attributes_attr_extra_state_attributes = self.entity_descriptionentity_description.attributes_fn(
263  tado_zone_data
264  )
None __init__(self, TadoConnector tado, dict[str, Any] device_info, TadoBinarySensorEntityDescription entity_description)
None __init__(self, TadoConnector tado, str zone_name, int zone_id, TadoBinarySensorEntityDescription entity_description)
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, TadoConfigEntry entry, AddEntitiesCallback async_add_entities)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103