Thanks. Would be great to couple that with some sort of name substitution logic as well!
Is there a way, for instance, to use a macro (or similar) to pull in the entity name in other parts of that entity’s definition?
Thanks. Would be great to couple that with some sort of name substitution logic as well!
Is there a way, for instance, to use a macro (or similar) to pull in the entity name in other parts of that entity’s definition?
When Home Assistant starts, the YAML processor executes before the Jinja processor.
In other words, Jinja templates are evaluated after the YAML processor was used to load entity configurations. In addition, a template’s scope is limited to the YAML key where it’s defined; it can’t reference other YAML keys.
Just use your text editor to copy-paste multiple instances (employing anchors and aliases).
Aaaah. Makes sense. Thanks.
Any hints on how I can get an entity’s id, name, friendly name, and unique_id in a Jinja template? Is there some sort of entity object I can inspect?
Your question suggests you’re attempting to do something that may not work (due to the processing order of YAML first and Jinja second). For example, the value of a Template Sensor’s unique_id
must be a string yet it appears you are trying to template it.
What I’m thinking, for instance, is rather than this:
turn_on:
- service: fan.set_percentage
target:
entity_id: fan.master_bedroom_fan
data:
percentage: "{{ states('input_number.master_bedroom_fan_percentage') }}"
Do something like this instead:
turn_on:
- service: fan.set_percentage
target:
entity_id: {{ *this_entity's_id* }}
data:
percentage: "{{ states('input_number.{{ the *master_bedroom_fan* part of this entity's id}}_percentage') }}"
Does that make sense?
If I can do that, then I can pretty much anchor the entire block.
This might work and only because those particular options support templates (and they’re evaluated after the entity has been configured):
turn_on:
- service: fan.set_percentage
target:
entity_id: '{{ this.entity_id }}'
data:
percentage: "{{ states('input_number.' ~ this.object_id ~'_percentage') }}"
this
refers to the entity (i.e. to itself) and is a State Object. entity_id
and object_id
are properties of a State Object.
For future reference, you cannot nest templates like this:
"{{ states('input_number.{{ the *master_bedroom_fan* part of this entity's id}}_percentage') }}"
This is superb - I define the fan once (master_bedroom_fan in this case) and then through the use of the above templating & YAML anchors definition of the remaining fans is done in just 3 lines (not counting the additional input_boolean & input_text entities, but they’re trivial definitions so copy/pasta is not an issue):
spare_room_fan:
<<: *hunter-fan-definition
friendly_name: "Spare Room Fan"
unique_id: spare_room_fan
One minor issue with this - I get warnings in HA Core Log for the overrides (though everything works fine!):
YAML file /config/configuration.yaml contains duplicate key "friendly_name". Check lines 178 and 253
YAML file /config/configuration.yaml contains duplicate key "unique_id". Check lines 179 and 254
I guess I can just ignore that…
Final challenge:
I use the number of speeds that the fan has in two places. The first is a simple kvp:
speed_count: 6
…and the second is used in a formula (using speed count divided by 100 - the constant that I multiply by percentage):
command: >
{% set speed_value = (percentage|float * 0.06)|round(0) %}
{% if (speed_value|float) > 0 %}
speed_{{speed_value}}
{% else %}
turn_off
{% endif %}
I thought I could use another alias for that, ie:
speed_count: &fan-speed-increments 6
command: >
{% set speed_value = (percentage|float * *fan-speed-increments|float / 100)|round(0) %}
{% if (speed_value|float) > 0 %}
speed_{{speed_value}}
{% else %}
turn_off
{% endif %}
… but this doesn’t work. I get error:
Master Bedroom Fan: Error executing script. Error for call_service at pos 1: Error rendering data template: ValueError: Template error: float got invalid input '*fan-speed-increments' when rendering template '{% set speed_value = (percentage|float * '*fan-speed-increments'|float / 100)|round(0) %} {% if (speed_value|float) > 0 %} speed_{{speed_value}} {% else %} turn_off {% endif %}' but no default was specified
Any tips for that?
Finally, FYI, this is the whole piece (for now):
fan:
- platform: template
fans:
master_bedroom_fan: &hunter-fan-definition
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
speed_count: &fan-speed-increments 6
value_template: "{{ states('input_boolean.' ~ this.object_id ~'_state') }}"
turn_on:
- service: fan.set_percentage
target:
entity_id: '{{ this.entity_id }}'
data:
percentage: "{{ states('input_number.' ~ this.object_id ~'_percentage') }}"
turn_off:
- service: input_boolean.turn_off
target:
entity_id: 'input_boolean.{{this.object_id}}_state'
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: turn_off
direction_template: "{{states('input_text.' ~ this.object_id ~'_direction')}}"
set_direction:
- service: input_text.set_value
target:
entity_id: 'input_text.{{this.object_id}}_direction'
data:
value: "{{direction}}"
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: reverse
percentage_template: "{{ states('input_number.' ~ this.object_id ~'_percentage') if is_state('input_boolean.' ~ this.object_id ~'_state', 'on') else 0 }}"
set_percentage:
- service: input_boolean.turn_{{ 'on' if percentage > 0 else 'off' }}
target:
entity_id: 'input_boolean.{{this.object_id}}_state'
- service: input_number.set_value
target:
entity_id: 'input_number.{{this.object_id}}_percentage'
data:
value: "{{ percentage }}"
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: >
{% set speed_value = (percentage|float * 0.06)|round(0) %}
{% if (speed_value|float) > 0 %}
speed_{{speed_value}}
{% else %}
turn_off
{% endif %}
spare_room_fan:
<<: *hunter-fan-definition
friendly_name: "Spare Room Fan"
unique_id: spare_room_fan
You shouldn’t ignore it. The warnings are legitimate.
In my linked example, the anchor is located at the start of what will be referenced by the alias (i.e. excludes what shouldn’t be repeated). Alternately, an alias can override a key in the anchor’s block (see the Override section here).
Because *fan-speed-increments
is a YAML alias and you’re using it in a Jinja template. Anchors and aliases are a YAML concept and are not valid in Jinja templates.
Thanks to your hints I’ve worked out a different… better… way to calculate speed value:
{% set speed_value = (percentage|float / this.attributes.percentage_step|float)|round(0)|int %}
Ah… I had mistakenly thought that the YAML processor would see that first & replace it with the value ‘6’ that’s then passed to Jinja…
That would require the YAML processor to inspect the contents of Jinja templates and have a sufficient understanding of Jinja to pick apart YAML from Jinja. It doesn’t do that.
Hmmmm… I’ve located the anchor at the start of the block to be referenced by the alias, and - as far as I can tell - I’m using the overrides as per the doc you referred.
Everything seems to work… I just see a warning in the log.
Here is where I’m at now (alias & overrides right down the bottom):
fan:
- platform: template
fans:
master_bedroom_fan: &hunter-fan-definition
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
speed_count: 6
value_template: "{{ states('input_boolean.' ~ this.object_id ~'_state') }}"
turn_on:
- service: fan.set_percentage
target:
entity_id: '{{ this.entity_id }}'
data:
percentage: "{{ states('input_number.' ~ this.object_id ~'_percentage') }}"
turn_off:
- service: input_boolean.turn_off
target:
entity_id: 'input_boolean.{{this.object_id}}_state'
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: turn_off
direction_template: "{{states('input_text.' ~ this.object_id ~'_direction')}}"
set_direction:
- service: input_text.set_value
target:
entity_id: 'input_text.{{this.object_id}}_direction'
data:
value: "{{direction}}"
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: reverse
percentage_template: "{{ states('input_number.' ~ this.object_id ~'_percentage') if is_state('input_boolean.' ~ this.object_id ~'_state', 'on') else 0 }}"
set_percentage:
- service: input_boolean.turn_{{ 'on' if percentage > 0 else 'off' }}
target:
entity_id: 'input_boolean.{{this.object_id}}_state'
- service: input_number.set_value
target:
entity_id: 'input_number.{{this.object_id}}_percentage'
data:
value: "{{ percentage }}"
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: >
{% set speed_value = (percentage|float / this.attributes.percentage_step|float)|round(0)|int %}
{% if (speed_value|int) > 0 %}
speed_{{speed_value}}
{% else %}
turn_off
{% endif %}
spare_room_fan:
<<: *hunter-fan-definition
friendly_name: "Spare Room Fan"
unique_id: spare_room_fan
Review the example in my very first reply. Take note of where the anchor is defined. Namely the location of this thing:
<<: &dimmer
Sorry - this notation is confusing me… &dimmer is the anchor, <<:
is saying insert here… but that’s what you use with aliases… non?
I thought the anchor was purely defined by &… and the override alias by <<: *…
I can’t find any examples or explanations of <<: &dimmer
type notation.
Obviously have a lot to learn…
More examples:
These examples are very clear, thank you @123!
The weird thing is not only can I not find any formal documentation that describes the "<<: &"
notation you are using (all I can find is ‘use “&…” as the anchor, and “<<: *…” as the override alias’), but when I try it with the Template Fan integration HA does not like it at all (developer / YAML check protests loudly).
I have tried:
fan:
- platform: template
fans:
master_bedroom_fan:
<<: &hunter-fan-definition
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
fan:
- platform: template
fans:
master_bedroom_fan:
<<: &hunter-fan-definition
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
fan:
- platform: template
fans:
master_bedroom_fan:
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
<<: &hunter-fan-definition
and
fan:
- platform: template
fans:
master_bedroom_fan: <<: &hunter-fan-definition
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
… none work unfortunately.
The only thing that works is:
fan:
- platform: template
fans:
master_bedroom_fan: &hunter-fan-definition
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
This works well and allows me to create multiple additional fans (all of which work nicely) which only require a few lines of YAML each (plus the additional input boolean, number, and text required per fan):
spare_room_fan:
<<: *hunter-fan-definition
friendly_name: "Spare Room Fan"
unique_id: spare_room_fan
… the only downside is that HA complains about the overrides being duplicate keys. Is this maybe a bug in HA YAML implementation?
In your second example, indent all options under the anchor.
fan:
- platform: template
fans:
master_bedroom_fan:
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
<<: &hunter_fan_definition
speed_count: 6
value_template: "{{ states('input_boolean.' ~ this.object_id ~'_state') }}"
turn_on:
- service: fan.set_percentage
target:
entity_id: '{{ this.entity_id }}'
data:
percentage: "{{ states('input_number.' ~ this.object_id ~'_percentage') }}"
turn_off:
- service: input_boolean.turn_off
target:
entity_id: 'input_boolean.{{this.object_id}}_state'
... etc ...
spare_room_fan:
friendly_name: "Spare Room Fan"
unique_id: spare_room_fan
<<: *hunter_fan_definition
some_other_room_fan:
friendly_name: "Some Other Room Fan"
unique_id: some_other_room_fan
<<: *hunter_fan_definition
Huh.
That worked - thank you!
Re the YAML - that’s a clever trick. I had thought “<<: &…” was effectively one thing, but actually it is two. “<<:” says insert the following indented parts (map) after it at that point, and there just happens to be a separate “&” anchor sitting there as well marking the following indented parts (map) for later re-use.
Interesting that I don’t see that sort of usage in very many YAML explainers.
Also interesting that HA complains about duplicate keys when given a ‘typical’ example of anchor usage such as:
default: &default-person
name: Bill
height: 175
age: 5
person1:
<<: *default-person
age: 20
Anyways, learn something new every day. Thanks for your help!
For anyone interested, final configuration here.
By using references to the state object & YAML anchors, aliases, and overrides we can declare a complex entity once, and then use that as a template for multiple additional entities.
Note, you’ll need to separately declare the required input boolean, text, and number entities as well.
This file (fans-config.yaml) is included with fan: !include fans-config.yaml
in configuration.yaml:
- platform: template
fans:
master_bedroom_fan:
friendly_name: "Master Bedroom Fan"
unique_id: master_bedroom_fan
<<: &hunter-fan-definition
speed_count: 6
value_template: "{{ states('input_boolean.' ~ this.object_id ~'_state') }}"
turn_on:
- service: fan.set_percentage
target:
entity_id: '{{ this.entity_id }}'
data:
percentage: "{{ states('input_number.' ~ this.object_id ~'_percentage') }}"
turn_off:
- service: input_boolean.turn_off
target:
entity_id: 'input_boolean.{{this.object_id}}_state'
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: turn_off
direction_template: "{{states('input_text.' ~ this.object_id ~'_direction')}}"
set_direction:
- service: input_text.set_value
target:
entity_id: 'input_text.{{this.object_id}}_direction'
data:
value: "{{direction}}"
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: reverse
percentage_template: "{{ states('input_number.' ~ this.object_id ~'_percentage') if is_state('input_boolean.' ~ this.object_id ~'_state', 'on') else 0 }}"
set_percentage:
- service: input_boolean.turn_{{ 'on' if percentage > 0 else 'off' }}
target:
entity_id: 'input_boolean.{{this.object_id}}_state'
- service: input_number.set_value
target:
entity_id: 'input_number.{{this.object_id}}_percentage'
data:
value: "{{ percentage }}"
- service: remote.send_command
target:
entity_id: remote.linknlink01
data:
device: '{{ this.object_id }}'
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: >
{% set speed_value = (percentage|float / this.attributes.percentage_step|float)|round(0)|int %}
{% if (speed_value|int) > 0 %}
speed_{{speed_value}}
{% else %}
turn_off
{% endif %}
spare_room_fan:
<<: *hunter-fan-definition
friendly_name: "Spare Room Fan"
unique_id: spare_room_fan
lounge_room_fan:
<<: *hunter-fan-definition
friendly_name: "Lounge Room Fan"
unique_id: lounge_room_fan
#...etc etc...