How can I determine if my battery will run out before power is restored

alias: Initialize Battery Depletion Time
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.house_power_status
    to: "off"
condition: []
action:
  - service: input_datetime.set_datetime
    data:
      datetime: "'1970-01-01 00:00:00'"
    target:
      entity_id: input_datetime.estimated_battery_depletion_time
mode: single








alias: Set Time Battery Depleted
description: ""
trigger:
  - platform: time_pattern
    minutes: /5
condition:
  - condition: state
    entity_id: input_boolean.house_power_status
    state: "off"
action:
  - service: input_datetime.set_datetime
    data:
      timestamp: |
        {% if states('sensor.a70_battery_level') | float < 0.0 %}
          {{ (((states('sensor.a70_battery_level') | float(0) - states('input_number.battery_setpoint') | float(0)) / states('sensor.a70batterycharge') | float(0) | abs) * 60) + as_timestamp(now())}}
        {% else %}
          {{ as_timestamp(now()) + 86400 }}
        {% endif %}
    target:
      entity_id: input_datetime.estimated_battery_depletion_time
mode: single

![a70turev|525x500](upload://q2dM4m9693hlKbCDaFJ1xTk1N94.png)
![a70turev2|499x500](upload://lEHfyNq3GACXCCc63Y4R0sLnzno.png)

@finity @crux2006

Right, I cannot believe that I looked forward to loadshedding tonight. So, I got everything set up and basically it kept on keeping to the same time, if not earlier.

So, I looked deeper into the formula above and simply swapped the ā€œinput_number.battery_setpointā€ and ā€œsensor.battery_state_of_chargeā€ around and it seems I am getting a fair prediction.

I probably can swap out the setpoint input numeric just with 100 that should be fine as the setpoint is 100, being the batteryā€™s full charge. Nevertheless, currently, as I type this, my SOC is 68% to deplete at 10:39pm ā€¦ it is now 9:37pmā€¦ This seems pretty much what I expected. Iā€™m going to monitor this and see how it goes.

Actually talking about the setpoint, I changed mine to 70 because at 30% SOC, my inverter shuts the power off to the house so that it can keep running the inverter until grid power is restored. Changing that, has definitely made the predicted time more realistic.

@finity @ipodmusicman

Can you share the place you changed with the code? I want to apply it immediately

@finity @ipodmusicman
Durumlar ā€“ Home Assistant (1)

Yeah, I think thatā€™s itā€¦ Iā€™ll test it, of course. I set the trigger time to 10 sec. I set the frequency of sending battery level data from the phone to fast. Now realistic values started to occurā€¦ The time is approximately 00:41 and the current battery level is 46%ā€¦ The final version of my code:

alias: Set Time Battery Depleted
description: ""
trigger:
  - platform: time_pattern
    seconds: /10
condition:
  - condition: state
    entity_id: input_boolean.house_power_status
    state: "off"
action:
  - service: input_datetime.set_datetime
    data:
      timestamp: |
        {% if states('sensor.a70batterycharge') | float < 0.0 %}
          {{ (((states('sensor.a70_battery_level') | float(0) - states('input_number.battery_setpoint') | float(0)) / states('sensor.a70batterycharge') | float(0) | abs) * 60) + as_timestamp(now())}}
        {% else %}
          {{ as_timestamp(now()) + 86400 }}
        {% endif %}
    target:
      entity_id: input_datetime.estimated_battery_depletion_time
mode: single

So you are saying the formula is this:

{{ (((states('input_number.setpoint') | float(0) - states('sensor.battery_soc') | float(0)) / states('sensor.battery_discharge_derivative') | float(0) | abs) * 60) + as_timestamp(now())}}

I donā€™t see how that could not give a negative value and then subtract the time from now instead of add to it.

is the input_number_setpoint value higher than the sensor.battery_soc value?

@finity I scrolled up to one of your posts above where you mentioned that the setpoint is actually a battery empty setpoint aka when one would consider the battery as depleted. In my case, my inverter shuts the power off at 30%, so in hindsight, the set point should be 30 and thus, the values should not be swapped around. We have loadshedding tonight at 6pm, so will put the values back, set the setpoint correctly and monitor.

1 Like

Yeah, thatā€™s what I was referring to.

the entity_id was poorly chosen (battery_setpoint) and thatā€™s why I first said

Hopefully now that itā€™s better understood then it should get you closer to working it out.

Hi @ipodmusicman, Iā€™m quite new to HA and you seem to be a advanced user. Where did you learn all the scripting tricks - did you just read through endless forums or had some good book or other resource?
At the moment Iā€™m trying to set a trigger for my batteryā€™s SOC. According to me [states.]sensor.battery_state_of_charge should work but it doesnā€™t - thatā€™s the level of knowledge Iā€™m at. :slight_smile:

@ipodmusicman - Thank you for creating this post - This is exactly what Iā€™m looking for.
This post has a lot of fragmented pieces of code. Would you mind sharing your final templates/helpers and automations?

Hi there, noooo, lots of Googling and searching on this forum. Although I am a software engineer by profession, Jinja has always been a bit of a sticking point, so Iā€™ve managed to find all the help from many awesome community members on this forum.

Hi folks,

@jacauc FYI good suggestion.

Here is my complete solution and it works brilliantly! I have been keeping an eye on this during many loadshedding events and Iā€™m very happy with the result.

Firstly, I defined an input number helper which acts as the battery setpoint. Its entity ID is input_number.battery_setpoint.
The maximum value is set to 10 with the step size set to 0,1
As contradictory to the posts above, I changed my inverter to shut the power off when the battery reaches 10% so this all measures based on the fact that the battery will deplete at 10%.

I defined an input date/time helper which acts as the estimated battery depletion time. Its entity ID is input_datetime.estimated_battery_depletion_time

I defined a derivative sensor helper which acts as the battery state of charge derivative. Its entity ID is sensor.battery_state_of_charge_derivative.
The derivative options are:

  • Precision: 3
  • Time window: 00:05:00 (5 minutes)
  • Metric prefix: none
  • Time unit: Minutes

I also have a binary sensor called binary_sensor.house_power_status. This specifies whether the house is on grid power or not.

I defined the following template sensors:

template:
  - sensor:
      - name: "Estimated Battery Depletion Time"
        state: "{{ as_timestamp(states('input_datetime.estimated_battery_depletion_time')) | timestamp_custom('%-I:%M %p')}}"
        icon: mdi:clock
        
      - name: "Estimated Battery Depletion Time Countdown"
        state: "{{ (as_timestamp(states('input_datetime.estimated_battery_depletion_time')) - as_timestamp(now())) | timestamp_custom('%H hr %M min', false)}}"
        icon: mdi:timer-sand

I am not actively using the ā€œEstimated Battery Depletion Time Countdownā€ as above though, so you can omit it.

These are the automations that are in play:

  - alias: Initialize Battery Depletion Time
    trigger:
      - platform: state
        entity_id: binary_sensor.house_power_status
        to: 'off'
    action:
      - service: input_datetime.set_datetime
        entity_id: input_datetime.estimated_battery_depletion_time
        data:
          datetime: '1970-01-01 00:00:00'
  
  - alias: Set Time Battery Depleted
    trigger:
      - platform: time_pattern
        minutes: "/5"
    condition:
      - condition: state
        entity_id: binary_sensor.house_power_status
        state: 'off'
    action:
      - service: input_datetime.set_datetime
        entity_id: input_datetime.estimated_battery_depletion_time
        data:
          timestamp: >
            {% if states('sensor.battery_state_of_charge_derivative') | float < 0.0 %}
              {{ (((states('sensor.battery_state_of_charge') | float(0) - states('input_number.battery_setpoint') | float(0)) / states('sensor.battery_state_of_charge_derivative') | float(0) | abs) * 60) + as_timestamp(now())}}
            {% else %}
              {{ as_timestamp(now()) + 86400 }}
            {% endif %}

On the UI side, I have a mushroom template card as below that shows me how many days, hours, minutes until the next loadshedding event is as well as what time the next loadshedding event is, but once power has gone off, it will show me the amount of time until the battery is depleted as well as the current SOC. The lightning bolt icon changes colour based on the status as below. This will also show me that if the power goes off outside of loadshedding, then it will show as a power failure. We have prepaid electricity so this is generally shown when the power has run out because I had forgotten to load the meter with more electricity.

type: custom:mushroom-template-card
primary: |-
  {% if is_state('sensor.loadshedding_local_status', '0') %}
    {% if is_state('binary_sensor.house_power_status', 'off') %}
      Power failure
    {% else %}
      Not currently loadshedding
    {% endif %}
  {% else %}
    {% if is_state('calendar.loadshedding_local_events', 'on') and 
          is_state('binary_sensor.house_power_status', 'off') %}
      Loadshedding in progress .. back on in {{ (((as_timestamp(states.calendar.loadshedding_local_events.attributes.end_time)) - as_timestamp(now()) ) % 86400 // 3600) | round(0) | int }} hr {{ (((as_timestamp(states.calendar.loadshedding_local_events.attributes.end_time)) - as_timestamp(now()) ) % 86400 // 60) | round(0) | int % 60 }} min
    {% elif is_state('calendar.loadshedding_local_events', 'off') and 
          is_state('binary_sensor.house_power_status', 'off') %}
      Power failure
    {% elif is_state('calendar.loadshedding_local_events', 'on') and 
          is_state('binary_sensor.house_power_status', 'on') %}
      {% if ((((as_timestamp(states.calendar.loadshedding_local_events.attributes.end_time)) - as_timestamp(now()) ) % 86400 // 60) | round(0) | int % 60) > 60 %}
        Loadshedding to kick in shortly ...
      {% else %}
        Power restored
      {% endif %}
    {% else %}
      {% if (as_timestamp(states.calendar.loadshedding_local_events.attributes.start_time)) - as_timestamp(now()) < 0 %}
        No upcoming loadshedding schedule available
      {% else %}
        Loadshedding in {{ state_attr('sensor.loadshedding_local_status', 'Area') }} in 
      {{ (((as_timestamp(states.calendar.loadshedding_local_events.attributes.start_time)) - as_timestamp(now())) // 86400) | round(0) | int }} d {{ (((as_timestamp(states.calendar.loadshedding_local_events.attributes.start_time)) - as_timestamp(now())) % 86400 // 3600) | round(0) | int }} hr {{ (((as_timestamp(states.calendar.loadshedding_local_events.attributes.start_time)) - as_timestamp(now())) % 3600 // 60) | round(0) | int }} min
      {% endif %}  
    {% endif %}
  {% endif %}
secondary: |
  {% if is_state('binary_sensor.house_power_status', 'off') %}
      Battery at {{ states('sensor.battery_state_of_charge') }} %.
      Battery to run out at {{ states('sensor.estimated_battery_depletion_time') }}
  {% elif not is_state('sensor.loadshedding_local_status', '0') %}
    {{ state_attr('calendar.loadshedding_local_events', 'start_time') }} to {{ as_timestamp(states.calendar.loadshedding_local_events.attributes.end_time)  | timestamp_custom("%H:%M:%S") }}
  {% endif %}
icon: mdi:lightning-bolt
icon_color: |-
  {% if is_state('sensor.loadshedding_local_status', '0') %}
    {% if is_state('binary_sensor.house_power_status', 'off') %}
      red
    {% else %}
      green
    {% endif %}
  {% else %}
    {% if is_state('calendar.loadshedding_local_events', 'on') and 
          is_state('binary_sensor.house_power_status', 'off') %}
      yellow
    {% elif is_state('calendar.loadshedding_local_events', 'off') and 
          is_state('binary_sensor.house_power_status', 'off') %}
      red
    {% else %}
      {% if (as_timestamp(states.calendar.loadshedding_local_events.attributes.start_time)) - as_timestamp(now()) < 0 %}
        yellow
      {% else %}
        yellow
      {% endif %}  
    {% endif %}
  {% endif %}
fill_container: false
multiline_secondary: false
1 Like