十八 Home Assistant 实体

实体

有关实体的一般介绍,请参阅实体架构

基本实现

以下是一个在内存中跟踪其状态的示例开关实体。此外,示例中的开关代表设备的主要功能,这意味着实体与其设备具有相同的名称。

有关如何为实体命名的信息,请参阅实体命名

from homeassistant.components.switch import SwitchEntity

class MySwitch(SwitchEntity):
    _attr_has_entity_name = True

    def __init__(self):
        self._is_on = False
        self._attr_device_info =...  # 用于自动设备注册
        self._attr_unique_id =...

    @property
    def is_on(self):
        """如果开关当前处于打开或关闭状态。"""
        return self._is_on

    def turn_on(self, **kwargs):
        """打开开关。"""
        self._is_on = True

    def turn_off(self, **kwargs):
        """关闭开关。"""
        self._is_on = False

这就是构建一个开关实体的全部内容!继续阅读以了解更多信息或查看视频教程。

更新实体

实体代表一个设备。有多种策略可使你的实体与设备的状态保持同步,最常用的是轮询。

轮询

通过轮询,Home Assistant会不时(取决于组件的更新间隔)询问实体以获取最新状态。当 should_poll 属性返回 True(默认值)时,Home Assistant将轮询实体。你可以使用 update() 或异步方法 async_update() 实现更新逻辑。此方法应从设备获取最新状态并将其存储在实例变量中,以便属性返回它。

订阅更新

当你订阅更新时,你的代码负责让Home Assistant知道有更新可用。确保 should_poll 属性返回 False

每当你从订阅中收到新状态时,可以通过调用 schedule_update_ha_state() 或异步回调 async_schedule_update_ha_state() 告诉Home Assistant有更新可用。如果你希望Home Assistant在将更新写入Home Assistant之前调用你的更新方法,请将布尔值 True 传递给该方法。

通用属性

实体基类具有所有Home Assistant实体共有的一些属性。这些属性可以添加到任何实体,无论其类型如何。所有这些属性都是可选的,不需要实现。

这些属性在状态写入状态机时总是被调用。

提示:属性应始终仅返回内存中的信息,而不进行I/O操作(如网络请求)。实现 update()async_update() 来获取数据。

因为这些属性在状态写入状态机时总是被调用,所以在属性中尽量少做工作很重要。

为避免在属性方法中进行计算,可以设置相应的实体类或实例属性,或者如果值从不改变,可以使用实体描述。

名称类型默认值描述
assumed_state布尔值False如果状态基于我们的假设而不是从设备读取,则返回 True
attribution字符串或 NoneNoneAPI提供者要求的品牌文本。
available布尔值True指示Home Assistant是否能够读取状态并控制底层设备。
device_class字符串或 NoneNone设备的额外分类。每个域指定自己的分类。设备类可能带有对测量单位和支持功能的额外要求。
entity_picture字符串或 NoneNone要为实体显示的图片的URL。
extra_state_attributes字典或 NoneNone要存储在状态机中的额外信息。它需要是进一步解释状态的信息,而不应是固件版本等静态信息。
has_entity_name布尔值False如果实体的 name 属性代表实体本身(新集成必需),则返回 True。这将在下面详细解释。
name字符串或 NoneNone实体的名称。避免硬编码自然语言名称,而应使用翻译后的名称
should_poll布尔值TrueHome Assistant是否应向实体检查更新状态。如果设置为 False,实体将需要通过调用其中一个计划更新方法来通知Home Assistant新的更新。
state字符串、整数、浮点数或 NoneNone实体的状态。在大多数情况下,这由域基实体实现,集成不应实现。
supported_features整数或 NoneNone实体支持的标志功能。域指定自己的。
translation_key字符串或 NoneNone用于在集成的 strings.json 的实体部分查找实体状态翻译以及将状态转换为匹配图标的键。
translation_placeholders字典或 NoneNone翻译实体名称的占位符定义

危险:允许更改 device_classsupported_features 或任何包含在域的 capability_attributes 中的属性。然而,由于这些实体属性通常根本不期望更改,并且一些实体使用者可能无法自由更新它们,我们建议仅在绝对必要时并以适度的间隔进行更改。

例如,此类更改将导致语音助手集成与支持的云服务重新同步。

危险:当 extra_state_attributes 也频繁更改时,生成大量状态更改的实体可能会迅速增加数据库的大小。通过删除非关键属性或创建额外的传感器实体,尽量减少这些实体的 extra_state_attributes 的数量。

注册表属性

以下属性用于填充实体和设备注册表。每次将实体添加到Home Assistant时都会读取它们。这些属性仅在 unique_id 不为 None 时有效。

名称类型默认值描述
device_info字典或 NoneNone用于自动设备注册设备注册表描述符。
entity_categoryEntityCategoryNoneNone非主要实体的分类。对于允许更改设备配置的实体(例如开关实体,使其能够打开和关闭开关的背景照明),设置为 EntityCategory.CONFIG。对于公开设备的某些配置参数或诊断信息但不允许更改的实体(例如显示RSSI或MAC地址的传感器),设置为 EntityCategory.DIAGNOSTIC
entity_registry_enabled_default布尔值True指示实体在首次添加到实体注册表时应启用还是禁用。这包括快速变化的诊断实体或可能不太常用的实体。例如,显示RSSI或电池电压的传感器通常应设置为 False,以防止这些实体产生不必要的(记录的)状态更改或UI混乱。
entity_registry_visible_default布尔值True指示实体在首次添加到实体注册表时应隐藏还是可见。
unique_id字符串或 NoneNone此实体的唯一标识符。它在一个平台(如 light.hue)内必须是唯一的。它不应由用户配置或更改。了解更多
高级属性

以下属性也可在实体上使用。然而,它们仅用于高级用途,应谨慎使用。这些属性在状态写入状态机时总是被调用。

名称类型默认值描述
capability_attributes字典或 NoneNone存储在实体注册表中的状态属性。此属性由域基实体实现,集成不应实现。
force_update布尔值False即使数据相同,也将每次更新写入状态机。例如,当你直接从连接的传感器而不是缓存中读取值时使用。谨慎使用,会使状态机产生大量数据。
icon字符串或 NoneNone在前端使用的图标。不建议使用此属性。有关使用图标的更多信息
state_attributes字典或 NoneNone基域的状态属性。此属性由域基实体实现,集成不应实现。
unit_of_measurement字符串或 NoneNone实体状态的测量单位。在大多数情况下,例如对于 numbersensor 域,这由域基实体实现,集成不应实现。
系统属性

以下属性由Home Assistant使用和控制,集成不应覆盖它们。

名称类型默认值描述
enabled布尔值True指示实体在实体注册表中是否启用。如果平台不支持实体注册表,它也返回 True。禁用的实体将不会添加到Home Assistant中。
实体命名

避免将实体的名称设置为硬编码的英语字符串,而应进行翻译。不应翻译的名称示例包括专有名词、型号名称和第三方库提供的名称。

一些实体根据其设备类自动命名,这包括 binary_sensorbuttonnumbersensor 实体,在许多情况下不需要命名。例如,一个未命名的传感器,其设备类设置为 temperature,将被命名为“Temperature”。

has_entity_nameTrue(新集成必需)

实体的 name 属性仅标识实体所代表的数据点,不应包括设备名称或实体类型。因此,对于代表其设备功率使用情况的传感器,这将是“Power usage”。

如果实体代表设备的单个主要功能,实体通常应使其 name 属性返回 None。设备的“主要功能”例如智能灯泡的 LightEntity

friendly_name 状态属性通过将实体名称与设备名称组合生成,如下所示:

  • 实体不是设备的成员:friendly_name = entity.name
  • 实体是设备的成员且 entity.name 不为 Nonefriendly_name = f"{device.name} {entity.name}"
  • 实体是设备的成员且 entity.nameNonefriendly_name = f"{device.name}"

实体名称应以大写字母开头,其余单词为小写(除非是专有名词或大写缩写)。

示例:作为设备主要功能的开关实体
注意:示例使用类属性来实现属性,有关实现属性的其他方法,请参阅属性实现。注意:示例不完整,必须实现 unique_id 属性,并且实体必须与设备一起注册

from homeassistant.components.switch import SwitchEntity

class MySwitch(SwitchEntity):
    _attr_has_entity_name = True
    _attr_name = None

示例:不是设备主要功能或不是设备一部分的开关实体
注意:示例使用类属性来实现属性,有关实现属性的其他方法,请参阅属性实现。注意:如果实体是设备的一部分,则必须实现 unique_id 属性,并且实体必须与设备一起注册

from homeassistant.components.switch import SwitchEntity

class MySwitch(SwitchEntity):
    _attr_has_entity_name = True

    @property
    def translation_key(self):
        """返回用于翻译实体名称和状态的翻译键。"""
        return my_switch

示例:未翻译的、不是设备主要功能或不是设备一部分的开关实体

from homeassistant.components.switch import SwitchEntity

class MySwitch(SwitchEntity):
    _attr_has_entity_name = True

    @property
    def name(self):
        """实体的名称。"""
        return "Model X"
has_entity_name 未实现或为 False(已弃用)

实体的 name 属性可能是设备名称和实体所代表的数据点的组合。

属性实现
属性函数

为每个属性编写属性方法只需几行代码,例如

class MySwitch(SwitchEntity):
    @property
    def icon(self) -> str | None:
        """实体的图标。"""
        return "mdi:door"
 ...
实体类或实例属性

或者,一种更简短的形式是根据以下模式之一设置实体类或实例属性:

class MySwitch(SwitchEntity):
    _attr_icon = "mdi:door"
 ...
class MySwitch(SwitchEntity):
    def __init__(self, icon: str) -> None:
        self._attr_icon = icon
 ...

这与第一个示例完全相同,但依赖于基类中属性的默认实现。属性名称以 _attr_ 开头,后跟属性名称。例如,默认的 device_class 属性返回 _attr_device_class 类属性。

并非所有实体类都支持其特定实体属性的 _attr_ 属性,请参阅相应实体类的文档以获取详细信息。

提示:如果集成需要访问其自己的属性,它应该访问属性(self.name),而不是类或实例属性(self._attr_name)。

实体描述

设置实体属性的第三种方法是使用实体描述。为此,在实体实例上设置一个名为 entity_description 的属性,其值为一个 EntityDescription 实例。实体描述是一个数据类,其属性对应于大多数可用的实体属性。每个支持实体平台的实体集成(例如开关集成)将定义自己的 EntityDescription 子类,希望使用实体描述的实现平台应使用该子类。

默认情况下,EntityDescription 实例有一个必需的属性名为 key。这是一个字符串,对于实现平台的所有实体描述应是唯一的。此属性的一个常见用例是将其包含在被描述实体的 unique_id 中。

使用实体描述的主要好处是它以声明方式定义了平台的不同实体类型,当有许多不同的实体类型时,使代码更易于阅读。

示例
以下代码片段给出了何时实现属性函数、何时使用类或实例属性以及何时使用实体描述的最佳实践示例。

from __future__ import annotations
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(kw_only=True)
class ExampleSensorEntityDescription(SensorEntityDescription):
    """描述示例传感器实体。"""
    exists_fn: Callable[[ExampleDevice], bool] = lambda _: True
    value_fn: Callable[[ExampleDevice], StateType]

SENSORS = (
    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:
    """根据配置项设置示例传感器。"""
    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):
    """表示示例传感器。"""
    entity_description: ExampleSensorEntityDescription
    _attr_entity_category = (
        EntityCategory.DIAGNOSTIC
    )  # 这将是所有ExampleSensorEntity实例共有的
    def __init__(
        self, device: ExampleDevice, entity_description: ExampleSensorEntityDescription
    ) -> None:
        """设置实例。"""
        self._device = device
        self.entity_description = entity_description
        self._attr_available = False  # 这覆盖默认值
        self._attr_unique_id = f"{device.serial}_{entity_description.key}"

    def update(self) -> None:
        """更新实体状态。"""
        try:
            self._device.update()
        except ExampleException:
            if self.available:  # 读取当前状态,无需前缀 _attr_
                LOGGER.warning("更新 %s 失败", self.entity_id)
            self._attr_available = False  # 设置属性值
            return
        self._attr_available = True
        # 这里我们不需要检查设备是否可用
        self._attr_native_value = self.entity_description.value_fn(
            self._device
        )  # 更新“native_value”属性
生命周期钩子

使用这些生命周期钩子在实体发生某些事件时执行代码。所有生命周期钩子都是异步方法。

async_added_to_hass()

当实体分配了 entity_idhass 对象后,在首次写入状态机之前被调用。示例用途:恢复状态、订阅更新或设置回调/调度函数/监听器。

async_will_remove_from_hass()

当实体即将从Home Assistant中删除时被调用。示例用途:从服务器断开连接或取消订阅更新。

图标

Home Assistant中的每个实体都有一个图标,用作视觉指示器,以便在前端更轻松地识别实体。Home Assistant使用Material Design Icons图标集

在大多数情况下,Home Assistant会根据实体的域、device_class 和状态自动选择一个图标。如果可能,最好使用默认图标,以提供一致的体验并避免用户混淆。但是,可以覆盖默认图标并为实体提供自定义图标。

无论提供的图标如何,用户始终可以在前端根据自己的喜好自定义图标。

有两种方法可以为实体提供自定义图标,要么通过提供图标翻译,要么通过提供图标标识符。

图标翻译

这是为实体提供自定义图标的首选和最现代的方法。图标翻译的工作方式与我们的常规翻译类似,但不是翻译实体的状态,而是将实体的状态翻译为图标。

实体的 translation_key 属性定义要使用的图标翻译。此属性用于在集成的 icons.json 文件的实体部分查找翻译。

为了区分实体及其翻译,提供不同的翻译键。以下示例显示了一个 Moon 域的 sensor 实体的 icons.json,其 translation_key 属性设置为 phase

{
  "entity": {
    "sensor": {
      "phase": {
        "default": "mdi:moon",
        "state": {
          "new_moon": "mdi:moon-new",
          "first_quarter": "mdi:moon-first-quarter",
          "full_moon": "mdi:moon-full",
          "last_quarter": "mdi:moon-last-quarter"
        }
      }
    }
  }
}

请注意,图标以 mdi: 加上一个标识符开头。当实体的状态不在 state 部分时,使用 default 图标。state 部分是可选的,如果未提供,将对所有状态使用 default 图标。

在前端显示状态属性图标的情况下,也可以为实体状态属性提供图标。例如气候预设和风扇模式。无法为其他状态属性提供图标。以下示例为一个 climate 实体提供图标,其 translation_key 属性设置为 ubercool。此实体有一个 preset_mode 状态属性,可以设置为 vacationnight。前端将在例如气候卡中使用这些图标。

{
  "entity": {
    "climate": {
      "ubercool": {
        "state_attributes": {
          "preset_mode": {
            "default": "mdi:confused",
            "state": {
              "vacation": "mdi:umbrella-beach",
              "night": "mdi:weather-night"
            }
          }
        }
      }
    }
  }
}
图标属性

为实体提供图标的另一种方法是设置实体的 icon 属性,该属性返回一个引用 mdi 图标的字符串。由于此属性是一个方法,因此可以根据自定义逻辑返回不同的图标,这与图标翻译不同。例如,可以根据状态计算图标,如下例所示,或者根据不属于实体状态的某些内容返回不同的图标。

class MySwitch(SwitchEntity):
    @property
    def icon(self) -> str | None:
        """根据时间确定实体的图标。"""
        if now().hour < 12:
            return "mdi:weather-night"
        return "mdi:weather-sunny"
...

无法使用 icon 属性为状态属性提供图标。请注意,不建议使用 icon 属性;建议使用上述图标翻译方法。

从记录器历史中排除状态属性

不适合记录状态历史的状态属性应通过将它们包含在 _entity_component_unrecorded_attributes_unrecorded_attributes 中来从状态历史记录中排除。

_entity_component_unrecorded_attributes: frozenset[str] 可以在基组件类中设置,例如在 light.LightEntity 中。

_unrecorded_attributes: frozenset[str] 可以在集成的平台中设置,例如在平台 hue.light 中定义的实体类中。

可以使用 MATCH_ALL 常量排除所有属性,而不是分别输入它们。这对于提供未知属性的集成或当你只想排除所有属性而无需分别输入它们时很有用。

使用 MATCH_ALL 常量不会停止记录 device_classstate_classunit_of_measurementfriendly_name,因为它们可能还有其他用途,因此不应从记录中排除。

从记录中排除的平台状态属性示例包括 image 实体的 entity_picture 属性,一段时间后该属性将无效,以及 fan 实体的 preset_modes 属性,该属性不太可能更改。从记录中排除的集成特定状态属性示例包括平台 trafikverket.camera 中的 descriptionlocation 状态属性,它们不会更改。

提示:_entity_component_unrecorded_attributes_unrecorded_attributes 必须声明为类属性;实例属性将被忽略。

更改实体模型

如果你想为实体或其任何子类型(灯光、开关等)添加新功能,你需要首先在我们的架构仓库中提出建议。只有在各种供应商中常见的功能添加才会被考虑。

总结

文档主要围绕Home Assistant中的实体展开详细阐述。首先介绍了实体的基本实现,包括以开关实体为例说明其构建方式及属性设置,接着阐述了更新实体的两种策略(轮询和订阅更新)及其原理和操作要点,然后依次说明了实体的通用属性(如 assumed_stateattribution 等)、注册表属性(如 device_infounique_id 等)、高级属性(如 capability_attributesforce_update 等)和系统属性(如 enabled)的定义、作用、默认值及使用注意事项,还涉及实体命名的规范和多种情况(如 has_entity_nameTrue 或其他情况),以及属性实现的三种方式(属性函数、实体类或实例属性、实体描述)并给出示例,之后讲述了实体的生命周期钩子(async_added_to_hass()async_will_remove_from_hass())及其用途,关于图标部分介绍了在Home Assistant中的作用、默认选择方式、自定义图标的两种方法(图标翻译和图标属性)及各自特点,最后提及从记录器历史中排除状态属性的方法和更改实体模型的相关要求。为开发者在Home Assistant中进行实体相关的开发工作提供了全面、深入且系统的指导。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值