Skip to content

MaxVRAM/HA-Fade-Volume-Script

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Home Assistant Media Player Volume Fade

This script fades the volume of a target_player media player, starting at it's current volume_level, to a user-defined target_volume over the user-defined duration in seconds. It also applies one of three curve algorithms to shape fade, defaulting to logarithmic, which is often considered the most natural sounding fade.

I struggled to find any comprehensive solutions on the Home Assistant forums for fading media player volumes with common attenuation curves. I really don't like abrupt changes to audio volume, so I put this script together.

For those interested, the script is fully commented, and I've put together a quick explanation of the script's working below.

Input Parameters

  • target_player: media_player entity
  • target_volume: float 0 - 1 --- volume to end at
  • duration: float 0.1 - 60 --- duration of fade in seconds
  • curve: selector [logarithmic, bezier, linear]

Working

The script works first calculating the number of total_steps required to fade based on the user-defined duration multiplied by a hard-coded step_duration of 100ms (or 10 per second). For example, a duration of 5 seconds equates to 500 steps.

It determines the difference between the media player's current volume and the user-defined target_volume. It applies the difference value as a factor to the shaped fade amount, adds it to the original volume, then pushes the new volume to the media player entity volume_level for each step in a while loop.

Timing Limitation

From what I could gather, Home Assistant calls its services on a 1 second clock. I don't know the details, however it's clear that sub-second delay calls aren't timed perfectly.

Issue: The lack of ms-accurate timing of Home Assistant's delay calls might cause slight discrepancies to fade durations. The larger the duration, the more noticeable this might become.

To make this script duration-accurate, instead of defining total_steps, a steps_left value could be used, defined by the script's start_time, end_time (which would be fixed), and the current_time for each iteration of the loop. The repeat condition could then use a pre-defined end-time, with the fade steps increasing or decreasing depending on if the calls are lagging or ahead..... but I've already spent way too much time on this, so be my guest :)

Algorithms:

Where x is the normalised time value based on the duration, starting at 0 and ending at 1.

  • (red) Linear: f(x) = x
  • (blue) Bezier: f(x) = x / (1 + (1 - x ))
  • (green) Logarithmic: f(x) = x * x * (3 - 2x)

drawing

Script code

Add this to your Home Assistant script config .yaml, and use it anywhere that allows a service call (such as automations):

fade_volume:
  alias: Fade the volume of a media player
  mode: restart
  fields:
    target_player:
      name: Target media player
      description: Target media player of volume fade.
      required: true
      example: media_player.lounge_sonos
      selector:
        entity:
          domain: media_player
    target_volume:
      name: Target volume
      description: Volume the media play will be at the end of the fade duration.
      required: true
      default: 0.5
      example: '0.5'
      selector:
        number:
          max: 1
          min: 0
          step: 0.01
          mode: slider
    duration:
      name: Fade duration
      description: Length of time in seconds the fade should take.
      required: true
      default: 5
      example: '5'
      selector:
        number:
          mode: box
          min: 0
          max: 100000
          unit_of_measurement: s
    curve:
      name: Fade curve algorithm
      description: Shape of the fade curve to apply.
      required: true
      default: logarithmic
      example: logarithmic
      selector:
        select:
          options:
          - logarithmic
          - bezier
          - linear
  variables:
    steps_per_second: 10
    total_steps: '{{ (steps_per_second * duration) | int(0) }}'
    start_volume: '{{ state_attr(target_player, ''volume_level'') | float(0) }}'
    start_diff: '{{ (target_volume - start_volume) | float(0) }}'
  sequence:
  - repeat:
      while:
      - condition: template
        value_template: '{{ repeat.index < total_steps }}'
      - condition: template
        value_template: '{{ ((state_attr(target_player, "volume_level") - target_volume)
          | abs) > 0.001 }}'
      sequence:
      - service: media_player.volume_set
        data_template:
          entity_id: '{{ target_player }}'
          volume_level: "{% set t = repeat.index / total_steps %} {% if curve == 'logarithmic'\
            \ %}\n  {{ (start_volume + (t / (1 + (1 - t))) * start_diff) | float(0)\
            \ }}\n{% elif curve == 'bezier' %}\n  {{ (start_volume + (t * t * (3 -\
            \ 2 * t)) * start_diff) | float(0) }}\n{% else %}\n  {{ (start_volume\
            \ + t * start_diff) | float(0) }}\n{% endif %}\n"
      - delay: '00:00:00.1'
  - service: media_player.volume_set
    data_template:
      entity_id: '{{ target_player }}'
      volume_level: '{{ target_volume }}'
  icon: mdi:tune-vertical

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published