In a given service, with a list of entities to act upon which has generated by a template, is it possible to have the target value for these entities set by a template based on upon their current value?
Specifically, I want to fade out (one or more of) my media players. I have a working script which is passed a list of media players to fade using the volume_down service (see below).
However there are a couple of drawbacks with this.
Firstly it seems the step used in volume_down is very small (and as far as I can tell not configurable).
Secondly, if two or more media players are at different volumes they will not reach zero volume at anything like the same time.
Looking at what I have below (which works albeit with the above drawbacks) I want to know if it is possible to change it use:
service: media_player.volume_set and have the volume_level calculated as the current volume for each entity less 10% (for example).
I can handle the looping, I just want to know if there is any way to refer in volume_level to the entity currently being acted upon from the list in 'entity_id`? (I suspect not)
As another (unlikely) alternative, is there a way to call a script (to do the fading, running in parallel mode) for each of the entities originally passed in as a list?
Of course all this would be moot (at least for Sonos users) if the group volume was exposed and could be changed!
Here is what I have now:
media_player_fade_out:
sequence:
repeat:
while:
- condition: template
value_template: >
{% set ns = namespace(not_faded=False) %}
{% for sonos in sonos_used %}
{% if state_attr(sonos, 'volume_level') != 0 %}
{% set ns.not_faded = True %}
{% endif %}
{% endfor %}
{{ ns.not_faded }}
sequence:
- service: media_player.volume_down
data:
entity_id: >
{% set ns = namespace(entities='') %}
{% for sonos in sonos_used if state_attr(sonos, 'volume_level') != 0 %}
{% set ns.entities = ns.entities ~ sonos %}
{% if not loop.last %}
{% set ns.entities = ns.entities ~ ', ' %}
{% endif %}
{% endfor %}
{% if ns.entities == '' %}
none
{% else %}
{{ ns.entities }}
{% endif %}
But you’ll have to loop each one individually from it’s current position if you want to use volue_set over volume_down. You should separate the script as well, so that you supply it a single list.
turn_down_for_what:
variables:
media_player_list: "{{ media_players | default([]) }}"
sequence:
- condition: template
value_template: "{{ media_player_list | length != 0 }}"
- repeat:
count: "{{ media_player_list | length }}"
sequence:
- variables:
current_entity_id: "{{ media_player_list[repeat.index - 1] }}"
volume_level: >
{% set current = state_attr(current_entity_id, 'volume_level') %}
{% set next = current - 0.1 %}
{{ next if next > 0 else 0 }}
- service: media_player.volume_set
target:
entity_id: "{{ current_entity_id }}
data:
volume_level: "{{ volume_level }}"
There’s a number of ways you can do this, but none of them are easy because jinja tries to forbit you from creating things like this.
The easiest would be using namespace
{% set used = 'media_player.sonos_kitchen', 'media_player.sonos_dining_room' %}
{% set ns = namespace(values=[]) %}
{% for e in expand(used) %}
{% set ns.values = ns.values + [(e.entity_id, e.attributes.volume_level or 0)] %}
{% endfor %}
{% set mapper = dict(ns.values) %}
{{ mapper }}
Just keep in mind that the previous template will treat media playsers that are off as a volume level of 0. If you want to filter out players that are off, just add | selectattr('state','eq','on') in the for loop line.