Skip to content

Conversation

@rockinglama
Copy link

@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.

@rockinglama rockinglama force-pushed the feature/price_euro_per_wh_accu_sensor branch from e5891b4 to c6f66e7 Compare December 2, 2025 08:28
@rockinglama rockinglama force-pushed the feature/price_euro_per_wh_accu_sensor branch from c6f66e7 to 9169b5c Compare December 2, 2025 08:42
ohAnd and others added 2 commits December 7, 2025 19:30
…on - fixes Max grid charge rate wrong calculation

Fixes ohAnd#171
Files changed:
M	src/version.py
@ohAnd
Copy link
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 ohAnd#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 ohAnd#167
…ogic and updating related MQTT topics to reflect final states after overrides - fixes Missing State in HA for Allow Discharge EVCC

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

ohAnd commented Dec 19, 2025

@rockinglama ... ping ;-)

@rockinglama
Copy link
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
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
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.

2 participants