Can't seem to set volume_level based on itself from data_template

Hi,

I’m trying to get my Aqara Cube to increase and decrease the volume of my music based on turning it clockwise or counter-clockwise. My media_player is an Onkyo TX-NR717 amplifier which works fine with volume_level between 0 and 1.

The Aquara Cube is “too slow” to change the volume if I use volume_up and volume_down, because it uses 0.5 increments in the amplifier’s 0 to 80 range (40 being loud enough), and the cube takes one or two seconds before firing each event.

I tried to following to use volume_set based on the current volume_level, which I found in some other thread and seems like a nice solution, but it doesn’t work for me:

service: media_player.volume_set
entity_id: media_player.onkyo_tx_nr717
data_template:
  volume_level: >-
    {% if trigger.event.data.event | int > 0 %}
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level') | float + 0.05 }}
    {% else %}
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level') | float - 0.05 }}
    {% endif %}

If I instead replace the entire “state_attr” lines with only absolute values such as 0.5 and 0.1, it works as expected, by changing to volume to the low value when turning one way, to the loud value when turning the other way.

When I go to see the state of my entity, it displays the expected value for volume_level such as can be seen here:
Screenshot_2020-10-19 Developer Tools - Home Assistant

But when trying the +0.05% and -0.05% approach from above, it seems like the float it’s getting is 0 each time. I must be doing something stupid or wrong on the state_attr lines, but I’m unable to figure out what it is. Is someone able to point it out? Or tell me how to debug the output of the state_attr() on each trigger?

Thanks in advance to anyone able to help me out :slight_smile:

Matthias

I can’t replicate the problem you are experiencing. In my case, this works:

The volume_level attribute is already a float value so there’s no need for the template to use the float filter.

NOTE:
The template will fail if the media_player is off because there’s no volume_level attribute defined when it’s off.

Is there an error in the logs? Maybe the result needs rounding to a couple of decimal places or something?

Thanks for the replies. I already have a condition in my automation about the media_player being on, so that’s covered. I don’t have any about the current volume_level, so I do see some errors in the logs about it needing to be above 0 when it’s already below 0.05 and trying to lower it, but those are expected.

The only other log entries I see are about “Cube Rotate: Already running” (the name of my automation), which probably happen when I rotate it multiple times in a row too fast. But even when slowly rotating the cube, and not seeing those logs, things still don’t work as expected.

I also tried adding |round(2) after the |float and that didn’t change anything.

Very weird. Then it hit me: I do have one “non standard” thing volume related:

media_player:
  - platform: onkyo
    host: tx-nr717.marmotte.ici
    name: Onkyo TX-NR717
    # In percent, so 60 vs. original 80
    max_volume: 75
    sources:
      bd: 'Wii'
      cbl: 'Pi3'
      stb: 'Wii U'
      game: 'S. Nintendo'
      aux1: 'AUX'

In the above, I commented out the max_volume line, restarted HA… and now it works as expected! Weird and most probably a bug. Should I report this on GitHub, maybe?

Now I’m off to tweaking my data_template to increase/decrease volume based on the data.event value! :wink:

Matthias

Yes, report it against the onkyo integration.

Weird.

Even if max_volume is defined in the configuration, you said the template works when used like this:

The documentation indicates max_volume is used for scaling the volume level. I have trouble understanding how the presence of the max_volume option affects a template using state_attr().

“Weird” is what comes to mind, yes.

I have reported this at https://github.com/home-assistant/core/issues/42090

Right now, I have the following working perfectly:

service: media_player.volume_set
entity_id: media_player.onkyo_tx_nr717
data_template:
  volume_level: >-
    {% if trigger.event.data.event | int > 8000 %}
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level')|float|round(2) + 0.05 }}
    {% elif trigger.event.data.event | int > 4000 %}    
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level')|float|round(2) + 0.025 }}
    {% elif trigger.event.data.event | int > 0 %}
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level')|float|round(2) + 0.0125 }}
    {% elif trigger.event.data.event | int < -8000 %}
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level')|float|round(2) - 0.05 }}
    {% elif trigger.event.data.event | int < -4000 %}
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level')|float|round(2) - 0.025 }}
    {% elif trigger.event.data.event | int < 0 %} 
    {{ state_attr('media_player.onkyo_tx_nr717', 'volume_level')|float|round(2) - 0.0125 }}
    {% endif %}

I now just turn my little Aqara Cube and the volume goes up or down depending on which way and how much. Neat!

Matthias

If you use Jinja2 variables, it can improve the template’s legibility:

service: media_player.volume_set
entity_id: media_player.onkyo_tx_nr717
data_template:
  volume_level: >-
    {% set e = trigger.event.data.event | int %}
    {% set v = state_attr('media_player.onkyo_tx_nr717', 'volume_level')|round(2) %}
    {% if e > 8000 %} {{ v + 0.05 }}
    {% elif e > 4000 %} {{ v + 0.025 }}
    {% elif e > 0 %} {{ v + 0.0125 }}
    {% elif e < -8000 %} {{ v - 0.05 }}
    {% elif e < -4000 %} {{ v - 0.025 }}
    {% elif e < 0 %} {{ v - 0.0125 }}
    {% endif %}

Thanks! That’s indeed a much cleaner approach.

you could always just use an equation. FWIW a new compensation integration is coming through that allows you to fit polynomials to datasets, you have a dataset. You can then use the coefficients to do the math for you.

Anyways, here’s the equation that would work for you for any rotation

service: media_player.volume_set
entity_id: media_player.onkyo_tx_nr717
data_template:
  volume_level: >-
    {% set e = trigger.event.data.event | int %}
    {% set v = state_attr('media_player.onkyo_tx_nr717', 'volume_level')|round(2) %}
    {{ v + 0.0000625 * e }}

when the new compensation integration is built, you can use it to make fits for you and give you the coefficients

compensation:
  media_player_onkyo:
    entity_id: media_player.onkyo_tx_nr717
    attribute: volume_level
    data_points:
    - 0.5 -> 8000
    - 0.25 -> 4000
    - 0 -> 0
    - -0.25 -> -4000
    - -0.5 -> -8000
service: media_player.volume_set
entity_id: media_player.onkyo_tx_nr717
data_template:
  volume_level: >-
    {% set e = trigger.event.data.event | int %}
    {% set v = state_attr('media_player.onkyo_tx_nr717', 'volume_level')|round(2) %}
    {% set c = state_attr('sensor.media_player_onkyo', 'coefficients')[0] %}
    {{ v + 1 / c * e }}

Thanks a lot petro for these insightful details and examples! I have tested the equation approach, but reverted back to having 3 mapped intervals because of two issues:

  1. The Cube is very bad at sending small values. When you don’t turn it much, no event happens. This could be worked around in the equation, I guess, but…
  2. I don’t want to be able to turn the volume too high too fast, especially now that I’ve had to remove the max_volume to work around my initial problem :slight_smile:

Still, thank you very much for all of these details!

Matthias