Help/idea with automation/scripts (Sonoff Basic with Tasmota)

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 }}
1 Like

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 }}
1 Like

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.

1 Like

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:

2019-02-18_11-13-21

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.

1 Like

Incredible job sir, again :slight_smile:

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!

1 Like

Hello again @petro! How are you today my friend?

Sorry to bother you, AGAIN hehe :sweat_smile:

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.

1 Like

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! :slight_smile:

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!

1 Like

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

1 Like