Calculate sensor values based on time elapsed since last update in a template sensor

Hi, I have a peculiar requirement of a sensor. I have a UPS which gets discharges when mains isn’t present and I capture the energy consumed via the UPS too. My requirement is to create a sensor to calculate the %energy level left in the UPS when discharging and charging. The logic is something like this:
If mains is absent
I want to decrement the % based on power consumption
else
I want to increment the % based on the amount of time that has elapsed, logic below

While calculating % based on consumption is straight forward as I have the total capacity in kWH and consumed energy in kWH , I just have to compute the % left.
The challenge is in the calculating % level when charging , the logic is something like this:
Assumptions: Charging of UPS is linear and it takes 5 hours to pump in a full capacity of 1kWH into the UPS. This means the UPS charges 1% every 3 min.

  • store the last time when increment of level was done
  • calculate the time interval and increment the % by 1 every 3 min

While I know templating of sensors, I think this requires either to store the time in a variable somewhere or create a sensor which stores this data. For the latter I dont know how to set this time interval sensor from this sensor.

Any inputs on this approach or something better would be appreciated, Thanks

 - platform: template
    sensors:
      home_ups_energy_level:
        unique_id: home_ups_energy_level
        friendly_name: "UPS Energy Level"
        unit_of_measurement: "%"
        value_template: >-
          if(states('binary_sensor.mains_line_status') //mains is present
          {
            //calculate the % increment for UPS based on time elapsed since last increment

          }
		  else
		  {
			{{ ((1.0 - states('sensor.total_home_ups_energy') | float)/ 1.0 *100 )| int }} // %energy left based on 1kWH full capacity
		  }

Here is one solution… there is maybe more than one…

I created an input_number called last_change_home_ups where I store the value of the sensor.home_ups_energy_level when there is a change in the main line status… I created an automation for that:

- alias: main_line_status_change
  initial_state: true
  trigger:
    - platform: state
      entity_id: binary_sensor.mains_line_status
  action:
    - service: input_number.set_value
      target:
        entity_id: input_number.last_change_home_ups
      data:
        value: "{{ state('sensor.home_ups_energy_level') | float }}"     

I created a template sensor like this ( I assume that you consume/load 1% every 3 minutes, this can be updated based on you real number):

template:
  - sensor:
      - name: "home ups energy level"
        unique_id: home_ups_energy_level
        state: >
            {% if states('binary_sensor.mains_line_status') == 'on' %}
                 {% set mylist = (100,((states('input_number.last_change_home_ups') | float) + (as_timestamp(now()) - as_timestamp(states.binary_sensor.mains_line_status.last_changed)) / (3 * 60))) %}
                 {{ mylist | min | float }}
            {% else %}
                 {% set mylist = (0,((states('input_number.last_change_home_ups') | float) - (as_timestamp(now()) - as_timestamp(states.binary_sensor.mains_line_status.last_changed)) / (3 * 60))) %}
                 {{ mylist | max | float }}
            {% endif %}

If the line is on, I am incrementing the ups level recorded at the last change in main line status by a value equal to : number of seconds since the last change in mainline status divided by 60 to have it in minutes divided by 3 has you increment by 1% every 3 minutes… with a maximum of 100%… Same if the line is off… I decrement with a minimum of 0%…

Test and see if it works as you expect…

that’s a good start! Thanks for this, I will try this out and come back if I face any issues.

I was not able to make it work , however no of tricks I tried. It seems with the current approach there seems to be a circular reference between all the sensors and inputs i have defined which is causing the values to be updated wrongly when charging. The discharging code is working fine.

Just so that others know what have to tried to do is : I have created 2 input numbers
input_number.last_ups_level.state → this stores the value of UPS level every time the UPS level changes. This is causing a circular reference as I use this input to calculate the UPS level in the first place. Automation for storing value in this is in nodered
input_number.starting_ups_level → This stores the starting value when power goes off , so that my discharing code does not start with 100% rather the actual value when power went off. Value is this is stored in an automation in nodered.

  - platform: template
    sensors:
      home_ups_energy_level:
        unique_id: home_ups_energy_level
        friendly_name: "UPS Energy Level"
        unit_of_measurement: "%"
        icon_template: mdi:car-coolant-level
        value_template: >-
            {#set the time it takes to charge the battery by 1% of in secs. During the last leg charging takes longer #}
            {#I still have to get the right values to account for non-linear charging pattenr for my inverter #}

            {% if states.input_number.last_ups_level.state | int <= 50 %}
              {% set time_to_1_percent = 200 %}
            {% elif states.input_number.last_ups_level.state | int <= 75 %}
              {% set time_to_1_percent = 200 %}
            {% elif states.input_number.last_ups_level.state | int <= 90 %}
              {% set time_to_1_percent = 240 %}
            {% elif states.input_number.last_ups_level.state | int <= 95 %}
              {% set time_to_1_percent = 240 %}
            {% else %} {#means it is > 95%, charging during the last leg is happens slowly #}
              {% set time_to_1_percent = 480 %}
            {% endif %}

            {% if states('binary_sensor.mains_line_status') == 'on' %}
                {% set mylist = (100,((states.input_number.last_ups_level.state) | int) + (as_timestamp(now()) - as_timestamp(states.sensor.home_ups_energy_level.last_changed)) / (time_to_1_percent)) %}
                {{ mylist | min | int }}
            {% else %}
                {% set starting_consumption = (100 - states.input_number.starting_ups_level.state | int) * 0.78 %}
                {% set mylist = (0,(0.78 - (states.sensor.total_home_ups_energy.state|float + starting_consumption|float )) /0.78*100) %}
                {{ mylist | max | int }}
            {% endif %}

Note: That the my sensor sensor.total_home_ups_energy gives me the total energy consumed via the UPS when its discharging and it is reset to zero every time mains power is back (via a flow in node-red).

Can someone give some other idea to make this work? I think the difficulty here is that HA does not expose the old value of the sensor when computing the new value. If it was there , things would have been so easy.

1 Like

can someone help on this please?