Rescaling volume in Home Assistant

02/12/2018 - Home Assistant, mpd, volume

I use Home Assistant with mpd as a media player component.

I used an input number going from 0 to 1 directly as a volume control, but found it to be scaled badly (anything below around 0.8 was too quiet to hear). To get around this, I am using a simple volume rescaling in Home Assistant, until I have a chance to see if I can find any underlying reasons for the problem.

Scaling the volume

The goal is to scale a user input going from 0 to 1 to a different scale, also going from 0 to 1, that can be used as the volume setting for mpd. I found the inverse of the solution given here to work well.

Specifically, they suggest remapping the volume according to:

$$ y = \frac{z^x - 1}{z - 1} $$

where \(x\) is a user input between 0 and 1, \(z\) is a base, and \(y\) is the rescaled volume, also between 0 and 1. With this form, the rescaled volume goes up slowly when \(x\) is small, but then much faster at larger values of \(x\). The bigger the base \(z\) is, the stronger this effect will be.

As I have the issue that the volume goes up too slowly in the beginning and too fast at the end, I need the inverse transform:

$$ x = \log_{z} \left( y * (z - 1) + 1 \right) $$

where this time, I use \(y\) as the user input and \(x\) as the rescaled volume, used with mpd. I have found a value of \(z = 15\) to work well for me.

Home Assistant

To make this work with Home Assistant and mpd, I use a combination of an input_number, a sensor and an automation:

input_number:
  input_volume:
    name: Volume
    initial: 0.4
    min: 0.0
    max: 1.0
    step: 0.01

sensor:
  - platform: template
    sensors:
      mpd_volume:
        friendly_name: 'mpd volume'
        value_template: "{{ log((states('input_number.input_volume') | float) * (15 - 1) + 1, 15) | round(3) }}"

automation:
  - alias: Set radio volume
    trigger:
      platform: state
      entity_id: sensor.mpd_volume
    action:
      - service: media_player.volume_set
        data_template:
          entity_id: media_player.mpd
          volume_level: '{{ trigger.to_state.state }}'