Home Assistant Unofficial Reference 2024.12.1
binary_sensor.py
Go to the documentation of this file.
1 """Platform for binary sensor integration."""
2 
3 from __future__ import annotations
4 
5 from smarttub import SpaError, SpaReminder
6 import voluptuous as vol
7 
9  BinarySensorDeviceClass,
10  BinarySensorEntity,
11 )
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers import entity_platform
15 from homeassistant.helpers.entity_platform import AddEntitiesCallback
16 from homeassistant.helpers.typing import VolDictType
17 
18 from .const import ATTR_ERRORS, ATTR_REMINDERS, DOMAIN, SMARTTUB_CONTROLLER
19 from .entity import SmartTubEntity, SmartTubSensorBase
20 
21 # whether the reminder has been snoozed (bool)
22 ATTR_REMINDER_SNOOZED = "snoozed"
23 
24 ATTR_ERROR_CODE = "error_code"
25 ATTR_ERROR_TITLE = "error_title"
26 ATTR_ERROR_DESCRIPTION = "error_description"
27 ATTR_ERROR_TYPE = "error_type"
28 ATTR_CREATED_AT = "created_at"
29 ATTR_UPDATED_AT = "updated_at"
30 
31 # how many days to snooze the reminder for
32 ATTR_REMINDER_DAYS = "days"
33 RESET_REMINDER_SCHEMA: VolDictType = {
34  vol.Required(ATTR_REMINDER_DAYS): vol.All(
35  vol.Coerce(int), vol.Range(min=30, max=365)
36  )
37 }
38 SNOOZE_REMINDER_SCHEMA: VolDictType = {
39  vol.Required(ATTR_REMINDER_DAYS): vol.All(
40  vol.Coerce(int), vol.Range(min=10, max=120)
41  )
42 }
43 
44 
46  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
47 ) -> None:
48  """Set up binary sensor entities for the binary sensors in the tub."""
49 
50  controller = hass.data[DOMAIN][entry.entry_id][SMARTTUB_CONTROLLER]
51 
52  entities: list[BinarySensorEntity] = []
53  for spa in controller.spas:
54  entities.append(SmartTubOnline(controller.coordinator, spa))
55  entities.append(SmartTubError(controller.coordinator, spa))
56  entities.extend(
57  SmartTubReminder(controller.coordinator, spa, reminder)
58  for reminder in controller.coordinator.data[spa.id][ATTR_REMINDERS].values()
59  )
60 
61  async_add_entities(entities)
62 
63  platform = entity_platform.async_get_current_platform()
64 
65  platform.async_register_entity_service(
66  "snooze_reminder",
67  SNOOZE_REMINDER_SCHEMA,
68  "async_snooze",
69  )
70  platform.async_register_entity_service(
71  "reset_reminder",
72  RESET_REMINDER_SCHEMA,
73  "async_reset",
74  )
75 
76 
78  """A binary sensor indicating whether the spa is currently online (connected to the cloud)."""
79 
80  _attr_device_class = BinarySensorDeviceClass.CONNECTIVITY
81  # This seems to be very noisy and not generally useful, so disable by default.
82  _attr_entity_registry_enabled_default = False
83 
84  def __init__(self, coordinator, spa):
85  """Initialize the entity."""
86  super().__init__(coordinator, spa, "Online", "online")
87 
88  @property
89  def is_on(self) -> bool:
90  """Return true if the binary sensor is on."""
91  return self._state_state is True
92 
93 
95  """Reminders for maintenance actions."""
96 
97  _attr_device_class = BinarySensorDeviceClass.PROBLEM
98 
99  def __init__(self, coordinator, spa, reminder):
100  """Initialize the entity."""
101  super().__init__(
102  coordinator,
103  spa,
104  f"{reminder.name.title()} Reminder",
105  )
106  self.reminder_idreminder_id = reminder.id
107  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{spa.id}-reminder-{reminder.id}"
108 
109  @property
110  def reminder(self) -> SpaReminder:
111  """Return the underlying SpaReminder object for this entity."""
112  return self.coordinator.data[self.spaspa.id][ATTR_REMINDERS][self.reminder_idreminder_id]
113 
114  @property
115  def is_on(self) -> bool:
116  """Return whether the specified maintenance action needs to be taken."""
117  return self.reminderreminder.remaining_days == 0
118 
119  @property
121  """Return the state attributes."""
122  return {
123  ATTR_REMINDER_SNOOZED: self.reminderreminder.snoozed,
124  ATTR_REMINDER_DAYS: self.reminderreminder.remaining_days,
125  }
126 
127  async def async_snooze(self, days):
128  """Snooze this reminder for the specified number of days."""
129  await self.reminderreminder.snooze(days)
130  await self.coordinator.async_request_refresh()
131 
132  async def async_reset(self, days):
133  """Dismiss this reminder, and reset it to the specified number of days."""
134  await self.reminderreminder.reset(days)
135  await self.coordinator.async_request_refresh()
136 
137 
139  """Indicates whether an error code is present.
140 
141  There may be 0 or more errors. If there are >0, we show the first one.
142  """
143 
144  _attr_device_class = BinarySensorDeviceClass.PROBLEM
145 
146  def __init__(self, coordinator, spa):
147  """Initialize the entity."""
148  super().__init__(
149  coordinator,
150  spa,
151  "Error",
152  )
153 
154  @property
155  def error(self) -> SpaError | None:
156  """Return the underlying SpaError object for this entity."""
157  errors = self.coordinator.data[self.spaspa.id][ATTR_ERRORS]
158  if len(errors) == 0:
159  return None
160  return errors[0]
161 
162  @property
163  def is_on(self) -> bool:
164  """Return true if an error is signaled."""
165  return self.errorerror is not None
166 
167  @property
169  """Return the state attributes."""
170  if (error := self.errorerror) is None:
171  return {}
172 
173  return {
174  ATTR_ERROR_CODE: error.code,
175  ATTR_ERROR_TITLE: error.title,
176  ATTR_ERROR_DESCRIPTION: error.description,
177  ATTR_ERROR_TYPE: error.error_type,
178  ATTR_CREATED_AT: error.created_at.isoformat(),
179  ATTR_UPDATED_AT: error.updated_at.isoformat(),
180  }
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)