跳转到内容

替换

ESPHome 有一种强大的方式来减少配置文件中的重复:替换。 通过替换,你可以为同一种类的节点维护一个通用的源文件,并在需要时替换相应的表达式。

substitutions:
bme280_temperature_offset: "-1.0"
sensor:
- platform: bme280_i2c
temperature:
name: BME280 Temperature
filters:
- offset: ${bme280_temperature_offset}

在顶层的 substitutions 部分,你可以放入任意数量的键值对。在验证配置之前,ESPHome 会自动将所有出现的替换替换为其值。替换的语法基于 bash,且区分大小写:$substitution_key 或 ${substitution_key}(二者相同)。

替换变量可以是任何有效的 YAML 类型,例如:

substitutions:
device:
name: Kitchen AC
port: 12
enabled: true
color: "yellow"
unused_pins: [12, 23, 27]

会执行两次替换,从而实现复合替换:

substitutions:
foo: yellow
bar_yellow_value: !secret yellow_secret
bar_green_value: !secret green_secret
something:
test: ${bar_${foo}_value}

支持上述写法是为了向后兼容。建议从现在起使用键值字典:

substitutions:
foo: yellow
bar:
yellow: !secret yellow_secret
green: !secret green_secret
something:
test: ${bar[foo]}

可以在 ${ ... } 语法中使用简单的 Jinja 表达式和过滤器。

所有替换变量都会通过其名称在 Jinja 表达式中可访问。

如果替换变量是键值字典,你可以用点表示法访问其成员:${ device.name }, 或索引 ${ device["name"] }。

列表可以索引:${ unused_pins[2] }

substitutions:
native_width: 480
native_height: 320
high_dpi: true
scale: 1.5
sensor_pin:
number: 3
inverted: true
debug_label:
width: 200
height: 20
enabled: true
display:
- platform: ili9xxx
dimensions:
width: ${native_width * 2 if high_dpi else native_width}
height: ${native_height * 2 if high_dpi else native_height}
lvgl:
widgets:
- label:
id: debug_info
hidden: ${not debug_label.enabled}
width: ${ (debug_label.width * scale) | round | int }
height: ${ (debug_label.height * scale) | round | int }
text: |
High DPI is ${high_dpi and "enabled" or "disabled"}.
binary_sensor:
- platform: gpio
name: Binary sensor on pin ${sensor_pin.number}
pin: ${sensor_pin}

请注意,在其他项目中 Jinja 使用 {{ ... }} 语法作为表达式分隔符。 在 ESPHome 中,我们已经将 Jinja 配置为使用 ${...},因此它与 现有的替换语法相同,并且可以避免与 Home Assistant 自身对 Jinja 的使用产生冲突。

要了解可以使用哪些类型的表达式和过滤器, 请参阅 Jinja 表达式 文档。

除了 Jinja 的 +、-、*、/ 等原生运算符外,Python 的 math 库作为模块暴露出来:

substitutions:
x: 20
y: 50
lvgl:
widgets:
- label:
x: $x
y: $y
text: Distance is ${math.sqrt(x*x+y*y)}.

要查看可用的数学函数,请参阅 Python math 库 文档。

除了 Jinja 表达式,ESPHome 还支持若干可在替换中使用的内置函数。

  • ord 返回给定字符的 Unicode 码点。示例:ord("A") == 65
  • chr 返回给定 Unicode 码点对应的字符。示例:chr(65) == "A"
  • len 返回字符串的长度。示例:len("Hello") == 5

你可以通过在任何值前面使用 !literal 标签来阻止 ESPHome 替换变量或处理 Jinja:

substitutions:
value: "Test Value"
lvgl:
widgets:
- label:
text: !literal "This is a ${value}"

在上面的示例中,text 属性的值将字面地为 This is a ${value}。

ESPHome 的 !include 接受一个可以在被包含文件内进行替换的变量列表。文件名本身也可以包含替换表达式,从而允许你动态选择要加载的文件。

binary_sensor:
- platform: gpio
id: button1
pin: GPIOXX
on_multi_click: !include { file: on-multi-click.yaml, vars: { id: 1 } } # 行内语法
- platform: gpio
id: button2
pin: GPIOXX
on_multi_click: !include # 多行语法
file: on-multi-click.yaml
vars:
id: 2

on-multi-click.yaml:

- timing: !include click-single.yaml
then:
- mqtt.publish:
topic: ${device_name}/button${id}/status
payload: single
- timing: !include click-double.yaml
then:
- mqtt.publish:
topic: ${device_name}/button${id}/status
payload: double

你还可以使用替换来构造要包含的文件名:

substitutions:
platform: esp32
binary_sensor:
- platform: gpio
id: button1
pin: GPIOXX
on_multi_click: !include { file: on-multi-click-${platform}.yaml, vars: { id: 1 } }
- platform: gpio
id: button2
pin: GPIOXX
on_multi_click: !include
file: on-multi-click-${platform}.yaml
vars:
id: 2

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

你可以通过添加带有 KEY 和 VALUE 参数的 -s 开关,从命令行定义或覆盖替换。这将覆盖替换 KEY 并为其赋值 VALUE。该开关可以多次使用。考虑以下 example.yaml 文件:

substitutions:
name: my_default_name
esphome:
name: $name

…以及以下命令:

Terminal window
esphome -s name my_device01 config example.yaml

你将得到类似以下的输出:

substitutions:
name: my_device01
esphome:
name: my_device01
# ...

命令行替换优先于配置文件中的替换。这可用于创建通用的 “模板” 配置文件(如上例中的 example.yaml),多个设备可以共用它们,并在命令行提供 替换。

此外,你可以使用 YAML 插入运算符 << 语法,创建一个 YAML 文件,从中派生多个节点:

# 在 common.yaml 中
esphome:
name: $devicename
# ...
sensor:
- platform: dht
# ...
temperature:
name: Temperature
humidity:
name: Humidity
# 在 nodemcu1.yaml 中
substitutions:
devicename: nodemcu1
<<: !include common.yaml

NOTE

不支持在 YAML 插入运算符(<<: !include)的 !include 文件名中使用替换。

TIP

若要从仪表板中隐藏这些基础文件,你可以

  • 将它们放入子目录中(仪表板只显示顶层目录中的文件)
  • 在文件名前加一个点,例如 .base.yaml