Skip to content

calculating price for stored energy in battery and using dynamically for optimization #185

@ohAnd

Description

@ohAnd

based on discussion in PR #172

Sorry for the late response. I was a little busy the last few weeks.

This is all still work in progress, but working pretty well so far.
I have a rather complex template sensor to calculate the battery energy price.
For this to work, I have created two sensors calculating the PV and the grid power that flows into the battery.
In addition, I have a sensor with the current energy price (in my case, Tibber) and the total energy stored in the battery, which is provided by my BMS.

My battery is never discharged below ~15%.
I am subtracting this energy amount (ENERGY_FLOOR_KWH) before doing the whole calculation.

template:
  - trigger:
      # Trigger calculation whenever power, price, or battery energy changes
      - platform: state
        entity_id: 
          - sensor.batterie_leistung_netz
          - sensor.batterie_leistung_pv
          - sensor.strompreis
          - sensor.batterie_energie
    sensor:
      - name: "Batterie Speicher Preis"
        unique_id: batterie_speicher_preis
        unit_of_measurement: "€/kWh"
        icon: mdi:currency-eur
        state: >-
          {# --- CONFIGURATION --- #}
          {% set ENERGY_FLOOR_KWH = 6.8 %}
          {% set EFFICIENCY_RATE = 0.88 %}   {# 12% loss expressed as 0.88 efficiency #}
          {% set price_pv = 0.0 %}
          
          {# Calculate loss factor: 1 / 0.88 ≈ 1,136 (accounts for energy lost during charging) #}
          {% set EFFICIENCY_LOSS_FACTOR = 1.0 / EFFICIENCY_RATE %}
          
          {# --- INPUT SENSORS (Safely convert to float or None) --- #}
          {% set current_energy_kwh = states('sensor.batterie_energie') | float(none) %}
          {% set power_grid_w = states('sensor.batterie_leistung_netz') | float(none) %}
          {% set power_pv_w = states('sensor.batterie_leistung_pv') | float(none) %}
          {% set price_grid = states('sensor.strompreis') | float(none) %}
          {% set old_avg_price = this.state | float(0) %}
          
          {# --- FAILSAFE: Check for unavailable sensors --- #}
          {% if current_energy_kwh is none or power_grid_w is none or power_pv_w is none or price_grid is none %}
            {% set final_price = old_avg_price if old_avg_price > 0 else 0.00 %}
          {% else %}
            
            {# Usable energy above the 6.8 kWh floor #}
            {% set circulating_energy_kwh = [current_energy_kwh - ENERGY_FLOOR_KWH, 0] | max %}
            {% set total_power_w = power_grid_w + power_pv_w %}
            {% set final_price = old_avg_price %}
            
            {# Precise time tracking between updates #}
            {% set time_delta_seconds = (now() - this.last_updated).total_seconds() | float(1) %}
            {% set time_delta_hours = time_delta_seconds / 3600 %}
            
            {# --- CHARGING LOGIC --- #}
            {% if total_power_w > 10 and time_delta_seconds > 0.01 %}
              
              {# Calculate added energy in kWh #}
              {% set added_grid_kwh = (power_grid_w / 1000) * time_delta_hours %}
              {% set added_pv_kwh = (power_pv_w / 1000) * time_delta_hours %}
              {% set added_total_kwh = added_grid_kwh + added_pv_kwh %}
              
              {# Apply Efficiency Loss Factor only to the NEWLY added costs #}
              {% set cost_grid_effective = added_grid_kwh * price_grid * EFFICIENCY_LOSS_FACTOR %}
              {% set cost_pv_effective = added_pv_kwh * price_pv * EFFICIENCY_LOSS_FACTOR %}
              {% set cost_added_effective = cost_grid_effective + cost_pv_effective %}
              
              {# Estimate state BEFORE this update for mathematical consistency #}
              {% set prev_circulating_energy = [circulating_energy_kwh - added_total_kwh, 0] | max %}
              
              {# Value of existing inventory #}
              {% set value_old = prev_circulating_energy * old_avg_price %}
              
              {# Weighted average calculation #}
              {% set new_total_value = value_old + cost_added_effective %}
              {% set new_circulating_energy = prev_circulating_energy + added_total_kwh %}
              
              {% if new_circulating_energy > 0.01 %}
                {% set final_price = new_total_value / new_circulating_energy %}
              {% else %}
                {% set final_price = 0.00 %}
              {% endif %}
            
            {% else %}              
              {# --- DISCHARGING / STANDBY --- #}
              {% if circulating_energy_kwh < 0.01 %}
                 {% set final_price = 0.00 %}
              {% else %}
                 {# Hold price during discharge or until new energy is added #}
                 {% set final_price = old_avg_price if old_avg_price > 0 else price_grid %}
              {% endif %}
            {% endif %}
          {% endif %}
          
          {{ final_price | float(0) | round(4) }}

Originally posted by @rockinglama in #172 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions