AutomatiseringDashboard

Intelligent opladning af husbatteri

Hvis du har solceller med et batteri tilkoblet, kan du bruge Home Assistant (HA) til intelligent opladning af dit husbatteri. Når solproduktionen er lav, kan du overveje at købe strøm på de billigste tidspunkter og bruge det på de dyreste.

Der findes flere forskellige løsninger (bl.a. EMHASS og Batpred), der kan hjælpe med at styre opladningen. De kræver dog en del opsætning og er ikke udviklet til brug med danske elprismodeller.

Alternativet er at lave sine egne automatiseringer til styringen. Jeg forklarer i dette indlæg, hvad jeg selv har gjort.

Kan det betale sig?

Hvorvidt – og i givet fald hvornår – det kan betale sig at købe strøm til at oplade et husbatteri, er lidt et spørgsmål om personlig holdning:

  1. Du kan mene, at når dit batteri er købt og installeret, så gælder det om at bruge det – uanset hvor lille eller stor besparelse, du får. Så bliver styringen mere simpel, da batteriet så altid skal oplades på de tidspunkter, hvor prisen er lavest.
  2. Du kan også vælge at antage, at dine batterier har et begrænset antal års / cyklers levetid og at det derfor handler om at få det største udbytte for hver opladning / afladning. Det får man ved kun at lade op fra solceller eller hvis prisforskellen er den samme som elprisen på en sommerdag, dvs. ca. 1,5 kr., hvis man betaler elafgift.
  3. Du kan vælge at foretage opladning, når prisforskellen overstiger tabet ved konvertering fra AC->DC->AC. Det tab afhænger af din installation, men regn med minimum ca. 20%, jf. afsnit 7.1 i Teknologisk Instituts rapport om Optimal udnyttelse af solcelle-el i enfamiliehus.
  4. Du kan fastsætte en pris, der tager højde for både ovenstående tab samt et værditab på dit batteri. Værditabet afhænger af prisen på dit batteri og den forventede brugstid. Eksempel: Et batteri til 10.000 kr. er garanteret en max degenerering på 30% efter 6.000 cykler. Så er værditabet: 30% x 10.000 / 6.000 = 0,5 kr. pr. cyklus.

Min løsning

Jeg har selv valgt løsning nummer 4. Derfor oplader jeg kun mit batteri, hvis der er mere end 75 øres prisforskel mellem tidspunktet for hhv. opladning og brug af batteriet.

Om vinteren er elprisen oftest lavest om natten og højest i tidsrummet mellem kl. 17 og 21. Det skyldes primært, at prisen for levering af strømmen (nettarif) er meget højere på dette tidspunkt. Derfor vil det ofte kunne betale sig at oplade sit husbatteri:

  • om natten (kl. 0-6) for at dække forbrug den næste dag
  • Inden kl. 17 for at dække forbruget i de dyre timer mellem kl. 17-21

Her er et eksempel på forskellen mellem de gennemsnitlige elpriser i løbet af i dag. Oversigten er inddelt i de tidsintervaller, der anvendes til de forskellige nettariffer:

Eksempel på prisforskelle i de gennemsnitlige elpriser (inkl. nettarif og alle afgifter mv.) i løbet af dagen.

Her kan man se, at der er mere end 1,5 krone i forskel mellem gennemsnitsprisen i dagtimerne og aftentimerne. Derimod vil det med en grænse på 75 øre ikke kunne betale sig at oplade om natten inden dagtimer. Elprisen er dog typisk lidt højere i morgentimerne mellem 6-9, så det kan måske også betale sig at købe strøm til at dække forbruget i de timer.

Hvordan gør man?

Udover elprisen er der en række andre forhold, som man skal overveje i sin styring af opladningen af et husbatteri:

  • Hvor mange timers elforbrug skal dækkes af batteriet?
  • Forventes solen at kunne dække hele eller dele af forbruget?
  • Hvor hurtigt kan batteriet oplades?

Opsæt integrationer

Så for at kunne lave styringen af opladningen af dit husbatteri, skal følgende integrationer være installeret og opsat i HA:

  • Integration, der henter de aktuelle elpriser. Jeg bruger Energi Data Service integrationen. Hvis du ikke allerede har installeret Energi Data Service integrationen, skal du starte med at gøre det. Du kan læse mere om integrationen og opsætningen i dette indlæg.
  • integration med prognoser for solproduktion. Jeg bruger selv Solcast integrationen
  • integration til styring af din inverter. Jeg har selv en Deye inverter, men andre modeller kan også integreres. Det kan du finde eksempler på i dette indlæg.

EV Smart Charging

Jeg har desuden valgt at bruge  EV Smart Charging integrationen. Den kan bruges til at finde de billigste timer at foretage opladningen på. Læs evt. om integrationen i dette indlæg om smart opladning af elbil.

Det er ikke alle sensorer i EV Smart Charging integrationen, der relevante at bruge til styring af et husbatteri. De vigtigste er beskrevet her:

  • charging_speed: Opladningshastigheden udtrykt i procent pr. time. For eksempel, hvis batteriet har en samlet kapacitet på 10 kWh og kan oplades med 5 kWh pr. time, skal du indstille denne parameter til 50 (5/10*100)
  • apply_price_limit: Aktiver eller deaktiver prisloft. Hvis du anvender prisloft, vil opladning vil ikke blive udført i timer, hvor elprisen er over det fastsatte prisloft.
  • electricity_price_limit: Prisloft i kr./kWh der anvendes, hvis anvendelsen er aktiveret.
  • ev_connected: Switch, der angiver om elbilen (læs: husbatteriet) er koblet til opladning. Den bør altid være tændt, da opladning ellers ikke vil starte.
  • smart_charging_activated: Switch, der aktiverer eller deaktiverer smart opladning. Den skal være tændt medmindre man ønsker at styre opladningen.
  • charging: Sensor, der viser om opladning er i gang (tændt) eller frakoblet (slukket). Den bruges af automatiseringer til at starte og stoppe opladningen.
  • status: Sensor, der indeholder status på opladningen og planlægningen. Den kan bruges til at vise detaljer om planlægningen på dashboardet enten i tekst form eller i en graf.

Hjælpere

Jeg har oprettet en række entiteter (hjælpere), der kan bruges til at hjælpe med at planlægge og styre opladningen:

  • Minimum prisforskel på tidspunktet for hhv. opladning af og brug fra husbatteriet
  • Grænse for hvornår den forventede solproduktion den næste dag er utilstrækkelig
  • Niveau (i %), som husbatteriet, skal oplades til inden aftenspids eller i løbet af natten

Med hjælperne kan de forskellige parametre for opladningen nemt ændres fra mit dashboard i HA:

Skærmbillede med hjælpere og indtastede værdier, der bruges til styring af opladningen.

De indtastede værdier anvendes i automatiseringerne, som styrer opladning af husbatteriet.

Beregn niveau for opladning

Som nævnt har jeg valgt at bruge EV Smart Charging integrationen til at styre de faktiske tidspunkter, hvor batteriet skal oplades.

Integrationen skal vide, hvilket niveau batteriet skal oplades til. Det kan indstilles i de hjælpere, jeg har lavet. Jeg har et målniveau for opladning om natten og inden højlastperioden kl. 17.

Jeg bruger en template sensor til at fastsætte niveauet, der bruges i EV Smart Charging integrationen. Målniveauet sættes til 0%, hvis opladning ikke skal foretages. F.eks. hvis den forventede solproduktion er højere end den indstillede grænse. Eller hvis prisforskellen er for lav.

Hvis alle betingelser er opfyldt, indstilles niveauet for opladning baseret på tidspunktet før kl. 17 eller efter kl. 21.

En af betingelserne er, at den mulige besparelse overstiger den fastsatte grænse. Derfor er det nødvendigt at udregne prisforskellene mellem forskellige tidspunkter på dagen. Jeg har valgt at fokusere på gennemsnitspriser i forskellige tidsintervaller. Denne template sensor udregner gennemsnitspriserne og forskellene mellem de forskellige tidsintervaller:

template:
  - sensor:
      #─────────────────────────────────────────────────────────────────────────
      # This template calculates this price differences between the average
      # electricity prices during different time intervals:
      #
      # - Hours between 00 and 05
      # - Hours between 06 and 16
      # - Hours between 17 and 20
      # - Hours between 21 and 23
      #─────────────────────────────────────────────────────────────────────────
      - name: "Solar Control - Price differences"
        unique_id: solar_control_price_differences
        icon: mdi:battery-charging
        state: "{{ states('input_number.solar_control_house_battery_target_soc_before_peak')|float }}"
        unit_of_measurement: "%"
        attributes:
          price_diffs: >
            {#──────────────────────────────────────────────────────────────────── 
            CONFIGURATION
            ──────────────────────────────────────────────────────────────────────
            Change name of sensor with electricity prices provided by the Energi 
            Data Service integration
            ────────────────────────────────────────────────────────────────────#}
            {% set eds = "sensor.energi_data_service" -%}
            {#──────────────────────────────────────────────────────────────────── 
            END OF CONFIGURATION
            ────────────────────────────────────────────────────────────────────#}
            {% set prices = state_attr(eds, "today") | list + state_attr(eds, "tomorrow") | list if state_attr(eds, "tomorrow_valid") else state_attr(eds, "today") -%}
            {% set current_time = now().hour -%}
            {% set ns = namespace(prices = []) -%}
            {% set start_index = current_time | int -%}
            {% set start_index = 0 %}
            {% set end_index = ([ prices | length] | min ) | int  -%}
            {% set ns.p_td_00_06 = 0 -%}
            {% set ns.p_td_06_17 = 0 -%}
            {% set ns.p_td_17_21 = 0 -%}
            {% set ns.p_td_21_00 = 0 -%}
            {% set ns.f_td_00_06 = 0 -%}
            {% set ns.f_td_06_17 = 0 -%}
            {% set ns.f_td_17_21 = 0 -%}
            {% set ns.f_td_21_00 = 0 -%}

            {% set ns.p_tm_00_06 = 0 -%}
            {% set ns.p_tm_06_17 = 0 -%}
            {% set ns.p_tm_17_21 = 0 -%}
            {% set ns.p_tm_21_00 = 0 -%}
            {% set ns.f_tm_00_06 = 0 -%}
            {% set ns.f_tm_06_17 = 0 -%}
            {% set ns.f_tm_17_21 = 0 -%}
            {% set ns.f_tm_21_00 = 0 -%}

            {% for index in range(start_index, end_index) -%}
              {%- set price = prices[index] | float -%}
              {% if (index < 6) -%}{% set ns.p_td_00_06 = price+ns.p_td_00_06 -%}{% endif-%}
              {% if (index > 5 and index < 17) -%}{% set ns.p_td_06_17 = price+ns.p_td_06_17 -%}{% endif-%}
              {% if (index > 16 and index < 21) -%}{% set ns.p_td_17_21 = price+ns.p_td_17_21 -%}{% endif-%}
              {% if (index > 20 and index < 24) -%}{% set ns.p_td_21_00 = price+ns.p_td_21_00 -%}{% endif-%}
              {% if (index > 23 and index < 30) -%}{% set ns.p_tm_00_06 = ns.p_tm_00_06+price -%}{% endif-%}
              {% if (index > 29 and index < 41) -%}{% set ns.p_tm_06_17 = ns.p_tm_06_17+price -%}{% endif-%}
              {% if (index > 40 and index < 45) -%}{% set ns.p_tm_17_21 = ns.p_tm_17_21+price -%}{% endif-%}
              {% if (index > 44 and index < 48) -%}{% set ns.p_tm_21_00 = ns.p_tm_21_00+price -%}{% endif-%}
            {% endfor -%}

            {% set p_td_00_06_avg = ns.p_td_00_06/6 %}
            {% set p_td_06_17_avg = ns.p_td_06_17/11  %}
            {% set p_td_17_21_avg = ns.p_td_17_21/4 %}
            {% set p_td_21_00_avg = ns.p_td_21_00/3 %}
            {% set p_tm_00_06_avg = ns.p_tm_00_06/6 %}
            {% set p_tm_06_17_avg = ns.p_tm_06_17/11  %}
            {% set p_tm_17_21_avg = ns.p_tm_17_21/4 %}
            {% set p_tm_21_00_avg = ns.p_tm_21_00/3 %}

            {% set diffs = {"Today_00_06_vs_06_17":  "%.2f" | format(p_td_06_17_avg - p_td_00_06_avg)|float, 
                            "Today_06_17_vs_17_21":  "%.2f" | format(p_td_17_21_avg - p_td_06_17_avg)|float,  
                            "Tomorrow_00_06_vs_06_17":  "%.2f" | format(p_tm_06_17_avg - p_tm_00_06_avg)|float } 
                            %}

            {{ diffs }}

Sensoren benytter priserne fra Energi Data Service integrationen.

Nu kan der laves en ny template sensor, der beregner målniveauet for opladning:

      #─────────────────────────────────────────────────────────────────────────
      # This template sets the house battery target SoC based on values defined
      # in input numbers and whether charging from grid is enabled and defined
      # charging criteria's are met.
      #─────────────────────────────────────────────────────────────────────────
      - name: "Solar Control - House battery target SoC"
        unique_id: solar_control_house_battery_target_soc
        icon: mdi:battery-charging
        state: "{{ this.attributes.targetSOC | default(0) }}"
        unit_of_measurement: "%"
        attributes:
          status: >
            {#──────────────────────────────────────────────────────────────────── 
            CONFIGURATION
            ──────────────────────────────────────────────────────────────────────
            Change name of sensor with  Solar forecast provided the Solcast 
            integration
            ────────────────────────────────────────────────────────────────────#}
            {% set forecast0 = states("sensor.solcast_pv_forecast_forecast_today")|float -%}
            {% set forecast1 = states("sensor.solcast_pv_forecast_forecast_tomorrow")|float -%}
            {#──────────────────────────────────────────────────────────────────── 
            END OF CONFIGURATION
            ────────────────────────────────────────────────────────────────────#}
            {% set min_prod = states("input_number.solar_control_production_charge_limit")|float -%}
            {% set min_diff = states("input_number.solar_control_house_battery_min_saving")|float -%}
            {% if now().hour > 16 %}
               {% set cur_diff = states.sensor.solar_control_prices_differences.attributes.price_diffs.Tomorrow_00_06_vs_06_17 | float %}
               {% if forecast1 > min_prod %}  
                 {% set min_diff = 9999 %}
               {% endif %}
            {% elif now().hour <6 %}
               {% set cur_diff = states.sensor.solar_control_prices_differences.attributes.price_diffs.Today_00_06_vs_06_17 | float %}
               {% if forecast0 > min_prod %}  
                 {% set min_diff = 9999 %}
               {% endif %}
            {% else %}
               {% set cur_diff = states.sensor.solar_control_prices_differences.attributes.price_diffs.Today_06_17_vs_17_21 | float %}
            {% endif %}
            {% if cur_diff > min_diff %}
            charge
            {% else %}
            no_charging
            {% endif %}
          targetSOC: >
            {% if this.attributes.status =='charge' %}
              {% if now().hour > 16 or now().hour <6 %}
                {{ states('input_number.solar_control_house_battery_target_soc_midnight')|int }}
              {% else %}
                {{ states('input_number.solar_control_house_battery_target_soc_before_peak')|int }}
              {% endif %}
            {% else %}
              0
            {% endif %}

Sensoren anvender prognoser for de kommende dages solproduktion fra Solcast integration. Er betingelser

I historikken kan man se, hvordan batteriets målniveau ændres over dagen:

Historik fra template sensor, der fastsætter målniveau for opladning

Den nye sensor bruges i EV Smart Charging opsætningen:

Konfiguration af EV Smart Charging integrationen

Nu kan opladningen af husbatteriet styres af EV Smart Charging integrationen:

Skærmbillede, der viser planlagt opladning af husbatteri.

I eksemplet ovenfor er opladning planlagt til at ske til 40% mellem kl. 12-13, hvor prisen er lavest.

Automatisering

Nu er data på plads og det sidste, der mangler, er at bygge automatiseringer til at starte og stoppe opladningen. Her er et eksempel på en automatisering, der starter opladningen. Automatiseringen udløses, når sensoren sensor.ev_smart_charging_charging skifter status fra “off” til “on”. Handlingen, der er krævet for at starte opladningen vil være afhænge af hvilken inverter, der anvendes. Eksemplet tager udgangspunkt i en Deye hybrid inverter, der er integreret via ESPHome. Inverteren har forskellige tidsindstillinger til styring af opladning, så automatiseringen er delt to forskellige tidsperioder:

automation:
  #─────────────────────────────────────────────────────────────────────────────
  # This automation activates grid charging of house battery when the EV Smart
  # Charging sensor has status = 'on' and sets the defined target SoC
  #─────────────────────────────────────────────────────────────────────────────
  - alias: "Solar control: Start grid charging"
    id: solar_control_start_grid_charging
    description: Enable grid charging based on EV Smart Charging status
    triggers:
      - trigger: state
        entity_id:
          - sensor.ev_smart_charging_house_charging
        to: "on"
        from: "off"
    conditions: []
    actions:
      - choose:
          - conditions:
              - condition: time
                before: "05:54:00"
            sequence:
              - action: switch.turn_on
                metadata: {}
                data: {}
                target:
                  entity_id: switch.deyemodbus_deye_time_point_1_charge_enable
              - action: number.set_value
                target:
                  entity_id: number.deyemodbus_deye_time_point_1_capacity
                data:
                  value: >-
                    {{states('input_number.solar_control_house_battery_target_soc_midnight')|int}}
          - conditions:
              - condition: time
                after: "05:59:00"
            sequence:
              - action: switch.turn_on
                metadata: {}
                data: {}
                target:
                  entity_id: switch.deyemodbus_deye_time_point_4_charge_enable
              - action: number.set_value
                target:
                  entity_id: number.deyemodbus_deye_time_point_4_capacity
                data:
                  value: >-
                    {{states('input_number.solar_control_house_battery_target_soc_before_peak')|int}}
    mode: single

På samme måde, kan der laves en automatisering, der stopper opladningen, når sensoren sensor.ev_smart_charging_charging skifter status fra “on” til “off”.

Samlet package med ovenstående kode

I stedet for at oprette hjælpere, skabelon og automatiseringer særskilt, kan man samle det hele i én fil – en såkaldt package.

Jeg har lavet en package fil, som du kan hente på min GitHub side. Den indeholder ovenstående koder og kan let indsættes i din egen HA løsning. Den indeholder også automatiseringer til opladning af elbil med overskudsproduktion fra solceller. Det har jeg skrevet om i dette indlæg. Hvis du ikke har elbil, kan de dele af koden slettes. Eller du kan deaktivere automatiseringerne i HA.

Der er forskellige måder at integrere en package på i din HA. Det kan du læse mere i den officielle HA dokumentation. Selv har jeg valgt metoden, hvor packages tilføjes i en mappe (packages) i hver deres fil.

Det kræver blot en enkelt tilføjelse i din configuration.yaml fil med reference til den oprettede mappe:

homeassistant:
  packages: !include_dir_named packages

Package filen skal opdateres med navne på sensorer, der er anvendt i din installation, f.eks. sensorer til elpriser, prognose for solproduktion mv.

Det eneste, som package filen ikke indeholder, er kode til dashboard. Min dashboard kode, der er vist i eksemplerne ovenfor, kan også findes på min GitHub side.

Muligheder for forbedring?

Kan min opladning gøres smartere? Helt sikkert. Og det kan det kan helt sikkert også gøres mere enkelt. F.eks. ved at undlade at bruge EV Smart Charging integrationen. Eller ved bare at indtaste værdier direkte i automatiseringerne i stedet for at bruge hjælpere.

Jeg har dog tidligere styret opladningen manuelt og foretrækker derfor at have en grad af kontrol.

Det kan omvendt også gøres mere avanceret – f.eks. ved at kigge på det historiske elforbrug fordelt over dagen for at udregne behov for opladning. Eller ved at kigge på de enkelte timepriser i løbet af dagen i stedet for gennemsnitspriser. Endeligt kan man også overveje at beregne niveau for opladning set i forhold til den forventede solproduktion.

Måske kommer der en version 2 på et tidspunkt, men indtil videre dække automatiseringerne fint mit behov. Skriv gerne en kommentar, hvis du har andre løsninger til din opladning eller forslag til forbedringer.

Hvor nyttigt var dette indlæg?

Gennemsnitlig bedømmelse 5 / 5. Bedømmelser: 2

Ingen stemmer indtil videre! Vær den første til at bedømme dette indlæg.

Jeg beklager, at dette indlæg ikke var nyttigt for dig!

Hvordan kan indlægget forbedres?

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *