Battery level of a lifepo4 battery

Hi all! I have a lifepo4 battery, and I have a current and voltage sensor from an ESPHome device.
Is there any blueprint to get the battery level from those sensors? I couldn’t find any

I was thinking of making it using:

  • an integration sensor on the shunt
  • an utility_meter on the integration sensor
  • an automation, triggered when the voltage is low or high (battery empty or full) that call the reset service of the utility_meter

do you have any advice/suggestions?

Just realised Blueprints are just for automations. :sweat_smile:
In the meantime making some progress:

sensor:
  - platform: integration
    source: sensor.current1
    name: battery_1_energy

utility_meter:
  battery1:
    source: sensor.battery_1_energy

template:
  - sensor:
      - name: "Battery SOC"
        state: >
          {% set v=states('sensor.battery1_voltage') %}
          {% set c=states('utility_meter.battery1')/80 %}
          {% if v<=11 %}
            {{0}}
          {% elif v>11 && v<=12 %}
            {{(0.02 * (v - 11) / 1.0 + 0.08 * (1-(v - 11) / 1.0)) * (1 * (v - 11) / 1.0 + 0.95 * (1-(v - 11) / 1.0)) + c * (1-(1 * (v - 11) / 1.0 + 0.95 * (1-(v - 11) / 1.0)))}}
          {% elif v>12 && v<=12.5 %}
            {{(0.08 * (v - 12) / 0.5 + 0.14 * (1-(v - 12) / 0.5)) * (0.95 * (v - 12) / 0.5 + 0.8 * (1-(v - 12) / 0.5)) + c * (1-(0.95 * (v - 12) / 0.5 + 0.8 * (1-(v - 12) / 0.5)))}}
          {% elif v>12.5 && v<=12.8 %}
            {{(0.14 * (v - 12.5) / 0.3 + 0.17 * (1-(v - 12.5) / 0.3)) * (0.8 * (v - 12.5) / 0.3 + 0.6 * (1-(v - 12.5) / 0.3)) + c * (1-(0.8 * (v - 12.5) / 0.3 + 0.6 * (1-(v - 12.5) / 0.3)))}}
          {% elif v>12.8 && v<=12.9 %}
            {{(0.17 * (v - 12.8) / 0.1 + 0.2 * (1-(v - 12.8) / 0.1)) * (0.6 * (v - 12.8) / 0.1 + 0.3 * (1-(v - 12.8) / 0.1)) + c * (1-(0.6 * (v - 12.8) / 0.1 + 0.3 * (1-(v - 12.8) / 0.1)))}}
          {% elif v>12.9 && v<=13 %}
            {{(0.2 * (v - 12.9) / 0.1 + 0.3 * (1-(v - 12.9) / 0.1)) * (0.3 * (v - 12.9) / 0.1 + 0.2 * (1-(v - 12.9) / 0.1)) + c * (1-(0.3 * (v - 12.9) / 0.1 + 0.2 * (1-(v - 12.9) / 0.1)))}}
          {% elif v>13 && v<=13.1 %}
            {{(0.3 * (v - 13) / 0.1 + 0.4 * (1-(v - 13) / 0.1)) * (0.2 * (v - 13) / 0.1 + 0.2 * (1-(v - 13) / 0.1)) + c * (1-(0.2 * (v - 13) / 0.1 + 0.2 * (1-(v - 13) / 0.1)))}}
          {% elif v>13.1 && v<=13.2 %}
            {{(0.4 * (v - 13.1) / 0.1 + 0.7 * (1-(v - 13.1) / 0.1)) * (0.2 * (v - 13.1) / 0.1 + 0.3 * (1-(v - 13.1) / 0.1)) + c * (1-(0.2 * (v - 13.1) / 0.1 + 0.3 * (1-(v - 13.1) / 0.1)))}}
          {% elif v>13.2 && v<=13.3 %}
            {{(0.7 * (v - 13.2) / 0.1 + 0.9 * (1-(v - 13.2) / 0.1)) * (0.3 * (v - 13.2) / 0.1 + 0.5 * (1-(v - 13.2) / 0.1)) + c * (1-(0.3 * (v - 13.2) / 0.1 + 0.5 * (1-(v - 13.2) / 0.1)))}}
          {% elif v>13.3 && v<=13.4 %}
            {{(0.9 * (v - 13.3) / 0.1 + 0.99 * (1-(v - 13.3) / 0.1)) * (0.5 * (v - 13.3) / 0.1 + 0.8 * (1-(v - 13.3) / 0.1)) + c * (1-(0.5 * (v - 13.3) / 0.1 + 0.8 * (1-(v - 13.3) / 0.1)))}}
          {% elif v>13.4 && v<=13.6 %}
            {{(0.99 * (v - 13.4) / 0.2 + 1 * (1-(v - 13.4) / 0.2)) * (0.8 * (v - 13.4) / 0.2 + 1 * (1-(v - 13.4) / 0.2)) + c * (1-(0.8 * (v - 13.4) / 0.2 + 1 * (1-(v - 13.4) / 0.2)))}}
          {% elif v>13.6 %}
            {{1}}
          {% endif %}

automation:
  - alias: "Reset Battery SOC when empty"
    trigger:
      platform: numeric_state
      entity_id: sensor.battery_voltage
      below: 11.5
    action:
      service: utility_meter.battery1.reset
      target:
        entity_id: input_number.battery_soc

automation:
  - alias: "Reset Battery SOC when full"
    trigger:
      platform: numeric_state
      entity_id: sensor.battery_voltage
      above: 13.8
    action:
      service: utility_meter.battery1.calibrate
      target:
        entity_id: input_number.battery_soc
      data:
        value: 80

Batteries are a bit tricky to work with.
They are designed to provide as much power as possible for as long as possible and then die quickly there after.
When the point of “death” is depends the specific battery design, so you will have to test this to find it.
Even though the battery is designed to live for as long as possible it will loose a bit of power anyway and for some devices the level can get below the accepted level needed for running before the point of “death” is reached.

Also be aware that temperature will change this characteristics, which goes for both ambient temperature, but also from internal temperature changes due to charging and discharging.
And then there is also the fact that the battery will get slowly worse over time when used, so its max capacity will go down.

Sensor template and binary sensor template can be configured using the user interface at Settings > Devices & Services > Helpers. Select the + Add helper button and then select the Template helper.

{## obtenemos los voltajes de cada inversor ##}
{% set v1=states('sensor.smgii_battery_average_voltage')|float %}
{% set v2=states('sensor.smgii_2_battery_average_voltage')|float %}
{## calculamos la media de medida entre los voltajes que llegan a los dos inversores ##}
{% set v = (v1+v2)/2 %}
{## tabla de voltajes SOC lofepo4 16s ##}
{% set lifepo4_s16_json = {
  "SOC_0": 40,
  "SOC_10": 48,
  "SOC_20": 51.2,
  "SOC_30": 51.50,
  "SOC_40": 52.00,
  "SOC_50": 52.20,
  "SOC_60": 52.30,
  "SOC_70": 52.80,
  "SOC_80": 53.10,
  "SOC_90": 53.60,
  "SOC_95": 55.36,
  "SOC_100": 58.40
} %}
{## inicializamos soc a -1 para obtener error de salida ##}
{% set soc_battery = -1 %}

{% if v<=lifepo4_s16_json.SOC_0 %}
{% set soc_battery=0 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_0 and v<=lifepo4_s16_json.SOC_10 %}
{% set vB=lifepo4_s16_json.SOC_10 %}
{## cálculo proporcional entre 0% y 10% ##}
{% set soc_battery=(v*10)/vB %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_10 and v<=lifepo4_s16_json.SOC_20 %}
{% set vA=lifepo4_s16_json.SOC_10 %}
{% set vB=lifepo4_s16_json.SOC_20 %}
{## cálculo proporcional entre 10% y 20% ##}
{% set soc_battery=(((v*10)/vA)+((v*20)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_20 and v<=lifepo4_s16_json.SOC_30 %}
{% set vA=lifepo4_s16_json.SOC_20 %}
{% set vB=lifepo4_s16_json.SOC_30 %}
{## cálculo proporcional entre 20% y 30% ##}
{% set soc_battery=(((v*20)/vA)+((v*30)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_30 and v<=lifepo4_s16_json.SOC_40 %}
{% set vA=lifepo4_s16_json.SOC_30 %}
{% set vB=lifepo4_s16_json.SOC_40 %}
{## cálculo proporcional entre 30% y 40% ##}
{% set soc_battery=(((v*30)/vA)+((v*40)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_40 and v<=lifepo4_s16_json.SOC_50 %}
{% set vA=lifepo4_s16_json.SOC_40 %}
{% set vB=lifepo4_s16_json.SOC_50 %}
{## cálculo proporcional entre 40% y 50% ##}
{% set soc_battery=(((v*40)/vA)+((v*50)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_50 and v<=lifepo4_s16_json.SOC_60 %}
{% set vA=lifepo4_s16_json.SOC_50 %}
{% set vB=lifepo4_s16_json.SOC_60 %}
{## cálculo proporcional entre 50% y 60% ##}
{% set soc_battery=(((v*50)/vA)+((v*60)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_60 and v<=lifepo4_s16_json.SOC_70 %}
{% set vA=lifepo4_s16_json.SOC_60 %}
{% set vB=lifepo4_s16_json.SOC_70 %}
{## cálculo proporcional entre 60% y 70% ##}
{% set soc_battery=(((v*60)/vA)+((v*70)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_70 and v<=lifepo4_s16_json.SOC_80 %}
{% set vA=lifepo4_s16_json.SOC_70 %}
{% set vB=lifepo4_s16_json.SOC_80 %}
{## cálculo proporcional entre 70% y 80% ##}
{% set soc_battery=(((v*70)/vA)+((v*80)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_80 and v<=lifepo4_s16_json.SOC_90 %}
{% set vA=lifepo4_s16_json.SOC_80 %}
{% set vB=lifepo4_s16_json.SOC_90 %}
{## cálculo proporcional entre 80% y 90% ##}
{% set soc_battery=(((v*80)/vA)+((v*90)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_90 and v<=lifepo4_s16_json.SOC_95 %}
{% set vA=lifepo4_s16_json.SOC_90 %}
{% set vB=lifepo4_s16_json.SOC_95 %}
{## cálculo proporcional entre 90% y 95% ##}
{% set soc_battery=(((v*90)/vA)+((v*95)/vB))/2 %}
{% endif %}

{% if v>lifepo4_s16_json.SOC_95 and v<=lifepo4_s16_json.SOC_100 %}
{% set vA=lifepo4_s16_json.SOC_95 %}
{% set vB=lifepo4_s16_json.SOC_100 %}
{## cálculo proporcional entre 95% y 100% ##}
{% set soc_battery=(((v*95)/vA)+((v*100)/vB))/2 %}
{% endif %}
{{soc_battery}}

image