Jinja2 模板语言在 Home Assistant 中的应用
模板在 Home Assistant 里是什么
Home Assistant 内置了 Jinja2 模板引擎。模板让你可以在配置里动态计算值,而不是把所有东西写死。模板用 {{ }}(输出)和 {% %}(逻辑块)包裹。
模板几乎可以用在 Home Assistant 的所有配置里:
- 自动化的触发器、条件、动作参数
- Lovelace 卡片的 Markdown 内容
- 通知的标题和消息体
- Entity(template sensor/binary_sensor)
- TTS 语音合成的文本
模板里的全局上下文
在模板里你可以访问 Home Assistant 的运行时数据:
| 对象 | 含义 | 示例 |
|---|---|---|
states | 所有实体的当前状态 | states('sensor.temperature') |
state_attr | 实体的属性 | state_attr('sensor.weather', 'humidity') |
now() | 当前时间 | now().strftime('%Y-%m-%d %H:%M') |
utcnow() | UTC 时间 | 类似 now(),UTC 时区 |
as_timestamp | 把字符串转时间戳 | as_timestamp('2024-01-01') |
distance() | 两点距离 | distance('zone.home', 'zone.work') |
is_state | 判断实体是否某状态 | is_state('light.kitchen', 'on') |
has_value | 实体是否有有效值 | has_value('sensor.battery') |
expand | 获取组内所有实体 | expand('group.all_lights') |
基础语法
输出值
{{ states('sensor.living_room_temperature') }}
# → 24.5
{{ state_attr('sensor.sun', 'next_rising') }}
# → 2025-01-15 06:45:00
过滤器(管道符)
Jinja2 的过滤器用 | 管道符链式调用:
{{ states('sensor.temperature') | float + 2 | int }}
# 把温度转浮点 +2 度,再转整数
{{ states('sensor.battery') | int }}%
# 电量转整数
{{ states('sensor.power') | round(1) }}
# 保留一位小数
{{ state_attr('sensor.sun', 'next_rising') | as_local | strftime('%H:%M') }}
# 把 UTC 时间转本地时间,格式化为 HH:MM
条件(if)
{% if is_state('person.me', 'home') %}
主人在家
{% else %}
主人不在家
{% endif %}
循环(for)
{% for light in states.light %}
{% if light.state == 'on' %}
{{ light.name }}({{ light.entity_id }})
{% endif %}
{% endfor %}
实战场景
场景一:通知里动态填充数据
alias: 温度过高推送
trigger:
- platform: numeric_state
entity_id: sensor.kitchen_temperature
above: 30
action:
- service: notify.mobile_app_me
data:
title: "厨房高温预警!"
message: >
厨房当前温度已达 {{ states('sensor.kitchen_temperature') }}°C!
湿度 {{ states('sensor.kitchen_humidity') }}%
请检查是否有设备过热。
data:
ttl: 0
priority: high
场景二:Lovelace Markdown 卡片
type: markdown
content: |
## 家里概况
👤 **{{ states('sensor.person_count') }}** 人在家
☀️ **{{ states('sensor.sun_elevation') | round(1) }}**° 太阳高度
🌡️ 客厅 **{{ states('sensor.temperature_living_room') }}°C**
💡 **{% if is_state('light.living_room', 'on') %}开{% else %}关{% endif %}**
🔋 电池 **{{ states('sensor.battery_state') }}%**
⚡ 太阳能 **{{ states('sensor.solar_production') }}W**
场景三:Template Sensor(自定义传感器)
Template Sensor 是用模板创建的自定义实体——基于其他实体的状态计算出新的值:
template:
- sensor:
# 客厅平均温度(多传感器平均)
- name: 客厅平均温度
state: >
{{ ((states('sensor.temp_a') | float) + (states('sensor.temp_b') | float)) / 2 | round(1) }}
unit_of_measurement: "°C"
# 家里总能耗(所有打开的灯的功率之和)
- name: 家里总光功率
state: >
{% set ns = namespace(total = 0) %}
{% for light in expand('group.all_lights') %}
{% if light.state == 'on' and state_attr(light.entity_id, 'brightness') %}
{% set ns.total = ns.total + (state_attr(light.entity_id, 'brightness') | int) %}
{% endif %}
{% endfor %}
{{ ns.total }}
unit_of_measurement: "lm"
# 上班通勤时间估算
- name: 预计通勤时间
state: "{{ distance('zone.home', 'zone.work', 'km') | round(1) * 4 }}"
unit_of_measurement: "分钟"
场景四:条件开关的动态状态
switch:
- platform: template
switches:
auto_mode:
friendly_name: "自动模式"
value_template: >
{{ is_state('input_boolean.auto_mode', 'on') }}
turn_on:
service: input_boolean.turn_on
target:
entity_id: input_boolean.auto_mode
turn_off:
service: input_boolean.turn_off
target:
entity_id: input_boolean.auto_mode
场景五:自动化里的动态目标
action:
- service: light.turn_on
target:
entity_id: >
{% if is_state('person.me', 'home') %}
{{ states('sensor.preferred_lights') }}
{% else %}
light.entrance
{% endif %}
模板调试技巧
Developer Tools → Template 编辑器
Home Assistant 内置了一个模板调试器:Settings → System → Developer Tools → Template
在左侧输入模板,右侧实时显示渲染结果。也可以选一个 Trigger(比如 state trigger)来模拟自动化触发时的上下文。
错误排查
模板报错时 Home Assistant 会在日志里写 Jinja2 错误信息。常见错误:
| 错误 | 原因 | 修复 | ||
|---|---|---|---|---|
UndefinedError: 'xxx' is undefined | 引用了不存在的实体或对象 | 检查实体 ID 拼写 | ||
TypeError: unsupported operand types | 对字符串做了数值运算 | 用 ` | float 或 | int` 先转类型 |
Filter 'xxx' is not defined | 用了不存在的过滤器 | 查 Jinja2 文档看正确名称 | ||
| 渲染为空 | 实体可能是 unknown 或 unavailable | 先检查实体是否有有效值 |
高级技巧
ns namespace(在 for 循环里累积变量)
{% set ns = namespace(total = 0) %}
{% for entity in states.sensor %}
{% if state_attr(entity.entity_id, 'device_class') == 'power' and entity.state != 'unknown' %}
{% set ns.total = ns.total + (entity.state | float) %}
{% endif %}
{% endfor %}
{{ ns.total | round(0) }}
避免 Division by Zero
{% set production = states('sensor.solar') | float %}
{% set consumption = states('sensor.grid') | float %}
{% if consumption != 0 %}
自给率:{{ ((production / consumption) * 100) | round(1) }}%
{% else %}
自给率:100%(无电网消耗)
{% endif %}