If you have ever wanted Home Assistant to do something slightly more clever than a basic on/off rule — say, calculate the average temperature across three rooms, or only send a notification after dark — you need templates. Templates are short snippets of Jinja2 code that let Home Assistant evaluate logic dynamically, turning your static configuration into something genuinely adaptive. This Home Assistant template guide walks you through everything you need to know, from the built-in editor to real examples you can drop straight into your own setup.
What Are Jinja2 Templates and Why Do They Matter?
Home Assistant uses Jinja2, the same templating engine found in Python web frameworks, to evaluate expressions at runtime. Wherever HA needs to work something out — a sensor value, an automation condition, a notification message — a template can be used instead of a hardcoded value.
Templates are enclosed in double curly braces {{ }} for output and {% %} for logic blocks such as if statements and for loops. For example:
{{ states('sensor.living_room_temp') }}
That single line returns the current state of your living room temperature sensor as a string. Templates can also do maths, compare dates, apply filters, and call dozens of Home Assistant-specific helper functions.
According to the official Home Assistant templating documentation, it is strongly recommended to use the built-in helper functions — states(), is_state(), state_attr(), and is_state_attr() — wherever possible, because they handle missing or unavailable entities gracefully rather than throwing errors during startup.
The Template Editor: Your Testing Playground
Before embedding a template in an automation or sensor, test it in the Template Editor. In Home Assistant, go to Settings → Developer Tools → Template. You will see a split-panel interface: write your template code on the left and watch the live output appear on the right as you type.
The editor has access to all your real entity data, so you can test against live values. If there is a syntax error, a red message appears immediately pointing to the problem. When you are happy with the output, copy the template and paste it into your YAML configuration or automation editor.
One important caveat: the editor evaluates the template once, on load. Variables like trigger and this (which exist inside automations at runtime) are not available in the editor — referencing them will show an error there, even though they work correctly in a deployed automation.
Core Template Functions
Home Assistant extends Jinja2 with a large library of custom functions. These are the ones you will use most often:
states()
states('entity_id') returns the current state of an entity as a string. To use it in maths, convert it to a number first:
{% set temp = states('sensor.bedroom_temperature') | float(default=0) %}
{{ temp + 2 }}
Always use the string argument form states('sensor.name') rather than dot notation (states.sensor.name) — the dot form raises an error if the entity does not exist, while the function form returns unknown gracefully.
is_state()
is_state('entity_id', 'state_value') checks whether an entity is in a specific state and returns true or false. Use it in conditions:
{% if is_state('binary_sensor.front_door', 'on') %}
The front door is open.
{% endif %}
state_attr()
state_attr('entity_id', 'attribute_name') retrieves a specific attribute from an entity, such as brightness or colour temperature:
{{ state_attr('light.living_room', 'brightness') }}
It returns None if the attribute does not exist, so it is safe to call even on entities that may be unavailable.
now()
now() returns a Python datetime object representing the current local time. You can extract parts of it using filters:
{{ now().hour }}
{{ now().strftime('%A') }}
{{ now().weekday() }}
This is useful for time-of-day conditions — for example, checking whether it is after sunset or before 08:00 on a weekday.
Creating a Template Sensor
Template sensors let you derive a new entity from existing data. Since Home Assistant 2023, the modern format places template entities under the template: key in your configuration.yaml (or a dedicated template.yaml if you use packages). The legacy sensor: - platform: template format was deprecated in HA 2025.12.
Here is a practical example: averaging the temperatures from two sensors in different rooms to get a whole-home reading.
template:
- sensor:
- name: "Average Home Temperature"
unique_id: average_home_temperature
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >
{% set lounge = states('sensor.lounge_temperature') | float(default=0) %}
{% set bedroom = states('sensor.bedroom_temperature') | float(default=0) %}
{{ ((lounge + bedroom) / 2) | round(1) }}
After restarting (or reloading your template configuration), the new sensor.average_home_temperature entity appears in Home Assistant and updates whenever either source sensor changes. You can use it on your dashboard, in automations, or as the input to another template. This connects well with the broader topic of building helper entities — see our Home Assistant helpers guide for complementary approaches using the UI rather than YAML.
Template Binary Sensors
Binary sensors evaluate to either on or off. A template binary sensor is ideal when you need to combine multiple conditions into a single flag — for example, detecting whether the house is "occupied" based on several presence indicators:
template:
- binary_sensor:
- name: "House Occupied"
unique_id: house_occupied
device_class: occupancy
state: >
{{ is_state('device_tracker.sepehr_phone', 'home')
or is_state('binary_sensor.motion_lounge', 'on') }}
You can also add a delay_off to avoid false negatives when a sensor briefly resets:
delay_off:
minutes: 5
Using Templates in Automations
Templates are most powerful inside automations — in conditions, actions, and notification messages. This is the foundation of our automations guide, but here is how templates slot in specifically.
Template Conditions
Add a template condition block to check arbitrary logic before an automation continues:
condition:
- condition: template
value_template: >
{{ states('sensor.electricity_price') | float(default=0) < 0.20
and now().hour >= 23 }}
This condition passes only when the electricity price is below 20p/kWh and the time is after 23:00 — useful for triggering overnight EV charging or hot water heating at cheap-rate tariff hours.
Dynamic Action Data
Templates can also build dynamic notification messages or service call parameters:
action:
- action: notify.mobile_app_my_phone
data:
title: "Energy Update"
message: >
Your home is using {{ states('sensor.home_power') | round(0) }} W
right now. Today's consumption is {{ states('sensor.daily_energy') }} kWh.
5 Practical Template Examples for UK Homes
1. Gas Boiler Lockout at Cheap-Rate Hours
If you are on an Octopus Agile or Economy 7 tariff, suppress the boiler during peak-price hours:
condition:
- condition: template
value_template: "{{ now().hour not in [17, 18, 19] }}"
2. Lux-Adjusted Lighting Trigger
Turn lights on only when natural light has genuinely dropped, not just at a fixed time:
trigger:
- trigger: numeric_state
entity_id: sensor.garden_lux
below: 50
condition:
- condition: template
value_template: "{{ is_state('sun.sun', 'below_horizon') or states('sensor.garden_lux') | int(0) < 50 }}"
3. Combined Downstairs Occupancy Sensor
Merge multiple motion sensors into one entity for cleaner automation logic:
template:
- binary_sensor:
- name: "Downstairs Occupied"
unique_id: downstairs_occupied
device_class: occupancy
state: >
{{ is_state('binary_sensor.motion_lounge', 'on')
or is_state('binary_sensor.motion_kitchen', 'on')
or is_state('binary_sensor.motion_hallway', 'on') }}
4. Heating Degree Days Sensor
Heating degree days (HDD) measure how cold it has been relative to a base temperature of 15.5 °C — a standard UK metric for estimating gas demand:
template:
- sensor:
- name: "Heating Degree Days"
unique_id: heating_degree_days
unit_of_measurement: "HDD"
state: >
{% set outside = states('sensor.outdoor_temperature') | float(default=15.5) %}
{% set base = 15.5 %}
{{ [base - outside, 0] | max | round(1) }}
5. Time-of-Day Greeting for Dashboard
Display a personalised greeting on your Lovelace dashboard based on the current hour:
template:
- sensor:
- name: "Time of Day Greeting"
unique_id: time_of_day_greeting
state: >
{% set h = now().hour %}
{% if h < 12 %}
Good morning
{% elif h < 17 %}
Good afternoon
{% else %}
Good evening
{% endif %}
Using Templates in Lovelace Cards
Many dashboard cards accept templates directly. In the card editor, switch to YAML mode and use the content or state field. The Markdown card is the most flexible — it renders any template output as formatted text:
type: markdown
content: >
## Home Status
**Temperature:** {{ states('sensor.average_home_temperature') }} °C
**Door:** {{ 'Open' if is_state('binary_sensor.front_door', 'on') else 'Closed' }}
For more advanced dashboard layouts, our Lovelace dashboard guide covers card types, custom cards, and layout strategies.
Common Errors and How to Debug Them
UndefinedError: 'sensor.name' is undefined. This usually means you used dot notation (states.sensor.name) and the entity does not exist yet. Switch to states('sensor.name') to return unknown gracefully instead.
TypeError: float() argument must be a string or a number, not 'NoneType'. You are calling | float without a default. Use | float(default=0) to handle unavailable sensors safely.
Template output is 'unknown' or 'unavailable'. At least one entity referenced in your template is not yet ready — common after a restart. Add an availability template to your sensor:
availability: >
{{ states('sensor.lounge_temperature') not in ['unknown', 'unavailable']
and states('sensor.bedroom_temperature') not in ['unknown', 'unavailable'] }}
The template works in the editor but fails in an automation. Check whether your template references trigger or this — these only exist inside an automation context. Also check for YAML quoting issues: multiline templates should use the > or | block scalar rather than wrapping in quotes.
Debugging approach: copy the template into Developer Tools → Template, confirm the output, then move it into your YAML. If it still misbehaves, check Home Assistant's home-assistant.log for the precise error — template errors are logged with context about which entity or automation triggered them.




