使用异步
Home Assistant 提供了兼容层 API,但直接使用异步 Core 会快得多。Core 中的大多数组件都已经改写为异步版本,包括实体组件 helper(如灯、开关等的基础设施)、脚本、组和自动化。
与 Core 交互
Home Assistant Core 中的大多数方法都同时提供两种形式:异步版本,以及供其他线程调用的同步包装版本。后者本质上是以线程安全方式调用异步版本的包装器。
因此,如果你是在 callback 或 coroutine 中与 Core(hass 对象)交互,应优先使用以 async_ 开头的方法。如果你要调用某个 async_ 函数,你当前的函数本身也必须是 coroutine。
实现异步组件
要让组件支持异步,请实现 async_setup。
def setup(hass, config):
"""Set up component."""
# Code for setting up your component outside of the event loop.
改为:
async def async_setup(hass, config):
"""Set up component."""
# Code for setting up your component inside of the event loop.
实现异步平台
对于平台,也支持异步初始化。你需要实现 async_setup_platform,而不是 setup_platform。
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up platform."""
# Code for setting up your platform outside of the event loop.
改为:
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up platform."""
# Code for setting up your platform inside of the event loop.
与原始参数相比,唯一的区别是 add_entities 被异步安全的 callback async_add_entities 取代。
实现异步实体
你可以通过把更新方法改成异步形式,让实体支持异步更新。但前提是该实体依赖的库本身也支持异步。
class MyEntity(Entity):
def update(self):
"""Retrieve latest state."""
self._state = fetch_state()
改为:
class MyEntity(Entity):
async def async_update(self):
"""Retrieve latest state."""
self._state = await async_fetch_state()
要确保实体上定义的所有属性都不会触发 I/O。所有数据都必须在更新方法中获取并缓存在实体对象上。因为这些属性会从事件循环中被读取,一旦属性访问触发 I/O,就会导致 Home Assistant Core 卡住,直到 I/O 完成。
从线程调用异步函数
有时你会处于某个线程中,但需要调用一个只能异步使用的函数。Home Assistant 提供了一些 helper 来处理这种情况。
在下面的示例中,say_hello 会调度 async_say_hello,并阻塞当前线程直到函数执行完成并返回结果。
import asyncio
def say_hello(hass, target):
return asyncio.run_coroutine_threadsafe(
async_say_hello(hass, target), hass.loop
).result()
async def async_say_hello(hass, target):
return f"Hello {target}!"
警告: 请务必小心。如果异步函数内部又使用了 executor job,可能会导致死锁。
从异步代码调用同步函数
如果你在异步上下文中运行,有时仍然需要调用同步函数。写法如下:
# hub.update() is a sync function.
result = await hass.async_add_executor_job(hub.update)
从异步代码启动独立任务
如果你想启动一个不会阻塞当前异步上下文的任务,可以把它创建为事件循环中的任务,让它并行执行。
hass.async_create_task(async_say_hello(hass, target))