In an ideal world this would be whatever your expected battery pack capacity is in watts and divide by 100 - so assuming 6 HV2600 batteries (6*2,560wh) = 15,360 / 100 = 153.6 watts per % change in SoC.
As a precursor, it is worth pointing out that the battery SoC on LFP batteries is notoriously unreliable but when taken over a larger range (say 20%) accuracy improves and it will give you a reasonable representation of how the pack is performing over time.
Known issues - more a known problem of LFP batteries reporting SoC, but over a longer period of time (>8 hours) where there is patchy solar and repeated charge / discharge in the battery and the SoC neither goes up or down much ,the accuracy falls as efficiency and granularity errors creep in.
Clearly the best accuracy comes from the simple use case where you charge the batteries up and then discharge the batteries.
To minimise this effect the sensor resets itself at 100% and 10% SoC (the BMS itself resets some of its counters) and also where the difference in SoC since last measurement is zero and if the nett change in SoC is > 19% (so in effect the measurement is taken up to and over a 20% range). Any values greater than 25% above/below expected are rejected.
It requires 3 helpers, 4 sensors and an automation that checks for changes and calculates the watt hour display.
HELPERS
Last SoC - used to record last soc state and only update when state changes, set as follows - Starting SoC - used to record the starting position of SoC and is used as the divisor by the calculation, set as follows - Watt hour calc - the sensor that holds the currently calculated watt hour usage, set as follows - Now onto the sensors
batt_watt_change - a rolling sensor that counts every watt of either charge (+) or discharge (-)
Code: Select all
- sensor:
- name: "batt_watt_change"
unit_of_measurement: "W"
device_class: power
state_class: measurement
state: >
{{ (( ( (states('sensor.battery_charge') | float(default=0) * 1) - states('sensor.battery_discharge') | float(default=0)) * 1000)) | round(0) }}
Code: Select all
- method: left
name: batt_wh_test
platform: integration
round: 2
source: sensor.batt_watt_change
unit_time: h
Code: Select all
utility_meter:
batt_wh_count:
source: sensor.batt_wh_test
cycle: monthly
net_consumption: True
Code: Select all
- platform: filter
name: "Battery Wh Display"
entity_id: input_number.watt_hour_calc
filters:
- filter: time_simple_moving_average
window_size: "00:30"
- filter: lowpass
time_constant: 20
Code: Select all
alias: Check SoC
description: ""
trigger:
- platform: time_pattern
seconds: /30
condition:
- condition: or
conditions:
- condition: numeric_state
entity_id: sensor.battery_soc
above: input_number.last_soc
- condition: numeric_state
entity_id: sensor.battery_soc
below: input_number.last_soc
action:
- if:
- condition: template
value_template: >-
{{ (states('sensor.battery_soc') | int(0) == 100) or
(states('sensor.battery_soc') | int(0) == 10 ) or
(states('input_number.starting_soc') | int(0) ==0 ) }}
then:
- service: input_number.set_value
target:
entity_id: input_number.starting_soc
data:
value: "{{ (states('sensor.battery_soc') | int(0)) }}"
- if:
- condition: numeric_state
entity_id: sensor.battery_soc
above: 99
then:
- service: utility_meter.calibrate
data:
value: "0"
target:
entity_id: sensor.batt_wh_count
- service: input_number.set_value
data:
value: 154
target:
entity_id: input_number.watt_hour_calc
else:
- if:
- condition: numeric_state
entity_id: sensor.battery_soc
below: 11
then:
- service: utility_meter.calibrate
data:
value: "0"
target:
entity_id: sensor.batt_wh_count
- service: input_number.set_value
data:
value: 154
target:
entity_id: input_number.watt_hour_calc
else:
- service: utility_meter.calibrate
data:
value: "0"
target:
entity_id: sensor.batt_wh_count
- service: input_number.set_value
data:
value: 154
target:
entity_id: input_number.watt_hour_calc
else:
- if:
- condition: template
value_template: >-
{{ (( ((states('sensor.battery_soc') | int(0) - states('input_number.starting_soc')|int(0) ) ) == 0) or (states('sensor.battery_soc')|int(0)-states('input_number.starting_soc')|int(0))|abs > 19 ) }}
then:
- service: utility_meter.calibrate
data:
value: "0"
target:
entity_id: sensor.batt_wh_count
- if:
- condition: template
value_template: >-
{{ (states('sensor.battery_soc')|int(0)-states('input_number.starting_soc')|int(0))|abs > 19 }}
then:
- service: input_number.set_value
target:
entity_id: input_number.starting_soc
data:
value: "{{ (states('sensor.battery_soc') | int(0)) }}"
- service: input_number.set_value
data:
value: >-
{% set expectwh = 154 %} {% set Batsoc = states('sensor.battery_soc')|int(0) - states('input_number.starting_soc')|int(0) %} {% if Batsoc > 0 %}
{% set whtest = ((states('sensor.batt_wh_count')|int(0))|abs / Batsoc) | round(2) %}
{% else %}
{% if Batsoc == 0 %}
{% set whtest = expectwh %}
{% else %}
{% set whtest = ((states('sensor.batt_wh_count')|int(0))|abs / Batsoc|abs) | round(2) %}
{% endif %}
{% endif %}
{% if whtest > (expectwh*1.25) or whtest < (expectwh*0.75) %}
{% set whtest = expectwh %}
{% endif %} {{ whtest }}
target:
entity_id: input_number.watt_hour_calc
- service: input_number.set_value
data:
value: "{% set Batsoc = states('sensor.battery_soc')|int(0) %} {{ Batsoc }} "
target:
entity_id: input_number.last_soc
mode: single