Converting a value to a percentage

Hi all!

I have a DIY electric motorbike, which runs a custom BMS system. It pings the pack voltage via MQTT when charging to HA.

The sensor config is as follows

  • platform: mqtt
    name: ‘3Fazer - Voltage’
    state_topic: ‘ev/3fazer’
    unit_of_measurement: 'V

I would like to convert this voltage to battery charge % if possible inside HA.

100% is 116.2V
0% 95.2V

The battery drain is more or less linear. Any assistance on how I can do this would be much appreciated. as always.

Have you considered using a template sensor.

  - sensor:
    - name: "battery_sensor"
      unique_id: "Battery Sensor"
      state: >-
        {% set voltage = states('sensor.3fazer_-_voltage') | float(0) %}
        {{ ((voltage - 95.2) / 0.21) | int }}

This will give you the % in a truncated integer.

Thank you so much, worked perfectly.

You don’t need a template sensor. The MQTT sensor supports a value template.

- platform: mqtt
  name: '3Fazer - Voltage'
  state_topic: 'ev/3fazer'
  value_template: "{{ ((value|float(0) - 95.2) / 0.21)|round(0) }}"
  unit_of_measurement: '%'

Also using |int truncates the decimal, where as |round(0) will round to the nearest whole number.


70.99|int = 70
70.99|round(0) = 71
1 Like

Also thanks! I did like the seperation so I can monitor voltage as well.

Updated it to use round vs int and that has correct it showing 99% vs 100% at full charge. Thanks

Hi @tom_l

Possibly kind of dumb question - how did you get the value of ‘0.21’ in the example above?

I have a sensor where I measure mm, and want to find the percentage where min are 100 mm and max are 500 mm…?

From this data:

If given two points (x1,y1) (x2,y2) you can work out the equation for the line that passes through them:

y = mx + c

So your two points (0%, 100mm) and (100%, 500mm) gives:

y = 0.25*x - 25


% full = 0.25*(x in mm) - 25

1 Like

Damn, @tom_l :slight_smile:
Thanx ! :innocent:


I was thinking wrong;

My sensor range are 100 mm (full) to 500 mm (empty) to monitor a water tank.
In my case, when the states(sensor.water_level) = 100, it should show 100% and opposite, when at 500 it should show 0%.

How on earth should I calculate this?

To make it worse, I wanted to put this into a gauge. It worked well until I realized it was all opposite… duh…

Are you able to help on this?

Exactly the same way as in the link I provided above.

%full = -0.25*(x in mm) + 125

Could you please show me how you calculated the numbers?
Sorry, advanced math ain’t my strong side, I do electronics and ESP32 stuff (using a TOF to measure the distance to the water level) :slight_smile:

I am using slider (input_number) as the min & max can change (different tanks to monitor).
Initially I resolved it differently:

        state: >-
          {% set fill = states('sensor.tof_poc') | float(0) %}
          {% set min = states('input_number.mm_min') | float(0) %}
          {% set max = states('input_number.mm_max') | float(0) %}
          {{ (((fill - min) * 100) / (max-min)) | round(0) }}

How am I going to put that advanced calculation - even the other way where 100 mm = 100% and 500 mm = 0%? And even bring into consideration the input sliders where min and max can change?

Think I’m about to give up this route and look for something else…

I did. Watch the video in the link I posted. It’s high school maths.

You could try this instead:

You cant use templates with it but it is easy to change the points config. Which really you should not have to once it is set up.

As a mental exercise for the points (0%, input_number.mm_min) and (100%, input_number.mm_max):

        state: >-
          {% set distance = states('sensor.tof_poc') | float(0) %}
          {% set x1 = states('input_number.mm_min') | float(0) %}
          {% set x2 = states('input_number.mm_max') | float(0) %}
          {% set m = 100 / (x2 - x1) %}
          {% set c = 100 - (m * x2) %}
          {{ distance * m  + c }}

this will work for any values of input_number.mm_min and input_number.mm_max

1 Like

I actually got it with your example :slight_smile: I’m passed 50, so I do not remember so much from math classes back in the days :wink:

Interesting part - if I changed

          {% set x1 = states('input_number.mm_min') | float(0) %}
          {% set x2 = states('input_number.mm_max') | float(0) %}


          {% set x1 = states('input_number.mm_max') | float(0) %}
          {% set x2 = states('input_number.mm_min') | float(0) %}

I get the reversed % where 100mm are 100% and 500 mm are 0%.

Thank you so much @tom_l

Um no. All you have to do is change the input number values in the frontend using the sliders.

input_number.mm_min = 100
input_number.mm_max = 500


input_number.mm_min = 500
input_number.mm_max = 100

Will both work the way you want them to. That was the whole point of using input numbers in the template, so you don’t have to change the template.

I noticed that straight away… playing with both scenarios :slight_smile:
Need to make sure this way of calculating stick to my brain :slight_smile:

Thats my config. Works perfectly. 0.1m = 100%, 1.1m=0%

  - platform: ultrasonic
    trigger_pin: 27
    echo_pin: 26
    unit_of_measurement: "%"
    accuracy_decimals: 0
    name: "Lenti esoviz tartaly"
    update_interval: 10s
      - calibrate_linear:
          - 0.1 -> 100
          - 1.1 -> 0

That appears to be ESPHome configuration. Not Home Assistant.