跳转到内容

包(Packages)

当你拥有许多 ESPHome 设备(或在大规模生产和分发它们)时,一个常见的需求往往会出现: 配置模块化。你可能想把配置拆分成通用(组)元素,构建 成可重用的部分,随后可以被许多/所有设备使用。只有你的配置中独特的片段 才会保留在任何给定设备的 YAML 配置文件中。

这可以通过 ESPHome 的 packages 功能实现。

来自包的所有定义将以非破坏性方式与你的设备的主配置合并。这 允许覆盖包中所包含的配置(的部分)。你主配置中的替换 (substitutions)将覆盖包中同名的替换。

字典按键逐个合并。组件列表按组件 ID 合并(如果指定)。其他列表按 连接方式合并。所有其他配置值都被后面的值替换。

ESPHome 使用 !include 从其他文件”引入”包;此功能在 !include 中有描述。

packages: 键的值可以是:

  • 一个有效的包引用列表
  • 一个将键映射到包引用的映射。使用映射时,键仅作参考,本身没有意义。

下面显示了所有格式的示例。

考虑以下示例,作者将公共配置片段(如 Wi-Fi 和 API)放入基础文件, 然后在主配置中用一些设备特定配置进行扩展。

注意 device_base.yaml 中描述 api 组件的配置片段是如何与主配置文件中的操作 定义合并的。

# 在 config.yaml 中
packages: # 作为列表
- !include common/wifi.yaml
- !include common/device_base.yaml
api:
actions:
- action: start_laundry
then:
- switch.turn_on: relay
# 任何其他配置...
# 在 wifi.yaml 中
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# 在 device_base.yaml 中
esphome:
name: ${node_name}
esp32:
board: wemos_d1_mini32
logger:
api:
encryption:
key: !secret api_encryption_key

也可以利用正确的配置语法从 Git 仓库加载包。 替换(Substitutions) 可以在远程包内部使用,这允许用户 用他们自己的替换值在本地覆盖它们。

NOTE

远程包中不能有 secret 查找。它们应改为在打包的 YAML 中使用带 可选默认值的替换,本地设备 YAML 可以使用来自本地密钥的值来设置。

# Git 仓库示例,作为映射
packages:
# 简写形式 github://username/repository/[folder/]file-path.yml[@branch-or-tag]
remote_package_shorthand: github://esphome/non-existant-repo/file1.yml@main
remote_package_files:
url: https://github.com/esphome/non-existant-repo
files: [file1.yml, file2.yml] # 可选;如果未指定,将包含所有文件
ref: main # 可选
refresh: 1d # 可选
remote_package_files2:
url: https://github.com/esphome/non-existant-repo
files:
- path: file1.yml
vars:
a: 1
b: 2
- path: file1.yml # 同一个文件可以用不同的 vars 多次指定。
vars:
a: 3
b: 4
- file2.yml
ref: main # 可选
refresh: 1d # 可选

使用简写形式 URL 时,支持以下公共代码托管网站:

对于每个包:

  • url (必选, string): 仓库的 URL。

  • path (可选, string): 包含文件的公共基础路径。

  • username (可选, string): 如果需要身份验证,用于身份验证的用户名。

  • password (可选, string): 如果需要身份验证,用于身份验证的密码。

  • files (可选): 要包含的文件列表。可以是以下之一:

    • 文件路径列表
    • 包含 path 和 vars 的对象列表
  • ref (可选, string): 从仓库拉取内容时使用的 Git 引用。

  • refresh (可选, Time): 从仓库刷新内容的间隔。

由于包是通过 !include 系统合并的,因此可以向它们提供变量。这意味着 包可以用作模板,允许将复杂或重复的配置存储在包文件中, 然后多次并入配置。

包还可以包含一个 defaults 块,为 !include 块未提供的变量提供替换。

例如,如果配置需要支持三扇使用 gpio 开关平台和 time_based 窗帘平台的车库门,可以像这样构建:

# 在 config.yaml 中
packages:
left_garage_door: !include
file: garage-door.yaml
vars:
door_name: Left
middle_garage_door: !include
file: garage-door.yaml
vars:
door_name: Middle
right_garage_door: !include
file: garage-door.yaml
vars:
door_name: Right
# 在 garage-door.yaml 中
switch:
- name: ${door_name} Garage Door Switch
platform: gpio
# ...

你可以根据条件包含一个包,或者通过将你的包用 !include 加载 到一个替换变量中(而不是放在 packages 下)来动态选择一个包:

# 在 config.yaml 中
substitutions:
left_garage_door: !include
file: garage-door.yaml
vars:
door_name: Left
enable_extra_door: true
packages:
extra_door: ${ left_garage_door if enable_extra_door else {} }

上面利用了 Jinja 表达式 来确定 left_garage_door 包是否真正被包含。enable_extra_door 也可以从 命令行 设置。

替换可以直接用在 !include 的文件名中,允许你根据 替换值动态选择加载哪个文件:

config.yaml
substitutions:
platform: esp32
packages:
hardware: !include device-${platform}.yaml
light:
- platform: status_led
name: "Board Status"
pin: $LED_GPIO
device-esp32.yaml
substitutions:
LED_GPIO: GPIO2
esp32:
board: esp32dev

ESPHome 会在加载文件之前展开 ${platform},因此在上面的示例中,device-${platform}.yaml 解析为 device-esp32.yaml。ESPHome 中任何有效的替换表达式——包括 Jinja 表达式——都可以用在文件名中。

NOTE

使用 YAML 合并键(<<: !include filename.yaml)时不支持文件名替换。

要对包含的配置进行更改或添加额外配置,可以使用 !extend config_id,其中 config_id 是要修改配置的 ID。

例如,为配置之间共享的公共 uptime 传感器设置特定的更新间隔:

# 在 common.yaml 中
captive_portal:
sensor:
- platform: uptime
id: uptime_sensor
update_interval: 5min
packages:
- !include common.yaml
sensor:
- id: !extend uptime_sensor
update_interval: 10s

也支持 LVGL 风格的配置层级:

# 在 interface.yaml 中
lvgl:
pages:
- id: main_page
widgets:
- label:
id: title_label
text: "Main Page"
packages:
- !include interface.yaml
lvgl:
pages:
- id: !extend main_page
widgets:
- label:
id: !extend title_label
text: "New Title"
text_color: red

!extend 也适用于替换和 Jinja:

substitutions:
switches:
- left_switch
- right_switch
- center_switch
mains_switch: 1
switch:
- platform: gpio
id: left_switch
pin: 1
- platform: gpio
id: right_switch
pin: 2
- platform: gpio
id: center_switch
pin: 3
- id: !extend ${ switches[mains_switch] }
name: "Mains switch"

要从包含的配置中移除现有条目,可以使用 !remove [config_id],其中 config_id 是 要修改条目的 ID。

例如,移除配置之间共享的公共 uptime 传感器:

packages:
- !include common.yaml # 参见上文
sensor:
- id: !remove uptime_sensor

在 LVGL 风格的配置中移除一个项目:

packages:
- !include interface.yaml # 参见上文
lvgl:
pages:
- id: !extend main_page
widgets:
- label:
id: !remove title_label

移除特定设备的 captive portal:

packages:
- !include common.yaml # 参见上文
captive_portal: !remove

只为特定设备移除一个属性:

packages:
common: !include common.yaml # 参见上文
sensor:
- id: !extend uptime_sensor
update_interval: !remove

!remove 也适用于替换:

substitutions:
disable_reboot: true
switch:
- platform: restart
id: restart_switch
name: "Living Room Restart"
- id: !remove ${disable_reboot and "restart_switch"}