HA 配置地狱名不虚传:一台设备跨 5 个文件,我踩过的坑一次讲完
【免费下载链接】core:house_with_garden: Open source home automation that puts local control and privacy first.项目地址: https://gitcode.com/GitHub_Trending/co/core
如果你在网上搜索 Home Assistant(以下简称 HA),会看到两种截然相反的叙事:一边是"开源智能家居天花板、本地优先、隐私安全"的溢美之词,另一边则是"配置体系十分混乱,一个设备的完美接入需要涉及多个配置"的真实吐槽。这句吐槽出自一篇 2017 年的接入教程,八年过去,文章里的感慨依然精准戳中每一位新用户的痛点——而它并不夸张:在当前的 HA 里,一台设备的状态和自动化逻辑,确实可能散落在五六个不同的文件里。好消息是,这些"散落"大多有迹可循,且官方在源码层面提供了相当完善的归拢机制,只是很少有人把它们讲透。
这篇文章将直接深入 core 仓库的源码,把"配置到底散在哪、为什么散、怎么根治"一次讲完。
先看入口:configuration.yaml 只是"总装车间"
HA 的配置不是一块巨石,而是一条装配流水线。入口文件 configuration.yaml 在源码中定义为YAML_CONFIG_FILE,而官方生成的全新默认配置只有寥寥几行:
# Loads default set of integrations. Do not remove. default_config: # Load frontend themes from the themes folder frontend: themes: !include_dir_merge_named themes automation: !include automations.yaml script: !include scripts.yaml scene: !include scenes.yaml这段内容来自 homeassistant/config.py 中的DEFAULT_CONFIG常量。注意最后三行:自动化、脚本、场景的配置在默认情况下就根本不放在 configuration.yaml 里,而是被!include指令分流到了automations.yaml、scripts.yaml、scenes.yaml三个独立文件——这三个文件名同样定义在源码中(AUTOMATION_CONFIG_PATH、SCRIPT_CONFIG_PATH、SCENE_CONFIG_PATH)。
这就是第一层"配置地狱"的来源:当你从 UI 里创建一个自动化时,HA 会自动把它写进automations.yaml;你手动改 configuration.yaml 时它毫无反应。很多新手在 configuration.yaml 里翻了半天找不到自己刚建的自动化,原因就是它在另一个文件里。
一台设备跨 5 个文件:拆开看每一份都在记录什么
以一台典型的智能灯为例,它的完整生命周期会同时涉及以下文件:
- configuration.yaml:若设备走传统 platform 配置方式,这里需要声明平台与实体参数;即便走新式流程,这里也可能残留
homeassistant:等全局设置; - .storage/core.config_entries.json:这是现代 HA 配置的主战场。源码 homeassistant/config_entries.py 中
STORAGE_KEY = "core.config_entries",ConfigEntry类(config_entries.py第 406 行)保存了domain、data、options、unique_id等字段——你在 UI 里添加集成时,写入的就是这个文件; - .storage/core.entity_registry.json:实体注册表,
STORAGE_KEY = "core.entity_registry"(homeassistant/helpers/entity_registry.py),记录每个实体的entity_id、所属 config entry、别名、禁用状态等; - .storage/core.device_registry.json:设备注册表(
homeassistant/helpers/device_registry.py中STORAGE_KEY = "core.device_registry"),把多个实体归并到同一物理设备下; - automations.yaml / scripts.yaml / scenes.yaml:该设备相关的自动化、脚本与场景。
也就是说,"这台灯是谁""它有哪些属性""它属于哪台设备""开关它的逻辑""联动它的场景"被拆进了至少五个文件,而.storage目录本身(源码定义于 homeassistant/helpers/storage.py 的STORAGE_DIR = ".storage")还承载着core.config_entries、core.entity_registry、core.device_registry等若干 JSON 文件。
踩坑点随之而来:很多人习惯备份"配置目录",以为拷走 configuration.yaml 就够了,结果恢复后发现集成全丢——因为真正的核心数据在.storage里。更有甚者,手贱编辑.storage下的 JSON,因为格式问题(比如多写一个逗号)导致整个 HA 起不来。
!include 家族:官方给出的"拆分工具箱"
面对配置膨胀,HA 在 YAML 层提供了四件套:!include、!include_dir_list、!include_dir_named、!include_dir_merge_named。默认配置里的frontend: themes: !include_dir_merge_named themes就是一个例子:themes/目录下每个 YAML 文件都会按文件名作为键合并进themes字典——这让"每个主题一个文件"成为可能。
但!include的坑也在这里:它只能做"文件级"的合并,无法做"键级"的覆盖。两个文件里如果都定义了sensor:,后者会直接冲突;如果你天真地把一个设备的所有配置塞进一个文件再!include它,当第二个设备也这样干时,冲突立刻出现。这也是为什么很多人的 configuration.yaml 最终退化成一个"巨型文件"——不是不想拆,是!include拆不动。
真正的官方答案:packages 包机制
如果你翻源码 homeassistant/config.py 的merge_packages_config函数(第 660 行),会发现官方其实提供了远比!include高级的归拢机制——homeassistant: packages。
它在 core 的配置模式中定义(homeassistant/core_config.py 的_PACKAGES_CONFIG_SCHEMA),允许你在 configuration.yaml 里这样组织:
homeassistant: packages: living_room_light: switch: - platform: your_platform ... automation: - alias: "客厅灯联动" ...merge_packages_config的注释直白地写着:"Merge packages into the top-level configuration. Ignores packages that cannot be setup."(把包合并进顶层配置,忽略无法加载的包,并原地修改 config)。它做的工作远不止字符串拼接:
- 逐包用
_validate_package_definition校验,非法包会被剔除并记录错误日志,而不是拖垮整个启动; - 对每个组件做智能合并:源码通过
PLATFORM_SCHEMA、CONFIG_SCHEMA甚至集成自定义的PACKAGE_MERGE_HINT判断该组件应该按"列表追加"(merge_list)还是"字典深合并"(_recursive_merge)处理; - 重复键会被检测并单独告警(
duplicate key),避免静默覆盖。
这套机制的意义在于:一个设备相关的 platform、automation、script 可以真正归入同一个"包",跨文件拆分由系统代劳。这是!include无法比拟的——!include只是把文件粘进来,packages 则是把语义单元合并进去。
不过 packages 也有自己的坑:它依然在 configuration.yaml 的homeassistant:节点下,文件本身还是会长大;且它只合并"YAML 配置型"组件,对走 config entry 流程(写入.storage)的现代集成无能为力。所以 packages 适合"收编"旧式 platform 配置和自动化,而不是万能钥匙。
常用插件的配置坑:File editor、Samba、Node-RED
社区里被问烂的三大件是 File editor、Samba Share 与 Node-RED——它们与"配置地狱"的关系是:它们都是"拆墙工具",而拆墙之后你把东西堆到了哪里,决定了你接下来的痛苦程度。
- File editor(文件编辑器):它解决的是"在网页上直接编辑 configuration.yaml"的问题。坑在于:编辑器里能看到的只是配置目录的 YAML 文件,
.storage下的 JSON 同样可见可编辑,但没有校验——你随手改坏一个逗号,重启即红灯。社区公认的稳妥姿势是:只改 YAML,JSON 一律不动,改完先跑一次配置检查。 - Samba Share(网络共享):它把配置目录映射成 SMB 共享,方便用 Windows/macOS 的编辑器改文件。坑有两个:一是权限——Samba 容器与 HA 核心进程的用户/组不一致时,会出现"改完保存成功但 HA 读不到",本质上是在不同 UID/GID 间写文件;二是多端同时编辑——编辑器自动保存与 HA 自动重载叠加,极易把文件写成半个中间态,然后喜提 YAML 解析错误。
- Node-RED(自动化工具):它把自动化逻辑从 HA 搬进了自己的流程编辑器,看似绕开了
automations.yaml。坑在于:Node-RED 的节点里频繁出现的实体 ID 与entity_registry强绑定,一旦你在 HA 里重命名实体或删除重建集成,Node-RED 里所有引用了旧 ID 的节点瞬间全部失效,而报错信息往往只给一句 "entity not found"。排查时既要在.storage/core.entity_registry.json里对 ID,又要在 Node-RED 流程里改引用,又是一场跨文件追踪。
这三者的共同教训是:插件只是"打开了编辑通道",并没有改变配置分发的底层事实——你仍然需要先搞清楚目标配置属于 YAML 文件还是.storage,再决定怎么改。
蓝图:把"复用"从文件层面抽出来
如果你要配置 N 个同型号传感器、M 个一模一样的自动化,蓝图(Blueprint)是根治"重复配置"的正解,而且它是官方一等公民:仓库源码 homeassistant/components/blueprint/ 完整实现了整套机制,BLUEPRINT_FOLDER = "blueprints"(见 blueprint/const.py),用户蓝图按域存放在blueprints/automation/、blueprints/script/等目录下(blueprint/models.py 第 216 行按domain拼接路径)。
蓝图的核心是"模板 + 输入参数"。仓库自带的 motion_light.yaml 是一个教科书级示例,它把"人来灯亮、人走灯灭"的完整自动化抽象成三个输入:
blueprint: name: Motion-activated Light domain: automation input: motion_entity: name: Motion Sensor selector: entity: filter: - device_class: motion domain: binary_sensor light_target: name: Light selector: target: entity: domain: light no_motion_wait: name: Wait time default: 120 selector: number: min: 0 max: 3600 unit_of_measurement: seconds triggers: trigger: state entity_id: !input motion_entity from: "off" to: "on" actions: - action: light.turn_on target: !input light_target - wait_for_trigger: trigger: state entity_id: !input motion_entity from: "on" to: "off" - delay: !input no_motion_wait - action: light.turn_off target: !input light_target注意两个细节:!input语法让自动化正文可以引用输入参数;selector让 UI 里填参数时直接弹出实体选择器。仓库里另一份 notify_leaving_zone.yaml 则展示了更进阶的用法——用variables与模板在蓝图内部做条件判断(区分离开 home 特殊 zone 与普通 zone)。
蓝图的价值在于:一份蓝图文件,M 台设备复用。实例化时 UI 只把use_blueprint加输入参数写进automations.yaml,正文模板全部来自蓝图文件(源码 blueprint/models.py 的BlueprintInputs.inputs_with_default负责输入与默认值合并)。于是"多设备同逻辑"的自动化不再需要在 configuration.yaml 里复制粘贴 N 遍——这正是配置散乱的另一大来源。
治理配置的完整建议
结合源码与实战,给出一套可落地的"反配置地狱"清单:
- 区分两类配置:YAML 文件(
configuration.yaml、automations.yaml、scripts.yaml、scenes.yaml)与.storageJSON(config entries、entity registry、device registry)。前者适合手工管理,后者默认交给 UI,绝不手改; - 把"按设备/按房间"作为归拢单位:用
homeassistant: packages把一个设备相关的 platform、automation、script 收进同一个包,利用merge_packages_config的智能合并避免!include的文件级冲突; - 用蓝图消灭重复自动化:同型号多设备一律走蓝图实例化,正文只留
use_blueprint与输入参数; - 插件只作通道,不作数据层:File editor、Samba 改文件前先跑配置检查;Node-RED 里避免硬编码实体 ID,尽量通过事件/状态引用解耦;
- 备份要对症:完整备份必须包含
.storage,仅备份 YAML 等于没备份。
配置分散的真相是:HA 把"设备身份"(registry)与"行为逻辑"(automation/script/blueprint)刻意分开了,这本是工程上的合理解耦——只有当你把每一份文件的职责摸清,它才从"地狱"变回"体系"。下一次配置报错时,先别急着怪 HA,问自己一句:这个实体,到底该去哪个文件里找它?
【免费下载链接】core:house_with_garden: Open source home automation that puts local control and privacy first.项目地址: https://gitcode.com/GitHub_Trending/co/core
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考