maxym
October 11, 2021, 3:39pm
1
After 2 years of using HA and collecting a lot of self-made entities (manually configured mqtt ones + related templates) I decided to look at options of get rid of repeating code.
Here is an example of package containing declaration of window sensor (Shelly DW) through MQTT providing open/close and tilt combined into single entity, as well as battery sensor.
pck_dw_bathroom:
binary_sensor:
- platform: mqtt
name: "Window Bathroom State"
state_topic: "shellies/shellydw-bathroom/sensor/state"
payload_on: "open"
payload_off: "close"
device_class: "window"
- platform: template
sensors:
window_bathroom:
friendly_name: Window Bathroom
device_class: "window"
value_template: >-
{{ is_state("binary_sensor.window_bathroom_state", "on") }}
icon_template: >-
{% if is_state("binary_sensor.window_bathroom_state", "off") %}
mdi:window-closed
{% elif states('sensor.window_bathroom_tilt')|float > 2 %}
mdi:angle-acute
{% else %}
mdi:window-open
{% endif %}
attribute_templates:
battery: >-
{{ state_attr('sensor.window_bathroom_battery','battery') }}
tilt: >-
{{ state_attr('binary_sensor.window_bathroom_tilt','tilt') }}
count: >-
{{ state_attr('sensor.doorwindow_counter','counters')['window_bathroom_state'] }}
sensor:
- platform: mqtt
name: "Window Bathroom Battery"
state_topic: "shellies/shellydw-bathroom/sensor/battery"
unit_of_measurement: "%"
force_update: true
device_class: battery
- platform: mqtt
name: "Window Bathroom Tilt"
state_topic: "shellies/shellydw-bathroom/sensor/tilt"
unit_of_measurement: "degrees"
I have lot of such sensors. All differs only by names (entity and mqtt topic ones) and optionally by tilt angle.
Is there any way to maintain somehow customized single snippet of such code?
I tried to find any information of using yaml anchors but seems this method is not feasible. I though it’s possible to set anchors values on top of file, then include the “template”, but when having more such files with anchors named the same way all templates are filled with the same values (so anchors doen’t work like local variables for the file they are set in)
What are your workflows to cope with similar patterns?
with regards
petro
(Petro)
October 11, 2021, 5:03pm
2
use a template to write the code for you in the template editor.
Also, why don’t you just make a cover out of that?
petro
(Petro)
October 11, 2021, 5:15pm
3
{% set my_configs = [
("Window Bathroom","window_bathroom","shellies/shellydw-bathroom/sensor"),
("Window Livingroom","window_livingroom","shellies/shellydw-livingroom/sensor"),
("Window Kitchen","window_kitchen","shellies/shellydw-kitchen/sensor"),
] %}
# configs to paste
{% set text = '
binary_sensor:
- platform: mqtt
name: "{0} State"
state_topic: "{2}/state"
payload_on: "open"
payload_off: "close"
device_class: "window"
- platform: template
sensors:
{1}:
friendly_name: {0}
device_class: "window"
value_template: >-
{{{{ is_state("binary_sensor.{1}_state", "on") }}}}
icon_template: >-
{{% if is_state("binary_sensor.{1}_state", "off") %}}
mdi:window-closed
{{% elif states("sensor.{1}_tilt")|float > 2 %}}
mdi:angle-acute
{{% else %}}
mdi:window-open
{{% endif %}}
attribute_templates:
battery: >-
{{{{ state_attr("sensor.{1}_battery","battery") }}}}
tilt: >-
{{{{ state_attr("binary_sensor.{1}_tilt","tilt") }}}}
count: >-
{{{{ state_attr("sensor.doorwindow_counter","counters")["{1}_state"] }}}}
sensor:
- platform: mqtt
name: {0} Battery
state_topic: "{2}/battery"
unit_of_measurement: "%"
force_update: true
device_class: battery
- platform: mqtt
name: {0} Tilt
state_topic: "{2}/tilt"
unit_of_measurement: "degrees"
' %}
{% for name, id, topic in my_configs %}
{{ text.format(name, id, topic) }}
{% endfor %}
output:
# configs to paste
binary_sensor:
- platform: mqtt
name: "Window Bathroom State"
state_topic: "shellies/shellydw-bathroom/sensor/state"
payload_on: "open"
payload_off: "close"
device_class: "window"
- platform: template
sensors:
window_bathroom:
friendly_name: Window Bathroom
device_class: "window"
value_template: >-
{{ is_state("binary_sensor.window_bathroom_state", "on") }}
icon_template: >-
{% if is_state("binary_sensor.window_bathroom_state", "off") %}
mdi:window-closed
{% elif states("sensor.window_bathroom_tilt")|float > 2 %}
mdi:angle-acute
{% else %}
mdi:window-open
{% endif %}
attribute_templates:
battery: >-
{{ state_attr("sensor.window_bathroom_battery","battery") }}
tilt: >-
{{ state_attr("binary_sensor.window_bathroom_tilt","tilt") }}
count: >-
{{ state_attr("sensor.doorwindow_counter","counters")["window_bathroom_state"] }}
sensor:
- platform: mqtt
name: Window Bathroom Battery
state_topic: "shellies/shellydw-bathroom/sensor/battery"
unit_of_measurement: "%"
force_update: true
device_class: battery
- platform: mqtt
name: Window Bathroom Tilt
state_topic: "shellies/shellydw-bathroom/sensor/tilt"
unit_of_measurement: "degrees"
binary_sensor:
- platform: mqtt
name: "Window Livingroom State"
state_topic: "shellies/shellydw-livingroom/sensor/state"
payload_on: "open"
payload_off: "close"
device_class: "window"
- platform: template
sensors:
window_livingroom:
friendly_name: Window Livingroom
device_class: "window"
value_template: >-
{{ is_state("binary_sensor.window_livingroom_state", "on") }}
icon_template: >-
{% if is_state("binary_sensor.window_livingroom_state", "off") %}
mdi:window-closed
{% elif states("sensor.window_livingroom_tilt")|float > 2 %}
mdi:angle-acute
{% else %}
mdi:window-open
{% endif %}
attribute_templates:
battery: >-
{{ state_attr("sensor.window_livingroom_battery","battery") }}
tilt: >-
{{ state_attr("binary_sensor.window_livingroom_tilt","tilt") }}
count: >-
{{ state_attr("sensor.doorwindow_counter","counters")["window_livingroom_state"] }}
sensor:
- platform: mqtt
name: Window Livingroom Battery
state_topic: "shellies/shellydw-livingroom/sensor/battery"
unit_of_measurement: "%"
force_update: true
device_class: battery
- platform: mqtt
name: Window Livingroom Tilt
state_topic: "shellies/shellydw-livingroom/sensor/tilt"
unit_of_measurement: "degrees"
binary_sensor:
- platform: mqtt
name: "Window Kitchen State"
state_topic: "shellies/shellydw-kitchen/sensor/state"
payload_on: "open"
payload_off: "close"
device_class: "window"
- platform: template
sensors:
window_kitchen:
friendly_name: Window Kitchen
device_class: "window"
value_template: >-
{{ is_state("binary_sensor.window_kitchen_state", "on") }}
icon_template: >-
{% if is_state("binary_sensor.window_kitchen_state", "off") %}
mdi:window-closed
{% elif states("sensor.window_kitchen_tilt")|float > 2 %}
mdi:angle-acute
{% else %}
mdi:window-open
{% endif %}
attribute_templates:
battery: >-
{{ state_attr("sensor.window_kitchen_battery","battery") }}
tilt: >-
{{ state_attr("binary_sensor.window_kitchen_tilt","tilt") }}
count: >-
{{ state_attr("sensor.doorwindow_counter","counters")["window_kitchen_state"] }}
sensor:
- platform: mqtt
name: Window Kitchen Battery
state_topic: "shellies/shellydw-kitchen/sensor/battery"
unit_of_measurement: "%"
force_update: true
device_class: battery
- platform: mqtt
name: Window Kitchen Tilt
state_topic: "shellies/shellydw-kitchen/sensor/tilt"
unit_of_measurement: "degrees"
EDIT Then just keep a copy of the commented out template in a file so you can update it in the future and add/remove crap from it.
1 Like
maxym
October 11, 2021, 5:48pm
4
Thank you for that idea. I hoped in something similar evaluated by HA in runtime (on its start). Something like loop in Ansible.
Your way might definitively help but is sensitive to loosing the source template
petro
(Petro)
October 11, 2021, 5:58pm
5
You can try to use lovelace_gen
petro:
{% for name, id, topic in my_configs %}
{{ text.format(name, id, topic) }}
{% endfor %}
Liked the idea, and I want to use this method in Lovelace card - to reduce a code.
Does it only work with Lovelace gen?
Tried to use this method in some Lovelace yaml file for some view, got an error…
petro
(Petro)
October 22, 2021, 10:29pm
7
Only works with Lovelace gen might work with Lovelace gen. Lovelace gen is for Lovelace not the normal configuration. Although it might work for the regular config
Thank you!
Then I start using it in config first…
Could you clarify this:
value_template: >-
{{{{ is_state("binary_sensor.{1}_state", "on") }}}}
icon_template: >-
{{% if is_state("binary_sensor.{1}_state", "off") %}}
mdi:window-closed
{{% elif states("sensor.{1}_tilt")|float > 2 %}}
mdi:angle-acute
{{% else %}}
mdi:window-open
{{% endif %}}
Does it mean that in templates I must use {{%
instead of {%
, {{{{
instead of {{
?
petro
(Petro)
October 22, 2021, 10:44pm
9
You have to escape {, and you do that by doubling it. However that will be slightly different with Lovelace gen, you should check out it’s docs because it’s limited in comparison to HA jinja.
Just tried it for my weather sensors.
There were 3 places, 3 weather providers = 3x3 = 9 files.
Now it is ONE file.
Thank you very much!
UPDATE:
Unfortunately, I got the idea in a wrong way…
I believed that I can put that code into some yaml-file and all sensors will be generated then from this code.
But - all this is just to generate the code in the Templates window & then to put the generated code into yaml-files… Surely, it saves lot of time for writing a code & checking it. But it would be great to pass the “Paste to Template window” stage)))
If the sensors are MQTT, then you can automate their definition in loops. The idea is to use an automation that runs for instance at home assistant start and sends MQTT discovery messages, that can create MQTT sensors on the fly.
An example that I use for todo-lists (I really should create a topic about those when I have time):
automation:
- id: parametragtodolistmqtt
alias: Paramétrage TodoList MQTT
description: ''
mode: single
trigger:
- platform: homeassistant
event: start
condition: []
action:
- repeat:
for_each:
- name: croquettes
duration: 8
- name: draps_lucas
duration: 312
- name: draps_matisse
duration: 312
- name: draps_parents
duration: 312
- name: eponges
duration: 168
- name: filtre_sable
duration: 744
sequence:
- variables:
payload:
device:
name: Todo List
identifiers: todolist
command_topic: "TodoList/Action"
payload_on: "DO {{repeat.item.name}}"
payload_off: "UNDO {{repeat.item.name}}"
state_topic: "TodoList/State/{{repeat.item.name}}"
state_on: "DONE"
state_off: "TODO"
json_attributes_topic: |
TodoList/LastDone/{{repeat.item.name}}
json_attributes_template: "
{{ '{{' }}
{ 'duration': '{{repeat.item.duration}}',
'last_done': value,
'expiry': (value|as_datetime +
timedelta(hours={{repeat.item.duration}})
).isoformat() }
| tojson
{{ '}}' }}"
unique_id: "todo_list_{{repeat.item.name}}"
name: "todo_{{repeat.item.name}}"
- service: mqtt.publish
data:
topic: |
homeassistant/switch/todo_{{repeat.item.name}}/config
payload: |
{{ payload | tojson }}
Note that to escape {{
the jinja documentation tells to use the {{ '{{' }}
construct, or use {%raw%}
markers if there are bigger chunks that you want to protect.
1 Like