Estimating Battery Runtime Using Home Assistant

I have a whole house battery solar system with 3kW of roof mounted panels and 9.6kWh of battery storage. I can monitor this using Home Assistant and it’s interesting to see my power usage and generation stats. Interesting, but not that relevant to daily life.

What is relevant is knowing how long the battery has left, especially now we’re in autumn/winter and the typical British weather is in full effect. As I look out my window right now it’s misty, yesterday it rained all day.

Home Assistant Gauges for Monitoring

I have three gauges in Home Assistant that show the amount of power being generated by the solar panels, the state of charge of the batteries and the load on the batteries. They’re just basic needle gauges with some colours.

Estimating Battery Runtime

One thing I really wanted was to calculate when the batteries would go flat. My phone can tell me how long before it is fully charged, and knowing this is really useful. Knowing when my house batteries are going to run out would be equally useful. Doing this requires a few different parts to be set up in Home Assistant though.

The end result is something like this

Tracking average power usage

The first task is to calculate the average power usage. I can’t take the current battery load directly as it jumps up and down as power is consumed – boiling a kettle will briefly consume 3kW of energy and I don’t want the runtime estimation to leap around.

To smooth things out I created a Home Assistant Statistics Sensor which observes the state of a sensor and provides an aggregated measurement of that sensor over a time period. I set mine up to use the last 30 minutes. I tend to use high wattage devices for short periods of maybe 10-20 minutes, or for more than an hour.

Here is the code for the statistics sensor I use for battery load

- platform: statistics
  name: "House Battery Load 30 mins"
  entity_id: sensor.solis_battery_power
  state_characteristic: mean
    minutes: 30Code language: JavaScript (javascript)

This is inside a separate “sensors.yaml” file, included in the main Home Assistant configuration.yaml like this

sensor: !include sensors.yamlCode language: CSS (css)

This has a nice side effect of creating a useful graph of the average power usage of my house over the day. And with my inverter it correctly reports battery drain as negative and charging as positive.

If you’re doing this yourself, this is whatever reading your inverter provides that means “current power in watts coming out of the battery”, it isn’t the same as “current power in watts that your house is using” because the inverter itself is also running off the batteries, as are the electronics in the batteries themselves.

Calculating Runtime

Now we know the average power usage, some “simple” maths can be used to estimate the runtime. This is done using a Home Assistant Sensor Template. These are a bit awkward to understand and debug, so I recommend using the Home Assistant Developer Tools page to fiddle around.

- name: "House Battery Runtime Raw"
    unit_of_measurement: "hours"
    state: >
      {% set battery_load = -1 * float(states('sensor.house_battery_load_30_mins')) if states('sensor.house_battery_load_30_mins')|is_number else 0 %}
      {% if battery_load == 0 %}
      {% set runtime = 0 %}
      {% else %}
      {% set remaining_capacity = 7.68 - (0.0768 * (100.0 - float(states('sensor.solis_remaining_battery_capacity')))) %}
      {% set runtime = remaining_capacity / (battery_load/1000) %}
      {% endif %}
      {{ runtime }}
Code language: JavaScript (javascript)

This goes into a file called “template.yaml” that contains a - sensor: section and is included like the sensors.yaml above – this is all fairly standard Home Assistant admin for splitting the config into separate files.

Here’s how it works…

Calculate the load on the battery

set battery_load = -1 * 
if states('sensor.house_battery_load_30_mins')|is_number else 0Code language: PHP (php)

The battery_load_30_mins is the average, because the load on the battery shuts off when it is “empty” Home Assistant then returns the value “unavailable” rather than zero, so we need to check if the load is a number, if not report the value zero.

It is being multiplied by -1 to make the negative battery drain be positive so the maths works.

I have yet to see what happens when the battery is being charged, I expect the estimated time to go a bit wonky. I might need to fix that in the future, but I’m unsure what to actually do – if the battery is charging the runtime is infinite until it starts discharging again!

Cope with the battery being offline

When the battery gets to 20% state of charge, the inverter shuts off and the house goes back on grid power. At this point battery load is zero, so I have a small bit of logic to report the runtime as 0 hours left.

{% if battery_load == 0 %}
{% set runtime = 0 %}Code language: JavaScript (javascript)

Calculate the remaining capacity

To work out how long we have left, we need to know how much capacity is left in the battery.

I have 9.6kWh of battery, but the inverter shuts down at 20% SOC. So this is the same as close enough to having a 7.68kWh battery (20% of 9.6 is 1.92, 9.6-1.92 = 7.68). Remember, this is an estimation, if this code says your battery will go flat in 3h 42m it won’t be that accurate but should be close enough to be useful.

I need to know how much battery was used, but the inverter reports the current battery state of charge, as a percentage. So I need to invert this. I.e instead of the battery being at 80%, I need to know it has been used 20%.

I can then work out what 20% of the 7.68kWh is in kWh, and that is the remaining energy in the battery.

set remaining_capacity = 7.68 - (0.0768 * (100.0 - float(states('sensor.solis_remaining_battery_capacity'))))Code language: JavaScript (javascript)

Ignore all the brackets at the end, it’s just because we have to call the “states” function and cast the value to a float. The basic maths is fairly simple.

Work out the capacity used (100 – the current state of charge) multiply that by 0.0768 to turn it into kWh and then subtract it from a “full” battery.

Calculate the runtime in hours

The final part is to divide the remaining capacity by the battery load in kW.

runtime = remaining_capacity / (battery_load/1000)

Calculating runtime in a human readable way

A raw value like “32.5643” hours is great for a graph and further maths, but no use to us. I need to know if the battery is going to run flat before half midnight when it charges up again.

Two more template sensors can do this for us.

- name: "House Battery Runtime"
    state: >
      {% set runtime = states('sensor.house_battery_runtime_raw') %}
      {% set hours = runtime | float %}
      {% set minutes = ((hours % 1) * 60) | int %}
      {% set hours = (hours - (hours % 1)) | int %}
      {{ 'Remaining Time: %02i:%02i'%(hours, minutes) }}Code language: JavaScript (javascript)

This just does some basic modulo maths to turn the value into hours and minutes.

We can go one better though, how about an actual time of day. “Your battery runs out in 3h 24m” is pretty handy to know. But “Your battery will run out at 7:45pm” is even better.

- name: "House Battery Empty At"
    state: >
      {% set runtime = states('sensor.house_battery_runtime_raw') %}
      {% set hours = runtime | float %}
      {% set minutes = ((hours % 1) * 60) | int %}
      {% set hours = (hours - (hours % 1)) | int %}
      {% set empty_at = now() + timedelta(hours = hours, minutes = minutes) %}
      {{ empty_at.strftime('%A %d %B %H:%M %Z') }}
Code language: JavaScript (javascript)

This uses the now() function to get the current time, some minor Python magic to work out the difference in time, and then a bit of basic formatting to print the value in a format I like, look up the Python docs on strftime() for more.

Oh if you didn’t know, the templating system Home Assistant uses is called Jinja2 and it understands some bits of Python.

One response to “Estimating Battery Runtime Using Home Assistant”

  1. Adam avatar

    Excellent tutorial.
    Have been trying to get this working for ages. But as a HA newbie could not get in to work. Thanks for your help.