Home Assistant Unofficial Reference 2024.12.1
switch.py
Go to the documentation of this file.
1 """Integration with the Rachio Iro sprinkler system controller."""
2 
3 from abc import abstractmethod
4 from contextlib import suppress
5 from datetime import timedelta
6 import logging
7 from typing import Any
8 
9 import voluptuous as vol
10 
11 from homeassistant.components.switch import SwitchEntity
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import ATTR_ENTITY_ID, ATTR_ID
14 from homeassistant.core import CALLBACK_TYPE, HomeAssistant, ServiceCall, callback
15 from homeassistant.exceptions import HomeAssistantError
16 from homeassistant.helpers import config_validation as cv, entity_platform
17 from homeassistant.helpers.dispatcher import async_dispatcher_connect
18 from homeassistant.helpers.entity import Entity
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 from homeassistant.helpers.event import async_track_point_in_utc_time
21 from homeassistant.util.dt import as_timestamp, now, parse_datetime, utc_from_timestamp
22 
23 from .const import (
24  CONF_MANUAL_RUN_MINS,
25  DEFAULT_MANUAL_RUN_MINS,
26  DOMAIN as DOMAIN_RACHIO,
27  KEY_CURRENT_STATUS,
28  KEY_CUSTOM_CROP,
29  KEY_CUSTOM_SHADE,
30  KEY_CUSTOM_SLOPE,
31  KEY_DEVICE_ID,
32  KEY_DURATION,
33  KEY_ENABLED,
34  KEY_ID,
35  KEY_IMAGE_URL,
36  KEY_NAME,
37  KEY_ON,
38  KEY_RAIN_DELAY,
39  KEY_RAIN_DELAY_END,
40  KEY_REPORTED_STATE,
41  KEY_SCHEDULE_ID,
42  KEY_STATE,
43  KEY_SUBTYPE,
44  KEY_SUMMARY,
45  KEY_TYPE,
46  KEY_ZONE_ID,
47  KEY_ZONE_NUMBER,
48  SCHEDULE_TYPE_FIXED,
49  SCHEDULE_TYPE_FLEX,
50  SERVICE_SET_ZONE_MOISTURE,
51  SERVICE_START_MULTIPLE_ZONES,
52  SERVICE_START_WATERING,
53  SIGNAL_RACHIO_CONTROLLER_UPDATE,
54  SIGNAL_RACHIO_RAIN_DELAY_UPDATE,
55  SIGNAL_RACHIO_SCHEDULE_UPDATE,
56  SIGNAL_RACHIO_ZONE_UPDATE,
57  SLOPE_FLAT,
58  SLOPE_MODERATE,
59  SLOPE_SLIGHT,
60  SLOPE_STEEP,
61 )
62 from .device import RachioPerson
63 from .entity import RachioDevice, RachioHoseTimerEntity
64 from .webhooks import (
65  SUBTYPE_RAIN_DELAY_OFF,
66  SUBTYPE_RAIN_DELAY_ON,
67  SUBTYPE_SCHEDULE_COMPLETED,
68  SUBTYPE_SCHEDULE_STARTED,
69  SUBTYPE_SCHEDULE_STOPPED,
70  SUBTYPE_SLEEP_MODE_OFF,
71  SUBTYPE_SLEEP_MODE_ON,
72  SUBTYPE_ZONE_COMPLETED,
73  SUBTYPE_ZONE_PAUSED,
74  SUBTYPE_ZONE_STARTED,
75  SUBTYPE_ZONE_STOPPED,
76 )
77 
78 _LOGGER = logging.getLogger(__name__)
79 
80 ATTR_DURATION = "duration"
81 ATTR_PERCENT = "percent"
82 ATTR_SCHEDULE_SUMMARY = "Summary"
83 ATTR_SCHEDULE_ENABLED = "Enabled"
84 ATTR_SCHEDULE_DURATION = "Duration"
85 ATTR_SCHEDULE_TYPE = "Type"
86 ATTR_SORT_ORDER = "sortOrder"
87 ATTR_WATERING_DURATION = "Watering Duration seconds"
88 ATTR_ZONE_NUMBER = "Zone number"
89 ATTR_ZONE_SHADE = "Shade"
90 ATTR_ZONE_SLOPE = "Slope"
91 ATTR_ZONE_SUMMARY = "Summary"
92 ATTR_ZONE_TYPE = "Type"
93 
94 START_MULTIPLE_ZONES_SCHEMA = vol.Schema(
95  {
96  vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
97  vol.Required(ATTR_DURATION): cv.ensure_list_csv,
98  }
99 )
100 
101 
103  hass: HomeAssistant,
104  config_entry: ConfigEntry,
105  async_add_entities: AddEntitiesCallback,
106 ) -> None:
107  """Set up the Rachio switches."""
108  zone_entities = []
109  has_flex_sched = False
110  entities = await hass.async_add_executor_job(_create_entities, hass, config_entry)
111  for entity in entities:
112  if isinstance(entity, RachioZone):
113  zone_entities.append(entity)
114  if isinstance(entity, RachioSchedule) and entity.type == SCHEDULE_TYPE_FLEX:
115  has_flex_sched = True
116 
117  async_add_entities(entities)
118 
119  def start_multiple(service: ServiceCall) -> None:
120  """Service to start multiple zones in sequence."""
121  zones_list = []
122  person = hass.data[DOMAIN_RACHIO][config_entry.entry_id]
123  entity_id = service.data[ATTR_ENTITY_ID]
124  duration = iter(service.data[ATTR_DURATION])
125  default_time = service.data[ATTR_DURATION][0]
126  entity_to_zone_id = {
127  entity.entity_id: entity.zone_id for entity in zone_entities
128  }
129 
130  for count, data in enumerate(entity_id):
131  if data in entity_to_zone_id:
132  # Time can be passed as a list per zone,
133  # or one time for all zones
134  time = int(next(duration, default_time)) * 60
135  zones_list.append(
136  {
137  ATTR_ID: entity_to_zone_id.get(data),
138  ATTR_DURATION: time,
139  ATTR_SORT_ORDER: count,
140  }
141  )
142 
143  if len(zones_list) != 0:
144  person.start_multiple_zones(zones_list)
145  _LOGGER.debug("Starting zone(s) %s", entity_id)
146  else:
147  raise HomeAssistantError("No matching zones found in given entity_ids")
148 
149  platform = entity_platform.async_get_current_platform()
150  platform.async_register_entity_service(
151  SERVICE_START_WATERING,
152  {
153  vol.Optional(ATTR_DURATION): cv.positive_int,
154  },
155  "turn_on",
156  )
157 
158  # If only hose timers on account, none of these services apply
159  if not zone_entities:
160  return
161 
162  hass.services.async_register(
163  DOMAIN_RACHIO,
164  SERVICE_START_MULTIPLE_ZONES,
165  start_multiple,
166  schema=START_MULTIPLE_ZONES_SCHEMA,
167  )
168 
169  if has_flex_sched:
170  platform = entity_platform.async_get_current_platform()
171  platform.async_register_entity_service(
172  SERVICE_SET_ZONE_MOISTURE,
173  {vol.Required(ATTR_PERCENT): cv.positive_int},
174  "set_moisture_percent",
175  )
176 
177 
178 def _create_entities(hass: HomeAssistant, config_entry: ConfigEntry) -> list[Entity]:
179  entities: list[Entity] = []
180  person: RachioPerson = hass.data[DOMAIN_RACHIO][config_entry.entry_id]
181  # Fetch the schedule once at startup
182  # in order to avoid every zone doing it
183  for controller in person.controllers:
184  entities.append(RachioStandbySwitch(controller))
185  entities.append(RachioRainDelay(controller))
186  zones = controller.list_zones()
187  schedules = controller.list_schedules()
188  flex_schedules = controller.list_flex_schedules()
189  current_schedule = controller.current_schedule
190  entities.extend(
191  RachioZone(person, controller, zone, current_schedule) for zone in zones
192  )
193  entities.extend(
194  RachioSchedule(person, controller, schedule, current_schedule)
195  for schedule in schedules + flex_schedules
196  )
197  entities.extend(
198  RachioValve(person, base_station, valve, base_station.status_coordinator)
199  for base_station in person.base_stations
200  for valve in base_station.status_coordinator.data.values()
201  )
202  return entities
203 
204 
206  """Represent a Rachio state that can be toggled."""
207 
208  @callback
209  def _async_handle_any_update(self, *args, **kwargs) -> None:
210  """Determine whether an update event applies to this device."""
211  if args[0][KEY_DEVICE_ID] != self._controller_controller.controller_id:
212  # For another device
213  return
214 
215  # For this device
216  self._async_handle_update_async_handle_update(args, kwargs)
217 
218  @abstractmethod
219  def _async_handle_update(self, *args, **kwargs) -> None:
220  """Handle incoming webhook data."""
221 
222 
223 class RachioStandbySwitch(RachioSwitch):
224  """Representation of a standby status/button."""
225 
226  _attr_has_entity_name = True
227  _attr_translation_key = "standby"
228 
229  @property
230  def unique_id(self) -> str:
231  """Return a unique id by combining controller id and purpose."""
232  return f"{self._controller.controller_id}-standby"
233 
234  @callback
235  def _async_handle_update(self, *args, **kwargs) -> None:
236  """Update the state using webhook data."""
237  if args[0][0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_ON:
238  self._attr_is_on_attr_is_on = True
239  elif args[0][0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_OFF:
240  self._attr_is_on_attr_is_on = False
241 
242  self.async_write_ha_stateasync_write_ha_state()
243 
244  def turn_on(self, **kwargs: Any) -> None:
245  """Put the controller in standby mode."""
246  self._controller_controller.rachio.device.turn_off(self._controller_controller.controller_id)
247 
248  def turn_off(self, **kwargs: Any) -> None:
249  """Resume controller functionality."""
250  self._controller_controller.rachio.device.turn_on(self._controller_controller.controller_id)
251 
252  async def async_added_to_hass(self) -> None:
253  """Subscribe to updates."""
254  if KEY_ON in self._controller_controller.init_data:
255  self._attr_is_on_attr_is_on = not self._controller_controller.init_data[KEY_ON]
256 
257  self.async_on_removeasync_on_remove(
259  self.hasshass,
260  SIGNAL_RACHIO_CONTROLLER_UPDATE,
261  self._async_handle_any_update_async_handle_any_update,
262  )
263  )
264 
265 
267  """Representation of a rain delay status/switch."""
268 
269  _attr_has_entity_name = True
270  _attr_translation_key = "rain_delay"
271 
272  def __init__(self, controller) -> None:
273  """Set up a Rachio rain delay switch."""
274  self._cancel_update_cancel_update: CALLBACK_TYPE | None = None
275  super().__init__(controller)
276 
277  @property
278  def unique_id(self) -> str:
279  """Return a unique id by combining controller id and purpose."""
280  return f"{self._controller.controller_id}-delay"
281 
282  @callback
283  def _async_handle_update(self, *args, **kwargs) -> None:
284  """Update the state using webhook data."""
285  if self._cancel_update_cancel_update:
286  self._cancel_update_cancel_update()
287  self._cancel_update_cancel_update = None
288 
289  if args[0][0][KEY_SUBTYPE] == SUBTYPE_RAIN_DELAY_ON:
290  endtime = parse_datetime(args[0][0][KEY_RAIN_DELAY_END])
291  _LOGGER.debug("Rain delay expires at %s", endtime)
292  self._attr_is_on_attr_is_on = True
293  assert endtime is not None
294  self._cancel_update_cancel_update = async_track_point_in_utc_time(
295  self.hasshass, self._delay_expiration_delay_expiration, endtime
296  )
297  elif args[0][0][KEY_SUBTYPE] == SUBTYPE_RAIN_DELAY_OFF:
298  self._attr_is_on_attr_is_on = False
299 
300  self.async_write_ha_stateasync_write_ha_state()
301 
302  @callback
303  def _delay_expiration(self, *args) -> None:
304  """Trigger when a rain delay expires."""
305  self._attr_is_on_attr_is_on = False
306  self._cancel_update_cancel_update = None
307  self.async_write_ha_stateasync_write_ha_state()
308 
309  def turn_on(self, **kwargs: Any) -> None:
310  """Activate a 24 hour rain delay on the controller."""
311  self._controller_controller.rachio.device.rain_delay(self._controller_controller.controller_id, 86400)
312  _LOGGER.debug("Starting rain delay for 24 hours")
313 
314  def turn_off(self, **kwargs: Any) -> None:
315  """Resume controller functionality."""
316  self._controller_controller.rachio.device.rain_delay(self._controller_controller.controller_id, 0)
317  _LOGGER.debug("Canceling rain delay")
318 
319  async def async_added_to_hass(self) -> None:
320  """Subscribe to updates."""
321  if KEY_RAIN_DELAY in self._controller_controller.init_data:
322  self._attr_is_on_attr_is_on = self._controller_controller.init_data[
323  KEY_RAIN_DELAY
324  ] / 1000 > as_timestamp(now())
325 
326  # If the controller was in a rain delay state during a reboot, this re-sets the timer
327  if self._attr_is_on_attr_is_on is True:
328  delay_end = utc_from_timestamp(
329  self._controller_controller.init_data[KEY_RAIN_DELAY] / 1000
330  )
331  _LOGGER.debug("Re-setting rain delay timer for %s", delay_end)
332  self._cancel_update_cancel_update = async_track_point_in_utc_time(
333  self.hasshass, self._delay_expiration_delay_expiration, delay_end
334  )
335 
336  self.async_on_removeasync_on_remove(
338  self.hasshass,
339  SIGNAL_RACHIO_RAIN_DELAY_UPDATE,
340  self._async_handle_any_update_async_handle_any_update,
341  )
342  )
343 
344 
346  """Representation of one zone of sprinklers connected to the Rachio Iro."""
347 
348  _attr_icon = "mdi:water"
349 
350  def __init__(self, person, controller, data, current_schedule) -> None:
351  """Initialize a new Rachio Zone."""
352  self.idid = data[KEY_ID]
353  self._attr_name_attr_name = data[KEY_NAME]
354  self._zone_number_zone_number = data[KEY_ZONE_NUMBER]
355  self._zone_enabled_zone_enabled = data[KEY_ENABLED]
356  self._attr_entity_picture_attr_entity_picture = data.get(KEY_IMAGE_URL)
357  self._person_person = person
358  self._shade_type_shade_type = data.get(KEY_CUSTOM_SHADE, {}).get(KEY_NAME)
359  self._zone_type_zone_type = data.get(KEY_CUSTOM_CROP, {}).get(KEY_NAME)
360  self._slope_type_slope_type = data.get(KEY_CUSTOM_SLOPE, {}).get(KEY_NAME)
361  self._summary_summary = ""
362  self._current_schedule_current_schedule = current_schedule
363  self._attr_unique_id_attr_unique_id = f"{controller.controller_id}-zone-{self.id}"
364  super().__init__(controller)
365 
366  def __str__(self):
367  """Display the zone as a string."""
368  return f'Rachio Zone "{self.name}" on {self._controller!s}'
369 
370  @property
371  def zone_id(self) -> str:
372  """How the Rachio API refers to the zone."""
373  return self.idid
374 
375  @property
376  def zone_is_enabled(self) -> bool:
377  """Return whether the zone is allowed to run."""
378  return self._zone_enabled_zone_enabled
379 
380  @property
381  def extra_state_attributes(self) -> dict[str, Any]:
382  """Return the optional state attributes."""
383  props = {ATTR_ZONE_NUMBER: self._zone_number_zone_number, ATTR_ZONE_SUMMARY: self._summary_summary}
384  if self._shade_type_shade_type:
385  props[ATTR_ZONE_SHADE] = self._shade_type_shade_type
386  if self._zone_type_zone_type:
387  props[ATTR_ZONE_TYPE] = self._zone_type_zone_type
388  if self._slope_type_slope_type:
389  if self._slope_type_slope_type == SLOPE_FLAT:
390  props[ATTR_ZONE_SLOPE] = "Flat"
391  elif self._slope_type_slope_type == SLOPE_SLIGHT:
392  props[ATTR_ZONE_SLOPE] = "Slight"
393  elif self._slope_type_slope_type == SLOPE_MODERATE:
394  props[ATTR_ZONE_SLOPE] = "Moderate"
395  elif self._slope_type_slope_type == SLOPE_STEEP:
396  props[ATTR_ZONE_SLOPE] = "Steep"
397  return props
398 
399  def turn_on(self, **kwargs: Any) -> None:
400  """Start watering this zone."""
401  # Stop other zones first
402  self.turn_offturn_offturn_off()
403 
404  # Start this zone
405  if ATTR_DURATION in kwargs:
406  manual_run_time = timedelta(minutes=kwargs[ATTR_DURATION])
407  else:
408  manual_run_time = timedelta(
409  minutes=self._person_person.config_entry.options.get(
410  CONF_MANUAL_RUN_MINS, DEFAULT_MANUAL_RUN_MINS
411  )
412  )
413  # The API limit is 3 hours, and requires an int be passed
414  self._controller_controller.rachio.zone.start(self.zone_idzone_idzone_id, manual_run_time.seconds)
415  _LOGGER.debug(
416  "Watering %s on %s for %s",
417  self.namename,
418  self._controller_controller.name,
419  str(manual_run_time),
420  )
421 
422  def turn_off(self, **kwargs: Any) -> None:
423  """Stop watering all zones."""
424  self._controller_controller.stop_watering()
425 
426  def set_moisture_percent(self, percent) -> None:
427  """Set the zone moisture percent."""
428  _LOGGER.debug("Setting %s moisture to %s percent", self.namename, percent)
429  self._controller_controller.rachio.zone.set_moisture_percent(self.idid, percent / 100)
430 
431  @callback
432  def _async_handle_update(self, *args, **kwargs) -> None:
433  """Handle incoming webhook zone data."""
434  if args[0][KEY_ZONE_ID] != self.zone_idzone_idzone_id:
435  return
436 
437  self._summary_summary = args[0][KEY_SUMMARY]
438 
439  if args[0][KEY_SUBTYPE] == SUBTYPE_ZONE_STARTED:
440  self._attr_is_on_attr_is_on = True
441  elif args[0][KEY_SUBTYPE] in [
442  SUBTYPE_ZONE_STOPPED,
443  SUBTYPE_ZONE_COMPLETED,
444  SUBTYPE_ZONE_PAUSED,
445  ]:
446  self._attr_is_on_attr_is_on = False
447 
448  self.async_write_ha_stateasync_write_ha_state()
449 
450  async def async_added_to_hass(self) -> None:
451  """Subscribe to updates."""
452  self._attr_is_on_attr_is_on = self.zone_idzone_idzone_id == self._current_schedule_current_schedule.get(KEY_ZONE_ID)
453 
454  self.async_on_removeasync_on_remove(
456  self.hasshass, SIGNAL_RACHIO_ZONE_UPDATE, self._async_handle_update_async_handle_update_async_handle_update
457  )
458  )
459 
460 
462  """Representation of one fixed schedule on the Rachio Iro."""
463 
464  def __init__(self, person, controller, data, current_schedule) -> None:
465  """Initialize a new Rachio Schedule."""
466  self._schedule_id_schedule_id = data[KEY_ID]
467  self._duration_duration = data[KEY_DURATION]
468  self._schedule_enabled_schedule_enabled = data[KEY_ENABLED]
469  self._summary_summary = data[KEY_SUMMARY]
470  self.typetype = data.get(KEY_TYPE, SCHEDULE_TYPE_FIXED)
471  self._current_schedule_current_schedule = current_schedule
472  self._attr_unique_id_attr_unique_id = (
473  f"{controller.controller_id}-schedule-{self._schedule_id}"
474  )
475  self._attr_name_attr_name = f"{data[KEY_NAME]} Schedule"
476  super().__init__(controller)
477 
478  @property
479  def icon(self) -> str:
480  """Return the icon to display."""
481  return "mdi:water" if self.schedule_is_enabledschedule_is_enabled else "mdi:water-off"
482 
483  @property
484  def extra_state_attributes(self) -> dict[str, Any]:
485  """Return the optional state attributes."""
486  return {
487  ATTR_SCHEDULE_SUMMARY: self._summary_summary,
488  ATTR_SCHEDULE_ENABLED: self.schedule_is_enabledschedule_is_enabled,
489  ATTR_SCHEDULE_DURATION: f"{round(self._duration / 60)} minutes",
490  ATTR_SCHEDULE_TYPE: self.typetype,
491  }
492 
493  @property
494  def schedule_is_enabled(self) -> bool:
495  """Return whether the schedule is allowed to run."""
496  return self._schedule_enabled_schedule_enabled
497 
498  def turn_on(self, **kwargs: Any) -> None:
499  """Start this schedule."""
500  self._controller_controller.rachio.schedulerule.start(self._schedule_id_schedule_id)
501  _LOGGER.debug(
502  "Schedule %s started on %s",
503  self.namename,
504  self._controller_controller.name,
505  )
506 
507  def turn_off(self, **kwargs: Any) -> None:
508  """Stop watering all zones."""
509  self._controller_controller.stop_watering()
510 
511  @callback
512  def _async_handle_update(self, *args, **kwargs) -> None:
513  """Handle incoming webhook schedule data."""
514  # Schedule ID not passed when running individual zones, so we catch that error
515  with suppress(KeyError):
516  if args[0][KEY_SCHEDULE_ID] == self._schedule_id_schedule_id:
517  if args[0][KEY_SUBTYPE] in [SUBTYPE_SCHEDULE_STARTED]:
518  self._attr_is_on_attr_is_on = True
519  elif args[0][KEY_SUBTYPE] in [
520  SUBTYPE_SCHEDULE_STOPPED,
521  SUBTYPE_SCHEDULE_COMPLETED,
522  ]:
523  self._attr_is_on_attr_is_on = False
524 
525  self.async_write_ha_stateasync_write_ha_state()
526 
527  async def async_added_to_hass(self) -> None:
528  """Subscribe to updates."""
529  self._attr_is_on_attr_is_on = self._schedule_id_schedule_id == self._current_schedule_current_schedule.get(
530  KEY_SCHEDULE_ID
531  )
532 
533  self.async_on_removeasync_on_remove(
535  self.hasshass, SIGNAL_RACHIO_SCHEDULE_UPDATE, self._async_handle_update_async_handle_update_async_handle_update
536  )
537  )
538 
539 
541  """Representation of one smart hose timer valve."""
542 
543  _attr_name = None
544 
545  def __init__(self, person, base, data, coordinator) -> None:
546  """Initialize a new smart hose valve."""
547  super().__init__(data, coordinator)
548  self._person_person = person
549  self._base_base = base
550  self._attr_unique_id_attr_unique_id = f"{self.id}-valve"
551 
552  def turn_on(self, **kwargs: Any) -> None:
553  """Turn on this valve."""
554  if ATTR_DURATION in kwargs:
555  manual_run_time = timedelta(minutes=kwargs[ATTR_DURATION])
556  else:
557  manual_run_time = timedelta(
558  minutes=self._person_person.config_entry.options.get(
559  CONF_MANUAL_RUN_MINS, DEFAULT_MANUAL_RUN_MINS
560  )
561  )
562 
563  self._base_base.start_watering(self.idid, manual_run_time.seconds)
564  self._attr_is_on_attr_is_on = True
565  self.schedule_update_ha_stateschedule_update_ha_state(force_refresh=True)
566  _LOGGER.debug("Starting valve %s for %s", self._name_name, str(manual_run_time))
567 
568  def turn_off(self, **kwargs: Any) -> None:
569  """Turn off this valve."""
570  self._base_base.stop_watering(self.idid)
571  self._attr_is_on_attr_is_on = False
572  self.schedule_update_ha_stateschedule_update_ha_state(force_refresh=True)
573  _LOGGER.debug("Stopping watering on valve %s", self._name_name)
574 
575  @callback
576  def _update_attr(self) -> None:
577  """Handle updated coordinator data."""
578  data = self.coordinator.data[self.idid]
579 
580  self._static_attrs_static_attrs = data[KEY_STATE][KEY_REPORTED_STATE]
581  self._attr_is_on_attr_is_on = KEY_CURRENT_STATUS in self._static_attrs_static_attrs
None _async_handle_update(self, *args, **kwargs)
Definition: switch.py:283
None _async_handle_update(self, *args, **kwargs)
Definition: switch.py:512
None __init__(self, person, controller, data, current_schedule)
Definition: switch.py:464
None _async_handle_any_update(self, *args, **kwargs)
Definition: switch.py:209
None _async_handle_update(self, *args, **kwargs)
Definition: switch.py:219
None __init__(self, person, base, data, coordinator)
Definition: switch.py:545
None __init__(self, person, controller, data, current_schedule)
Definition: switch.py:350
None _async_handle_update(self, *args, **kwargs)
Definition: switch.py:432
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None schedule_update_ha_state(self, bool force_refresh=False)
Definition: entity.py:1244
str|UndefinedType|None name(self)
Definition: entity.py:738
None turn_off(self, **Any kwargs)
Definition: entity.py:1705
datetime|None parse_datetime(str|None value)
Definition: sensor.py:138
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: switch.py:106
list[Entity] _create_entities(HomeAssistant hass, ConfigEntry config_entry)
Definition: switch.py:178
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103
CALLBACK_TYPE async_track_point_in_utc_time(HomeAssistant hass, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action, datetime point_in_time)
Definition: event.py:1542
datetime now(HomeAssistant hass)
Definition: template.py:1890
float as_timestamp(dt.datetime|str dt_value)
Definition: dt.py:145