Skip to content

Feature: add dynamic battery energy price from Home-Assistant/OpenHAB#172

Merged
ohAnd merged 20 commits into
ohAnd:developfrom
rockinglama:feature/price_euro_per_wh_accu_sensor
Dec 20, 2025
Merged

Feature: add dynamic battery energy price from Home-Assistant/OpenHAB#172
ohAnd merged 20 commits into
ohAnd:developfrom
rockinglama:feature/price_euro_per_wh_accu_sensor

Conversation

@rockinglama
Copy link
Copy Markdown
Contributor

@rockinglama rockinglama commented Dec 2, 2025

Summary

EOS currently supports only a fixed battery energy price.
This change allows price_euro_per_wh_accu to be provided dynamically by a sensor in Home Assistant or an item in OpenHAB, instead of only from static configuration.

In my setup, a Home Assistant entity exposes the current cost of using stored battery energy, which improves forecasting and decisions about when to draw from the grid versus the battery, this is especially helpful during darker months with limited PV production.

Key Changes

  • Added support for sourcing price_euro_per_wh_accu from config/home-assistant/openhab
  • Updated documentation for the new configuration options
  • Added tests covering Home Assistant and OpenHAB price fetching and error handling

Compatibility

Fully backward compatible: the default remains the static price_euro_per_wh_source set in the config file, so existing setups continue to work unchanged.

Special notes for the reviewer

I verified the Home Assistant path end-to-end.
I implemented but could not manually test the OpenHAB path, as I don’t have an OpenHAB installation available.

The ha_addon config should be updated as well to expose these new options.
I’m happy to open a follow-up PR there if this feature is generally accepted.

@ohAnd
Copy link
Copy Markdown
Owner

ohAnd commented Dec 7, 2025

nice idea / extension ...

before we going deeper - I'm currently more interested in how your battery costs are calculated/aggregated for provisioning – it would have to be some kind of rolling average over the last few charging cycles regarding PV charging (~0 ct) vs. grid charging at the current price (while charging), in order to represent the cost per Wh of the current battery capacity.

Can you explain a little in this direction - also with the idea to extend the feature directly to external / internal / fixed ...

ohAnd and others added 7 commits December 14, 2025 18:57
…rge_demand method - fixes Override Charge funktioniert nicht mehr unter EOS Connect develop

Fixes #173
Files changed:
M	src/version.py
…ion in get_pv_akku_data function - fixes part 2 of evopt lädt zu wenig/langsam

Fixes #167
…ogic and updating related MQTT topics to reflect final states after overrides - fixes Missing State in HA for Allow Discharge EVCC

Fixes #175
Files changed:
M	src/version.py
…obustness against None values and API errors - closes #178 [FIX] catch more exceptions in main loop
@ohAnd
Copy link
Copy Markdown
Owner

ohAnd commented Dec 19, 2025

@rockinglama ... ping ;-)

@rockinglama
Copy link
Copy Markdown
Contributor Author

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) }}

@ohAnd
Copy link
Copy Markdown
Owner

ohAnd commented Dec 20, 2025

@rockinglama I've refactored a little to have reuse inner battery interface and removed the second config param for source ... usually either HA or OH if used by external source ... and if no sensor name is given the fixed one will be used

For the other topic "internal generation of battery price" I will create a new issue to discuss further...

@ohAnd ohAnd merged commit a709347 into ohAnd:develop Dec 20, 2025
1 of 2 checks passed
@Popoboxxo
Copy link
Copy Markdown

@rockinglama gamechanger! Awesome :D!

ohAnd added a commit that referenced this pull request Apr 4, 2026
…cu_sensor

Feature: add dynamic battery energy price from Home-Assistant/OpenHAB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants