Thanks a lot @petro! I really appreciate your help. Thanks!
I think this should work. You have a dictionary with a single item. To get the key and values from a dictionary, you use .items(). To get the first pair, you add the [0]. From there, we join the 2 strings together with an underscore. Lastly, we make it lowercase.
Hope that helps.
- service_template: >
script.interruptor_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.items()[0] | join('_') | lower }}
Wow! Thank you so much for your time sir!
In the meantime, a friend of mine had a good idea: I can program Tasmota to send two strings on the BUTTON topic, so now I have stat/[topic]/BUTTON {"BUTTON":"2","ACTION":"TOGGLE"}
for TOGGLE
using button 2. Iâve set the automation like this:
- alias: BotÔes Sonoff
initial_state: true
trigger:
- platform: mqtt
topic: stat/+/BUTTON
trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}.entity_id is defined }}"
action:
- service_template: script.sonoff_botao_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}
This is the script called:
sonoff_botao_cafeteira_casa_1_hold:
alias: "Luz principal"
sequence:
- service: light.toggle
entity_id: light.cozinha
This works like a charm!
Sorry I could not tell you soon enough, nevertheless, this could help many people here on the forum.
Now I have another problem to fix: If I call a script that does not exists, a log is created, like it should. I want to create a condition to only start the script if it exists, so I came up with this:
condition:
condition: template
value_template: "{{ states.script.sonoff_botao_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}.entity_id is defined }}"
Unfortunately I get a syntax errorâŠ
Error rendering template: TemplateSyntaxError: expected token 'end of print statement', got '{'
Any idea how can I check this template before call the template service?
The idea is to have something like this:
- alias: BotÔes Sonoff
initial_state: true
trigger:
- platform: mqtt
topic: stat/+/BUTTON
condition:
condition: template
value_template: "{{ states.script.sonoff_botao_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}.entity_id is defined }}"
action:
- service_template: script.sonoff_botao_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}
Thanks a lot!
This condition needs to be reworked. You cant have templates in templates, for example {{ {{ }} {{ }} }} will not work. So you have to combine strings and use methods to access the objects.
condition:
condition: template
value_template: >
{% set topic = trigger.topic.split('/')[1] | lower %}
{% set button = trigger.payload_json.BUTTON | lower %}
{% set action = trigger.payload_json.ACTION | lower %}
{% set object_id = 'sonoff_botao_{}_{}_{}'.format(topic, button, action) %}
{{ states.script[object_id] != None }}
Hello again! Thank you so much for your help my friend.
Iâve tried your code and I get this error after the automation triggers:
Error during template condition: UndefinedError: 'str object' has no attribute 'payload_json'
Here is the final code:
- alias: BotÔes Sonoff
initial_state: true
trigger:
- platform: mqtt
topic: stat/+/BUTTON
condition:
condition: template
value_template: >
{% set trigger = trigger.topic.split('/')[1] | lower %}
{% set button = trigger.payload_json.BUTTON | lower %}
{% set action = trigger.payload_json.ACTION | lower %}
{% set entity_id = 'script.sonoff_botao_{}_{}_{}'.format(trigger, button, action) %}
{{ not is_state(entity_id, 'unknown') }}
action:
- service_template: script.sonoff_botao_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}
Also, testing {{ not is_state(entity_id, 'unknown') }}
with a valid entity_id
on my system how as true on both an existing or not existing value⊠Maybe {{ entity_id is defined }}
could work better?
Any idea what could had gone wrong? Thanks.
just edited it, try again.
Thanks, will give it a try.
I am not sure about the {{ not is_state(entity_id, 'unknown') }}
part. Maybe {{ entity_id is defined }}
could work better?
Here is an example:
Maybe like this?
- alias: BotÔes Sonoff
initial_state: true
trigger:
- platform: mqtt
topic: stat/+/BUTTON
action:
- condition: template
value_template: >
{% set topic = trigger.topic.split('/')[1] | lower %}
{% set button = trigger.payload_json.BUTTON | lower %}
{% set action = trigger.payload_json.ACTION | lower %}
{% set entity_id = 'states.script.sonoff_botao_{}_{}_{}.state'.format(topic, button, action) %}
{{ entity_id is defined }}
- service_template: script.sonoff_botao_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}
is defined wonât work on the entity_id because it will always be defined, because the entity id is a string. Is defined doesnât have anything to do with HA, itâs a method in Jinja. It has no access to the states object, so itâs literally checking to see if its a defined variable. It will always return true because we define it in the line before.
So with that being said, we need to access the state object directly instead of using the is_state method. Try the new template, made changes again.
Hello @petro
Iâve published a MQTT message simulating a non existing topic/script and it did not work: Error while executing automation automation.botoes_sonoff. Service not found for call_service at pos 1: (ServiceNotFound(...), 'Service script.sonoff_botao_teste_2_toggle not found')
This means that no errors were found on the code itself, just the condition returned true
when it should have been false
âŠ
Here is the code that Iâve used:
- alias: BotÔes Sonoff
initial_state: true
trigger:
platform: mqtt
topic: stat/+/BUTTON
condition:
condition: template
value_template: >
{% set topic = trigger.topic.split('/')[1] | lower %}
{% set button = trigger.payload_json.BUTTON | lower %}
{% set action = trigger.payload_json.ACTION | lower %}
{% set object_id = 'sonoff_botao_{}_{}_{}'.format(topic, button, action) %}
{{ states.script[object_id] is defined }}
action:
- service_template: script.sonoff_botao_{{ trigger.topic.split('/')[1] | lower }}_{{ trigger.payload_json.BUTTON | lower }}_{{ trigger.payload_json.ACTION | lower }}
I believe the condition placed before the action should not be the problem, right? Since trigger data is created therefore data from the trigger is available to be used on the conditionâŠ
Thanks again!
check it out again, canât use is defined.
Incredible job sir, again
Thank you so much, worked like a charm. No errors from non existing scripts on my log from now on. Helped me a lot!
I have not found any information on the forums about this scenario, looked for hours and hours. I really think this great material might help many others.
Thanks mate!
Hello again @petro! How are you today my friend?
Sorry to bother you, AGAIN hehe
I am now testing ESPHome no my setup and want to create an automation that triggers scripts dynamically just like you helped me before, would mind helping me out again?
Now I have a sensor, created for every Sonoff device running ESPHome that shows me the button action, just like this:
sensor.ender3_interruptores
that outputs buttons actions with these states: button_00_single
, button_00_double
, button_00_triple
, button_00_quadruple
, button_00_many
and button_00_long
.
So, every sensor ends with interruptores
and all states have the same topic: button_##_ACTION
.
Since I will have many sensors (sensor.light1_interruptores
, sensor.light2_interruptores
and so on) is it possible to create an automation that triggers dynamically (will add and/or remove devices randomly) with all sensors ending with interruptores
, parse the trigger and to state code, checks if script exists as a condition and calls that specific script like before?
It is the same idea as before but without using MQTT and only with Home Assistant standard device information.
Thanks a lot mate!
Itâs possible but there is no glob function for triggers, so youâll need to add all the sesnor interruptores to the trigger. The rest is pretty simple, you just need to split your string on underscores.
{% set dump, number, action = <'however you get the topic'>.split('_') %}
then build if statemens based off number and action.
Thanks for you quick reply!
I am not home so Iâve created this code remotely, so I can test it later. Here is what I came up with:
Data:
tigger.to_state.entity_id
= sensor.ender3_interruptores
trigger.to_state.state
= button_00_single
- alias: BotÔes Sonoff EspHome
initial_state: true
trigger:
- platform: state
entity_id:
- sensor.ender3_interruptores
- sensor.luz_principal_da_sala_interruptores
- sensor.mesa_de_jantar_interruptores
condition:
condition: template
value_template: >
{% set entity = trigger.to_state.entity_id.split('.')[1] | lower %}
{% set topic = entity.split('_')[0] | lower %}
{% set dump, number, action = trigger.to_state.state.split('_') %}
{% set object_id = 'sonoff_botao_{}_{}_{}'.format(topic, number, action) %}
{{ states.script[object_id] != None }}
action:
- service_template: >
{% set entity = trigger.to_state.entity_id.split('.')[1] | lower %}
{% set topic = entity.split('_')[0] | lower %}
{% set dump, number, action = trigger.to_state.state.split('_') %}
{% set object_id = 'sonoff_botao_{}_{}_{}'.format(topic, number, action) %}
script[object_id]
Result: script.sonoff_botao_ender3_00_single
, correct?
I am very positive this might work!
Will test later on and get back to you.
Thanks!
Hello @petro! I am proud to inform that this works perfectly!
- alias: BotÔes Sonoff EspHome
initial_state: true
trigger:
- platform: state
entity_id:
- sensor.ender3_interruptores
- sensor.luz_principal_da_sala_interruptores
- sensor.mesa_de_jantar_interruptores
condition:
condition: template
value_template: >
{% set entity = trigger.to_state.entity_id.split('.')[1] | lower %}
{% set topic = entity.split('_')[0] | lower %}
{% set dump, number, action = trigger.to_state.state.split('_') %}
{% set object_id = 'sonoff_botao_{}_{}_{}'.format(topic, number, action) %}
{{ states.script[object_id] != None }}
action:
- service_template: >
{% set entity = trigger.to_state.entity_id.split('.')[1] | lower %}
{% set topic = entity.split('_')[0] | lower %}
{% set dump, number, action = trigger.to_state.state.split('_') %}
{% set object_id = 'sonoff_botao_{}_{}_{}'.format(topic, number, action) %}
script.sonoff_botao_{{ topic }}_{{ number }}_{{ action }}
Too bad I cannot create a dynamically triggered automation using template and parsing the dataâŠ
Thanks for your assistance!
you canât dynamically do it because the triggers wont fire properly if the template has no entity_idâs. Itâs a drawback but it makes sense