Skip Navigation

PV-Peak-Shave mit Home Assistant und Huawei LUNA2000 (Grünspeicher selbst gebaut)

Was ist das hier?

Eine Home Assistant Automation (Blueprint) die meinen Hausakku vor der solaren Mittagsspitze gezielt ins Netz entlädt - damit der Akku leer ist wenn die PV-Spitze kommt und diese vollständig aufgenommen werden kann, statt ins Netz zu drücken. Das Ziel ist Netzdienlichkeit, nicht Gewinnmaximierung.


Warum Peak-Shave?

Das Stromnetz hat täglich ein massives Problem: Zwischen 11 und 14 Uhr speisen Millionen PV-Anlagen gleichzeitig ins Netz ein. Die Netzfrequenz steigt, Regelenergie wird teuer, im Extremfall müssen Anlagen abgeregelt werden.

Ein Hausakku könnte helfen — aber nur wenn er zur richtigen Zeit leer ist. Im Normalbetrieb lädt der Akku am Morgen und ist zur Mittagsspitze bereits voll. Er kann dann die PV-Spitze nicht mehr aufnehmen und die überschüssige Energie geht trotzdem ins Netz.

Die Idee: Den Akku vor dem Peak aktiv entladen, damit er die Spitze aufnehmen kann. Der Akku verhält sich dann wie ein Grünspeicher — er verschiebt Energie zeitlich statt sie zu verschleiern.


Voraussetzungen

Hardware:

  • Huawei SUN2000 Wechselrichter (M1 oder MB0 Serie)
  • Huawei LUNA2000 Batteriespeicher
  • Huawei Smart Dongle (WLAN-FE oder 4G)

Software:

Benötigte Entitäten:

  • select.batterien_betriebsmodus — Betriebsmodus umschalten
  • sensor.batterien_batterieladung — aktueller SOC in %
  • sensor.netzbezug_live — aktueller Netzbezug in W
  • sensor.sun_next_noon — Zeitpunkt des nächsten Sonnenhöchststands
  • sensor.energy_production_today — Tagesprognose Ost-Dach (Forecast.Solar)
  • sensor.energy_production_today_2 — Tagesprognose West-Dach (Forecast.Solar)
  • sensor.energy_production_today_remaining — Restprognose ab jetzt
  • sensor.energy_production_today_remaining_2 — Restprognose ab jetzt (zweiter String)

Die Entity-IDs können bei eurer Installation abweichen — bitte anpassen.


Die Logik in einfacher Sprache

Wann startet die Automation?

Einmal täglich wird geprüft ob Peak-Shave heute sinnvoll ist:

  • Die Tagesprognose muss über einem einstellbaren Schwellwert liegen (Standard 15 kWh)
  • Der Akku muss genug Ladung haben (mehr als Ziel-SOC + 5%)
  • Die Sonne muss aufgegangen sein
  • Es muss noch genug Zeit bis zum Sonnenhöchststand sein

Wann genau startet sie?

Der Startzeitpunkt wird dynamisch berechnet:

 
    
Startzeit = Solar-Noon − Puffer (90 Min.) − Entladezeit − 10 Min. Sicherheit

  

Die Entladezeit hängt vom aktuellen SOC ab — je voller der Akku, desto früher startet die Automation. Bei SOC 80% und 5 kW Entladeleistung dauert es etwa 100 Minuten den Akku auf 20% zu entladen. Die Automation startet dann entsprechend früher.

Was passiert während des Entladens?

Der Wechselrichter wird auf fully_fed_to_grid gesetzt — PV-Strom und Akkustrom gehen ins Netz. Die Automation läuft still im Hintergrund und überwacht drei Stopp-Bedingungen.

Fünf Fälle — klare Prioritäten

Fall A — Kein Netzbezug (höchste Priorität) Wenn der Netzbezug über 500 W für 60 Sekunden steigt, wird Peak-Shave sofort abgebrochen. Eigenverbrauch hat immer Vorrang. Die 60-Sekunden-Verzögerung verhindert Abbrüche durch kurze Leistungsspitzen.

Fall B — SOC-Ziel erreicht Wenn der Akku den eingestellten Mindest-SOC (Standard 20%) erreicht, wird zurück auf Eigenverbrauch geschaltet. Der Akku behält einen Sicherheitspuffer.

Fall C — Noon-Stopp Spätestens 90 Minuten vor dem Sonnenhöchststand wird gestoppt — damit genug Zeit bleibt den Akku von der PV-Spitze wieder vollzuladen.

Fall D — Startzeit-Check Alle 5 Minuten wird geprüft ob die berechnete Startzeit erreicht ist. Das 6-Minuten-Startfenster stellt sicher dass die Automation nicht mehrfach startet.

Fall E — Restprognose-Wächter Während des Entladens wird alle 5 Minuten geprüft ob die verbleibende PV-Prognose noch ausreicht um den Akku wieder vollzuladen. Wenn nicht — sofortiger Stopp. Das verhindert dass der Akku abends zu leer ist weil Wolken die Prognose zunichte gemacht haben.

 
    
Stopp wenn: Restprognose < (10 kWh − Akku aktuell in kWh)

  


Wirtschaftlichkeit

Das Ergebnis ist wirtschaftlich knapp negativ — das ist beabsichtigt:

  • Einspeisevergütung: ~8,5 ct/kWh
  • Zusätzlicher Akkuverschleiß: ~1,08 € pro Vollzyklus (6.500 € Akku ÷ 6.000 Zyklen)
  • Ca. 100 Peak-Shave Tage pro Jahr × 0,58 Extra-Zyklen = ~63 € Verschleiß
  • Ca. 100 Tage × 10 kWh × 8,5 ct = ~85 € Einnahmen
  • Ergebnis: ~22 € Gewinn pro Jahr — kaum über Break-even

Wer Peak-Shave wirtschaftlich betreiben will braucht eine höhere Einspeisevergütung oder dynamische Tarife. Das hier ist bewusst ein Beitrag zur Netzdienlichkeit — kein Geschäftsmodell.


Einstellbare Parameter

ParameterStandardBeschreibung
Prognose-Schwelle15 kWhMindest-Tagesprognose für Start
Ziel-SOC20%Mindest-SOC beim Entladen
Stopp vor Solar-Noon90 Min.Sicherheitspuffer für Nachladen
Netzbezugs-Schwelle500 WAb wann wird abgebrochen
Netzbezugs-Dauer60 Sek.Wie lange muss Netzbezug anliegen

Hinweis zur Prognose-Schwelle: Forecast.Solar liegt in meiner Erfahrung systematisch 20-30% zu niedrig. Ich habe die Schwelle deshalb von 15 auf 12 kWh gesenkt. Wer genaue Prognosedaten hat kann höher ansetzen.

Hinweis zum Ziel-SOC: Mit Fall E als Sicherheitsnetz kann der Ziel-SOC aggressiver eingestellt werden — z.B. 10%. Fall E stoppt die Entladung automatisch wenn danach nicht mehr genug PV kommt um den Akku wieder vollzuladen.


Das Blueprint YAML

 yaml
    
blueprint:
  name: "PV Peak-Shave Einspeisung (Grünspeicher)"
  description: >
    Entlädt den Akku vor dem solaren Höchststand gezielt ins Netz (fully_fed_to_grid),
    damit der Akku zur PV-Spitze leer ist und wieder vollgeladen werden kann.
    Verhält sich wie ein Grünspeicher mit Peak-Shave.
    Höchste Priorität: kein Netzbezug.
  domain: automation
  input:

    prognose_schwelle:
      name: Prognose-Schwelle (kWh)
      description: >
        Mindest-Tagesprognose damit die Automation überhaupt startet.
        Unter diesem Wert passiert nichts.
      default: 15
      selector:
        number:
          min: 5
          max: 40
          step: 0.5
          unit_of_measurement: kWh

    ziel_soc:
      name: Ziel-SOC beim Entladen (%)
      description: >
        Bis auf diesen SOC wird der Akku vor dem PV-Peak entladen.
        20% = 2 kWh Sicherheitspuffer bei 10 kWh Akku.
      default: 20
      selector:
        number:
          min: 5
          max: 50
          step: 5
          unit_of_measurement: "%"

    minuten_vor_noon:
      name: Stopp vor Solar-Noon (Minuten)
      description: >
        Spätestens so viele Minuten vor dem solaren Höchststand wird
        auf Maximaler Eigenverbrauch zurückgeschaltet.
      default: 90
      selector:
        number:
          min: 30
          max: 180
          step: 15
          unit_of_measurement: min

    netzbezug_schwelle:
      name: Netzbezugs-Schwelle (W)
      description: >
        Wenn der Netzbezug diesen Wert überschreitet, wird sofort auf
        Maximaler Eigenverbrauch zurückgeschaltet.
      default: 500
      selector:
        number:
          min: 100
          max: 2000
          step: 50
          unit_of_measurement: W

    netzbezug_dauer:
      name: Netzbezugs-Dauer vor Abbruch (Sekunden)
      description: >
        Der Netzbezug muss diese Zeit lang über der Schwelle liegen,
        bevor abgebrochen wird (verhindert kurze Spitzen).
      default: 60
      selector:
        number:
          min: 10
          max: 300
          step: 10
          unit_of_measurement: s

mode: queued
max: 2
max_exceeded: silent

trigger:
  - platform: time_pattern
    minutes: "/5"
    id: "check_startzeit"

  - platform: numeric_state
    entity_id: sensor.batterien_batterieladung
    below: !input ziel_soc
    id: "soc_ziel_erreicht"

  - platform: numeric_state
    entity_id: sensor.netzbezug_live
    above: !input netzbezug_schwelle
    for:
      seconds: 60
    id: "netzbezug_alarm"

  - platform: time_pattern
    minutes: "/5"
    id: "check_noon_stopp"

variables:
  prognose_schwelle_var: !input prognose_schwelle
  ziel_soc_var: !input ziel_soc
  minuten_vor_noon_var: !input minuten_vor_noon
  netzbezug_schwelle_var: !input netzbezug_schwelle

action:
  - choose:

    # ══════════════════════════════════════════════════════════════════
    # FALL A: Notfall-Abbruch – Netzbezug zu hoch (höchste Priorität)
    # ══════════════════════════════════════════════════════════════════
    - conditions:
        - condition: trigger
          id: "netzbezug_alarm"
        - condition: state
          entity_id: select.batterien_betriebsmodus
          state: "fully_fed_to_grid"
      sequence:
        - action: select.select_option
          target:
            entity_id: select.batterien_betriebsmodus
          data:
            option: maximise_self_consumption
        - action: system_log.write
          data:
            message: >
              PV Peak-Shave: ABBRUCH – Netzbezug
              {{ states('sensor.netzbezug_live') | round(0) }} W
              über Schwelle {{ netzbezug_schwelle_var }} W.
            level: warning

    # ══════════════════════════════════════════════════════════════════
    # FALL B: SOC-Ziel erreicht
    # ══════════════════════════════════════════════════════════════════
    - conditions:
        - condition: trigger
          id: "soc_ziel_erreicht"
        - condition: state
          entity_id: select.batterien_betriebsmodus
          state: "fully_fed_to_grid"
      sequence:
        - action: select.select_option
          target:
            entity_id: select.batterien_betriebsmodus
          data:
            option: maximise_self_consumption
        - action: system_log.write
          data:
            message: >
              PV Peak-Shave: SOC {{ states('sensor.batterien_batterieladung') | round(0) }} %
              <= Ziel {{ ziel_soc_var }} %. Zurück auf Eigenverbrauch.
            level: info

    # ══════════════════════════════════════════════════════════════════
    # FALL C: Solar-Noon naht – Noon-Stopp
    # ══════════════════════════════════════════════════════════════════
    - conditions:
        - condition: trigger
          id: "check_noon_stopp"
        - condition: state
          entity_id: select.batterien_betriebsmodus
          state: "fully_fed_to_grid"
        - condition: template
          value_template: >
            {% set noon_ts = states('sensor.sun_next_noon') | as_timestamp(0) %}
            {% set now_ts = now().timestamp() %}
            {% set minuten_bis_noon = (noon_ts - now_ts) / 60 %}
            {{ minuten_bis_noon <= minuten_vor_noon_var and minuten_bis_noon >= 0 }}
      sequence:
        - action: select.select_option
          target:
            entity_id: select.batterien_betriebsmodus
          data:
            option: maximise_self_consumption
        - action: system_log.write
          data:
            message: >
              PV Peak-Shave: Noon-Stopp –
              {{ ((states('sensor.sun_next_noon') | as_timestamp(0) - now().timestamp()) / 60) | round(0) }}
              Min. bis Solar-Noon.
            level: info

    # ══════════════════════════════════════════════════════════════════
    # FALL E: Restprognose reicht nicht mehr für vollen Akku
    # ══════════════════════════════════════════════════════════════════
    - conditions:
        - condition: trigger
          id: "check_noon_stopp"
        - condition: state
          entity_id: select.batterien_betriebsmodus
          state: "fully_fed_to_grid"
        - condition: template
          value_template: >
            {% set verbleibend = states('sensor.energy_production_today_remaining') | float(0)
                               + states('sensor.energy_production_today_remaining_2') | float(0) %}
            {% set akku_kwh = (states('sensor.batterien_batterieladung') | float(0) / 100) * 10 %}
            {{ verbleibend < (10 - akku_kwh) }}
      sequence:
        - action: select.select_option
          target:
            entity_id: select.batterien_betriebsmodus
          data:
            option: maximise_self_consumption
        - action: system_log.write
          data:
            message: >
              PV Peak-Shave: Stopp – Restprognose
              {{ (states('sensor.energy_production_today_remaining') | float(0) +
                  states('sensor.energy_production_today_remaining_2') | float(0)) | round(1) }} kWh
              reicht nicht für vollen Akku
              (aktuell {{ (states('sensor.batterien_batterieladung') | float(0) / 100 * 10) | round(1) }} kWh).
            level: info

    # ══════════════════════════════════════════════════════════════════
    # FALL D: Startzeit-Check – Peak-Shave starten
    # ══════════════════════════════════════════════════════════════════
    - conditions:
        - condition: trigger
          id: "check_startzeit"
        - condition: not
          conditions:
            - condition: state
              entity_id: select.batterien_betriebsmodus
              state: "fully_fed_to_grid"
        - condition: state
          entity_id: sun.sun
          state: "above_horizon"
        - condition: template
          value_template: >
            {% set noon_ts = states('sensor.sun_next_noon') | as_timestamp(0) %}
            {% set now_ts = now().timestamp() %}
            {% set minuten_bis_noon = (noon_ts - now_ts) / 60 %}
            {{ minuten_bis_noon > minuten_vor_noon_var }}
        - condition: template
          value_template: >
            {% set akku_kwh = (states('sensor.batterien_batterieladung') | float(0) / 100) * 10 %}
            {% set akku_min_kwh = (ziel_soc_var | float(20) / 100) * 10 %}
            {% set entladbar_kwh = [akku_kwh - akku_min_kwh, 0] | max %}
            {% set entladeleistung_kw = 3.5 %}
            {% set entladezeit_min = (entladbar_kwh / entladeleistung_kw * 60) | int %}
            {% set noon_ts = states('sensor.sun_next_noon') | as_timestamp(0) %}
            {% set start_ts = noon_ts - (minuten_vor_noon_var * 60) - (entladezeit_min * 60) - 600 %}
            {% set now_ts = now().timestamp() %}
            {{ now_ts >= start_ts and now_ts < (start_ts + 360) }}
        - condition: template
          value_template: >
            {% set prognose = states('sensor.energy_production_today') | float(0)
                            + states('sensor.energy_production_today_2') | float(0) %}
            {{ prognose >= prognose_schwelle_var }}
        - condition: template
          value_template: >
            {{ states('sensor.batterien_batterieladung') | float(0) > ziel_soc_var | float(20) + 5 }}
      sequence:
        - action: select.select_option
          target:
            entity_id: select.batterien_betriebsmodus
          data:
            option: fully_fed_to_grid
        - action: system_log.write
          data:
            message: >
              PV Peak-Shave: START –
              Prognose {{ (states('sensor.energy_production_today') | float(0) +
                           states('sensor.energy_production_today_2') | float(0)) | round(1) }} kWh,
              SOC {{ states('sensor.batterien_batterieladung') | round(0) }} %,
              Solar-Noon in {{ ((states('sensor.sun_next_noon') | as_timestamp(0) -
                                 now().timestamp()) / 60) | round(0) }} Min.
            level: info

  


Installation

  1. YAML-Datei als pv_peak_shave_einspeisung.yaml in den Ordner config/blueprints/automation/ kopieren
  2. Home Assistant neu laden oder Entwicklerwerkzeuge → YAML → Blueprints neu laden
  3. Einstellungen → Automationen → Automation erstellen → Blueprint auswählen
  4. Entity-IDs anpassen falls sie bei eurer Installation abweichen
  5. Parameter nach eurer Anlage einstellen
  6. Automation speichern und aktivieren


Hinweise und Einschränkungen

Akkugröße: Das Blueprint ist auf 10 kWh ausgelegt (Huawei LUNA2000-10kWh). Wer eine andere Akkugröße hat muss in Fall E den Wert 10 durch die eigene Kapazität in kWh ersetzen.

Entladeleistung: Im Blueprint ist 3,5 kW Entladeleistung hardcodiert (Zeile entladeleistung_kw = 3.5). Bitte an die eigene Anlage anpassen.

Forecast.Solar: Die Prognosegenauigkeit variiert stark je nach Standort und Dachausrichtung. Die Prognose-Schwelle sollte nach ein paar Wochen Beobachtung kalibriert werden.

Manueller Eingriff: Wenn Peak-Shave manuell abgebrochen wird, startet die Automation an diesem Tag nicht mehr automatisch neu — das Startfenster ist dann verpasst. Ein manueller Neustart ist möglich.


Was diese Automation nicht ist

Das ist kein wirtschaftliches Optimierungssystem. Es gibt elegantere Ansätze um den Akkuverschleiß zu minimieren — z.B. Ladestart verzögern statt aktiv entladen. Aber diese Ansätze erreichen kein echtes Peak-Shave weil der Akku beim solaren Höchststand nicht leer genug ist.

Wer primär den Akku schonen will und Netzdienlichkeit als Nebenziel hat sollte einen anderen Ansatz wählen.


Getestet mit: Huawei SUN2000-4KTL-M1, SUN2000-5KTL-M1, LUNA2000-10kWh, huawei_solar v1.6.0, Home Assistant 2026.4

Comments

2