-
-
Notifications
You must be signed in to change notification settings - Fork 14
Open
Description
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
Labels
No labels