动作、触发器、条件
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_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
# 与下面的相同:
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:
分支。
在选择的分支(then
或 else
)执行完成后,将执行下一个动作。
例如,下面是一个自动化,它检查传感器值是否低于 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
配置变量
至少必须提供 condition
、all
或 any
中的一个。
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: "二进制传感器可能准备好了"
配置变量
while
动作
这个动作类似于 if 动作。while
动作会一直循环一个块,直到给定的条件为 true
。
# 在触发器中:
on_...:
- while:
condition:
binary_sensor.is_on: some_binary_sensor
then:
- logger.log: "仍在执行"
- light.toggle: some_light
- delay: 5s
配置变量
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
条件
检查条件组合。all
是 and
的同义词,any
是 or
的同义词。
all
和 any
也可以直接用于代替 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 (必须,templatable,Time): 条件必须为
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 没有提供定时器结构,但你可以通过结合 script
和 delay
来轻松实现它们。你可以通过使用 script
模式 single
和 restart
分别来实现绝对超时和滑动超时。
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
条件来知道你的“定时器”是否活跃。