获取数据
你的集成需要从API获取数据,以便能够将其提供给Home Assistant。这个API可以通过网络(本地或云端)、套接字、通过USB记忆棒暴露的串行端口等提供。
推送与轮询
API有多种不同的形式,但核心上可分为两类:推送和轮询。
- 推送:我们订阅一个API,当有新数据可用时,API会通知我们。它将更改推送给我们。推送API很棒,因为它们消耗的资源较少。当发生更改时,我们可以收到更改通知,而不必重新获取所有数据并查找更改。由于实体可以被禁用,你应该确保你的实体在
async_added_to_hass
回调中进行订阅,并在移除时取消订阅。 - 轮询:我们将按指定的时间间隔从API获取最新数据。然后,你的集成将把这些数据提供给它的实体,实体再将数据写入Home Assistant。
因为轮询非常常见,Home Assistant默认假设你的实体基于轮询。如果不是这种情况,从Entity.should_poll
属性返回False
。当你禁用轮询时,你的集成将负责调用其中一种方法,以指示Home Assistant是时候将实体状态写入Home Assistant了:
- 如果你在异步函数中执行且不需要调用实体更新方法,调用Entity.async_write_ha_state()
。这是一个异步回调,它将在向事件循环让步的同时将状态写入状态机。
- Entity.schedule_update_ha_state(force_refresh=False)
/ Entity.async_schedule_update_ha_state(force_refresh=False)
将安排实体更新。如果force_refresh
设置为True
,Home Assistant将在写入状态之前调用你的实体更新方法(update()
/ async_update()
)。
轮询API端点
我们将在这里解释几种不同的API类型以及将它们集成到Home Assistant中的最佳方法。请注意,一些集成将遇到以下几种类型的组合。
为所有实体协调进行单一API轮询获取数据
这个API将有一个单一的方法来获取Home Assistant中所有实体的数据。在这种情况下,我们将希望对这个端点进行单一的定期轮询,然后在有新数据可用时立即通知实体。
Home Assistant提供了一个DataUpdateCoordinator
类来帮助你尽可能高效地管理这个过程。
“使用DataUpdateCoordinator
的示例集成”。
from datetime import timedelta
import logging
import async_timeout
from homeassistant.components.light import LightEntity
from homeassistant.core import callback
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
UpdateFailed,
)
from.const import DOMAIN
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, entry, async_add_entities):
"""配置项示例。"""
# 假设API对象在这里由__init__.py存储
my_api = hass.data[DOMAIN][entry.entry_id]
coordinator = MyCoordinator(hass, my_api)
# 获取初始数据,以便在实体订阅时有数据可用
#
# 如果刷新失败,async_config_entry_first_refresh将
# 抛出ConfigEntryNotReady异常,设置将稍后重试
#
# 如果你不想在失败时重试设置,使用
# coordinator.async_refresh()代替
#
await coordinator.async_config_entry_first_refresh()
async_add_entities(
MyEntity(coordinator, idx) for idx, ent in enumerate(coordinator.data)
)
class MyCoordinator(DataUpdateCoordinator):
"""我的自定义协调器。"""
def __init__(self, hass, my_api):
"""初始化我的协调器。"""
super().__init__(
hass,
_LOGGER,
# 数据名称。用于日志记录目的。
name="My sensor",
# 轮询间隔。只有在有订阅者时才会轮询。
update_interval=timedelta(seconds=30),
)
self.my_api = my_api
async def _async_update_data(self):
"""从API端点获取数据。
这是预处理数据到查找表的地方,
以便实体可以快速查找它们的数据。
"""
try:
# 注意:asyncio.TimeoutError和aiohttp.ClientError已经
# 由数据更新协调器处理。
async with async_timeout.timeout(10):
# 获取活动上下文变量,以限制从API获取的数据
# 注意:如果没有必要或无法限制从API检索的数据,则不需要使用上下文。
listening_idx = set(self.async_contexts())
return await self.my_api.fetch_data(listening_idx)
except ApiAuthError as err:
# 抛出ConfigEntryAuthFailed将取消未来的更新
# 并启动一个配置流程,源为SOURCE_REAUTH(async_step_reauth)
raise ConfigEntryAuthFailed from err
except ApiError as err:
raise UpdateFailed(f"与API通信时出错:{err}")
class MyEntity(CoordinatorEntity, LightEntity):
"""使用CoordinatorEntity的实体。
CoordinatorEntity类提供:
should_poll
async_update
async_added_to_hass
available
"""
def __init__(self, coordinator, idx):
"""将协调器传递给CoordinatorEntity。"""
super().__init__(coordinator, context=idx)
self.idx = idx
@callback
def _handle_coordinator_update(self) -> None:
"""处理来自协调器的更新数据。"""
self._attr_is_on = self.coordinator.data[self.idx]["state"]
self.async_write_ha_state()
async def async_turn_on(self, **kwargs):
"""打开灯。
示例方法,展示如何请求数据更新。
"""
# 执行打开操作。
#...
# 更新数据
await self.coordinator.async_request_refresh()
为每个单独实体单独轮询
一些API将为每个设备提供一个端点。有时可能无法将API中的设备映射到单个实体。如果你从单个API设备端点创建多个实体,请参阅上一节。
如果你可以将一个设备端点精确映射到单个实体,你可以在update()
/ async_update()
方法中获取这个实体的数据。确保轮询设置为True
,Home Assistant将定期调用这个方法。
如果你的实体在首次写入Home Assistant之前需要获取数据,将update_before_add=True
传递给add_entities
方法:add_entities([MyEntity()], update_before_add=True)
。
你可以通过在你的平台中定义SCAN_INTERVAL
常量来控制集成的轮询间隔。注意不要将其设置得太低。这将占用Home Assistant中的资源,可能会使承载API的设备不堪重负,或者可能导致你被云API阻止。允许的最小值是5秒。
from datetime import timedelta
SCAN_INTERVAL = timedelta(seconds=5)
推送API端点
如果你有一个推送数据的API端点,如果你愿意,仍然可以使用数据更新协调器。通过不向构造函数传递轮询参数update_method
和update_interval
来实现。
当新数据到达时,使用coordinator.async_set_updated_data(data)
将数据传递给实体。如果在轮询的协调器上使用此方法,它将重置到下次轮询数据的时间。
请求并行性
注意:这是一个高级主题。
Home Assistant有内置逻辑来确保集成不会过度请求API并消耗Home Assistant中的所有可用资源。这个逻辑是围绕限制并行请求的数量构建的。这个逻辑在服务调用和实体更新期间自动使用。
Home Assistant通过为每个集成维护一个信号量来控制并行更新(对update()
的调用)的数量。例如,如果信号量允许1个并行连接,如果有一个正在进行中,更新和服务调用将等待。如果值为0,集成本身负责在必要时限制并行请求的数量。
平台的并行请求默认值是根据添加到Home Assistant的第一个实体决定的。如果实体定义了async_update
方法,则为0,否则为1(这是一个遗留决策)。
平台可以通过在其平台中定义PARALLEL_UPDATES
常量(例如rflink/light.py
)来覆盖默认值。
总结
此页面主要介绍了在Home Assistant集成中获取数据的相关内容,包括推送与轮询两种方式获取数据的特点及使用场景,详细阐述了轮询API端点中为所有实体协调进行单一API轮询和为每个单独实体单独轮询的方法及示例代码,提及推送API端点如何使用数据更新协调器处理新数据,最后介绍了Home Assistant控制集成请求并行性的逻辑及相关参数设置。