mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-07-17 14:26:30 +00:00
Add entity description docs (#1963)
* Add entity description docs * Fix typing * Set available True * Correct English Co-authored-by: Allen Porter <allen@thebends.org> * Sort imports * Mention description key --------- Co-authored-by: Allen Porter <allen@thebends.org>
This commit is contained in:
parent
b615e0b645
commit
55ee5f1d9b
@ -233,32 +233,117 @@ entity class for details.
|
|||||||
If an integration needs to access its own properties it should access the property (`self.name`), not the class or instance attribute (`self._attr_name`).
|
If an integration needs to access its own properties it should access the property (`self.name`), not the class or instance attribute (`self._attr_name`).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### Entity description
|
||||||
|
|
||||||
|
The third way of setting entity properties is to use an entity description. To do this set an attribute named `entity_description` on the `Entity` instance with an `EntityDescription` instance. The entity description is a dataclass with attributes corresponding to most of the available `Entity` properties. Each entity integration that supports an entity platform, eg the `switch` integration, will define their own `EntityDescription` subclass that should be used by implementing platforms that want to use entity descriptions.
|
||||||
|
|
||||||
|
By default the `EntityDescription` instance has one required attribute named `key`. This is a string which is meant to be unique for all the entity descriptions of an implementing platform. A common use case for this attribute is to include it in the `unique_id` of the described entity.
|
||||||
|
|
||||||
|
The main benefit of using entity descriptions is that it defines the different entity types of a platform in a declarative manner, making the code much easier to read when there are many different entity types.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
The below code snippet gives an example of best practices for when to implement property functions, and when to use class or instance attributes.
|
The below code snippet gives an example of best practices for when to implement property functions, when to use class or instance attributes and when to use entity descriptions.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
class SomeEntity():
|
from __future__ import annotations
|
||||||
_attr_device_class = SensorDeviceClass.TEMPERATURE # This will be common to all instances of SomeEntity
|
|
||||||
def __init__(self, device):
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from example import ExampleDevice, ExampleException
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import (
|
||||||
|
SensorDeviceClass,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
SensorStateClass,
|
||||||
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import (
|
||||||
|
EntityCategory,
|
||||||
|
UnitOfElectricCurrent,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
|
from .const import DOMAIN, LOGGER
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ExampleSensorEntityDescriptionMixin:
|
||||||
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
|
value_fn: Callable[[ExampleDevice], StateType]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ExampleSensorEntityDescription(
|
||||||
|
SensorEntityDescription, ExampleSensorEntityDescriptionMixin
|
||||||
|
):
|
||||||
|
"""Describes Example sensor entity."""
|
||||||
|
|
||||||
|
exists_fn: Callable[[ExampleDevice], bool] = lambda _: True
|
||||||
|
|
||||||
|
|
||||||
|
SENSORS: tuple[ExampleSensorEntityDescription, ...] = (
|
||||||
|
ExampleSensorEntityDescription(
|
||||||
|
key="estimated_current",
|
||||||
|
native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
|
||||||
|
device_class=SensorDeviceClass.CURRENT,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=lambda device: device.power,
|
||||||
|
exists_fn=lambda device: bool(device.max_power),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up Example sensor based on a config entry."""
|
||||||
|
device: ExampleDevice = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
async_add_entities(
|
||||||
|
ExampleSensorEntity(device, description)
|
||||||
|
for description in SENSORS
|
||||||
|
if description.exists_fn(device)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExampleSensorEntity(SensorEntity):
|
||||||
|
"""Represent an Example sensor."""
|
||||||
|
|
||||||
|
entity_description: ExampleSensorEntityDescription
|
||||||
|
_attr_entity_category = (
|
||||||
|
EntityCategory.DIAGNOSTIC
|
||||||
|
) # This will be common to all instances of ExampleSensorEntity
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, device: ExampleDevice, entity_description: ExampleSensorEntityDescription
|
||||||
|
) -> None:
|
||||||
|
"""Set up the instance."""
|
||||||
self._device = device
|
self._device = device
|
||||||
|
self.entity_description = entity_description
|
||||||
self._attr_available = False # This overrides the default
|
self._attr_available = False # This overrides the default
|
||||||
self._attr_name = device.get_friendly_name()
|
self._attr_unique_id = f"{device.serial}_{entity_description.key}"
|
||||||
|
|
||||||
# The following should be avoided:
|
def update(self) -> None:
|
||||||
if some_complex_condition and some_other_condition and something_is_none_and_only_valid_after_update and device_available:
|
"""Update entity state."""
|
||||||
...
|
try:
|
||||||
|
|
||||||
def update(self)
|
|
||||||
if self.available # Read current state, no need to prefix with _attr_
|
|
||||||
# Update the entity
|
|
||||||
self._device.update()
|
self._device.update()
|
||||||
|
except ExampleException:
|
||||||
if error:
|
if self.available: # Read current state, no need to prefix with _attr_
|
||||||
|
LOGGER.warning("Update failed for %s", self.entity_id)
|
||||||
self._attr_available = False # Set property value
|
self._attr_available = False # Set property value
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self._attr_available = True
|
||||||
# We don't need to check if device available here
|
# We don't need to check if device available here
|
||||||
self._attr_is_on = self._device.get_state() # Update "is_on" property
|
self._attr_native_value = self.entity_description.value_fn(
|
||||||
|
self._device
|
||||||
|
) # Update "native_value" property
|
||||||
```
|
```
|
||||||
|
|
||||||
## Lifecycle hooks
|
## Lifecycle hooks
|
||||||
|
Loading…
x
Reference in New Issue
Block a user