Jinja Help Needed

For the life of me, I can’t figure out how to return an attribute from a list of entities. I can return the entity name that matches the criteria (last sonos player that is playing and is_coordinator: false, but it won’t then go and find the specified attribute, in this case, what song is playing. Anyone know how I can do this?

  sonos_group_song:
    friendly_name: Group Song
    entity_id:
      - media_player.sonos_master_bedroom
      - media_player.sonos_living_room
      - media_player.sonos_guest_room
      - media_player.sonos_basement
      - media_player.sonos_office
    value_template: >-
      {%- set players = [
      "media_player.sonos_master_bedroom",
      "media_player.sonos_office",
      "media_player.sonos_living_room",
      "media_player.sonos_guest_room",
      "media_player.sonos_basement"] -%}
      {% for player in players if is_state(player, 'playing') %}
        {% if is_state_attr(player, 'is_coordinator', false) %}
          {% if loop.last %}
            {% set value = player %}
          {% endif %}
        {% endif %}
        {{value|attr('media_title')}}
      {% endfor %}

What you are trying to do is not possible for jinja. You cannot perform loops and pull a single result. This is a limitation of jinja. It’s the same reason you cannot create a simple counter in jinja.

The only way you can get what you want here is to use if statements.

If you really don’t want to write nested if statements, i suggest using app daemon or create your own component using python.

1 Like

Ok thanks. I ended giving up and doing the same with if statements. Was hoping it was possible to do without it but nothing was working. Thanks for the confirmation.

Are you trying to only get 1 response? Or are you trying to get all playing players as a list?

Now that i think about it, this may not be correct and you may actually be able to get this working because you are outputting inside the loop.

Ok, so this should work. 2 things I noticed:

  1. attr() fitler does not work in the template editor. Not sure if its any different during execution, but for safety, i removed it and replaced it by grabbing the domain/device name and getting into the states object.

  2. Setting the value on loop.last has never worked for me, might just be better to output at that moment in time.

Give this a whirl:

  {%- set players = [
  "media_player.sonos_master_bedroom",
  "media_player.sonos_office",
  "media_player.sonos_living_room",
  "media_player.sonos_guest_room",
  "media_player.sonos_basement"] -%}
  {% for player in players if is_state(player, 'playing') and is_state_attr(player, 'is_coordinator', false) %}
    {% set domain, device = player.split('.') %}
    {% set p = states[domain][device] %}
    {% if loop.last %}
      {{ states[domain][device].attributes.media_title }}
    {% endif %}
  {% endfor %}

If you don’t want to have a hard coded list you can do the following:

{% for player in states.media_player if player.state == 'playing' and not player.attributes.is_coordinator %}
  {% if loop.last %}
    {{ player.attributes.media_title }}
  {% endif %}
{% endfor %}
1 Like

Perfect! It’s exactly what I was looking for. Thanks for your help.

@petro

One other question for you. I’ve got most of my project working with your help above but this one last one is stumping me. So what I want to do is check all my media_players and see if the corresponding input_boolean is on then return the media_player entity_id. Example, if input_boolean.sonos_office_slave is on then return media_player.sonos_office except in the case it matches my sensor which determines if it’s the master player. The following returns “Unknown”

  sonos_join_test:
    value_template: >-
      {% set master = states.sensor.sonos_group_master.state %}
      {% for player in states.media_player -%}
        {% set domain, device = player.split('.') %}
        {% set new_domain = "input_boolean"%}
        {% set new_device = [device, "slave"]|join("_") %}
        {% set switch = ["states",new_domain,new_device,"state"]|join(".") %}
        {%- if switch == 'off' and player.entity_id != master -%}
          {%- if loop.last -%}{{player.entity_id}}
          {%- else -%}{{player.entity_id}},
          {%- endif -%}
        {%- endif -%}
      {%- endfor %}

I think its because the way you handled this line:

{% set switch = ["states",new_domain,new_device,"state"]|join(".") %}

This sets switch to a string, not a ‘state object’. so your next if statement check is checking this:

'states.input_boolean.x_x_slave.state' == 'off'

that will always return false. you should do this for those lines instead:

{% set switch = [new_domain,new_device] | join(".") %}
{%- if is_state(switch, 'off') and player.entity_id != master -%}

@petro Thanks for you help. I replaced those lines and still getting “Unknown”. Here’s my sensor…

      {% set master = states.sensor.sonos_group_master.state %}
      {% for player in states.media_player -%}
        {% set domain, device = player.split('.') %}
        {% set new_domain = "input_boolean" %}
        {% set new_device = [device, "slave"]|join("_") %}
        {% set switch = [new_domain,new_device] | join(".") %}
        {%- if is_state(switch, 'on') and player.entity_id != master -%}
          {%- if loop.last -%}{{player.entity_id}}
          {%- else -%}{{player.entity_id}},
          {%- endif -%}
        {%- endif -%}
      {%- endfor %}

Hmm, i’m not sure. So how I debug these odd kind of things is doing things one by one. So i’ll start with the for loop and print out the player. I’ll verify that works. Then I’ll add each call piece by peice to see what the output is at each step.

I think you’re going to have to do that because nothing stands out to me.

Ok thanks. I’ll try it tonight.

Figured it out…this line

{% set domain, device = player.split(’.’) %}

needs to be

{% set domain, device = player.entity_id.split(’.’) %}

Ah yes, didn’t catch that.

Thanks for your time though, I really appreciate it. You’ve helped me simplify my automations a lot!

No problem. It’s always nice to have small files. Way easier to debug

Yeah, much easier to manage. Also helps when adding new devices or sharing configs with others, less hard coding to do.

Exactly. I’m super lazy, so I’ve learned all the ‘make things lazy’ tricks.

1 Like

In this case, lazy is GOOD! lol