1 """An abstract class for entities."""
3 from __future__
import annotations
5 from abc
import ABCMeta
7 from collections
import deque
8 from collections.abc
import Callable, Coroutine, Iterable, Mapping
10 from enum
import Enum, IntFlag, auto
11 import functools
as ft
14 from operator
import attrgetter
18 from types
import FunctionType
19 from typing
import TYPE_CHECKING, Any, Final, Literal, NotRequired, TypedDict, final
21 from propcache
import cached_property
22 import voluptuous
as vol
31 ATTR_SUPPORTED_FEATURES,
32 ATTR_UNIT_OF_MEASUREMENT,
48 get_hassjob_callable_job_type,
55 NoEntitySpecifiedError,
61 from .
import device_registry
as dr, entity_registry
as er, singleton
62 from .device_registry
import DeviceInfo, EventDeviceRegistryUpdatedData
64 async_track_device_registry_updated_event,
65 async_track_entity_registry_updated_event,
67 from .frame
import report_non_thread_safe_operation
68 from .typing
import UNDEFINED, StateType, UndefinedType
73 from .entity_platform
import EntityPlatform
75 _LOGGER = logging.getLogger(__name__)
76 SLOW_UPDATE_WARNING = 10
77 DATA_ENTITY_SOURCE =
"entity_info"
81 FLOAT_PRECISION = abs(
int(math.floor(math.log10(abs(sys.float_info.epsilon))))) - 1
84 CAPABILITIES_UPDATE_LIMIT = 100
86 CONTEXT_RECENT_TIME_SECONDS = 5
91 """Set up entity sources."""
97 @singleton.singleton(DATA_ENTITY_SOURCE)
99 """Get the entity sources."""
104 entity_id_format: str,
106 current_ids: list[str] |
None =
None,
107 hass: HomeAssistant |
None =
None,
109 """Generate a unique entity ID based on given entity IDs or used IDs."""
115 entity_id_format: str,
117 current_ids: Iterable[str] |
None =
None,
118 hass: HomeAssistant |
None =
None,
120 """Generate a unique entity ID based on given entity IDs or used IDs."""
121 name = (name
or DEVICE_DEFAULT_NAME).lower()
122 preferred_string = entity_id_format.format(
slugify(name))
124 if current_ids
is not None:
128 raise ValueError(
"Missing required parameter current_ids or hass")
130 test_string = preferred_string
132 while not hass.states.async_available(test_string):
134 test_string = f
"{preferred_string}_{tries}"
139 def get_capability(hass: HomeAssistant, entity_id: str, capability: str) -> Any |
None:
140 """Get a capability attribute of an entity.
142 First try the statemachine, then entity registry.
144 if state := hass.states.get(entity_id):
145 return state.attributes.get(capability)
147 entity_registry = er.async_get(hass)
148 if not (entry := entity_registry.async_get(entity_id)):
151 return entry.capabilities.get(capability)
if entry.capabilities
else None
155 """Get device class of an entity.
157 First try the statemachine, then entity registry.
159 if state := hass.states.get(entity_id):
160 return state.attributes.get(ATTR_DEVICE_CLASS)
162 entity_registry = er.async_get(hass)
163 if not (entry := entity_registry.async_get(entity_id)):
166 return entry.device_class
or entry.original_device_class
170 """Get supported features for an entity.
172 First try the statemachine, then entity registry.
174 if state := hass.states.get(entity_id):
175 return state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
177 entity_registry = er.async_get(hass)
178 if not (entry := entity_registry.async_get(entity_id)):
181 return entry.supported_features
or 0
185 """Get unit of measurement of an entity.
187 First try the statemachine, then entity registry.
189 if state := hass.states.get(entity_id):
190 return state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
192 entity_registry = er.async_get(hass)
193 if not (entry := entity_registry.async_get(entity_id)):
196 return entry.unit_of_measurement
199 ENTITY_CATEGORIES_SCHEMA: Final = vol.Coerce(EntityCategory)
206 custom_component: bool
207 config_entry: NotRequired[str]
213 unrecorded_attributes: frozenset[str]
217 """The platform state of an entity."""
236 """A class that describes Home Assistant entities."""
241 device_class: str |
None =
None
242 entity_category: EntityCategory |
None =
None
243 entity_registry_enabled_default: bool =
True
244 entity_registry_visible_default: bool =
True
245 force_update: bool =
False
246 icon: str |
None =
None
247 has_entity_name: bool =
False
248 name: str | UndefinedType |
None = UNDEFINED
249 translation_key: str |
None =
None
250 translation_placeholders: Mapping[str, str] |
None =
None
251 unit_of_measurement: str |
None =
None
254 @dataclasses.dataclass(frozen=True, slots=True)
256 """Container with state and attributes.
258 Returned by Entity._async_calculate_state.
263 attributes: dict[str, Any]
265 capability_attributes: Mapping[str, Any] |
None
269 """Metaclass which invalidates cached entity properties on write to _attr_.
271 A class which has CachedProperties can optionally have a list of cached
272 properties, passed as cached_properties, which must be a set of strings.
273 - Each item in the cached_property set must be the name of a method decorated
274 with @cached_property
275 - For each item in the cached_property set, a property function with the
276 same name, prefixed with _attr_, will be created
277 - The property _attr_-property functions allow setting, getting and deleting
278 data, which will be stored in an attribute prefixed with __attr_
279 - The _attr_-property setter will invalidate the @cached_property by calling
286 bases: tuple[type, ...],
287 namespace: dict[Any, Any],
288 cached_properties: set[str] |
None =
None,
291 """Start creating a new CachedProperties.
293 Pop cached_properties and store it in the namespace.
295 namespace[
"_CachedProperties__cached_properties"] = cached_properties
or set()
296 return super().
__new__(mcs, name, bases, namespace, **kwargs)
301 bases: tuple[type, ...],
302 namespace: dict[Any, Any],
305 """Finish creating a new CachedProperties.
307 Wrap _attr_ for cached properties in property objects.
310 def deleter(name: str) -> Callable[[Any],
None]:
311 """Create a deleter for an _attr_ property."""
312 private_attr_name = f
"__attr_{name}"
314 def _deleter(o: Any) ->
None:
315 """Delete an _attr_ property.
318 - Delete the __attr_ attribute
319 - Invalidate the cache of the cached property
321 Raises AttributeError if the __attr_ attribute does not exist
324 o.__dict__.pop(name,
None)
326 delattr(o, private_attr_name)
330 def setter(name: str) -> Callable[[Any, Any],
None]:
331 """Create a setter for an _attr_ property."""
332 private_attr_name = f
"__attr_{name}"
334 def _setter(o: Any, val: Any) ->
None:
335 """Set an _attr_ property to the backing __attr attribute.
337 Also invalidates the corresponding cached_property by calling
341 old_val := getattr(o, private_attr_name, _SENTINEL)
342 ) == val
and type(old_val)
is type(val):
344 setattr(o, private_attr_name, val)
346 o.__dict__.pop(name,
None)
350 def make_property(name: str) -> property:
351 """Help create a property object."""
353 fget=attrgetter(f
"__attr_{name}"), fset=setter(name), fdel=deleter(name)
356 def wrap_attr(cls: CachedProperties, property_name: str) ->
None:
357 """Wrap a cached property's corresponding _attr in a property.
359 If the class being created has an _attr class attribute, move it, and its
360 annotations, to the __attr attribute.
362 attr_name = f
"_attr_{property_name}"
363 private_attr_name = f
"__attr_{property_name}"
366 if attr_name
in cls.__dict__:
367 attr = getattr(cls, attr_name)
368 if isinstance(attr, (FunctionType, property)):
369 raise TypeError(f
"Can't override {attr_name} in subclass")
370 setattr(cls, private_attr_name, attr)
371 annotations = cls.__annotations__
372 if attr_name
in annotations:
373 annotations[private_attr_name] = annotations.pop(attr_name)
375 setattr(cls, attr_name, make_property(property_name))
377 cached_properties: set[str] = namespace[
"_CachedProperties__cached_properties"]
378 seen_props: set[str] = set()
379 for property_name
in cached_properties:
380 wrap_attr(cls, property_name)
381 seen_props.add(property_name)
385 for parent
in cls.__mro__[:0:-1]:
386 if "_CachedProperties__cached_properties" not in parent.__dict__:
388 cached_properties = getattr(parent,
"_CachedProperties__cached_properties")
389 for property_name
in cached_properties:
390 if property_name
in seen_props:
392 attr_name = f
"_attr_{property_name}"
395 if (attr_name)
not in cls.__dict__:
397 wrap_attr(cls, property_name)
398 seen_props.add(property_name)
402 """Add ABCMeta to CachedProperties."""
405 CACHED_PROPERTIES_WITH_ATTR_ = {
409 "capability_attributes",
415 "entity_registry_enabled_default",
416 "entity_registry_visible_default",
417 "extra_state_attributes",
423 "supported_features",
425 "translation_placeholders",
427 "unit_of_measurement",
432 metaclass=ABCCachedProperties, cached_properties=CACHED_PROPERTIES_WITH_ATTR_
434 """An abstract class for Home Assistant entities."""
439 entity_id: str =
None
444 hass: HomeAssistant =
None
449 platform: EntityPlatform =
None
452 entity_description: EntityDescription
455 _slow_reported =
False
458 _deprecated_supported_features_reported =
False
461 _disabled_reported =
False
465 _async_update_ha_state_reported =
False
468 _no_platform_reported =
False
471 _name_translation_placeholders_reported =
False
474 _update_staged =
False
478 _verified_state_writable =
False
481 parallel_updates: asyncio.Semaphore |
None =
None
484 registry_entry: er.RegistryEntry |
None =
None
487 _removed_from_registry: bool =
False
490 device_entry: dr.DeviceEntry |
None =
None
493 _on_remove: list[CALLBACK_TYPE] |
None =
None
495 _unsub_device_updates: CALLBACK_TYPE |
None =
None
498 _context: Context |
None =
None
499 _context_set: float |
None =
None
502 _platform_state = EntityPlatformState.NOT_ADDED
505 _entity_component_unrecorded_attributes: frozenset[str] = frozenset()
508 _unrecorded_attributes: frozenset[str] = frozenset()
511 __combined_unrecorded_attributes: frozenset[str] = (
512 _entity_component_unrecorded_attributes | _unrecorded_attributes
515 _job_types: dict[str, HassJobType] |
None =
None
520 _state_info: StateInfo =
None
522 __capabilities_updated_at: deque[float]
523 __capabilities_updated_at_reported: bool =
False
524 __remove_future: asyncio.Future[
None] |
None =
None
527 _attr_assumed_state: bool =
False
528 _attr_attribution: str |
None =
None
529 _attr_available: bool =
True
530 _attr_capability_attributes: dict[str, Any] |
None =
None
531 _attr_device_class: str |
None
532 _attr_device_info: DeviceInfo |
None =
None
533 _attr_entity_category: EntityCategory |
None
534 _attr_has_entity_name: bool
535 _attr_entity_picture: str |
None =
None
536 _attr_entity_registry_enabled_default: bool
537 _attr_entity_registry_visible_default: bool
538 _attr_extra_state_attributes: dict[str, Any]
539 _attr_force_update: bool
540 _attr_icon: str |
None
541 _attr_name: str |
None
542 _attr_should_poll: bool =
True
543 _attr_state: StateType = STATE_UNKNOWN
544 _attr_supported_features: int |
None =
None
545 _attr_translation_key: str |
None
546 _attr_translation_placeholders: Mapping[str, str]
547 _attr_unique_id: str |
None =
None
548 _attr_unit_of_measurement: str |
None
551 """Initialize an Entity subclass."""
554 cls._entity_component_unrecorded_attributes | cls._unrecorded_attributes
558 """Get the job type function for the given name.
560 This is used for entity service calls to avoid
561 figuring out the job type each time.
565 if function_name
not in self.
_job_types_job_types:
567 getattr(self, function_name)
569 return self.
_job_types_job_types[function_name]
573 """Return True if entity has to be polled for state.
575 False if entity pushes its state to HA.
577 return self._attr_should_poll
581 """Return a unique ID."""
582 return self._attr_unique_id
586 """Return if this entity does not have its own name.
588 Should be True if the entity represents the single main feature of a device.
590 if hasattr(self,
"_attr_name"):
591 return not self._attr_name
594 )
and name_translation_key
in self.
platformplatform.platform_translations:
596 if hasattr(self,
"entity_description"):
597 return not self.entity_description.name
598 return not self.
namename
602 """Return if the name of the entity is describing only the entity itself."""
603 if hasattr(self,
"_attr_has_entity_name"):
604 return self._attr_has_entity_name
605 if hasattr(self,
"entity_description"):
606 return self.entity_description.has_entity_name
611 component_translations: dict[str, str],
613 """Return a translated name of the entity based on its device class."""
618 name_translation_key = (
619 f
"component.{platform.domain}.entity_component.{device_class_key}.name"
621 return component_translations.get(name_translation_key)
625 """Return a translated name of the entity based on its device class."""
627 self.
platformplatform.object_id_component_translations
632 """Return a translated name of the entity based on its device class."""
636 """Return True if an unnamed entity should be named by its device class."""
641 """Return translation key for entity name."""
646 f
"component.{platform.platform_name}.entity.{platform.domain}"
647 f
".{self.translation_key}.name"
652 """Return translation key for unit of measurement."""
657 f
"Entity {type(self)} cannot have a translation key for "
658 "unit of measurement before being added to the entity platform"
662 f
"component.{platform.platform_name}.entity.{platform.domain}"
663 f
".{self.translation_key}.unit_of_measurement"
667 """Substitute placeholders in entity name."""
670 except KeyError
as err:
677 "Entity %s (%s) has translation placeholders '%s' which do not "
678 "match the name '%s', please %s"
691 device_class_name: str |
None,
692 platform_translations: dict[str, str],
693 ) -> str | UndefinedType |
None:
694 """Return the name of the entity."""
695 if hasattr(self,
"_attr_name"):
696 return self._attr_name
700 and (name := platform_translations.get(name_translation_key))
703 if hasattr(self,
"entity_description"):
704 description_name = self.entity_description.name
706 return device_class_name
707 return description_name
712 return device_class_name
717 """Return input for object id."""
723 type.__getattribute__(self.__class__,
"name")
724 is type.__getattribute__(Entity,
"name")
731 self.
platformplatform.object_id_platform_translations,
735 return None if name
is UNDEFINED
else name
738 def name(self) -> str | UndefinedType | None:
739 """Return the name of the entity."""
746 self.
platformplatform.platform_translations,
751 """Return the state of the entity."""
752 return self._attr_state
756 """Return the capability attributes.
758 Attributes that explain the capabilities of an entity.
760 Implemented by component base class. Convention for attribute names
761 is lowercase snake_case.
763 return self._attr_capability_attributes
766 """Return initial entity options.
768 These will be stored in the entity registry the first time the entity is seen,
769 and then never updated.
771 Implemented by component base class, should not be extended by integrations.
773 Note: Not a property to avoid calculating unless needed.
779 """Return the state attributes.
781 Implemented by component base class, should not be extended by integrations.
782 Convention for attribute names is lowercase snake_case.
788 """Return entity specific state attributes.
790 Implemented by platform classes. Convention for attribute names
791 is lowercase snake_case.
793 if hasattr(self,
"_attr_extra_state_attributes"):
794 return self._attr_extra_state_attributes
799 """Return device specific attributes.
801 Implemented by platform classes.
803 return self._attr_device_info
807 """Return the class of this device, from component DEVICE_CLASSES."""
808 if hasattr(self,
"_attr_device_class"):
809 return self._attr_device_class
810 if hasattr(self,
"entity_description"):
811 return self.entity_description.device_class
816 """Return the unit of measurement of this entity, if any."""
817 if hasattr(self,
"_attr_unit_of_measurement"):
818 return self._attr_unit_of_measurement
819 if hasattr(self,
"entity_description"):
820 return self.entity_description.unit_of_measurement
825 """Return the icon to use in the frontend, if any."""
826 if hasattr(self,
"_attr_icon"):
827 return self._attr_icon
828 if hasattr(self,
"entity_description"):
829 return self.entity_description.icon
834 """Return the entity picture to use in the frontend, if any."""
835 return self._attr_entity_picture
839 """Return True if entity is available."""
840 return self._attr_available
844 """Return True if unable to access real state of the entity."""
845 return self._attr_assumed_state
849 """Return True if state updates should be forced.
851 If True, a state change will be triggered anytime the state property is
852 updated, not just when the value changes.
854 if hasattr(self,
"_attr_force_update"):
855 return self._attr_force_update
856 if hasattr(self,
"entity_description"):
857 return self.entity_description.force_update
862 """Flag supported features."""
863 return self._attr_supported_features
867 """Return if the entity should be enabled when first added.
869 This only applies when fist added to the entity registry.
871 if hasattr(self,
"_attr_entity_registry_enabled_default"):
872 return self._attr_entity_registry_enabled_default
873 if hasattr(self,
"entity_description"):
874 return self.entity_description.entity_registry_enabled_default
879 """Return if the entity should be visible when first added.
881 This only applies when fist added to the entity registry.
883 if hasattr(self,
"_attr_entity_registry_visible_default"):
884 return self._attr_entity_registry_visible_default
885 if hasattr(self,
"entity_description"):
886 return self.entity_description.entity_registry_visible_default
891 """Return the attribution."""
892 return self._attr_attribution
896 """Return the category of the entity, if any."""
897 if hasattr(self,
"_attr_entity_category"):
898 return self._attr_entity_category
899 if hasattr(self,
"entity_description"):
900 return self.entity_description.entity_category
905 """Return the translation key to translate the entity's states."""
906 if hasattr(self,
"_attr_translation_key"):
907 return self._attr_translation_key
908 if hasattr(self,
"entity_description"):
909 return self.entity_description.translation_key
915 """Return the translation placeholders for translated entity's name."""
916 if hasattr(self,
"_attr_translation_placeholders"):
917 return self._attr_translation_placeholders
918 if hasattr(self,
"entity_description"):
919 return self.entity_description.translation_placeholders
or {}
929 """Return if the entity is enabled in the entity registry.
931 If an entity is not part of the registry, it cannot be disabled
932 and will therefore always be enabled.
938 """Set the context the entity currently operates under."""
943 """Update Home Assistant with current state of entity.
945 If force_refresh == True will update entity before setting state.
947 This method must be run in the event loop.
949 if self.
hasshass
is None:
950 raise RuntimeError(f
"Attribute hass is None for {self}")
954 f
"No entity id specified for entity {self.name}"
962 _LOGGER.exception(
"Update for %s fails", self.
entity_identity_id)
968 "Entity %s (%s) is using self.async_update_ha_state(), without"
969 " enabling force_refresh. Instead it should use"
970 " self.async_write_ha_state(), please %s"
982 """Verify the entity is in a writable state."""
983 if self.
hasshass
is None:
984 raise RuntimeError(f
"Attribute hass is None for {self}")
992 "Entity %s (%s) does not have a platform, this may be caused by "
993 "adding it manually instead of with an EntityComponent helper"
1004 f
"No entity id specified for entity {self.name}"
1011 """Write the state to the state machine from the event loop thread."""
1018 """Write the state to the state machine."""
1021 if self.
hasshass.loop_thread_id != threading.get_ident():
1026 """Convert state to string."""
1028 return STATE_UNAVAILABLE
1029 if (state := self.
statestate)
is None:
1030 return STATE_UNKNOWN
1031 if type(state)
is str:
1034 if isinstance(state, float):
1037 return f
"{state:.{FLOAT_PRECISION}}"
1041 """Return the friendly name.
1043 If has_entity_name is False, this returns self.name
1044 If has_entity_name is True, this returns device.name + self.name
1046 name = self.
namename
1047 if name
is UNDEFINED:
1053 device_name = device_entry.name_by_user
or device_entry.name
1056 return f
"{device_name} {name}" if device_name
else name
1060 """Calculate state string and attribute mapping."""
1066 ) -> tuple[str, dict[str, Any], Mapping[str, Any] | None, str | None, int | None]:
1067 """Calculate state string and attribute mapping.
1070 state - the stringified state
1071 attr - the attribute dictionary
1072 capability_attr - a mapping with capability attributes
1073 original_device_class - the device class which may be overridden
1074 supported_features - the supported features
1076 This method is called when writing the state to avoid the overhead of creating
1082 attr = capability_attr.copy()
if capability_attr
else {}
1088 attr.update(state_attributes)
1090 attr.update(extra_state_attributes)
1093 attr[ATTR_UNIT_OF_MEASUREMENT] = unit_of_measurement
1096 attr[ATTR_ASSUMED_STATE] = assumed_state
1098 if (attribution := self.
attributionattribution)
is not None:
1099 attr[ATTR_ATTRIBUTION] = attribution
1103 device_class := (entry
and entry.device_class)
or original_device_class
1105 attr[ATTR_DEVICE_CLASS] =
str(device_class)
1107 if (entity_picture := self.
entity_pictureentity_picture)
is not None:
1108 attr[ATTR_ENTITY_PICTURE] = entity_picture
1110 if (icon := (entry
and entry.icon)
or self.
iconicon)
is not None:
1111 attr[ATTR_ICON] = icon
1116 attr[ATTR_FRIENDLY_NAME] = name
1119 attr[ATTR_SUPPORTED_FEATURES] = supported_features
1121 return (state, attr, capability_attr, original_device_class, supported_features)
1125 """Write the state to the state machine."""
1126 if self.
_platform_state_platform_state
is EntityPlatformState.REMOVED:
1130 hass = self.
hasshass
1133 if (entry := self.
registry_entryregistry_entry)
and entry.disabled_by:
1138 "Entity %s is incorrectly being triggered for updates while it"
1139 " is disabled. This is a bug in the %s integration"
1142 self.
platformplatform.platform_name,
1146 state_calculate_start =
timer()
1147 state, attr, capabilities, original_device_class, supported_features = (
1155 supported_features = supported_features
or 0
1157 capabilities != entry.capabilities
1158 or original_device_class != entry.original_device_class
1159 or supported_features != entry.supported_features
1164 capabilities_updated_at := getattr(
1165 self,
"_Entity__capabilities_updated_at",
None
1169 maxlen=CAPABILITIES_UPDATE_LIMIT + 1
1172 capabilities_updated_at.append(time_now)
1173 while time_now - capabilities_updated_at[0] > 3600:
1174 capabilities_updated_at.popleft()
1175 if len(capabilities_updated_at) > CAPABILITIES_UPDATE_LIMIT:
1180 "Entity %s (%s) is updating its capabilities too often,"
1187 entity_registry = er.async_get(self.
hasshass)
1190 capabilities=capabilities,
1191 original_device_class=original_device_class,
1192 supported_features=supported_features,
1199 "Updating state for %s (%s) took %.3f seconds. Please %s",
1202 time_now - state_calculate_start,
1211 customize = hass.data[DATA_CUSTOMIZE]
1216 if custom := customize.get(entity_id):
1221 and time_now - self.
_context_set_context_set > CONTEXT_RECENT_TIME_SECONDS
1227 hass.states.async_set_internal(
1236 except InvalidStateError:
1238 "Failed to set state for %s, fall back to %s", entity_id, STATE_UNKNOWN
1240 hass.states.async_set(
1245 """Schedule an update ha state change task.
1247 Scheduling the update avoids executor deadlocks.
1249 Entity state and attributes are read when the update ha state change
1251 If state is changed more than once before the ha state change task has
1252 been executed, the intermediate state transitions will be missed.
1255 self.
hasshass.create_task(
1257 f
"Entity {self.entity_id} schedule update ha state",
1260 self.
hasshass.loop.call_soon_threadsafe(
1266 """Schedule an update ha state change task.
1268 This method must be run in the event loop.
1269 Scheduling the update avoids executor deadlocks.
1271 Entity state and attributes are read when the update ha state change
1273 If state is changed more than once before the ha state change task has
1274 been executed, the intermediate state transitions will be missed.
1277 self.
hasshass.async_create_task(
1279 f
"Entity schedule update ha state {self.entity_id}",
1287 """Log a warning if update is taking too long."""
1289 "Update of %s is taking over %s seconds",
1291 SLOW_UPDATE_WARNING,
1295 """Process 'update' or 'async_update' from entity.
1297 This method is a coroutine.
1302 hass = self.
hasshass
1303 assert hass
is not None
1312 update_warn = hass.loop.call_at(
1317 if hasattr(self,
"async_update"):
1318 await self.async_update()
1319 elif hasattr(self,
"update"):
1320 await hass.async_add_executor_job(self.update)
1326 update_warn.cancel()
1332 """Add a function to call when entity is removed or not added."""
1338 """Run when entity has been removed from entity registry.
1340 To be extended by integrations.
1346 hass: HomeAssistant,
1347 platform: EntityPlatform,
1348 parallel_updates: asyncio.Semaphore |
None,
1350 """Start adding an entity to a platform."""
1351 if self.
_platform_state_platform_state
is not EntityPlatformState.NOT_ADDED:
1353 f
"Entity '{self.entity_id}' cannot be added a second time to an entity"
1363 """Call callbacks registered by async_on_remove."""
1371 """Abort adding an entity to a platform."""
1376 self.
hasshass =
None
1381 """Finish adding an entity to a platform."""
1388 """Remove entity from Home Assistant.
1390 If the entity has a non disabled entry in the entity registry,
1391 the entity's state will be set to unavailable, in the same way
1392 as when the entity registry is loaded.
1394 If the entity doesn't have a non disabled entry in the entity registry,
1395 or if force_remove=True, its state will be removed.
1404 except BaseException
as ex:
1412 """Remove entity from Home Assistant."""
1444 """Run when entity about to be added to hass.
1446 To be extended by integrations.
1450 """Run when entity will be removed from hass.
1452 To be extended by integrations.
1457 """Run when the entity registry entry has been updated.
1459 To be extended by integrations.
1463 """Run when entity about to be added to hass.
1465 Not to be extended by integrations.
1467 is_custom_component =
"custom_components" in type(self).__module__
1468 entity_info: EntityInfo = {
1469 "domain": self.platform.platform_name,
1470 "custom_component": is_custom_component,
1472 if self.platform.config_entry:
1473 entity_info[
"config_entry"] = self.platform.config_entry.entry_id
1485 ), f
"Entity '{self.entity_id}' is being added while it's disabled"
1492 job_type=HassJobType.Callback,
1498 """Run when entity will be removed from hass.
1500 Not to be extended by integrations.
1509 self, event: Event[er.EventEntityRegistryUpdatedData]
1511 """Handle entity registry update."""
1512 action = event.data[
"action"]
1513 is_remove = action ==
"remove"
1515 if action ==
"update" or is_remove:
1516 self.
hasshass.async_create_task_internal(
1521 self, event: Event[er.EventEntityRegistryUpdatedData]
1523 """Handle entity registry update or remove."""
1525 if data[
"action"] ==
"remove":
1530 if data[
"action"] !=
"update":
1533 if "device_id" in data[
"changes"]:
1536 ent_reg = er.async_get(self.
hasshass)
1538 registry_entry = ent_reg.async_get(data[
"entity_id"])
1539 assert registry_entry
is not None
1542 if device_id := registry_entry.device_id:
1545 if registry_entry.disabled:
1549 assert old
is not None
1550 if registry_entry.entity_id == old.entity_id:
1566 """Unsubscribe from device registry updates."""
1574 self, event: Event[EventDeviceRegistryUpdatedData]
1576 """Handle device registry update."""
1579 if data[
"action"] !=
"update":
1582 if "name" not in data[
"changes"]
and "name_by_user" not in data[
"changes"]:
1590 """Subscribe to device registry updates."""
1595 if (device_id := self.
registry_entryregistry_entry.device_id)
is None:
1605 job_type=HassJobType.Callback,
1614 """Return the representation.
1616 If the entity is not added to a platform it's not safe to call _stringify_state.
1618 if self.
_platform_state_platform_state
is not EntityPlatformState.ADDED:
1619 return f
"<entity unknown.unknown={STATE_UNKNOWN}>"
1620 return f
"<entity {self.entity_id}={self._stringify_state(self.available)}>"
1622 async
def async_request_call[_T](self, coro: Coroutine[Any, Any, _T]) -> _T:
1623 """Process request batched."""
1634 """Suggest to report an issue."""
1637 platform_name = self.
platformplatform.platform_name
if self.
platformplatform
else None
1639 self.
hasshass, integration_domain=platform_name, module=type(self).__module__
1644 self, replacement: IntFlag
1646 """Report deprecated supported features values."""
1653 "https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation"
1657 "Entity %s (%s) is using deprecated supported features"
1658 " values which will be removed in HA Core 2025.1. Instead it should use"
1669 """A class that describes toggle entities."""
1672 TOGGLE_ENTITY_CACHED_PROPERTIES_WITH_ATTR_ = {
"is_on"}
1676 Entity, cached_properties=TOGGLE_ENTITY_CACHED_PROPERTIES_WITH_ATTR_
1678 """An abstract class for entities that can be turned on and off."""
1680 entity_description: ToggleEntityDescription
1681 _attr_is_on: bool |
None =
None
1682 _attr_state:
None =
None
1686 def state(self) -> Literal["on", "off"] | None:
1687 """Return the state."""
1688 if (is_on := self.
is_onis_on)
is None:
1690 return STATE_ON
if is_on
else STATE_OFF
1694 """Return True if entity is on."""
1695 return self._attr_is_on
1698 """Turn the entity on."""
1699 raise NotImplementedError
1702 """Turn the entity on."""
1703 await self.
hasshass.async_add_executor_job(ft.partial(self.
turn_onturn_on, **kwargs))
1706 """Turn the entity off."""
1707 raise NotImplementedError
1710 """Turn the entity off."""
1711 await self.
hasshass.async_add_executor_job(ft.partial(self.
turn_offturn_off, **kwargs))
1715 """Toggle the entity.
1717 This method will never be called by Home Assistant and should not be implemented
1722 """Toggle the entity.
1724 This method should typically not be implemented by integrations, it's enough to
1725 implement async_turn_on + async_turn_off or turn_on + turn_off.
Any __new__(mcs, str name, tuple[type,...] bases, dict[Any, Any] namespace, set[str]|None cached_properties=None, **Any kwargs)
None __init__(cls, str name, tuple[type,...] bases, dict[Any, Any] namespace, **Any kwargs)
__capabilities_updated_at_reported
None add_to_platform_abort(self)
_async_update_ha_state_reported
_deprecated_supported_features_reported
str|None attribution(self)
bool _name_translation_placeholders_reported
dict[str, Any]|None capability_attributes(self)
str|None entity_picture(self)
str|None _device_class_name_helper(self, dict[str, str] component_translations)
bool _verified_state_writable
None _report_deprecated_supported_features_values(self, IntFlag replacement)
None async_schedule_update_ha_state(self, bool force_refresh=False)
None _async_unsubscribe_device_updates(self)
bool _deprecated_supported_features_reported
str _stringify_state(self, bool available)
None _async_write_ha_state(self)
None __init_subclass__(cls, **Any kwargs)
str _suggest_report_issue(self)
__capabilities_updated_at
None _async_slow_update_warning(self)
None add_to_platform_finish(self)
str|None device_class(self)
bool _no_platform_reported
dict[str, Any]|None state_attributes(self)
EntityCategory|None entity_category(self)
None async_update_ha_state(self, bool force_refresh=False)
er.EntityOptionsType|None get_initial_entity_options(self)
Mapping[str, str] translation_placeholders(self)
str|UndefinedType|None _name_internal(self, str|None device_class_name, dict[str, str] platform_translations)
str|None _object_id_device_class_name(self)
None async_write_ha_state(self)
None _async_subscribe_device_updates(self)
HassJobType get_hassjob_type(self, str function_name)
_name_translation_placeholders_reported
str _substitute_name_placeholders(self, str name)
bool entity_registry_visible_default(self)
None _async_write_ha_state_from_call_soon_threadsafe(self)
None async_will_remove_from_hass(self)
CalculatedState _async_calculate_state(self)
bool _default_to_device_class_name(self)
None async_removed_from_registry(self)
tuple[str, dict[str, Any], Mapping[str, Any]|None, str|None, int|None] __async_calculate_state(self)
str|None _name_translation_key(self)
bool use_device_name(self)
int|None supported_features(self)
None add_to_platform_start(self, HomeAssistant hass, EntityPlatform platform, asyncio.Semaphore|None parallel_updates)
None _call_on_remove_callbacks(self)
Mapping[str, Any]|None extra_state_attributes(self)
None _async_verify_state_writable(self)
None async_on_remove(self, CALLBACK_TYPE func)
None async_internal_will_remove_from_hass(self)
None schedule_update_ha_state(self, bool force_refresh=False)
str|None translation_key(self)
None async_internal_added_to_hass(self)
None _async_registry_updated(self, Event[er.EventEntityRegistryUpdatedData] event)
None async_device_update(self, bool warning=True)
str|None _unit_of_measurement_translation_key(self)
str|None unit_of_measurement(self)
bool has_entity_name(self)
__combined_unrecorded_attributes
None async_remove(self, *bool force_remove=False)
None async_registry_entry_updated(self)
None __async_remove_impl(self, bool force_remove)
DeviceInfo|None device_info(self)
bool entity_registry_enabled_default(self)
str|None _device_class_name(self)
None _async_device_registry_updated(self, Event[EventDeviceRegistryUpdatedData] event)
bool _async_update_ha_state_reported
str|None suggested_object_id(self)
None async_added_to_hass(self)
str|UndefinedType|None name(self)
None async_set_context(self, Context context)
str|None _friendly_name_internal(self)
None _async_process_registry_update_or_remove(self, Event[er.EventEntityRegistryUpdatedData] event)
None async_turn_off(self, **Any kwargs)
None turn_off(self, **Any kwargs)
None async_turn_on(self, **Any kwargs)
None async_toggle(self, **Any kwargs)
None turn_on(self, **Any kwargs)
Literal["on", "off"]|None state(self)
None toggle(self, **Any kwargs)
HassJobType get_hassjob_callable_job_type(Callable[..., Any] target)
ReleaseChannel get_release_channel()
AreaRegistry async_get(HomeAssistant hass)
None async_setup(HomeAssistant hass)
str|None get_device_class(HomeAssistant hass, str entity_id)
dict[str, EntityInfo] entity_sources(HomeAssistant hass)
int get_supported_features(HomeAssistant hass, str entity_id)
Any|None get_capability(HomeAssistant hass, str entity_id, str capability)
str generate_entity_id(str entity_id_format, str|None name, list[str]|None current_ids=None, HomeAssistant|None hass=None)
str|None get_unit_of_measurement(HomeAssistant hass, str entity_id)
str async_generate_entity_id(str entity_id_format, str|None name, Iterable[str]|None current_ids=None, HomeAssistant|None hass=None)
CALLBACK_TYPE async_track_device_registry_updated_event(HomeAssistant hass, str|Iterable[str] device_ids, Callable[[Event[EventDeviceRegistryUpdatedData]], Any] action, HassJobType|None job_type=None)
CALLBACK_TYPE async_track_entity_registry_updated_event(HomeAssistant hass, str|Iterable[str] entity_ids, Callable[[Event[EventEntityRegistryUpdatedData]], Any] action, HassJobType|None job_type=None)
None report_non_thread_safe_operation(str what)
str async_suggest_report_issue(HomeAssistant|None hass, *Integration|None integration=None, str|None integration_domain=None, str|None module=None)
str ensure_unique_string(str preferred_string, Iterable[str]|KeysView[str] current_strings)