四 Home Assistant 配置流程(Config Flow)
配置项使用数据流入口框架来定义它们的配置流程。配置流程需要在集成文件夹中的config_flow.py文件中定义,扩展homeassistant.config_entries.ConfigFlow并在继承ConfigFlow时传递一个域键(domain key)。# 它创建的条目的模式版本# 如果版本更改,Home Assistant将调用你的迁移方法。
配置流程(Config Flow)
集成可以通过用户界面进行设置,方法是添加对配置流程的支持以创建配置项。想要支持配置项的组件需要定义一个配置流程处理程序(Config Flow Handler)。这个处理程序将管理根据用户输入、发现或其他来源(如Home Assistant操作系统)创建的配置项。
配置流程处理程序控制存储在配置项中的数据。这意味着在Home Assistant启动时无需验证配置是否正确。它还可以防止破坏性更改,因为如果版本更改,我们将能够将配置项迁移到新格式。
实例化处理程序时,Home Assistant将确保加载所有依赖项并安装组件的要求。
更新清单
你需要更新集成清单,告知Home Assistant你的集成有一个配置流程。这可以通过在清单中添加config_flow: true来完成(文档)。
定义配置流程
配置项使用数据流入口框架来定义它们的配置流程。配置流程需要在集成文件夹中的config_flow.py文件中定义,扩展homeassistant.config_entries.ConfigFlow并在继承ConfigFlow时传递一个域键(domain key)。
from homeassistant import config_entries
from.const import DOMAIN
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Example config flow."""
# 它创建的条目的模式版本
# 如果版本更改,Home Assistant将调用你的迁移方法
VERSION = 1
MINOR_VERSION = 1
更新清单并创建config_flow.py后,你需要运行python3 -m script.hassfest(仅一次),以便Home Assistant为你的集成激活配置项。
定义步骤
你的配置流程需要定义配置流程的步骤。每个步骤由一个唯一的步骤名称(step_id)标识。步骤回调方法遵循async_step_<step_id>模式。数据流入口文档描述了步骤的不同返回值。以下是如何定义用户步骤的示例:
import voluptuous as vol
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, info):
if info is not None:
pass # TODO: 处理信息
return self.async_show_form(step_id="user", data_schema=vol.Schema({vol.Required("password"): str}))
有一些步骤名称保留供系统使用:
步骤名称 | 描述 |
---|---|
bluetooth | 如果你的集成通过清单中的bluetooth指定的蓝牙被发现,则调用此步骤。 |
discovery(已弃用) | 如果你的集成被发现且未定义匹配步骤,则调用此步骤。 |
dhcp | 如果你的集成通过清单中的dhcp指定的DHCP被发现,则调用此步骤。 |
hassio | 如果你的集成通过Supervisor插件被发现,则调用此步骤。 |
homekit | 如果你的集成通过清单中的homekit指定的HomeKit被发现,则调用此步骤。 |
mqtt | 如果你的集成通过清单中的mqtt指定的MQTT被发现,则调用此步骤。 |
ssdp | 如果你的集成通过清单中的ssdp指定的SSDP/UPnP被发现,则调用此步骤。 |
usb | 如果你的集成通过清单中的usb指定的USB被发现,则调用此步骤。 |
user | 当用户通过用户界面启动流程或当发现且未定义匹配和发现步骤时调用此步骤。 |
zeroconf | 如果你的集成通过清单中的zeroconf指定的Zeroconf/mDNS被发现,则调用此步骤。 |
reauth | 如果您的集成表明需要重新身份验证,例如由于凭据过期,则无提示。 |
import | 保留用于从YAML配置迁移到配置条目。 |
唯一ID
配置流程可以为配置流程附加一个唯一ID(必须是字符串),以避免同一设备被设置两次。
设置唯一ID后,如果针对此唯一ID有另一个流程正在进行,它将立即中止。如果已经存在此ID的配置项,也可以快速中止。配置项将获取创建它们的流程的唯一ID。
在配置流程步骤中调用:
await self.async_set_unique_id(device_unique_id)
self._abort_if_unique_id_configured()
如果配置流程中止,“strings.json”中止部分中键为“already_configured”的文本资源将作为中止原因在界面中显示给用户。
{"config":{"abort":{"already_configured":"设备已配置"}}
通过设置唯一ID,用户可以选择忽略你的配置项的发现。这样,他们就不会再为此烦恼。如果集成使用蓝牙、DHCP、HomeKit、Zeroconf/mDNS、USB或SSDP/UPnP进行发现,则必须提供唯一ID。
如果没有唯一ID,或者可以省略“bluetooth”、“dhcp”、“zeroconf”、“hassio”、“homekit”、“ssdp”、“usb”和“discovery”步骤,即使它们在集成清单中已配置。在这种情况下,当发现项目时将调用“user”步骤。
或者,如果集成并非始终能获取唯一ID(例如,多个设备,有些有,有些没有),只要还没有配置该集成的任何实例,就可以使用一个助手来仍然允许发现。
if device_unique_id:
await self.async_set_unique_id(device_unique_id)
await self._async_handle_discovery_without_unique_id()
唯一ID要求
唯一ID用于将配置项与底层设备或API匹配。唯一ID必须稳定,用户不应能够更改它,并且必须是字符串。
唯一ID可用于在设备访问详细信息更改时更新配置项数据。例如,对于通过本地网络通信的设备,如果由于新的DHCP分配而更改了IP地址,集成可以使用唯一ID使用以下代码片段更新主机:
await self.async_set_unique_id(serial_number)
self._abort_if_unique_id_configured(updates={CONF_HOST: host, CONF_PORT: port})
唯一ID的可接受来源示例
- 设备的序列号
- MAC地址:使用“homeassistant.helpers.device_registry.format_mac”格式化;仅从设备API或发现处理程序获取MAC地址。依赖于读取arp缓存或本地网络访问(如“getmac”)的工具在所有支持的网络环境中可能无法正常工作,因此不可接受。
- 表示纬度和经度或其他唯一地理位置的字符串
- 物理打印在设备上或烧录到EEPROM中的唯一标识符
有时可接受的本地设备唯一ID来源
- 主机名:如果主机名的子集包含可接受来源之一,则可以使用该部分
有时可接受的云服务唯一ID来源
- 电子邮件地址:必须规范化为小写
- 用户名:如果用户名不区分大小写,则必须规范化为小写。
- 账户ID:不得有冲突
不可接受的唯一ID来源
- IP地址
- 设备名称
- 用户可以更改的主机名
- URL
取消忽略
你的配置流程可以通过在配置流程中实现“unignore”步骤来添加对重新发现先前被忽略的条目的支持。
async def async_step_unignore(self, user_input):
unique_id = user_input["unique_id"]
await self.async_set_unique_id(unique_id)
# 待办:发现设备并找到与唯一ID匹配的设备。
return self.async_show_form(…)
发现步骤
当集成被发现时,将使用发现信息调用相应的发现步骤(即“async_step_dhcp”或“async_step_zeroconf”)。该步骤必须检查以下事项:
-
确保没有其他此配置流程的实例正在设置发现的设备。如果有多种方法可以发现设备在网络上,就可能发生这种情况。
-
- 在大多数情况下,在流上设置唯一ID并检查是否已经存在具有相同唯一ID的配置条目就足够了,如关于管理配置流中的唯一ID一节所述 在某些情况下,无法确定唯一ID,或者唯一ID不明确,因为不同的发现源可能有不同的计算方法。在这种情况下: 在流上实现def
is_matching(self,other_flow:self)->bool方法。
调用hass.config_intes.flow.async_has_matching_flow(self)。
然后,您的流的is_matching方法将为其他正在进行的流调用一次。
- 在大多数情况下,在流上设置唯一ID并检查是否已经存在具有相同唯一ID的配置条目就足够了,如关于管理配置流中的唯一ID一节所述 在某些情况下,无法确定唯一ID,或者唯一ID不明确,因为不同的发现源可能有不同的计算方法。在这种情况下: 在流上实现def
-
确保设备尚未设置。
-
调用发现步骤不应导致完成的流程和配置项。始终要与用户确认。
无需身份验证的可发现集成
如果你的集成可被发现且无需任何身份验证,你将能够使用内置的可发现流程。此流程提供以下功能:
- 在完成配置流程之前检测网络上是否可以发现设备/服务。
- 支持所有基于清单的发现协议。
- 限制为仅1个配置项。由配置项负责发现所有可用设备。
要开始使用,运行“python3 -m script.scaffold config_flow_discovery”并按照说明操作。这将创建使用发现配置集成所需的所有样板代码。
通过OAuth2进行配置
Home Assistant对使用OAuth2授权框架提供账户链接的集成具有内置支持。为了能够利用此功能,你需要以一种允许Home Assistant负责刷新令牌的方式构建你的Python API库。有关如何做到这一点,请参阅我们的API库指南。
内置的OAuth2支持可以直接与使用应用程序凭据平台本地配置的客户端ID / 秘密以及Home Assistant云账户链接服务一起使用。此服务允许用户将其账户与集中管理的客户端ID/秘密链接。如果你希望你的集成成为此服务的一部分
要开始使用,运行“python3 -m script.scaffold config_flow_oauth2”并按照说明操作。这将创建使用OAuth2配置集成所需的所有样板代码。
翻译
配置流程处理程序的翻译在组件翻译文件“strings.json”的“config”键下定义。以下是Hue组件的示例:
{
"title":"Philips Hue Bridge",
"config":{
"step":{
"init":{
"title":"选择Hue桥接器",
"data":{
"host":"主机"
}
},
"link":{
"title":"链接中心",
"description":"按下桥接器上的按钮以将Philips Hue注册到Home Assistant。\n\n"
}
},
"error":{
"register_failed":"注册失败,请重试",
"linking":"发生未知链接错误。"
},
"abort":{
"discover_timeout":"无法发现Hue桥接器",
"no_bridges":"未发现Philips Hue桥接器",
"all_configured":"所有Philips Hue桥接器已配置",
"unknown":"发生未知错误",
"cannot_connect":"无法连接到桥接器",
"already_configured":"桥接器已配置"
}
}
}
当翻译合并到Home Assistant中时,它们将自动上传到Lokalise,翻译团队将帮助将它们翻译成其他语言。在本地开发时,你需要运行“python3 -m script.translations develop”来查看对“strings.json”所做的更改。有关翻译Home Assistant的更多信息。
配置项迁移
如前所述,每个配置项都分配有一个版本。这样,当配置项模式更改时,就能够将配置项数据迁移到新格式。
迁移可以通过在组件的“init.py”文件中实现“async_migrate_entry”函数以编程方式处理。如果迁移成功,该函数应返回“True”。
版本由主版本和次版本组成。如果次版本不同但主版本相同,即使集成未实现“async_migrate_entry”,集成设置也将被允许继续。这意味着次版本升级是向后兼容的,而主版本升级如果用户在没有从备份还原其配置的情况下降级Home Assistant Core,则会导致集成设置失败。
# 示例迁移函数
async def async_migrate_entry(hass, config_entry: ConfigEntry):
"""迁移旧条目。"""
_LOGGER.debug("从版本 %s 迁移", config_entry.version)
if config_entry.version > 1:
# 这意味着用户已从未来版本降级
return False
if config_entry.version == 1:
new = {**config_entry.data}
if config_entry.minor_version < 2:
# 待办:根据版本1.2中的更改修改配置项数据
pass
if config_entry.minor_version < 3:
# 待办:根据版本1.3中的更改修改配置项数据
pass
hass.config_entries.async_update_entry(config_entry, data=new, minor_version=3, version=1)
_LOGGER.debug("迁移到版本 %s.%s 成功", config_entry.version, config_entry.minor_version)
return True
重新配置
配置条目可以通过添加重新配置步骤来允许重新配置。这为集成提供了一种方法,允许用户更改配置条目数据,而无需实现用于更改设置数据的OptionsFlow,这不是可选的。
这不是为了处理身份验证问题或重新配置此类问题。为此,我们有重新认证步骤,在身份验证出现问题的情况下,应该实现该步骤以自动启动。
import voluptuous as vol
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow for Example integration."""
async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
if user_input is not None:
# TODO: process user input
self.async_set_unique_id(user_id)
self._abort_if_unique_id_mismatch()
return self.async_update_reload_and_abort(
self._get_reconfigure_entry(),
data_updates=data,
)
return self.async_show_form(
step_id="reconfigure",
data_schema=vol.Schema({vol.Required("input_parameter"): str}),
)
成功后,重新配置流程将更新当前条目并中止;他们不应该创建新条目。这通常是通过返回self.async_update_reload_and_abort帮助程序完成的。自动测试应验证重新配置流是否更新了现有的配置条目,并且不会创建其他条目。
可以使用if self.source==source_reconfigure来检查您是否处于重新配置流中。也可以使用self访问相应的配置条目_get_configure_entry()。应使用wait self.async_set_unique_id后跟self来确保unique_id不变_abort_if_nique_id_mismatch()。
重新身份验证
为了在集成质量等级上取得进步,需要妥善处理身份验证错误,如无效、过期或撤销的令牌。以下是如何按照构建Python库中的模式为“script.scaffold”创建的OAuth流添加重新身份验证的示例。
此示例在“init.py”中的配置项设置中捕获身份验证异常,并指示用户访问集成页面以重新配置集成。
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
from homeassistant.core import HomeAssistant
from. import api
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""设置配置项。"""
# 待办:替换为实际的API设置和异常处理
auth = api.AsyncConfigEntryAuth(...)
try:
await auth.refresh_tokens()
except TokenExpiredError as err:
raise ConfigEntryAuthFailed(err) from err
# 待办:继续进行组件设置
“config_flow.py”中的流处理程序还需要一些额外步骤来支持重新身份验证,包括显示确认、启动重新身份验证流程、更新现有配置项以及重新加载以再次调用设置。
class OAuth2FlowHandler(
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
):
"""处理OAuth2身份验证的配置流。"""
reauth_entry: ConfigEntry | None = None
async def async_step_reauth(self, user_input=None):
"""在API身份验证错误时执行重新身份验证。"""
self.reauth_entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"]
)
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(self, user_input=None):
"""通知用户需要重新身份验证的对话框。"""
if user_input is None:
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema({}),
)
return await self.async_step_user()
async def async_oauth_create_entry(self, data: dict) -> dict:
"""创建OAuth配置项或更新现有项以进行重新身份验证。"""
if self.reauth_entry:
return self.async_update_reload_and_abort(
self.reauth_entry,
data=data,
)
return await super().async_oauth_create_entry(data)
默认情况下,“async_update_reload_and_abort”助手方法在更新和重新加载后以“reauth_successful”中止流程。
根据集成的详细信息,可能还有其他考虑因素,例如确保在重新身份验证中使用相同的账户,或处理多个配置项。
重新身份验证确认对话框需要在“strings.json”中为重新身份验证确认和成功对话框添加额外定义:
{
"config": {
"step": {
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",,# 待办:替换为集成的名称
# TODO: Replace with the name of the integration
"description": "示例集成需要重新验证你的账户"
}
},
"abort": {
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
},
}
身份验证失败(如撤销的OAuth令牌)手动测试可能有点棘手。一个建议是复制“config/.storage/core.config_entries”并根据要测试的场景手动更改“access_token”、“refresh_token”和“expires_at”的值。然后,你可以逐步完成重新身份验证流程并确认值已被替换为新的有效令牌。
自动化测试应验证重新身份验证流程更新现有配置项且不创建额外项。
测试你的配置流程
具有配置流程的集成需要对“config_flow.py”中的所有代码进行全面测试,才能被接受到核心中。测试你的代码包含有关如何生成覆盖报告的更多详细信息。
总结
本文档详细阐述了Home Assistant中配置流程相关的内容,包括如何在集成中启用配置流程、定义配置流程及其步骤、处理唯一ID相关问题、支持取消忽略功能、处理发现步骤、应对无需身份验证和通过OAuth2配置的情况、进行配置项迁移、重新身份验证以及测试配置流程等,为开发者在Home Assistant集成开发中实现配置流程提供了全面且详细的指导,有助于确保集成的配置流程功能正常且符合要求。
更多推荐
所有评论(0)