YAML in Home Assistant

Ola Thoresen
5 min readAug 31, 2020

This is not going to be an in depth description of YAML, but just a very basic example of how the config in Home Assistant is to be understood if you have some understanding of lists and dictionaries in python, but struggle to wrap your head around yaml.

Consider the following meta-code, where a {} denotes a dictionary, and [] wraps around a list. I am using “=” as the assignment operator here, so this is not valid python, but it might make it understandable also for people who are coming from other worlds.

automation = [
{
'id' = '1234',
'alias' = 'my automation'
'trigger' = [
{
'entity_id' = 'sensor.foo',
'platform' = 'state',
'to' = 'off'
},
{
'entity_id' = 'sensor.bar',
'platform' = 'numeric_state',
'above' = 100
}
],
'condition' = [
{
'condition' = 'state',
'entity_id' = 'device_tracker.paulus',
'state' = 'home'
}
],
'action' = [
{
'delay' = {
'minutes' = 1,
'hours' = 0,
}
},
{
'service' = 'script.my_script',
'data_template = {
'foo' = 'bar',
'baz' = '{{ jinja2-template }}'
}
}
]
},
{
'id' = '4567',
'alias' = 'other automation'
'trigger' = [
{
'entity_id' = ....
....
}
],
'condition' = [
...
],
'action' = [
...
]
},
]

So, the “automation” entry in the config is a list ([]) consisting of several dictionaries ({}) (two in this example).
Each dictionary consists of key = value-pairs.
Some of the values are strings (the value of the keys id, alias etc), and some of the values are new lists (trigger, condition and action). They should maybe have been named triggers, conditions and actions to emphasize that they are indeed lists that can contain multiple entries, but that is just the way things are (see the last section of this page to understand why).

So each entry in the “trigger”, “condition” and “action” lists is again a dictionary, with key-value-pairs, where again the value can be either a string, a list or a dictionary.

So the “trigger” list in the first example above contains two entries (two dictionaries), which describes each of the two things that will trigger the automation to run (in the “trigger” list, the entries are treated as “or”, so if either of those things happen, the automation is run). Read more about different triggers here

The “condition” entry in the first example is also a list, but this is a list with only one item (one dictionary) in it. It checks if Paulus is home, and if he is, the automaton will continue.
Here, multiple conditions could be in the list, and by default they would be treated as “and”, so all of them must be “true” for they condition to be “true”.
Read more about conditions in the official documentation for how to use “and” and “or” and nest these things properly.

The “action” part of the automation is again a list, consisting of in this case two dictionaries. Each dictionary describes an action that should be taken. The first action is a delay, which is another dictionary with some key-value pairs to describe how long we should wait.

The second action is a dictionary where the first value (the value of ‘service’, is a string (“script.my_script”), while the value of the key ‘data_template’ is another dictionary, with new key-value pairs describing the data to be sent to the script.

After we wrap up all the open {}s and []s, we start on a new entry in the outer “automation” list, with a new dictionary, describing the next automation.

If we were to write this in YAML, it would be much more compact, and — at least after getting used to it — also more human readable:

automation:
- id: 1234
alias: my automation
trigger:
- entity_id: sensor.foo
platorm: state
to: 'off'
- entity_id: sensor.bar
platform: numeric_state
above: 100
condition:
- condition: state
entity_id: device_tracker.paulus
state: home
action:
- delay:
minutes: 1
hours: 0
- service: script.my_script
data_template:
foo: bar
baz: "{{ jinja2-template }}"
- id: 4567
alias: other automation
trigger:
- entity_id: ...
...
condition:
...
action:
...

So each entry in a dictionary is either a key on itself on a line (“automation”, “trigger”, “condition” etc. OR a “key: value” on a single line (id: 1234).
IF the key is on its own line, it means that what follows below is either a list, or a new dictionary. If the next line starts with a “-” the content of the outer dictionary is a list. If the next line starts with a few spaces and a keyword, the content of the outer dictionary is another dictionary.

Whitespace matters! Dashes matters! and Colon matters!

foo:
bar: baz

is not the same as

foo:
bar: baz

Or

foo:
- bar: baz

Or

foo:
- bar
- baz

The first means that “foo” and “bar” both are siblings. In the second example “bar” is a entry in the “foo” dictionary, and in the third, “bar” is a dictionary that is an item in the “foo”-list, and in the forth “bar” and “baz” are simple entries in the “foo” list:

{ 
'foo': None,
'bar': 'baz'
}
{
'foo': {
'bar': 'baz'
}
}
{
'foo': [
{
'bar': 'baz'
}
]
}
{
'foo': [
'bar',
'baz'
]
}

So

trigger:
- entity_id: xxxxxx

Means that the value of the “trigger” key in dictionary is a list. It can be multiple triggers, each with its own entity_id:

trigger:
- entity_id: xxxxxx
- entity_id: yyyyyy

While

data_template:
foo: bar
baz: "{{ jinja2 template }}"

Means that the value of the “data_template” key is another dictionary directly, there can be only one “foo” and one “baz” in this data_template.

The following is valid YAML but does not really make sense as “foo” is declared twice in the “data_template” dictionary (only one of them will actally be used):

data_template:
foo: bar
foo: baz

Adding confusion

Just to make things more difficult, you can actually skip the use of lists in the trigger, condition and action-part, and write them directly as dictionaries, and that is done in a lot of the official examples.

The parser will automatically understand it if there is only one trigger, and it is written as a dictionary directly. From one of the examples in the official documentation:

automation: 
- alias: 'Enciende Despacho'
trigger:
platform: state
entity_id: sensor.mini_despacho
to: 'on'

So, since the “trigger” here is a dictionary, not a list, it will be treated just the same is if it were a list with only one entry:

automation: 
- alias: 'Enciende Despacho'
trigger:
- platform: state
entity_id: sensor.mini_despacho
to: 'on'

That is why you will see both versions used, and it can be very confusing where you need a “-” and where you don’t, at least until you start dreaming in YAML. Personally I would strongly suggest to always use the list format, as it just makes more sense to me to be consistent.

--

--

Ola Thoresen

Noe over gjennomsnittlig interessert. Kjentmann i IP- og nettverksjungelen, og jobber i nLogic AS.