Last active
          November 1, 2025 08:50 
        
      - 
      
- 
        Save danielolsson100/4ee739d864dc90dac144e4d026ae090f to your computer and use it in GitHub Desktop. 
    ferroamp - charge or discharge battery sensor with home assistant and nordpool
  
        
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | binary_sensor: | |
| - platform: template | |
| sensors: | |
| bs_charge_ferroamp_battery: | |
| friendly_name: Charge Ferroamp Battery | |
| unique_id: bs_charge_ferroamp_battery | |
| value_template: >- | |
| {# Charge logic (6 hours = 24 quarters at 15-min intervals) - v1.3 #} | |
| {% set sensor = 'sensor.nordpool_kwh_se4_sek_2_10_025' %} | |
| {% set today = state_attr(sensor, 'today') | default([]) %} | |
| {% set tomorrow = state_attr(sensor, 'tomorrow') | default([]) %} | |
| {# Combine today and tomorrow if tomorrow data is available and valid #} | |
| {% set price_array = today + tomorrow if (tomorrow | count > 0 and tomorrow[0] != None) else today %} | |
| {# Get current price and calculate threshold (6 hours = 24 quarters at 15-min intervals) #} | |
| {% set current_price = states(sensor) | float(0) %} | |
| {% set price_threshold = (price_array | sort)[23] if price_array | count >= 24 else (price_array | sort)[-1] %} | |
| {# Return True if current price is at or below the threshold #} | |
| {{ current_price <= price_threshold }} | |
| {#--=[Charge battery logic]=-- | |
| ---------------------------------- | |
| current_price.............: {{current_price}} kr | |
| price_threshold...........: {{price_threshold}} kr #} | |
| bs_discharge_ferroamp_battery: | |
| friendly_name: Discharge Ferroamp Battery | |
| unique_id: bs_discharge_ferroamp_battery | |
| value_template: >- | |
| {# Optimized Home Assistant Battery Discharge Logic - v1.2 #} | |
| {# Calculate energy consumption #} | |
| {% set total_energy = state_attr('sensor.load_energy_consumed_daily', 'last_period') | float(0) %} | |
| {% set ev_energy = state_attr('sensor.easee_energy_daily', 'last_period') | float(0) %} | |
| {% set house_energy = total_energy - ev_energy %} | |
| {% set solar_energy_high = states('binary_sensor.solar_energy_high') | bool %} | |
| {% set solar1 = states('sensor.energy_production_today_remaining') %} | |
| {% set solar2 = states('sensor.energy_production_today_remaining_2') %} | |
| {% set solar3 = states('sensor.energy_production_today_remaining_3') %} | |
| {% set calc_solar_energy_today = | |
| (solar1 not in ['unavailable', 'unknown', None] and solar1|float(0) or 0) + | |
| (solar2 not in ['unavailable', 'unknown', None] and solar2|float(0) or 0) + | |
| (solar3 not in ['unavailable', 'unknown', None] and solar3|float(0) or 0) | |
| %} | |
| {# Battery state #} | |
| {% set battery_soc = states('sensor.ferroamp_eso_19110078_state_of_charge') | float(0) %} | |
| {% set battery_capacity = states('sensor.ferroamp_esm_74210273_rated_capacity') | float(0) / 1000 %} | |
| {% set usable_battery = (battery_capacity * battery_soc / 100) - 1.2 %} | |
| {% set battery_charging = states('binary_sensor.bs_charge_ferroamp_battery') | bool %} | |
| {# Time calculations - use single now() call for performance #} | |
| {% set current_time = now() %} | |
| {% set current_quarter = ((current_time.hour * 4 + ((now().minute // 15) + 1) | int)) %} | |
| {# Energy consumption calculations #} | |
| {% set avg_consumption = house_energy / 24 %} | |
| {% set battery_runtime_quarters = (((usable_battery + calc_solar_energy_today) / avg_consumption) * 4) | int if avg_consumption > 0 else 0 %} | |
| {# Nordpool price data #} | |
| {% set nordpool_prices = state_attr('sensor.nordpool_kwh_se4_sek_2_10_025', 'today') | default([]) %} | |
| {% set remaining_prices = nordpool_prices[current_quarter:] if nordpool_prices is not none else [] %} | |
| {% set remaining_count = remaining_prices | count %} | |
| {# Logic: Discharge battery if current price is high relative to remaining prices #} | |
| {% if battery_charging %} | |
| {# The battery is still charging, let's wait for it to complete #} | |
| False | |
| {% elif solar_energy_high %} | |
| {# Sun energy excess: auto mode to store sun energy #} | |
| True | |
| {% elif battery_runtime_quarters <= 0 %} | |
| {# No runtime estimated or invalid value: don't discharge by price logic #} | |
| False | |
| {% elif remaining_count <= battery_runtime_quarters %} | |
| {# Battery can last all remaining hours: discharge #} | |
| True | |
| {% else %} | |
| {# Check if current price is in top N most expensive remaining prices #} | |
| {% set sorted_remaining = remaining_prices | reject('equalto', None) | list | sort %} | |
| {% set sr_count = sorted_remaining | count %} | |
| {% if sr_count == 0 %} | |
| {# No valid remaining price data -> don't discharge #} | |
| False | |
| {% else %} | |
| {# Compute zero-based index and clamp it to valid range #} | |
| {% set idx = remaining_count - battery_runtime_quarters - 1 %} | |
| {% if idx < 0 %}{% set idx = 0 %}{% endif %} | |
| {% if idx >= sr_count %}{% set idx = sr_count - 1 %}{% endif %} | |
| {% set threshold_price = sorted_remaining[idx] %} | |
| {% set current_price = nordpool_prices[current_quarter] if (nordpool_prices | count) > current_quarter else none %} | |
| {% if current_price in [none, 'unknown', 'unavailable'] %} | |
| False | |
| {% else %} | |
| {{ (current_price | float(0)) >= (threshold_price | float(0)) }} | |
| {% endif %} | |
| {% endif %} | |
| {% endif %} | |
| {#--=[Discharge/AutoCharge logic]=-- | |
| ---------------------------------- | |
| total_energy..............: {{total_energy | round(2)}} kWh | |
| ev_energy.................: {{ev_energy | round(2)}} kWh | |
| house_energy..............: {{house_energy | round(2)}} kWh | |
| avg_consumption...........: {{avg_consumption | round(2)}} kWh | |
| battery_capacity..........: {{battery_capacity | round(2)}} kWh | |
| usable_battery............: {{usable_battery | round(2)}} kWh | |
| calc_solar_energy_today...: {{calc_solar_energy_today | round(2)}} kWh | |
| battery_soc...............: {{battery_soc | round(1)}} % | |
| nordpool_current_price....: {{nordpool_prices[current_quarter-1]}} kr | |
| current_quarter...........: {{current_quarter}} quarters | |
| battery_runtime_quarters..: {{battery_runtime_quarters}} quarters | |
| calc_autocharge_runtime...: {{(battery_runtime_quarters / 4)| round(1)}} h | |
| remaining_count...........: {{remaining_count}} quarters | |
| battery_charging..........: {{battery_charging}} #} | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment
  
            
This binary sensor now requires integrations from Easee/NordPool/Ferroamp and the use of utility meter in HomeAssistant to work
use at own risk ;)