跳转到内容

动作、触发器、条件

ESPHome 动作(actions)是我们让 ESPHome 设备执行操作的方式。

让我们从一个例子开始。假设你有一个包含以下内容的配置文件:

switch:
- platform: gpio
pin: GPIOXX
name: "Living Room Dehumidifier"
binary_sensor:
- platform: gpio
pin: GPIOXX
name: "Living Room Dehumidifier Toggle Button"

使用这个文件,你已经可以执行一些基本任务。你可以通过 Home Assistant 的前端控制客厅除湿机的开关状态。但在许多情况下,严格通过前端控制一切并不理想。这就是为什么你还在除湿机旁边安装了一个连接到 GPIOXX 引脚的简单按钮。只需按下这个按钮,就应该切换除湿机的状态。

你可以在 Home Assistant 的自动化引擎中编写自动化来完成此任务,但 IoT 设备不应依赖网络连接来执行其工作——尤其对于像开关除湿机这样简单的操作。

使用 ESPHome 的自动化引擎,你可以使用一种(希望)与 Home Assistant 同样易于使用的语法来定义自动化。例如,以下配置可以实现你对除湿机的期望行为:

switch:
- platform: gpio
pin: GPIOXX
name: "Living Room Dehumidifier"
id: dehumidifier1
binary_sensor:
- platform: gpio
pin: GPIOXX
name: "Living Room Dehumidifier Toggle Button"
on_press:
then:
- switch.toggle: dehumidifier1

让我们逐步了解这里发生的事情:

switch:
- platform: gpio
# ...
id: dehumidifier1

首先,我们必须给除湿机 switch 一个 ID,以便在自动化中引用它。

binary_sensor:
- platform: gpio
# ...
on_press:

现在我们给二进制传感器(代表按钮)附加一个特殊属性 on_press。这部分称为”触发器”。在此示例中,接下来的几行中的自动化将在有人开始按下按钮时执行。请注意,术语遵循鼠标按钮事件的命名方式。按下(press)发生在你开始按下按钮时。还有其他触发器可用,如 on_release、on_click 或 on_double_click。

# ...
on_press:
then:
- switch.toggle: dehumidifier1

现在来到实际的自动化块。使用 then,你可以告诉 ESPHome 按下时应该发生什么。在此块内,你可以定义多个将按顺序执行的”动作”。例如,switch.toggle 及其后面的行构成一个动作。每个动作用破折号分隔,只需添加另一个 - 即可按顺序执行多个动作,如下所示:

# ...
on_press:
then:
- switch.toggle: dehumidifier1
- delay: 2s
- switch.toggle: dehumidifier1

使用此自动化,按下按钮将使除湿机开启/关闭 2 秒,然后恢复到其原始状态。你还可以让单个触发器拥有多个自动化:

# ...
on_press:
- then:
- switch.toggle: dehumidifier1
- then:
- light.toggle: dehumidifier_indicator_light
# Same as:
on_press:
then:
- switch.toggle: dehumidifier1
- light.toggle: dehumidifier_indicator_light

作为最后一个示例,让我们让除湿机变得”智能”。当传感器报告的湿度高于 65% 时自动开启,当湿度降至 50% 以下时自动关闭:

sensor:
- platform: dht
humidity:
name: "Living Room Humidity"
on_value_range:
- above: 65.0
then:
- switch.turn_on: dehumidifier1
- below: 50.0
then:
- switch.turn_off: dehumidifier1
temperature:
name: "Living Room Temperature"

缩进真不少。😉

on_value_range 是传感器的一种特殊触发器,当传感器值在指定范围内/高于/低于指定范围时触发。在第一个示例中,范围定义为”大于或等于 65.0 的任何值”,第二个范围指任何 50% 或以下的(湿度)值。

最后,对于那些”纯”YAML 自动化还不够用的情况,ESPHome 还提供了另一个极其强大的工具:模板。

至此,ESPHome 中的动作介绍就结束了。它们是使用易于使用的语法自动化设备上几乎所有功能的强大工具。下面是一些常用动作的索引,你一定会发现它们对构建各种自动化非常有用(甚至必不可少)。

该动作将动作列表中下一个动作的执行延迟指定的时间段。

on_...:
then:
- switch.turn_on: relay_1
- delay: 2s
- switch.turn_off: relay_1
# Templated, waits for 1s (1000ms) only if a reed switch is active
- delay: !lambda "if (id(reed_switch).state) return 1000; else return 0;"

NOTE

这是一个”智能”异步延迟——延迟发生时,其他代码仍会在后台运行。使用 lambda 调用时,应以毫秒为单位返回延迟值。

该动作首先计算 condition:,如果条件返回 true 则执行 then: 分支,如果条件返回 false 则执行 else: 分支。

所选分支(then 或 else)执行完毕后,继续执行下一个动作。

例如,下面是一个自动化,它检查传感器值是否低于 30,如果是,则打开灯 5 秒钟。否则,立即关闭灯。

on_...:
then:
- if:
condition:
lambda: 'return id(some_sensor).state < 30;'
then:
- logger.log: "The sensor value is below 30!"
- light.turn_on: my_light
- delay: 5s
else:
- logger.log: "The sensor value is above 30!"
- light.turn_off: my_light

必须至少提供 condition、all 或 any 中的一项。

  • condition (可选, Condition):要检查的条件,用于确定执行哪个分支。 如果此处配置了条件列表,则所有条件都必须为 true,整体条件才为 true。

  • all (可选, Condition):接受一个条件列表,所有条件都必须为 true(因此等同于 condition)。

  • any (可选, Condition):接受一个条件列表;如果至少一个为 true,则条件为 true。

  • then (可选, Action):如果条件评估为 true 时执行的动作。默认为不执行任何操作。

  • else (可选, Action):如果条件评估为 false 时执行的动作。默认为不执行任何操作。

该动作执行任意一段 C++ 代码(参见 Lambda)。

on_...:
then:
- lambda: |-
id(some_binary_sensor).publish_state(false);

该动作允许你重复执行一个块指定次数。 例如,下面的自动化将闪烁灯五次。

on_...:
- repeat:
count: 5
then:
- lambda: ESP_LOGI("main", "Turning lights on for iteration [%d]", iteration);
- light.turn_on: some_light
- delay: 1s
- lambda: ESP_LOGI("main", "Turning lights off for iteration [%d]", iteration);
- light.turn_off: some_light
- delay: 10s
  • count (必选, int, 可模板化):动作应重复的次数。计数器可通过隐式脚本参数 iteration 在 lambda 中使用。

  • then (必选, Action):要重复执行的动作。

该动作允许你的自动化等待直到某个条件评估为 true。(这实际上是一种简写方式,等价于编写一个 then 块为空的 while 动作。)

# In a trigger:
on_...:
- logger.log: "Waiting for binary sensor"
- wait_until:
binary_sensor.is_on: some_binary_sensor
- logger.log: "Binary sensor is ready"

如果你想使用超时,则需要使用”condition”这个术语:

# In a trigger:
on_...:
- logger.log: "Waiting for binary sensor"
- wait_until:
condition:
binary_sensor.is_on: some_binary_sensor
timeout: 8s
- logger.log: "Binary sensor might be ready"
  • condition (必选, Condition):要等待变为 true 的条件。
  • timeout (可选, Time, 可模板化):超时前等待的时间。默认为永不超时。

该动作类似于 if 动作。while 动作在给定条件为 true 时循环执行一个块。

# In a trigger:
on_...:
- while:
condition:
binary_sensor.is_on: some_binary_sensor
then:
- logger.log: "Still executing"
- light.toggle: some_light
- delay: 5s
  • condition (必选, Condition):要检查的条件,用于确定是否执行。

  • then (必选, Action):要执行的动作,直到条件评估为 false。

使用此动作可以手动调用组件的 update() 方法。

请注意,这仅适用于某些组件类型,其他类型将导致编译错误。

on_...:
then:
- component.update: my_component
# The same as:
- lambda: 'id(my_component).update();'

使用此动作可以手动调用组件的 stop_poller() 方法。

执行此动作后,组件将停止刷新。

在轮询器暂停期间,仍然可以通过使用 component.update 触发按需更新。

请注意,这仅适用于 PollingComponent 类型,其他类型将导致编译错误。

on_...:
then:
- component.suspend: my_component
# The same as:
- lambda: 'id(my_component).stop_poller();'

使用此动作可以手动调用组件的 start_poller() 方法。

执行此动作后,组件将按原始的 update_interval 速率进行刷新。

这将允许组件在定义的间隔恢复自动更新。

此动作还允许更改更新间隔,无需先暂停,直接替换轮询器。

请注意,这仅适用于 PollingComponent 类型,其他类型将导致编译错误。

on_...:
then:
- component.resume: my_component
# The same as:
- lambda: 'id(my_component).start_poller();'
# Change the poller interval
on_...:
then:
- component.resume:
id: my_component
update_interval: 15s

“条件”为你的设备提供了一种仅在满足特定(一组)条件时才执行动作的方式。

检查条件的组合。all 是 and 的同义词,any 是 or 的同义词。 all 和 any 也可以直接替代 condition 使用。

on_...:
then:
- if:
condition:
# Same syntax for `and` as well as `xor` conditions
or:
- binary_sensor.is_on: some_binary_sensor
- binary_sensor.is_on: other_binary_sensor
# ...
- if:
any:
- not:
binary_sensor.is_off: some_binary_sensor
- binary_sensor.is_on: some_other_sensor

允许你检查给定条件是否已经持续为 true 至少指定的时间。

on_...:
if:
condition:
for:
time: 5min
condition:
api.connected:
then:
- logger.log: API has stayed connected for at least 5 minutes!
  • time (必选, 可模板化, Time):条件必须已经持续为 true 的时间。

  • condition (必选, condition):要检查的条件。

此条件检查给定组件是否处于空闲状态。如果组件已完成设置、未被标记为失败且当前未被循环任务调用,则认为该组件处于空闲状态。这对于将动作与组件状态同步非常有用,例如,需要大量时间更新显示面板的电子纸显示组件。

on_...:
then:
- if:
condition:
component.is_idle: some_component
# ...

此条件执行任意一段 C++ 代码(参见 Lambda),可用于在动作中创建条件流程。

on_...:
then:
- if:
condition:
# Should return either true or false
lambda: |-
return id(some_sensor).state < 30;
# ...
  • api: respond
  • at581x: reset, settings
  • ble_client: ble_write, connect, disconnect, numeric_comparison_reply, passkey_reply, remove_bond
  • homeassistant: action, event, tag_scanned

    没有网络连接时自动化能工作吗

    Section titled “没有网络连接时自动化能工作吗”

    这是一个常见问题,答案是**能!**你在 ESPHome 中定义的所有自动化都在微控制器本身上执行,即使 Wi-Fi 网络断开或 MQTT 服务器不可达,它们也能继续工作。

    不过有一个注意事项:ESPHome 会在无法连接到其 API 时定期自动重启。这有助于解决设备网络栈出现问题导致无法在网络中访问的情况。你可以通过以下任一组件中的 reboot_timeout 选项来调整此行为(甚至禁用自动重启):

    但请注意,禁用重启超时将有效地禁用重启看门狗,因此如果设备无法(或持续无法)在网络中访问,你将需要手动重新启动设备。

    虽然 ESPHome 没有提供专门的定时器构造,但你可以通过组合 script 和 delay 轻松实现定时器。通过分别使用脚本模式 single 和 restart,你可以实现绝对超时或滑动超时。

    script:
    - id: hallway_light_script
    mode: restart # Light will be kept on during 1 minute since
    # the latest time the script is executed
    then:
    - light.turn_on: hallway_light
    - delay: 1 min
    - light.turn_off: hallway_light
    ...
    on_...: # can be called from different wall switches
    - script.execute: hallway_light_script

    有时你还需要一个不执行任何操作的定时器;在这种情况下,你可以使用单个 delay 动作,然后在(自动化中)使用 script.is_running 条件来判断你的”定时器”是否处于活动状态。