动作、触发器、条件

ESPHome 的 动作 是我们让 ESPHome 设备 执行某些操作 的方法。

让我们从一个例子开始。假设你有一个配置文件,其中包含:

switch:
  - platform: gpio
    pin: GPIOXX
    name: "客厅除湿器"

binary_sensor:
  - platform: gpio
    pin: GPIOXX
    name: "客厅除湿器切换按钮"

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

可以 在 Home Assistant 的自动化引擎中编写自动化来完成这个任务,但物联网设备不应该依赖于网络连接来执行它们的工作——尤其是像开关除湿器这样简单的事情。

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

switch:
  - platform: gpio
    pin: GPIOXX
    name: "客厅除湿器"
    id: dehumidifier1

binary_sensor:
  - platform: gpio
    pin: GPIOXX
    name: "客厅除湿器切换按钮"
    on_press:
      then:
        - switch.toggle: dehumidifier1

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

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

首先,我们必须给除湿器的 switch 一个 ID,以便我们可以在自动化内部引用它。

触发器

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

我们现在将特殊的属性 on_press 添加到二进制传感器(代表按钮)。这部分称为“触发器”。在这个例子中,接下来的几行代码中的 自动化 将在有人 开始按下 按钮时执行。请注意,术语遵循你在鼠标按钮上如何称呼这些事件。按下 发生在你开始按下按钮时。还有其他触发器,如 on_releaseon_clickon_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

# 与下面的相同:
on_press:
  then:
    - switch.toggle: dehumidifier1
    - light.toggle: dehumidifier_indicator_light

作为最后一个例子,让我们使我们的除湿器“智能”。让它在上报的传感器湿度超过 65% 时自动开启,并在低于 50% 时再次关闭:

sensor:
  - platform: dht
    humidity:
      name: "客厅湿度"
      on_value_range:
        - above: 65.0
          then:
            - switch.turn_on: dehumidifier1
        - below: 50.0
          then:
            - switch.turn_off: dehumidifier1
    temperature:
      name: "客厅温度"

这有很多缩进。 😉

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

最后,对于“纯粹的” YAML 自动化无法完全满足的情况,ESPHome 提供了另一个非常强大的工具:模板

现在,ESPHome 动作的介绍就结束了。它们是一种易于使用的语法,可以自动化设备上的几乎所有内容。以下是一个索引,列出了你一定会发现有用(甚至必不可少)的常见动作,用于构建各种自动化。

常见动作

delay 动作

这个动作通过指定的时间段延迟动作列表中的下一个动作的执行。

on_...:
  then:
    - switch.turn_on: relay_1
    - delay: 2s
    - switch.turn_off: relay_1
    # 模板化,只有在干簧开关激活时才等待 1s(1000ms)
    - delay: !lambda "if (id(reed_switch).state) return 1000; else return 0;"

ℹ️ Note

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

if 动作

这个动作首先评估 condition:,然后如果条件返回 true,则执行 then: 分支;如果返回 false,则执行 else: 分支。

在选择的分支(thenelse )执行完成后,将执行下一个动作。

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

on_...:
  then:
    - if:
        condition:
          lambda: 'return id(some_sensor).state < 30;'
        then:
          - logger.log: "传感器值低于 30!"
          - light.turn_on: my_light
          - delay: 5s
        else:
          - logger.log: "传感器值高于 30!"
    - light.turn_off: my_light

配置变量

至少必须提供 conditionallany 中的一个。

  • condition (可选Condition): 要检查的条件,以确定要采取哪个分支。 如果配置了条件列表,则所有条件都必须为 true,条件才为 true

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

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

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

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

lambda 动作

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

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

repeat 动作

这个动作允许你重复指定次数的块。 例如,下面的自动化将点亮灯五次。

on_...:
  - repeat:
      count: 5
      then:
        - light.turn_on: some_light
        - delay: 1s
        - light.turn_off: some_light
        - delay: 10s

配置变量

  • count (必须,int): 动作应该重复的次数。计数器可以使用保留字 “iteration” 在 lambda 中访问。

  • then (必须Action): 要重复的动作。

wait_until 动作

这个动作允许你的自动化等待条件评估为 true。 (所以这只是编写 while 动作并带有空 then 块的简写方式。)

# 在触发器中:
on_...:
  - logger.log: "等待二进制传感器"
  - wait_until:
      binary_sensor.is_on: some_binary_sensor
  - logger.log: "二进制传感器准备好了"

如果你想要使用超时,则需要使用 “condition”:

# 在触发器中:
on_...:
  - logger.log: "等待二进制传感器"
  - wait_until:
      condition:
        binary_sensor.is_on: some_binary_sensor
      timeout: 8s
  - logger.log: "二进制传感器可能准备好了"

配置变量

  • condition (必须Condition): 要等待变为 true 的条件。
  • timeout (可选Time): 超时前等待的时间。默认为永不超时。

while 动作

这个动作类似于 if 动作。while 动作会一直循环一个块,直到给定的条件为 true

# 在触发器中:
on_...:
  - while:
      condition:
        binary_sensor.is_on: some_binary_sensor
      then:
      - logger.log: "仍在执行"
      - light.toggle: some_light
      - delay: 5s

配置变量

  • condition (必须Condition): 要检查的条件,以确定是否执行。

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

component.update 动作

使用这个动作,你可以手动调用组件的 update() 方法。

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

on_...:
  then:
    - component.update: my_component

    # 与下面的相同:
    - lambda: 'id(my_component).update();'

component.suspend 动作

使用这个动作,你可以手动调用组件的 stop_poller() 方法。

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

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

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

on_...:
  then:
    - component.suspend: my_component

    # 与下面的相同:
    - lambda: 'id(my_component).stop_poller();'

component.resume 动作

使用这个动作,你可以手动调用组件的 start_poller() 方法。

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

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

此动作还允许更改更新间隔,在不挂起的情况下调用它,将直接替换轮询器。

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

on_...:
  then:
    - component.resume: my_component

    # 与下面的相同:
    - lambda: 'id(my_component).start_poller();'

# 更改轮询器间隔
on_...:
  then:
    - component.resume:
        id: my_component
        update_interval: 15s

常见条件

“条件”为你的设备提供了一种方法,只有在特定的(一组)条件得到满足时才执行动作。

and / all / or / any / xor / not 条件

检查条件组合。alland 的同义词,anyor 的同义词。 allany 也可以直接用于代替 condition

on_...:
  then:
    - if:
        condition:
          # `and` 条件和 `xor` 条件的语法相同
          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

for 条件

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

on_...:
  if:
    condition:
      for:
        time: 5min
        condition:
          api.connected:
    then:
      - logger.log: API 已经保持连接至少 5 分钟了!

配置变量

  • time (必须templatableTime): 条件必须为 true 的时间。

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

lambda 条件

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

on_...:
  then:
    - if:
        condition:
          # 应该返回 true 或 false
          lambda: |-
            return id(some_sensor).state < 30;            
        # ...

所有动作

⚠️ Data not found for branch: 2025.8.4

所有条件

⚠️ Data not found for branch: 2025.8.4

小贴士和技巧

无网络连接时自动化是否工作

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

不过,有一个例外:如果 ESPHome 没有连接到其 API,它将自动定期重启。这有助于在设备网络栈出现问题,导致设备无法在网络中可达的情况下。你可以使用以下组件中的 reboot_timeout 选项来调整此行为(甚至禁用自动重启):

但是要注意,禁用重启超时实际上禁用了重启看门狗,因此如果设备被证明/仍然无法在网络中可达,你需要手动断电重启设备。

定时器和超时

虽然 ESPHome 没有提供定时器结构,但你可以通过结合 scriptdelay 来轻松实现它们。你可以通过使用 script 模式 singlerestart 分别来实现绝对超时和滑动超时。

script:
  - id: hallway_light_script
    mode: restart     # 灯将在最新执行脚本的时间后保持开启 1 分钟
    then:
      - light.turn_on: hallway_light
      - delay: 1 min
      - light.turn_off: hallway_light

...
  on_...:           # 可以从不同的墙壁开关调用
    - script.execute: hallway_light_script

有时,你可能还需要一个不执行任何操作的定时器;在这种情况下,你可以使用单个 delay 动作,然后在你的自动化中使用 script.is_running 条件来知道你的“定时器”是否活跃。

参考文献和更多信息