Templating
This is an advanced feature of Home Assistant. You’ll need a basic understanding of:
- Home Assistant architecture, especially states.
- The State object.
Templating is a powerful feature that allows you to control information going into and out of the system. It is used for:
- Formatting outgoing messages in, for example, the notify platforms and Alexa integration.
- Process incoming data from sources that provide raw data, like MQTT,
restsensor or thecommand_linesensor. - Automation Templating.
Building templates
Templating in Home Assistant is powered by the Jinja2
We will not go over the basics of the syntax, as Jinja2 does a great job of this in their templates documentation
The frontend has a template editor tool to help develop and debug templates. Navigate to Developer Tools > Template, create your template in the Template editor and check the results on the right.
Templates can get big pretty fast. To keep a clear overview, consider using YAML multiline strings to define your templates:
script:
msg_who_is_home:
sequence:
- action: notify.notify
data:
message: >
{% if is_state('device_tracker.paulus', 'home') %}
Ha, Paulus is home!
{% else %}
Paulus is at {{ states('device_tracker.paulus') }}.
{% endif %}
Important template rules
There are a few very important rules to remember when adding templates to YAML:
- You must surround single-line templates with double quotes (
") or single quotes ('). - It is advised that you prepare for undefined variables by using
if ... is not noneor thedefaultfilter, or both. - It is advised that when comparing numbers, you convert the number(s) to a
floator an intby using the respective filter . - While the
floatand intfilters do allow a default fallback value if the conversion is unsuccessful, they do not provide the ability to catch undefined variables.
Remembering these simple rules will help save you from many headaches and endless hours of frustration when using automation templates.
Enabled Jinja extensions
Jinja supports a set of language extensions that add new functionality to the language. To improve the experience of writing Jinja templates, we have enabled the following extensions:
-
Loop Controls
( breakandcontinue) -
Expression Statement
( do)
Reusing templates
You can write reusable Jinja templates by adding them to a custom_templates folder under your
configuration directory. All template files must have the .jinja extension and be less than 5MiB.
Templates in this folder will be loaded at startup. To reload the templates without
restarting Home Assistant, invoke the homeassistant.reload_custom_templates action.
Once the templates are loaded, Jinja includesconfig/custom_templates as the base directory.
For example, you might define a macro in a template in config/custom_templates/formatter.jinja:
{% macro format_entity(entity_id) %}
{{ state_attr(entity_id, 'friendly_name') }} - {{ states(entity_id) }}
{% endmacro %}
In your automations, you could then reuse this macro by importing it:
{% from 'formatter.jinja' import format_entity %}
{{ format_entity('sensor.temperature') }}
Home Assistant also allows you to write macros with non-string return values by
taking a named argument called returns and calling it with a return value. Once created,
pass the macro into the as_function filter to use the returned value:
{%- macro macro_is_switch(entity_name, returns) -%}
{%- do returns(entity_name.startswith('switch.')) -%}
{%- endmacro -%}
{%- set is_switch = macro_is_switch | as_function -%}
{{ "It's a switch!" if is_switch("switch.my_switch") else "Not a switch!" }}
In this way, you can export utility functions that return scalar or complex values rather than just macros that render to strings.
Home Assistant template extensions
Extensions allow templates to access all of the Home Assistant specific states and adds other convenience functions and filters.
Limited templates
Templates for some triggers as well as trigger_variables only support a subset of the Home Assistant template extensions. This subset is referred to as “Limited Templates”.
This
State-based and trigger-based template entities have the special template variable this available in their templates and actions. See more details and examples in the Template integration documentation.
States
Not supported in limited templates.
- Iterating
stateswill yield each state object. - Iterating
states.domainwill yield each state object of that domain. -
states.sensor.temperaturereturns the state object forsensor.temperature(avoid when possible, see note below). -
statescan also be used as a function,states(entity_id, rounded=False, with_unit=False), which returns the state string (not the state object) of the given entity,unknownif it doesn’t exist, andunavailableif the object exists but is not available.- The optional arguments
roundedandwith_unitcontrol the formatting of sensor state strings, please see the examples below.
- The optional arguments
-
states.sensor.temperature.state_with_unitformats the state string in the same way as if callingstates('sensor.temperature', rounded=True, with_unit=True). -
is_statecompares an entity’s state with a specified state or list of states and returnsTrueorFalse.is_state('device_tracker.paulus', 'home')will test if the given entity is the specified state.is_state('device_tracker.paulus', ['home', 'work'])will test if the given entity is any of the states in the list. -
state_attr('device_tracker.paulus', 'battery')will return the value of the attribute or None if it doesn’t exist. -
is_state_attr('device_tracker.paulus', 'battery', 40)will test if the given entity attribute is the specified state (in this case, a numeric value). Note that the attribute can beNoneand you want to check if it isNone, you need to usestate_attr('sensor.my_sensor', 'attr') is noneorstate_attr('sensor.my_sensor', 'attr') == None(note the difference in the capitalization of none in both versions). -
has_value('sensor.my_sensor')will test if the given entity is not unknown or unavailable. Can be used as a filter or a test.
Avoid using states.sensor.temperature.state, instead use states('sensor.temperature'). It is strongly advised to use the states(), is_state(), state_attr() and is_state_attr() as much as possible, to avoid errors and error message when the entity isn’t ready yet (e.g., during Home Assistant startup).
States examples
The next two statements result in the same value if the state exists. The second one will result in an error if the state does not exist.
{{ states('device_tracker.paulus') }}
{{ states.device_tracker.paulus.state }}
Print out a list of all the sensor states:
{% for state in states.sensor %}
{{ state.entity_id }}={{ state.state }},
{% endfor %}
Print out a list of all the sensor states sorted by entity_id:
{% for state in states.sensor | sort(attribute='entity_id') %}
{{ state.entity_id }}={{ state.state }},
{% endfor %}
Entities that are on:
{{ ['light.kitchen', 'light.dining_room'] | select('is_state', 'on') | list }}
Other state examples:
{% if is_state('device_tracker.paulus', 'home') %}
Ha, Paulus is home!
{% else %}
Paulus is at {{ states('device_tracker.paulus') }}.
{% endif %}
#check sensor.train_departure_time state
{% if states('sensor.train_departure_time') in ("unavailable", "unknown") %}
{{ ... }}
{% if has_value('sensor.train_departure_time') %}
{{ ... }}
{% set state = states('sensor.temperature') %}{{ state | float + 1 if is_number(state) else "invalid temperature" }}
{% set state = states('sensor.temperature') %}{{ (state | float * 10) | round(2) if is_number(state)}}
{% set state = states('sensor.temperature') %}
{% if is_number(state) and state | float > 20 %}
It is warm!
{% endif %}
{{ as_timestamp(states.binary_sensor.garage_door.last_changed) }}
{{ as_local(states.binary_sensor.garage_door.last_changed) }}
{{ as_timestamp(now()) - as_timestamp(states.binary_sensor.garage_door.last_changed) }}
{{ as_local(states.sensor.time.last_changed) }}
{{ states('sensor.expires') | as_datetime }}
# Make a list of states
{{ ['light.kitchen', 'light.dining_room'] | map('states') | list }}
Formatting sensor states
The examples below show the output of a temperature sensor with state 20.001, unit °C and user configured presentation rounding set to 1 decimal.
The following example results in the number 20.001:
{{ states('sensor.temperature') }}
The following example results in the string "20.0 °C":
{{ states('sensor.temperature', with_unit=True) }}
The following example result in the string "20.001 °C":
{{ states('sensor.temperature', with_unit=True, rounded=False) }}
The following example results in the number 20.0:
{{ states('sensor.temperature', rounded=True) }}
The following example results in the number 20.001:
{{ states.sensor.temperature.state }}
The following example results in the string "20.0 °C":
{{ states.sensor.temperature.state_with_unit }}
Attributes
Not supported in limited templates.
You can print an attribute with state_attr if state is defined.
Attributes examples
{% if states.device_tracker.paulus %}
{{ state_attr('device_tracker.paulus', 'battery') }}
{% else %}
??
{% endif %}
With strings:
{% set tracker_name = "paulus"%}
{% if states("device_tracker." + tracker_name) != "unknown" %}
{{ state_attr("device_tracker." + tracker_name, "battery")}}
{% else %}
??
{% endif %}
List of friendly names:
{{ ['binary_sensor.garage_door', 'binary_sensor.front_door'] | map('state_attr', 'friendly_name') | list }}
List of lights that are on with a brightness of 255:
{{ ['light.kitchen', 'light.dining_room'] | select('is_state', 'on') | select('is_state_attr', 'brightness', 255) | list }}
State translated
Not supported in limited templates.
The state_translated function returns a translated state of an entity using a language that is currently configured in the general settings.
State translated examples
{{ states("sun.sun") }} # below_horizon
{{ state_translated("sun.sun") }} # Below horizon
{{ "sun.sun" | state_translated }} # Below horizon
{{ states("binary_sensor.movement_backyard") }} # on
{{ state_translated("binary_sensor.movement_backyard") }} # Detected
{{ "binary_sensor.movement_backyard" | state_translated }} # Detected
Working with groups
Not supported in limited templates.
The expand function and filter can be used to sort entities and expand groups. It outputs a sorted array of entities with no duplicates.
Expand examples
{% for tracker in expand('device_tracker.paulus', 'group.child_trackers') %}
{{ state_attr(tracker.entity_id, 'battery') }}
{%- if not loop.last %}, {% endif -%}
{% endfor %}
The same thing can also be expressed as a filter:
{{ expand(['device_tracker.paulus', 'group.child_trackers'])
| selectattr("attributes.battery", 'defined')
| join(', ', attribute="attributes.battery") }}
{% for energy in expand('group.energy_sensors') if is_number(energy.state) %}
{{ energy.state }}
{%- if not loop.last %}, {% endif -%}
{% endfor %}
The same thing can also be expressed as a test:
{{ expand('group.energy_sensors')
| selectattr("state", 'is_number') | join(', ') }}
Entities
-
is_hidden_entity(entity_id)returns whether an entity has been hidden. Can also be used as a test.
Entities examples
{{ area_entities('kitchen') | reject('is_hidden_entity') }} # Gets a list of visible entities in the kitchen area
Devices
-
device_entities(device_id)returns a list of entities that are associated with a given device ID. Can also be used as a filter. -
device_attr(device_or_entity_id, attr_name)returns the value ofattr_namefor the given device or entity ID. Can also be used as a filter. Not supported in limited templates. -
is_device_attr(device_or_entity_id, attr_name, attr_value)returns whether the value ofattr_namefor the given device or entity ID matchesattr_value. Can also be used as a test. Not supported in limited templates. -
device_id(entity_id)returns the device ID for a given entity ID or device name. Can also be used as a filter. -
device_name(lookup_value)returns the device name for a given device ID or entity ID. Can also be used as a filter.
Devices examples
{{ device_attr('deadbeefdeadbeefdeadbeefdeadbeef', 'manufacturer') }} # Sony
{{ is_device_attr('deadbeefdeadbeefdeadbeefdeadbeef', 'manufacturer', 'Sony') }} # true
{{ device_id('sensor.sony') }} # deadbeefdeadbeefdeadbeefdeadbeef
{{ device_name('deadbeefdeadbeefdeadbeefdeadbeef') }} # Sony speaker
{{ device_name('sensor.sony') }} # Sony speaker
Config entries
-
config_entry_id(entity_id)returns the config entry ID for a given entity ID. Can also be used as a filter. -
config_entry_attr(config_entry_id, attr)returns the value ofattrfor the config entry of the given entity ID. Can also be used as a filter. The following attributes are allowed:domain,title,state,source,disabled_by. Not supported in limited templates.
Config entries examples
{{ config_entry_id('sensor.sony') }} # deadbeefdeadbeefdeadbeefdeadbeef
{{ config_entry_attr(config_entry_id('sensor.sony'), 'title') }} # Sony Bravia TV
Floors
-
floors()returns the full list of floor IDs. -
floor_id(lookup_value)returns the floor ID for a given floor name or alias, area name or alias, entity ID or device ID. Can also be used as a filter. -
floor_name(lookup_value)returns the floor name for a given device ID, entity ID, area ID, or floor ID. Can also be used as a filter. -
floor_areas(floor_name_or_id)returns the list of area IDs tied to a given floor ID or name. Can also be used as a filter. -
floor_entities(floor_name_or_id)returns the list of entity IDs tied to a given floor ID or name. Can also be used as a filter.
Floors examples
{{ floors() }} # ['floor_id']
{{ floor_id('First floor') }} # 'first_floor'
{{ floor_id('First floor alias') }} # 'first_floor'
{{ floor_id('my_device_id') }} # 'second_floor'
{{ floor_id('sensor.sony') }} # 'first_floor'
{{ floor_name('first_floor') }} # 'First floor'
{{ floor_name('my_device_id') }} # 'Second floor'
{{ floor_name('sensor.sony') }} # 'First floor'
{{ floor_areas('first_floor') }} # ['living_room', 'kitchen']
Areas
-
areas()returns the full list of area IDs -
area_id(lookup_value)returns the area ID for a given area name or alias, entity ID or device ID. Can also be used as a filter. -
area_name(lookup_value)returns the area name for a given device ID, entity ID, or area ID. Can also be used as a filter. -
area_entities(area_name_or_id)returns the list of entity IDs tied to a given area ID or name. Can also be used as a filter. -
area_devices(area_name_or_id)returns the list of device IDs tied to a given area ID or name. Can also be used as a filter.
Areas examples
{{ areas() }} # ['area_id']
{{ area_id('Living Room') }} # 'deadbeefdeadbeefdeadbeefdeadbeef'
{{ area_id('Living Room Alias') }} # 'deadbeefdeadbeefdeadbeefdeadbeef'
{{ area_id('my_device_id') }} # 'deadbeefdeadbeefdeadbeefdeadbeef'
{{ area_id('sensor.sony') }} # 'deadbeefdeadbeefdeadbeefdeadbeef'
{{ area_name('deadbeefdeadbeefdeadbeefdeadbeef') }} # 'Living Room'
{{ area_name('my_device_id') }} # 'Living Room'
{{ area_name('sensor.sony') }} # 'Living Room'
{{ area_entities('deadbeefdeadbeefdeadbeefdeadbeef') }} # ['sensor.sony']
{{ area_devices('Living Room') }} # ['my_device_id']
Entities for an integration
-
integration_entities(integration)returns a list of entities that are associated with a given integration, such ashueorzwave_js. -
integration_entities(config_entry_title)if you have multiple entries set-up for an integration, you can also use the title you’ve set for the integration in case you only want to target a specific entry.
If there is more than one entry with the same title, the entities for all the matching entries will be returned, even if the entries are for different integrations. It’s not possible to search for entities of an untitled integration.
Integrations examples
{{ integration_entities('hue') }} # ['light.hue_light_upstairs', 'light.hue_light_downstairs']
{{ integration_entities('Hue bridge downstairs') }} # ['light.hue_light_downstairs']
Labels
-
labels()returns the full list of label IDs, or those for a given area ID, device ID, or entity ID. -
label_id(lookup_value)returns the label ID for a given label name. -
label_name(lookup_value)returns the label name for a given label ID. -
label_description(lookup_value)returns the label description for a given label ID. -
label_areas(label_name_or_id)returns the list of area IDs tied to a given label ID or name. -
label_devices(label_name_or_id)returns the list of device IDs tied to a given label ID or name. -
label_entities(label_name_or_id)returns the list of entity IDs tied to a given label ID or name.
Each of the label template functions can also be used as a filter.
Labels examples
{{ labels() }} # ['christmas_decorations', 'energy_saver', 'security']
{{ labels("living_room") }} # ['christmas_decorations', 'energy_saver']
{{ labels("my_device_id") }} # ['security']
{{ labels("light.christmas_tree") }} # ['christmas_decorations']
{{ label_id('Energy saver') }} # 'energy_saver'
{{ label_name('energy_saver') }} # 'Energy saver'
{{ label_areas('security') }} # ['driveway', 'garden', 'porch']
{{ label_devices('energy_saver') }} # ['deadbeefdeadbeefdeadbeefdeadbeef']
{{ label_entities('security') }} # ['camera.driveway', 'binary_sensor.motion_garden', 'camera.porch']
Issues
-
issues()returns all open issues as a mapping of (domain, issue_id) tuples to the issue object. -
issue(domain, issue_id)returns a specific issue for the provided domain and issue_id.
Issues examples
{{ issues() }} # { ("homeassistant", "deprecated_yaml_ping"): {...}, ("cloud", "legacy_subscription"): {...} }
{{ issue('homeassistant', 'python_version') }} # {"breaks_in_ha_version": "2024.4", "domain": "homeassistant", "issue_id": "python_version", "is_persistent": False, ...}
Immediate if (iif)
A common case is to conditionally return a value based on another value. For example, return a “Yes” or “No” when the light is on or off.
This can be written as:
{% if is_state('light.kitchen', 'on') %}
Yes
{% else %}
No
{% endif %}
Or using a shorter syntax:
{{ 'Yes' if is_state('light.kitchen', 'on') else 'No' }}
Additionally, to the above, you can use the iif function/filter, which is
an immediate if.
Syntax: iif(condition, if_true, if_false, if_none)
iif returns the value of if_true if the condition is truthy, the value of if_false if it’s falsy and the value of if_none if it’s None.
An empty string, an empty mapping or an an empty list, are all falsy, refer to the Python documentation
if_true is optional, if it’s omitted True is returned if the condition is truthy.
if_false is optional, if it’s omitted False is returned if the condition is falsy.
if_none is optional, if it’s omitted the value of if_false is returned if the condition is None.
Examples using iif:
{{ iif(is_state('light.kitchen', 'on'), 'Yes', 'No') }}
{{ is_state('light.kitchen', 'on') | iif('Yes', 'No') }}
{{ (states('light.kitchen') == 'on') | iif('Yes', 'No') }}
The immediate if filter does not short-circuit like you might expect with a typical conditional statement. The if_true, if_false and if_none expressions will all be evaluated and the filter will simply return one of the resulting values. This means you cannot use this filter to prevent executing an expression which would result in an error.
For example, if you wanted to select a field from trigger in an automation based on the platform you might go to make this template: trigger.platform == 'event' | iif(trigger.event.data.message, trigger.to_state.state). This won’t work because both expressions will be evaluated and one will fail since the field doesn’t exist. Instead you have to do this trigger.event.data.message if trigger.platform == 'event' else trigger.to_state.state. This form of the expression short-circuits so if the platform is event the expression trigger.to_state.state will never be evaluated and won’t cause an error.
Time
now(), time_since(), time_until(), today_at(), and utcnow() are not supported in limited templates.
-
now()returns a datetime object that represents the current time in your time zone.- You can also use:
now().second,now().minute,now().hour,now().day,now().month,now().year,now().weekday()andnow().isoweekday()and otherdatetimeattributes and functions. - Using
now()will cause templates to be refreshed at the start of every new minute.
- You can also use:
-
utcnow()returns a datetime object of the current time in the UTC timezone.- For specific values:
utcnow().second,utcnow().minute,utcnow().hour,utcnow().day,utcnow().month,utcnow().year,utcnow().weekday()andutcnow().isoweekday(). - Using
utcnow()will cause templates to be refreshed at the start of every new minute.
- For specific values:
-
today_at(value)converts a string containing a 24-hour time format to a datetime object with today’s date in your time zone. Defaults to midnight (00:00).- Using
today_at()will cause templates to be refreshed at the start of every new minute.
# Is the current time past 10:15? {{ now() > today_at("10:15") }} - Using
-
as_datetime(value, default)converts a string containing a timestamp or a valid UNIX timestamp to a datetime object. If conversion fails, the function returns thedefaultvalue. If nodefaultis provided and the input is a string that cannot be converted to a datetime, it returnsNone. For other invalid inputs (e.g., a list, dictionary, or a numeric value too large to convert), it raises an error when nodefaultis supplied. In case the input is already a datetime object, it is returned unchanged. If the input is adatetime.dateobject, midnight is added as the time. This function can also be used as a filter. -
as_timestamp(value, default)converts a datetime object or string to UNIX timestamp. If that fails, returns thedefaultvalue, or if omitted raises an error. This function can also be used as a filter. -
as_local()converts a datetime object to local time. This function can also be used as a filter. -
strptime(string, format, default)parses a string based on a formatand returns a datetime object. If that fails, it returns the defaultvalue or, if omitted, raises an error. -
relative_timeconverts a datetime object to its human-friendly “age” string. The age can be in seconds, minutes, hours, days, months, or years (but only the biggest unit is considered. For example, if it’s 2 days and 3 hours, “2 days” will be returned). Note that it only works for dates in the past.- Using
relative_time()will cause templates to be refreshed at the start of every new minute.
- Using
-
time_since(datetime, precision)converts a datetime object into its human-readable time string. The time string can be in seconds, minutes, hours, days, months, and years.precisiontakes an integer (full number) and indicates the number of units returned. The last unit is rounded. For example:precision = 1could return “2 years” whileprecision = 2could return “1 year 11 months”. This function can also be used as a filter. If the datetime is in the future, returns 0 seconds. A precision of 0 returns all available units, default is 1. -
time_until(datetime, precision)converts a datetime object into a human-readable time string. The time string can be in seconds, minutes, hours, days, months, and years.precisiontakes an integer (full number) and indicates the number of units returned. The last unit is rounded. For example:precision = 1could return “2 years” whileprecision = 2could return “1 year 11 months”. This function can also be used as a filter. If the datetime is in the past, returns 0 seconds. A precision of 0 returns all available units, default is 1. -
timedeltareturns a timedelta object, which represents a duration (an amount of time between two datetimes). It accepts the same arguments as the Pythondatetime.timedeltafunction – days, seconds, microseconds, milliseconds, minutes, hours, weeks.# 77 minutes before current time. {{ now() - timedelta( hours = 1, minutes = 17 ) }} -
as_timedelta(string)converts a string to a timedelta object, which represents a duration (an amount of time between two datetimes). Expects data in the formatDD HH:MM:SS.uuuuuu,DD HH:MM:SS,uuuuuu, or as specified by ISO 8601 (e.g.P4DT1H15M20Swhich is equivalent to4 1:15:20) or PostgreSQL’s day-time interval format (e.g.3 days 04:05:06). This function can also be used as a filter.# Renders to "00:10:00" {{ as_timedelta("PT10M") }} -
Filter
timestamp_local(default)converts a UNIX timestamp to the ISO format string representation as date/time in your local timezone. If that fails, returns thedefaultvalue, or if omitted raises an error. If a custom string format is needed in the string, usetimestamp_custominstead. -
Filter
timestamp_utc(default)converts a UNIX timestamp to the ISO format string representation representation as date/time in UTC timezone. If that fails, returns thedefaultvalue, or if omitted raises an error. If a custom string format is needed in the string, usetimestamp_custominstead. -
Filter
timestamp_custom(format_string, local=True, default)converts an UNIX timestamp to its string representation based on a custom format, the use of a local timezone is the default. If that fails, returns thedefaultvalue, or if omitted raises an error. Supports the standard Python time formatting options.
UNIX timestampint or float).
If your template is returning a timestamp that should be displayed in the frontend (e.g., as a sensor entity with device_class: timestamp), you have to ensure that it is the ISO 8601 format (meaning it has the “T” separator between the date and time portion). Otherwise, frontend rendering on macOS and iOS devices will show an error. The following value template would result in such an error:
{{ states.sun.sun.last_changed }} => 2023-07-30 20:03:49.253717+00:00 (missing “T” separator)
To fix it, enforce the ISO conversion via isoformat():
{{ states.sun.sun.last_changed.isoformat() }} => 2023-07-30T20:03:49.253717+00:00 (contains “T” separator)
{{ 120 | timestamp_local }}
To/From JSON
The to_json filter serializes an object to a JSON string. In some cases, it may be necessary to format a JSON string for use with a webhook, as a parameter for command-line utilities or any number of other applications. This can be complicated in a template, especially when dealing with escaping special characters. Using the to_json filter, this is handled automatically.
to_json also accepts boolean arguments for pretty_print, which will pretty print the JSON with a 2-space indent to make it more human-readable, and sort_keys, which will sort the keys of the JSON object, ensuring that the resulting string is consistent for the same input.
If you need to generate JSON that will be used by a parser that lacks support for Unicode characters, you can add ensure_ascii=True to have to_json generate Unicode escape sequences in strings.
The from_json filter operates similarly, but in the other direction, de-serializing a JSON string back into an object.
To/From JSON examples
Template
{% set temp = {'temperature': 25, 'unit': '°C'} %}
stringified object: {{ temp }}
object|to_json: {{ temp|to_json(sort_keys=True) }}
Output
stringified object: {'temperature': 25, 'unit': '°C'}
object|to_json: {"temperature": 25, "unit": "°C"}
Conversely, from_json can be used to de-serialize a JSON string back into an object to make it possible to easily extract usable data.
Template
{% set temp = '{"temperature": 25, "unit": "°C"}'|from_json %}
The temperature is {{ temp.temperature }}{{ temp.unit }}
Output
The temperature is 25°C
from_json(default) function will attempt to convert the input to json. If that fails, returns the default value, or if omitted raises an error.
Template
{% set result = 'not json'|from_json('not json') %}
The value is {{ result }}
Output
The value is not json
Is defined
Sometimes a template should only return if a value or object is defined, if not, the supplied default value should be returned. This can be useful to validate a JSON payload.
The is_defined filter allows to throw an error if a value or object is not defined.
Example using is_defined to parse a JSON payload:
{{ value_json.val | is_defined }}
This will throw an error UndefinedError: 'value_json' is undefined if the JSON payload has no val attribute.
Version
-
version()Returns a AwesomeVersion objectfor the value given inside the brackets. - This is also available as a filter (
| version).
- This is also available as a filter (
Examples:
-
{{ version("2099.9.9") > "2000.0.0" }}Will returnTrue -
{{ version("2099.9.9") < "2099.10" }}Will returnTrue -
{{ "2099.9.9" | version < "2099.10" }}Will returnTrue -
{{ (version("2099.9.9") - "2100.9.10").major }}Will returnTrue -
{{ (version("2099.9.9") - "2099.10.9").minor }}Will returnTrue -
{{ (version("2099.9.9") - "2099.9.10").patch }}Will returnTrue
Distance
Not supported in limited templates.
-
distance()measures the distance between home, an entity, or coordinates. The unit of measurement (kilometers or miles) depends on the system’s configuration settings. -
closest()will find the closest entity.
Distance examples
If only one location is passed in, Home Assistant will measure the distance from home.
Using Lat Lng coordinates: {{ distance(123.45, 123.45) }}
Using State: {{ distance(states.device_tracker.paulus) }}
These can also be combined in any combination:
{{ distance(123.45, 123.45, 'device_tracker.paulus') }}
{{ distance('device_tracker.anne_therese', 'device_tracker.paulus') }}
Closest examples
The closest function and filter will find the closest entity to the Home Assistant location:
Query all entities: {{ closest(states) }}
Query all entities of a specific domain: {{ closest(states.device_tracker) }}
Query all entities in group.children: {{ closest('group.children') }}
Query all entities in group.children: {{ closest(states.group.children) }}
Find entities closest to a coordinate or another entity. All previous arguments still apply for second argument.
Closest to a coordinate: {{ closest(23.456, 23.456, 'group.children') }}
Closest to an entity: {{ closest('zone.school', 'group.children') }}
Closest to an entity: {{ closest(states.zone.school, 'group.children') }}
Since closest returns a state, we can combine it with distance too.
{{ closest(states).name }} is {{ distance(closest(states)) }} kilometers away.
The last argument of the closest function has an implicit expand, and can take any iterable sequence of states or entity IDs, and will expand groups:
Closest out of given entities:
{{ closest(['group.children', states.device_tracker]) }}
Closest to a coordinate:
{{ closest(23.456, 23.456, ['group.children', states.device_tracker]) }}
Closest to some entity:
{{ closest(states.zone.school, ['group.children', states.device_tracker]) }}
It will also work as a filter over an iterable group of entities or groups:
Closest out of given entities:
{{ ['group.children', states.device_tracker] | closest }}
Closest to a coordinate:
{{ ['group.children', states.device_tracker] | closest(23.456, 23.456) }}
Closest to some entity:
{{ ['group.children', states.device_tracker] | closest(states.zone.school) }}
Contains
Jinja provides by default a in operatorTrue when one element is in a provided list.
The contains test and filter allow you to do the exact opposite and test for a list containing an element. This is particularly useful in select or selectattr filter, as well as to check if a device has a specific attribute, a supported_color_modes, a specific light effect.
Some examples:
-
{{ state_attr('light.dining_room', 'effect_list') | contains('rainbow') }}will returntrueif the light has arainboweffect. -
{{ expand('light.office') | selectattr("attributes.supported_color_modes", 'contains', 'color_temp') | list }}will return all light that support color_temp in the office group. -
This more complex example uses the
{% set current_month = now().month %} {% set extra_ambiance = [ {'name':'Halloween', 'month': [10,11]}, {'name':'Noel', 'month': [1,11,12]} ]%} {% set to_add = extra_ambiance | selectattr('month', 'contains', current_month ) | map(attribute='name') | list %} {% set to_remove = extra_ambiance | map(attribute='name') | reject('in', to_add) | list %} {{ (state_attr('input_select.light_theme', 'options') + to_add ) | unique | reject('in', to_remove) | list }}containsfilter to match the current month with a list. In this case, it’s used to generate a list of light theme to give to theInput select: Set optionsaction.
Numeric functions and filters
Some of these functions can also be used in a filtersqrt(2), or as part of a filter like this 2|sqrt.
The numeric functions and filters raise an error if the input is not a valid number, optionally a default value can be specified which will be returned instead. The is_number function and filter can be used to check if a value is a valid number. Errors can be caught by the default filter.
-
{{ float("not_a_number") }}- the template will fail to render -
{{ "not_a_number" | sin }}- the template will fail to render -
{{ float("not_a_number", default="Invalid number!") }}- renders as"Invalid number!" -
{{ "not_a_number" | sin(default="Invalid number!") }}- renders as"Invalid number!"
-
float(value, default)function will attempt to convert the input to afloat. If that fails, returns thedefaultvalue, or if omitted raises an error. -
float(default)filter will attempt to convert the input to afloat. If that fails, returns thedefaultvalue, or if omitted raises an error. -
is_numberwill returnTrueif the input can be parsed by Python’sfloatfunction and the parsed input is notinfornan, in all other cases returnsFalse. Note that a Pythonboolwill returnTruebut the strings"True"and"False"will both returnFalse. Can be used as a filter. -
int(value, default)function is similar tofloat, but converts to anintinstead. Likefloat, it has a filter form, and an error is raised if thedefaultvalue is omitted. Fractional part is discarded:int("1.5")is1. -
bool(value, default)function converts the value to eithertrueorfalse. The following values are considered to betrue: booleantrue, non-zeroints andfloats, and the strings"true","yes","on","enable", and"1"(case-insensitive).falseis returned for the opposite values: booleanfalse, integer or floating-point0, and the strings"false","no","off","disable", and"0"(also case-insensitive). If the value is not listed here, the function returns thedefaultvalue, or if omitted raises an error. This function is intended to be used on states of binary sensors, switches, or similar entities, so its behavior is different from Python’s built-inboolconversion, which would consider e.g."on","off", and"unknown"all to betrue, but""to befalse; if that is desired, usenot not valueor a similar construct instead. Likefloatandint,boolhas a filter form. Usingnoneas the default value is particularly useful in combination with the immediate if filter: it can handle all three possible cases in a single line. -
log(value, base, default)will take the logarithm of the input. When the base is omitted, it defaults toe- the natural logarithm. Ifvalueorbasecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can also be used as a filter. -
sin(value, default)will return the sine of the input. Ifvaluecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
cos(value, default)will return the cosine of the input. Ifvaluecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
tan(value, default)will return the tangent of the input. Ifvaluecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
asin(value, default)will return the arcus sine of the input. Ifvaluecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
acos(value, default)will return the arcus cosine of the input. Ifvaluecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
atan(value, default)will return the arcus tangent of the input. Ifvaluecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
atan2(y, x, default)will return the four quadrant arcus tangent of y / x. Ifyorxcan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
sqrt(value, default)will return the square root of the input. Ifvaluecan’t be converted to afloat, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
max([x, y, ...])will obtain the largest item in a sequence. Uses the same parameters as the built-in maxfilter. -
min([x, y, ...])will obtain the smallest item in a sequence. Uses the same parameters as the built-in minfilter. -
average([x, y, ...], default)will return the average value of the sequence. If list is empty or contains non-numeric value, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
median([x, y, ...], default)will return the median value of the sequence. If list is empty or contains non-numeric value, returns thedefaultvalue, or if omitted raises an error. Can be used as a filter. -
statistical_mode([x, y, ...], default)will return the statistical mode value (most frequent occurrence) of the sequence. If the list is empty, it returns thedefaultvalue, or if omitted raises an error. It can be used as a filter. -
emathematical constant, approximately 2.71828. -
pimathematical constant, approximately 3.14159. -
taumathematical constant, approximately 6.28318. -
Filter
round(precision, method, default)will convert the input to a number and round it toprecisiondecimals. Round has four modes and the default mode (with no mode specified) will round-to-even. If the input value can’t be converted to a float, returns thedefaultvalue, or if omitted raises an error.-
round(precision, "floor", default)will always round down toprecisiondecimals -
round(precision, "ceil", default)will always round up toprecisiondecimals -
round(1, "half", default)will always round to the nearest .5 value.precisionshould be 1 for this mode
-
-
Filter
value_one|bitwise_and(value_two)perform a bitwise and(&) operation with two values. -
Filter
value_one|bitwise_or(value_two)perform a bitwise or(|) operation with two values. -
Filter
value_one|bitwise_xor(value_two)perform a bitwise xor(^) operation with two values. -
Filter
ordwill return for a string of length one an integer representing the Unicode code point of the character when the argument is a Unicode object, or the value of the byte when the argument is an 8-bit string. -
Filter
multiply(arg)will convert the input to a number and multiply it byarg. Useful in list operations in conjunction withmap. -
Filter
add(arg)will convert the input to a number and add it toarg. Useful in list operations in conjunction withmap.
Complex type checking
In addition to strings and numbers, Python (and Jinja) supports lists, sets, and dictionaries. To help you with testing these types, you can use the following tests:
-
x is listwill return whetherxis alistor not (e.g.[1, 2] is listwill returnTrue). -
x is setwill return whetherxis asetor not (e.g.{1, 2} is setwill returnTrue). -
x is tuplewill return whetherxis atupleor not (e.g.(1, 2) is tuplewill returnTrue). -
x is datetimewill return whetherxis adatetimeor not (e.g.datetime(2020, 1, 1, 0, 0, 0) is datetimewill returnTrue). -
x is string_likewill return whetherxis a string, bytes, or bytearray object.
Note that, in Home Assistant, Jinja has built-in tests for boolean (True/False), callable (any function), float (a number with a decimal), integer (a number without a decimal), iterable (a value that can be iterated over such as a list, set, string, or generator), mapping (mainly dict but also supports other dictionary like types), number (float or int), sequence (a value that can be iterated over and indexed such as list and string), and string.
Type conversions
While Jinja natively supports the conversion of an iterable to a list, it does not support conversion to a tuple or set. To help you with using these types, you can use the following functions:
-
set(x)will convert any iterablexto aset(e.g.set([1, 2]) == {1, 2}) -
tuple(x)will convert any iterablexto atuple(e.g.tuple("abc") == ("a", "b", "c"))
Note that, in Home Assistant, to convert a value to a list, a string, an int, or a float, Jinja has built-in functions with names that correspond to each type.
Iterating multiple objects
The zip() function can be used to iterate over multiple collections in one operation.
{% set names = ['Living Room', 'Dining Room'] %}
{% set entities = ['sensor.living_room_temperature', 'sensor.dining_room_temperature'] %}
{% for name, entity in zip(names, entities) %}
The {{ name }} temperature is {{ states(entity) }}
{% endfor %}
zip() can also unzip lists.
{% set information = [
('Living Room', 'sensor.living_room_temperature'),
('Dining Room', 'sensor.dining_room_temperature')
] %}
{% set names, entities = zip(*information) %}
The names are {{ names | join(', ') }}
The entities are {{ entities | join(', ') }}
Functions and filters to process raw data
These functions are used to process raw value’s in a bytes format to values in a native Python type or vice-versa.
The pack and unpack functions can also be used as a filter. They make use of the Python 3 struct library.
See: Python struct library documentation
- Filter
value | pack(format_string)will convert a native type to abytestype object. This will call functionstruct.pack(format_string, value). ReturnsNoneif an error occurs or whenformat_stringis invalid. - Function
pack(value, format_string)will convert a native type to abytestype object. This will call functionstruct.pack(format_string, value). ReturnsNoneif an error occurs or whenformat_stringis invalid. - Filter
value | unpack(format_string, offset=0)will try to convert abytesobject into a native Python object. Theoffsetparameter defines the offset position in bytes from the start of the inputbytesbased buffer. This will call functionstruct.unpack_from(format_string, value, offset=offset). ReturnsNoneif an error occurs or whenformat_stringis invalid. Note that the filterunpackwill only return the firstbytesobject, despite the functionstruct.unpack_fromsupporting to return multiple objects (e.g. withformat_stringbeing">hh". - Function
unpack(value, format_string, offset=0)will try to convert abytesobject into a native Python object. Theoffsetparameter defines the offset position in bytes from the start of the inputbytesbased buffer. This will call functionstruct.unpack_from(format_string, value, offset=offset). ReturnsNoneif an error occurs or whenformat_stringis invalid. Note that the functionunpackwill only return the firstbytesobject, despite the functionstruct.unpack_fromsupporting to return multiple objects (e.g. withformat_stringbeing">hh".
Some examples:
-
{{ 0xDEADBEEF | pack(">I") }}- renders asb"\xde\xad\xbe\xef" -
{{ pack(0xDEADBEEF, ">I") }}- renders asb"\xde\xad\xbe\xef" -
{{ "0x%X" % 0xDEADBEEF | pack(">I") | unpack(">I") }}- renders as0xDEADBEEF -
{{ "0x%X" % 0xDEADBEEF | pack(">I") | unpack(">H", offset=2) }}- renders as0xBEEF
String filters
- Filter
urlencodewill convert an object to a percent-encoded ASCII text string (e.g., for HTTP requests usingapplication/x-www-form-urlencoded). - Filter
slugify(separator="_")will convert a given string into a “slug”. - Filter
ordinalwill convert an integer into a number defining a position in a series (e.g.,1st,2nd,3rd,4th, etc). - Filter
value | from_hexDecodes a hex string to raw bytes. - Filter
value | base64_encodeEncodes a string or bytes to a base 64 string. - Filter
value | base64_decodeDecodes a base 64 string to a string, by default utf-8 encoding is used. - Filter
value | base64_decode("ascii")Decodes a base 64 string to a string, using ascii encoding. - Filter
value | base64_decode(None)Decodes a base 64 string to raw bytes.
Some examples:
-
{{ "homeassistant" | base64_encode }}- renders asaG9tZWFzc2lzdGFudA== -
{{ "aG9tZWFzc2lzdGFudA==" | base64_decode }}- renders ashomeassistant -
{{ "aG9tZWFzc2lzdGFudA==" | base64_decode(None) }}- renders asb'homeassistant' -
{{ "0F010003" | from_hex }}- renders asb'\x0f\x01\x00\x03' -
{{ "0F010003" | from_hex | base64_encode }}- renders asDwEAAw==
Hashing
The template engine contains a few filters and functions to hash a string of
data. A few very common hashing algorithms are supported: md5, sha1,
sha256, and sha512.
Some examples:
-
{{ md5("Home Assistant") }}- renders asf3f2b8b3b40084aa87e92b7ffb02ed13885fea2d07 -
{{ "Home Assistant" | md5 }}- renders asf3f2b8b3b40084aa87e92b7ffb02ed13885fea2d07 -
{{ sha1("Home Assistant") }}- renders as14bffd017c73917bfda2372aaf287570597b8e82 -
{{ "Home Assistant" | sha1 }}- renders as14bffd017c73917bfda2372aaf287570597b8e82 -
{{ sha256("Home Assistant") }}- renders asa18f473c9d3ed968a598f996dcf0b9de84de4ee04c950d041b61297a25bcea49 -
{{ "Home Assistant" | sha256 }}- renders asa18f473c9d3ed968a598f996dcf0b9de84de4ee04c950d041b61297a25bcea49 -
{{ sha512("Home Assistant") }}- renders asf251e06eb7d3439e1a86d6497d6a4531c3e8c809f538be62f89babf147d7d63aca4e77ae475b94c654fd38d8f543f778ce80007d6afef379d8a0e5d3ddf7349d -
{{ "Home Assistant" | sha512 }}- renders asf251e06eb7d3439e1a86d6497d6a4531c3e8c809f538be62f89babf147d7d63aca4e77ae475b94c654fd38d8f543f778ce80007d6afef379d8a0e5d3ddf7349d
Regular expressions
For more information on regular expressions
See: Python regular expression operations
- Test
string is match(find, ignorecase=False)will match the find expression at the beginning of the string using regex. - Test
string is search(find, ignorecase=False)will match the find expression anywhere in the string using regex. - Filter
string|regex_replace(find='', replace='', ignorecase=False)will replace the find expression with the replace string using regex. Access to the matched groups inreplaceis possible with'\\1','\\2', etc. - Filter
value | regex_findall(find='', ignorecase=False)will find all regex matches of the find expression invalueand return the array of matches. - Filter
value | regex_findall_index(find='', index=0, ignorecase=False)will do the same asregex_findalland return the match at index.
Shuffling
The template engine contains a filter and function to shuffle a list.
Shuffling can happen randomly or reproducibly using a seed. When using a seed it will always return the same shuffled list for the same seed.
Some examples:
-
{{ [1, 2, 3] | shuffle }}- renders as[3, 1, 2](random) -
{{ shuffle([1, 2, 3]) }}- renders as[3, 1, 2](random) -
{{ shuffle(1, 2, 3) }}- renders as[3, 1, 2](random) -
{{ [1, 2, 3] | shuffle("random seed") }}- renders as `[2, 3, 1] (reproducible) -
{{ shuffle([1, 2, 3], seed="random seed") }}- renders as `[2, 3, 1] (reproducible) -
{{ shuffle([1, 2, 3], "random seed") }}- renders as `[2, 3, 1] (reproducible) -
{{ shuffle(1, 2, 3, seed="random seed") }}- renders as `[2, 3, 1] (reproducible)
Flatten a list of lists
The template engine provides a filter to flatten a list of lists: flatten.
It will take a list of lists and return a single list with all the elements.
The depth of the flattening can be controlled using the levels parameter.
The flattening process is recursive, so it will flatten all nested lists, until
the number of levels (if specified) is reached.
Some examples:
-
{{ flatten([1, [2, [3]], 4, [5 , 6]]) }}- renders as[1, 2, 3, 4, 5, 6] -
{{ [1, [2, [3]], 4, [5 , 6]] | flatten }}- renders as[1, 2, 3, 4, 5, 6] -
{{ flatten([1, [2, [3]]], levels=1) }}- renders as[1, 2, [3]] -
{{ [1, [2, [3]]], flatten(levels=1) }}- renders as[1, 2, [3]] -
{{ flatten([1, [2, [3]]], 1) }}- renders as[1, 2, [3]] -
{{ [1, [2, [3]]], flatten(1) }}- renders as[1, 2, [3]]
Find common elements between lists
The template engine provides a filter to find common elements between two lists: intersect.
This function returns a list containing all elements that are present in both input lists.
Some examples:
-
{{ intersect([1, 2, 5, 3, 4, 10], [1, 2, 3, 4, 5, 11, 99]) }}- renders as[1, 2, 3, 4, 5] -
{{ [1, 2, 5, 3, 4, 10] | intersect([1, 2, 3, 4, 5, 11, 99]) }}- renders as[1, 2, 3, 4, 5] -
{{ intersect(['a', 'b', 'c'], ['b', 'c', 'd']) }}- renders as['b', 'c'] -
{{ ['a', 'b', 'c'] | intersect(['b', 'c', 'd']) }}- renders as['b', 'c']
Find elements in first list not in second list
The template engine provides a filter to find elements that are in the first list but not in the second list: difference.
This function returns a list containing all elements that are present in the first list but absent from the second list.
Some examples:
-
{{ difference([1, 2, 5, 3, 4, 10], [1, 2, 3, 4, 5, 11, 99]) }}- renders as[10] -
{{ [1, 2, 5, 3, 4, 10] | difference([1, 2, 3, 4, 5, 11, 99]) }}- renders as[10] -
{{ difference(['a', 'b', 'c'], ['b', 'c', 'd']) }}- renders as['a'] -
{{ ['a', 'b', 'c'] | difference(['b', 'c', 'd']) }}- renders as['a']
Find elements that are in either list but not in both
The template engine provides a filter to find elements that are in either of the input lists but not in both: symmetric_difference.
This function returns a list containing all elements that are present in either the first list or the second list, but not in both.
Some examples:
-
{{ symmetric_difference([1, 2, 5, 3, 4, 10], [1, 2, 3, 4, 5, 11, 99]) }}- renders as[10, 11, 99] -
{{ [1, 2, 5, 3, 4, 10] | symmetric_difference([1, 2, 3, 4, 5, 11, 99]) }}- renders as[10, 11, 99] -
{{ symmetric_difference(['a', 'b', 'c'], ['b', 'c', 'd']) }}- renders as['a', 'd'] -
{{ ['a', 'b', 'c'] | symmetric_difference(['b', 'c', 'd']) }}- renders as['a', 'd']
Combine all unique elements from two lists
The template engine provides a filter to combine all unique elements from two lists: union.
This function returns a list containing all unique elements that are present in either the first list or the second list.
Some examples:
-
{{ union([1, 2, 5, 3, 4, 10], [1, 2, 3, 4, 5, 11, 99]) }}- renders as[1, 2, 3, 4, 5, 10, 11, 99] -
{{ [1, 2, 5, 3, 4, 10] | union([1, 2, 3, 4, 5, 11, 99]) }}- renders as[1, 2, 3, 4, 5, 10, 11, 99] -
{{ union(['a', 'b', 'c'], ['b', 'c', 'd']) }}- renders as['a', 'b', 'c', 'd'] -
{{ ['a', 'b', 'c'] | union(['b', 'c', 'd']) }}- renders as['a', 'b', 'c', 'd']
Combining dictionaries
The template engine provides a function and filter to merge multiple dictionaries: combine.
It will take multiple dictionaries and merge them into a single dictionary. When used as a filter,
the filter value is used as the first dictionary. The optional recursive parameter determines
whether nested dictionaries should be merged (defaults to False).
Some examples:
-
{{ {'a': 1, 'b': 2} | combine({'b': 3, 'c': 4}) }}- renders as{'a': 1, 'b': 3, 'c': 4} -
{{ combine({'a': 1, 'b': 2}, {'b': 3, 'c': 4}) }}- renders as{'a': 1, 'b': 3, 'c': 4} -
{{ combine({'a': 1, 'b': {'x': 1}}, {'b': {'y': 2}, 'c': 4}, recursive=True) }}- renders as{'a': 1, 'b': {'x': 1, 'y': 2}, 'c': 4} -
{{ combine({'a': 1, 'b': {'x': 1}}, {'b': {'y': 2}, 'c': 4}) }}- renders as{'a': 1, 'b': {'y': 2}, 'c': 4}
Working with macros
Home Assistant provides two additional functions that make macros much more powerful.
-
applyis both a filter and a test that allows you to use any callable (macros or functions) wherever you can use other filters and tests.applyalso passes along any additional parameters to the function. For example, if you had a function calleddouble, you could call{{ [1, 2, 3, 4] | map('apply', double) | list }}, which would render as[2, 4, 6, 8].
Alternatively, if you had a function calledis_multiple_of, you could call{{ [1, 2, 3, 4] | select('apply', is_multiple_of, 2) | list }}, which would render as[2, 4]. -
as_functionis a filter that takes a macro that has a named parameter calledreturns. The macro can then call{%- do returns(return_value) -%}. After passing this macro intoas_function, the resulting function returns your return value directly, preserving the underlying data type rather than rendering a string. You can return dictionaries, numbers,True/False(allowing you to write your own tests when used withapply), or any other value your code might produce.
Merge action responses
Using action responses we can collect information from various entities at the same time.
Using the merge_response template we can merge several responses into one list.
| Variable | Description |
|---|---|
value |
The incoming value (must be an action response). |
The entity_id key is appended to each dictionary within the template output list as a reference of origin. If the input dictionary already contains an entity_id key, the template will fail.
The value_key key is appended to each dictionary within the template output list as a reference of origin if the original service call was providing a list of dictionaries, for example, calendar.get_events or weather.get_forecasts.
Examples of these two keys can be seen in example merge calendar action response template output.
Example
{% set combined_forecast = merge_response(response) %}
{{ combined_forecast[0].precipitation | float(0) | round(1) }}
Example how to sort
Sorting the dictionaries within the list based on a specific key can be done directly by using Jinja’s sort filter.
{{ merge_response(calendar_response) | sort(attribute='start') | ... }}
Example merge calendar action response
{
"calendar.sports": {
"events": [
{
"start": "2024-02-27T17:00:00-06:00",
"end": "2024-02-27T18:00:00-06:00",
"summary": "Basketball vs. Rockets",
"description": "",
}
]
},
"calendar.local_furry_events": {"events": []},
"calendar.yap_house_schedules": {
"events": [
{
"start": "2024-02-26T08:00:00-06:00",
"end": "2024-02-26T09:00:00-06:00",
"summary": "Dr. Appt",
"description": "",
},
{
"start": "2024-02-28T20:00:00-06:00",
"end": "2024-02-28T21:00:00-06:00",
"summary": "Bake a cake",
"description": "something good",
}
]
},
}
{{ merge_response(response_variable) }}
[
{
"description": "",
"end": "2024-02-27T18:00:00-06:00",
"entity_id": "calendar.sports",
"start": "2024-02-27T17:00:00-06:00",
"summary": "Basketball vs. Rockets",
"value_key": "events"
},
{
"description": "",
"end": "2024-02-26T09:00:00-06:00",
"entity_id": "calendar.yap_house_schedules",
"start": "2024-02-26T08:00:00-06:00",
"summary": "Dr. Appt",
"value_key": "events"
},
{
"description": "something good",
"end": "2024-02-28T21:00:00-06:00",
"entity_id": "calendar.yap_house_schedules",
"start": "2024-02-28T20:00:00-06:00",
"summary": "Bake a cake",
"value_key": "events"
}
]
Example non-list action responses
{
"vacuum.deebot_n8_plus_1": {
"header": {
"ver": "0.0.1",
},
"payloadType": "j",
"resp": {
"body": {
"msg": "ok",
},
},
},
"vacuum.deebot_n8_plus_2": {
"header": {
"ver": "0.0.1",
},
"payloadType": "j",
"resp": {
"body": {
"msg": "ok",
},
},
},
}
{{ merge_response(response_variable) }}
[
{
"entity_id": "vacuum.deebot_n8_plus_1",
"header": {
"ver": "0.0.1",
},
"payloadType": "j",
"resp": {
"body": {
"msg": "ok",
},
},
},
{
"entity_id": "vacuum.deebot_n8_plus_2",
"header": {
"ver": "0.0.1",
},
"payloadType": "j",
"resp": {
"body": {
"msg": "ok",
},
},
},
]
Processing incoming data
The other part of templating is processing incoming data. It allows you to modify incoming data and extract only the data you care about. This will only work for platforms and integrations that mention support for this in their documentation.
It depends per integration or platform, but it is common to be able to define a template using the value_template configuration key. When a new value arrives, your template will be rendered while having access to the following values on top of the usual Home Assistant extensions:
| Variable | Description |
|---|---|
value |
The incoming value. |
value_json |
The incoming value parsed as JSON. |
This means that if the incoming values looks like the sample below:
{
"on": "true",
"temp": 21
}
The template for on would be:
"{{value_json.on}}"
Nested JSON in a response is supported as well:
{
"sensor": {
"type": "air",
"id": "12345"
},
"values": {
"temp": 26.09,
"hum": 56.73
}
}
Just use the “Square bracket notation” to get the value.
"{{ value_json['values']['temp'] }}"
The following overview contains a couple of options to get the needed values:
# Incoming value:
{"primes": [2, 3, 5, 7, 11, 13]}
# Extract first prime number
{{ value_json.primes[0] }}
# Format output
{{ "%+.1f" | value_json }}
# Math
{{ value_json | float * 1024 if is_number(value_json) }}
{{ float(value_json) * (2**10) if is_number(value_json) }}
{{ value_json | log if is_number(value_json) }}
{{ log(1000, 10) }}
{{ sin(pi / 2) }}
{{ cos(tau) }}
{{ tan(pi) }}
{{ sqrt(e) }}
# Timestamps
{{ value_json.tst | timestamp_local }}
{{ value_json.tst | timestamp_utc }}
{{ value_json.tst | timestamp_custom('%Y', True) }}
To evaluate a response, go to Developer Tools > Template, create your output in “Template editor”, and check the result.
{% set value_json=
{"name":"Outside",
"device":"weather-ha",
"data":
{"temp":"24C",
"hum":"35%"
} }%}
{{value_json.data.hum[:-1]}}
Using templates with the MQTT integration
The MQTT integration relies heavily on templates. Templates are used to transform incoming payloads (value templates) to state updates or incoming actions (command templates) to payloads that configure the MQTT device.
Using value templates with MQTT
Value templates translate received MQTT payload to a valid state or attribute.
The received MQTT is available in the value template variable, and in the value_json template variable if the received MQTT payload is valid JSON.
In addition, the template variables entity_id, name and this are available for MQTT entity value templates. The this attribute refers to the entity state of the MQTT item.
Example value template:
With given payload:
{ "state": "ON", "temperature": 21.902, "humidity": null }
Template {{ value_json.temperature | round(1) }} renders to 21.9.
Template {{ value_json.humidity }} renders to None.
Using command templates with MQTT
For actions, command templates are defined to format the outgoing MQTT payload to a format supported by the remote device. When an action is executed, the template variable value has the action data in most cases unless otherwise specified in the documentation.
In addition, the template variables entity_id, name and this are available for MQTT entity command templates. The this attribute refers to the entity state of the MQTT item.
Example command template with JSON data:
With given value 21.9 template {"temperature": {{ value }} } renders to:
{
"temperature": 21.9
}
Example command template with raw data:
When a command template renders to a valid bytes literal, then MQTT will publish this data as raw data. In other cases, a string representation will be published. So:
- Template
{{ "16" }}renders to payload encoded string"16". - Template
{{ 16 }}renders to payload encoded string"16". - Template
{{ pack(0x10, ">B") }}renders to a raw 1 byte payload0x10.
Determining types
When working with templates, it can be useful to determine the type of the returned value from a method or the type of a variable at times.
For this, Home Assistant provides the typeof() template function and filter,
which is inspired by the JavaScripttypeof operator. It reveals the type of the given value.
This is mostly useful when you are debugging or playing with templates in the developer tools of Home Assistant. However, it might be useful in some other cases as well.
Some examples:
-
{{ typeof(42) }}- renders asint -
{{ typeof(42.0) }}- renders asfloat -
{{ typeof("42") }}- renders asstr -
{{ typeof([1, 2, 3]) }}- renders aslist -
{{ typeof({"key": "value"}) }}- renders asdict -
{{ typeof(True) }}- renders asbool -
{{ typeof(None) }}- renders asNoneType -
{{ 42 | typeof }}- renders asint -
{{ 42.0 | typeof }}- renders asfloat -
{{ "42" | typeof }}- renders asstr -
{{ [1, 2, 3] | typeof }}- renders aslist -
{{ {"key": "value"} | typeof }}- renders asdict -
{{ True | typeof }}- renders asbool -
{{ None | typeof }}- renders asNoneType -
{{ some_variable | typeof }}- renders the type ofsome_variable -
{{ states("sensor.living_room") | typeof }}- renders the type of the result ofstates()function
Some more things to keep in mind
entity_id that begins with a number
If your template uses an entity_id that begins with a number (example: states.device_tracker.2008_gmc) you must use a bracket syntax to avoid errors caused by rendering the entity_id improperly. In the example given, the correct syntax for the device tracker would be: states.device_tracker['2008_gmc']
Priority of operators
The default priority of operators is that the filter (|) has priority over everything except brackets. This means that:
{{ states('sensor.temperature') | float / 10 | round(2) }}
Would round 10 to 2 decimal places, then divide states('sensor.temperature') by 10 (rounded to 2 decimal places so 10.00). This behavior is maybe not the one expected, but priority rules imply that.