条件与模板:condition 入门

条件在流水线上的位置

上一篇把触发器讲透了,本篇往下走一格:condition。触发器决定「什么时候开始」,条件决定「开始之后要不要真正执行」。两者的区别在于:触发是瞬间的、一次性的;条件评估的是「此刻的世界长什么样」。

一个新手最容易混淆的地方:你想让灯在「天黑之后」打开,于是把「天黑」写成了触发——结果灯只会在日落那一秒打开,日落时如果你还没回家,这次触发就浪费了。正确写法是触发写「回家」,条件写「太阳已下山」:把持续性判断交给条件,把瞬间性事件交给触发。这一节先建立直觉,后面逐个认识条件平台。

还有一层常被忽略的作用:条件也是「防误触」的闸门。机器人吸尘器自动化加一个「无人在家」条件,就不会出现「刚坐下它就开始扫地」的尴尬;通知类自动化加一个「勿扰模式」条件,半夜就不会被吵醒。条件用得好,自动化才像个人而不是个定时炸弹。

常用条件平台一览

HA 的条件平台不少,日常用到的主要是这几种:

条件平台 判断什么 典型例子
state 实体当前状态 电视是开着的
numeric_state 数值区间 室温高于 26 度
time 时间区间 晚上 22 点到早上 6 点
sun 日照状态 太阳已下山
zone 设备是否在某区域 手机在家
template 模板返回真假 自定义任意逻辑
and/or/not 条件组合 多条件联合判断

其中 template 是「万能兜底」:前面所有条件能表达的,模板都能表达;代价是可读性差一截、调试多一步。日常能用专用条件就别上模板,维护起来你会感谢自己。

条件与触发还有一对镜像关系值得记住:多数条件平台都有同名触发器。state 既是触发也是条件,numeric_state、sun、time 亦然。同一个平台名,放在 trigger 段表示「变化时点火」,放在 condition 段表示「此刻是否如此」。分清段落,就不会把条件写成触发、把触发写成条件了。

state 与 numeric_state:最常用的一对

state 条件只看一件事:某个实体现在是不是某个状态。它支持 state: "on" 这种单值,也支持 state_list 式的多值判断。使用时有一个细节:HA 的状态是字符串,写 state: on 会被解析成布尔真,而实体实际的状态是 "on" 字符串,比较时可能翻车——养成带引号的习惯最保险。

numeric_state 条件画两条线:above 和 below 可单用可合用,落在区间内才成立。它同样支持 value_template,先用模板算出数字再比。典型场景是温控:温度在 26 到 30 之间才开空调制冷,低于 26 不必动。

一个实用组合示例如下:

condition:
  - condition: state
    entity_id: media_player.tv
    state: "on"
  - condition: numeric_state
    entity_id: sensor.living_temp
    above: 26
  - condition: sun
    after: sunset

三个条件默认是 AND 关系:电视开着、室温超过 26 度、太阳已下山,三件事同时成立才会继续执行动作。

time 与 sun:时间相关的条件

time 条件判断「现在是不是在某段时间内」,参数是 after 和 before。注意它只能判断区间,不能判断「具体几点」——如果需要「刚好 7 点」这种逻辑,应该用时间触发器而不是时间条件。搭配 weekday 还能限定星期几,工作日和周末两套行为可以共用一条自动化。

时间条件还有个隐藏坑:跨午夜区间。晚上 22 点到早上 6 点,直觉上写 after: "22:00" before: "06:00" 就行,但在 HA 里「22 点之后」同时「6 点之前」永远不会同时成立。跨午夜的写法要拆成两个条件用 OR 连接,或干脆只用一段判断(比如只判断 after 22 点,再交给逻辑补足)。类似的坑在「勿扰时段」这种场景里经常出现。

sun 条件最常用的两个参数是 after: sunset 和 before: sunrise,同样支持偏移量。很多人的「天黑判断」都卡在这里:直接用日落时间,但雨天阴天时实际天早早就黑了。稳妥做法是日落条件打底,再叠加一个光照传感器的 numeric_state 条件,两者取其一成立即可(用 OR 组合),体验立刻细腻起来。

template 条件:万能但需克制

template 条件接受任意 Jinja2 表达式,渲染结果为真才通过。它解决的问题大致是这些:

  • 跨实体计算:比如「两个房间温差超过 3 度」。
  • 字符串判断:比如「设备名包含某关键字」。
  • 复杂组合:AND/OR 嵌套太多层时,模板反而更清晰。

例如判断温差:

- condition: template
  value_template: >-
    {{ (states('sensor.living_temp') | float -
       states('sensor.bedroom_temp') | float) | abs > 3 }}

里面的 float 过滤器把字符串转成数字,abs 取绝对值——模板语法的细节会在「Jinja2 速学」一篇展开。这里先记住工作流:先在模板编辑器里跑通,再贴进条件。

模板条件的另一面是「开销」。它每次被评估都要渲染一次,配合每秒评估的模板触发器可能造成可感知的负载。日常规则是:模板里别做重计算(比如遍历全部实体排序),能缓存的结果交给自定义传感器。把模板当作精确的手术刀,而不是没事就挥的锤子。

条件组合:and、or、not 的嵌套

条件可以互相嵌套,形成任意深度的树。但嵌套层数越多,越难一眼读懂。两个建议:

  1. 优先用 OR 表达「多选一」:比如「访客模式开着或周末」才执行某个动作。
  2. 超过三层就拆自动化:与其维护一棵又高又深的条件树,不如按场景拆成两三条简单自动化,各自模式不同。

在 YAML 里看一个 OR 组合的例子:

condition:
  - condition: or
    conditions:
      - condition: state
        entity_id: input_boolean.weekend
        state: "on"
      - condition: state
        entity_id: input_boolean.guest_mode
        state: "on"

含义是「周末或访客模式,满足其一即可」。

组合条件还有个书写顺序的小学问:AND 条件从上到下依次评估,把最容易失败的放最前面,可以省掉后面那些无谓的计算——规则简单,但在条件很多的自动化里能明显降低开销。

条件嵌套结构

条件写在动作里:choose 与 if

条件不只能放在触发之后。动作内部的 choose 结构允许「执行中途做分支」,语法是每个分支一组 conditions 加一组 sequence,还可以给 default。它和自动化顶层的 condition 搭配使用很灵活:顶层条件管「这条自动化该不该跑」,choose 管「跑起来之后遇到不同情况怎么办」。

一个常见模式是:触发后先 choose 判断「现在白天还是晚上」,白天只推通知,晚上才开灯拉窗帘。顶层条件做 gate,动作内 choose 做路由,这个分工能让自动化保持线性可读。

顺带一提,if 结构在动作里同样可用,写法对程序员更友好。两者能力等价,团队协作时建议统一选一种风格,避免同一本书里两种语法混着读。

调试条件的两个技巧

  • 在编辑器里单独评估:开发者工具的「条件」页签可以选一条自动化并对着当前状态评估条件,立刻看到通过与否。
  • 日志辅助:在动作开头加一行推送通知或写日志的 service,把当时各关键实体的值发出来,比凭空猜快得多。

条件的取舍路线

延伸阅读

  • 模板语法系统学习见本系列「模板进阶:Jinja2 速学」一篇。
  • 条件常配合 helper 实体(如访客模式开关)使用,详见「helper 实体:开关、计数与下拉框」。
  • 部署环境检查可参考 《软路由指南》,长期跑在 NAS 上则见 《NAS 搭建指南》 的容器章节。