Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Component to pressing a button as platforms."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 from enum import StrEnum
7 import logging
8 from typing import final
9 
10 from propcache import cached_property
11 import voluptuous as vol
12 
13 from homeassistant.config_entries import ConfigEntry
14 from homeassistant.const import STATE_UNAVAILABLE
15 from homeassistant.core import HomeAssistant
16 from homeassistant.helpers import config_validation as cv
17 from homeassistant.helpers.entity import EntityDescription
18 from homeassistant.helpers.entity_component import EntityComponent
19 from homeassistant.helpers.restore_state import RestoreEntity
20 from homeassistant.helpers.typing import ConfigType
21 from homeassistant.util import dt as dt_util
22 from homeassistant.util.hass_dict import HassKey
23 
24 from .const import DOMAIN, SERVICE_PRESS
25 
26 _LOGGER = logging.getLogger(__name__)
27 
28 DATA_COMPONENT: HassKey[EntityComponent[ButtonEntity]] = HassKey(DOMAIN)
29 ENTITY_ID_FORMAT = DOMAIN + ".{}"
30 PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
31 PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
32 SCAN_INTERVAL = timedelta(seconds=30)
33 
34 MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
35 
36 
37 class ButtonDeviceClass(StrEnum):
38  """Device class for buttons."""
39 
40  IDENTIFY = "identify"
41  RESTART = "restart"
42  UPDATE = "update"
43 
44 
45 DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.Coerce(ButtonDeviceClass))
46 
47 # mypy: disallow-any-generics
48 
49 
50 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
51  """Set up Button entities."""
52  component = hass.data[DATA_COMPONENT] = EntityComponent[ButtonEntity](
53  _LOGGER, DOMAIN, hass, SCAN_INTERVAL
54  )
55  await component.async_setup(config)
56 
57  component.async_register_entity_service(
58  SERVICE_PRESS,
59  None,
60  "_async_press_action",
61  )
62 
63  return True
64 
65 
66 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
67  """Set up a config entry."""
68  return await hass.data[DATA_COMPONENT].async_setup_entry(entry)
69 
70 
71 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
72  """Unload a config entry."""
73  return await hass.data[DATA_COMPONENT].async_unload_entry(entry)
74 
75 
76 class ButtonEntityDescription(EntityDescription, frozen_or_thawed=True):
77  """A class that describes button entities."""
78 
79  device_class: ButtonDeviceClass | None = None
80 
81 
82 CACHED_PROPERTIES_WITH_ATTR_ = {
83  "device_class",
84 }
85 
86 
87 class ButtonEntity(RestoreEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
88  """Representation of a Button entity."""
89 
90  entity_description: ButtonEntityDescription
91  _attr_should_poll = False
92  _attr_device_class: ButtonDeviceClass | None
93  _attr_state: None = None
94  __last_pressed_isoformat: str | None = None
95 
96  def _default_to_device_class_name(self) -> bool:
97  """Return True if an unnamed entity should be named by its device class.
98 
99  For buttons this is True if the entity has a device class.
100  """
101  return self.device_classdevice_classdevice_class is not None
102 
103  @cached_property
104  def device_class(self) -> ButtonDeviceClass | None:
105  """Return the class of this entity."""
106  if hasattr(self, "_attr_device_class"):
107  return self._attr_device_class
108  if hasattr(self, "entity_description"):
109  return self.entity_description.device_class
110  return None
111 
112  @cached_property
113  @final
114  def state(self) -> str | None:
115  """Return the entity state."""
116  return self.__last_pressed_isoformat__last_pressed_isoformat
117 
118  def __set_state(self, state: str | None) -> None:
119  """Set the entity state."""
120  # Invalidate the cache of the cached property
121  self.__dict__.pop("state", None)
122  self.__last_pressed_isoformat__last_pressed_isoformat = state
123 
124  @final
125  async def _async_press_action(self) -> None:
126  """Press the button (from e.g., service call).
127 
128  Should not be overridden, handle setting last press timestamp.
129  """
130  self.__set_state__set_state(dt_util.utcnow().isoformat())
131  self.async_write_ha_stateasync_write_ha_state()
132  await self.async_pressasync_press()
133 
134  async def async_internal_added_to_hass(self) -> None:
135  """Call when the button is added to hass."""
136  await super().async_internal_added_to_hass()
137  state = await self.async_get_last_stateasync_get_last_state()
138  if state is not None and state.state not in (STATE_UNAVAILABLE, None):
139  self.__set_state__set_state(state.state)
140 
141  def press(self) -> None:
142  """Press the button."""
143  raise NotImplementedError
144 
145  async def async_press(self) -> None:
146  """Press the button."""
147  await self.hasshass.async_add_executor_job(self.presspress)
None __set_state(self, str|None state)
Definition: __init__.py:118
ButtonDeviceClass|None device_class(self)
Definition: __init__.py:104
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:71
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:66
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:50