Automation Templating not working as expexted

Hello,

I’m trying to get the following working:
Say a specific phrase to Google Assistant, which triggers an ifttt webhook with a data template, which then triggers an Automation in Home Assistant, which switches the input source of the tv based on the said-phrase (to google assistant)

The Webhook to ifttt is up and running and I’m receiving the corresponding message. So I’m gonna skip everything up to this part.
The received webhook looks like this (source is the part that I want to process):

{
    "event_type": "ifttt_webhook_received",
    "data": {
        "action": "call_service",
        "service": "media_player.select_source",
        "type": "source_change",
        "entity_id": "media_player.lg_webos_smart_tv",
        "source": " to Spotify",
        "webhook_id": XXXXXXXXXXXXXXXXXXX"
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-09T14:50:16.014181+00:00",
    "context": {
        "id": "XXXXXXXXXXXXX",
        "parent_id": null,
        "user_id": null
    }
}

The code which should extract the source and matches it against the sourcelist entry of the media_player device looks like so:

{% set namespace = namespace(source=[]) -%}
{% for word in trigger.event.data.source.split(' ') -%}
{% for source in state_attr(trigger.event.data.entity_id, 'source_list') -%}
{% for source_word in source.split(' ')  -%}
{% if (source_word|capitalize).startswith(word|capitalize) -%}
{% set namespace.source = namespace.source + [source] -%}
{%endif-%}{% endfor -%}{% endfor -%}{% endfor -%}
{{namespace.source[0]}}

The idea is that the automation fetches the list of available sources. Loops through them in search for a matching word (so I’m able to say Prime or Prime Video instead of the full “Amazon Prime Video”-Source). If it finds a match it adds it to the namespace.source
At the end it should only report the first one of the findings.

If I paste this code into the developer jinja editor, it works just as i want to (I just need to replace the trigger.event.data stuff with an example like “to Prime”.

The code of the full automation looks like this:

alias: IFTTT - Google Assistant - TV Input Source
description: ''
trigger:
  - event_data:
      action: call_service
    event_type: ifttt_webhook_received
    platform: event
condition:
  - condition: template
    value_template: '{{ trigger.event.data.service == "media_player.select_source"}}'
action:
  - service: '{{ trigger.event.data.service }}'
    data_template:
      entity_id: '{{ trigger.event.data.entity_id }}'
      source: >-
        "{% set namespace = namespace(source=[]) -%}{% for word in
        trigger.event.data.source.split(' ') -%}{% for source in
        state_attr(trigger.event.data.entity_id, 'source_list') -%}{% for
        source_word in source.split(' ')  -%}{% if
        (source_word|capitalize).startswith(word|capitalize) -%}{% set
        namespace.source = namespace.source + [source] -%}{%endif-%}{% endfor
        -%}{% endfor -%}{% endfor -%}{{namespace.source[0]}}"
mode: single

In the Home-Assistant Log I get the following messages no matter what source I choose:

Source 'Amazon Prime Video' not found for LG webOS Smart TV

Amazon Prime Video is the first source of the media_player source attribute.

Hopefully someone can help me.
Thanks in advance

Timo

Can you please show the sources of the LG webOS Smart TV from Developer Tools -> States?

If it works in the template tools, the only thing I can see is you’re wrapping your entire multiline template with quotes.

Try removing those?

      source: >-
        {% set namespace = namespace(source=[]) -%}
        {% for word in trigger.event.data.source.split(' ') -%}
        {% for source in state_attr(trigger.event.data.entity_id, 'source_list') -%}
        {% for source_word in source.split(' ')  -%}
        {% if (source_word|capitalize).startswith(word|capitalize) -%}
        {% set  namespace.source = namespace.source + [source] -%}
        {%endif-%}{% endfor -%}{% endfor -%}{% endfor -%}
        {{namespace.source[0]}}

Also, data_template is deprecated as of 0.115, so just change that to “data” while you’re here to future proof yourself when it’s finally removed.

@jocnnor: I’ve removed the quotes, but sadly without any changes in the behaviour.
@Burningstone: sure, here is a screenshot:

sources2

So, when I ran this, the source " to Spotify" matched Amazon Prime Video because of the leading space.

trigger.event.data.source.split(’ ‘) = [’ ', ‘to’, ‘Spotify’]. That first blank matched every source in my list, so whatever happened to be first was always selected.

You can try stripping the spaces first.

{% for word in trigger.event.data.source.strip().split(' ') -%}

But even doing this, I couldn’t get it to match any sources with what you have. Plus, it would have found the first one that happened to have the word ‘TO’ in it.

that strip() part fixed it. Thank you very much.

In my usecase (german) google assistant sometimes but not always adds a “to” (in german: zu) in front of the said source (e.g. “to Spotify”) and that gets pushed into the automation script. This probably wont be a problem in german since “zu” is pretty rare.

Again thank you @jocnnor :slight_smile:

here is the full automation for those wondering:

alias: IFTTT - Google Assistant - TV Input Source
description: ''
trigger:
  - event_data:
      action: call_service
    event_type: ifttt_webhook_received
    platform: event
condition:
  - condition: template
    value_template: '{{ trigger.event.data.service == "media_player.select_source"}}'
action:
  - service: '{{ trigger.event.data.service }}'
    data_template:
      entity_id: '{{ trigger.event.data.entity_id }}'
      source: >-
        {% set namespace = namespace(source=[]) -%}{% for word in
        trigger.event.data.source.strip().split(' ') -%}{% for source in
        state_attr(trigger.event.data.entity_id, 'source_list') -%}{% for
        source_word in source.split(' ')  -%}{% if
        (source_word|capitalize).startswith(word|capitalize) -%}{% set
        namespace.source = namespace.source + [source] -%}{%endif-%}{% endfor
        -%}{% endfor -%}{% endfor -%}{{namespace.source[0]}}
mode: single

Edit: fixed typo

There’s a typo in the source template. The word stript should be strip.

{% for word in trigger.event.data.source.stript().split(' ') -%}
                                              ^

I offer you a simpler version of the source template. Instead of using three nested for-loops, it uses just one combined with regex_search.

      source: >-
        {% set ns = namespace(source=[]) %}
        {% set source_list = state_attr(trigger.event.data.entity_id, 'source_list') %}
        {% set source = trigger.event.data.source.strip().replace('zu ', '') %}
         
        {% for s in source_list if (s | regex_search(source, ignorecase=True)) %}
          {% set ns.source = ns.source + [s] %}
        {% endfor %}
        {{ ns.source[0] if ns.source | count > 0 else 'unknown' }}

How it works

  • It prepares the received source string by stripping whitespace (using strip) and removing any instance of 'zu ' (using replace).

  • Most of the hard work is done by regex_search. It returns True if it finds a matching sub-string within a string. In other words if you ask it to search for 'Prime Video' (or even just 'Prime') in 'Amazon Prime Video' it will return True. It will also match 'prime video' because we have instructed it to ignore case.

  • The for-loop uses regex_search to iterate through all items in source_list. If it finds a match, it assigns it to the list (ns.source) in the same manner your template did.

  • Finally, it reports the zeroth item in ns.source but only if ns.source is not empty. If it is empty (no match was found) it reports unknown.

NOTE

To test the template, I could not duplicate your environment. Therefore the template may require additional adjustments to meet your requirements.

To test the template, I emulated your environment using the following:

{% set source_list = [ 'Amazon Prime Video', 'Apple TV', 'Disney+', 'Foto und Video', 'GALERIE', 'Game', 'HDMI 2', 'HDMI 4', 'Joyn', 'LG Content Store', 'Live TV', 'Musik', 'Netflix', 'Sky Ticket', 'Spotify\_- Musik und Podcasts', 'Twitch', 'Webbrowser', 'YouTube' ] %}
{% set event_data_source = 'zu Prime Video' %}


{% set source = event_data_source.strip().replace('zu ', '') %}
{% set ns = namespace(source=[]) %}

{% for s in source_list if (s | regex_search(source, ignorecase=True)) %}
  {% set ns.source = ns.source + [s] %}
{% endfor %}

{{ ns.source[0] if ns.source | count > 0 else 'unknown' }}

Beforehand I’ve also tried to get it to work with regex_search but i somehow couldn’t figure out your way on my own.
I think I’m gonna switch to your regex search version, since it looks cleaner in the automation.
Thanks

Let me know if it works. If not, I can help you correct it.

it works! i just replaced the written list and string with the trigger data and the state-attribute call. So it looks like this:

{% set source_list = state_attr(trigger.event.data.entity_id, 'source_list') %}
{% set event_data_source = trigger.event.data.source %}

{% set source = event_data_source.strip().replace('zu ', '') %}
{% set ns = namespace(source=[]) %}

{% for s in source_list if (s | regex_search(source, ignorecase=True)) %}
  {% set ns.source = ns.source + [s] %}
{% endfor %}

{{ ns.source[0] if ns.source | count > 0 else 'unknown' }}

Thanks again for the help everybody :slight_smile:

You didn’t have to do that because the first template I posted was the one intended for your use. The second template (the one you modified) was only provided to show you what I had used to test the principle.

Anyway, glad to hear it works for you.