Anker Solix E1600 Plus - Fast annähernd 0-Einspeisung

Hallo zusammen,

Ich möchte mal meine Automation für die Anker Solix E1600 Plus (ja, keine Pro ;-P) vorstellen.

Mein Ziel war es, niemals Strom ins Netz abzuführen, sondern die Einspeise­vorgabe der Anker-Solarbank dynamisch anzupassen – unter Berücksichtigung von PV-Ertrag, Haushaltsbedarf, Netzeinspeisung, Akku-SoC und einer zeitgesteuerten Abend­entladung. Die erste Priorität ist, möglichst viel produzierten PV-Strom direkt zu verbrauchen, ohne ihn ins Netz einzuspeisen. Die zweite Priorität ist, mit dem Delta aus Prio 1 den Akku zu laden. Dies hat einen einfachen Grund. Durch das Laden und Entladen des Akkus entstehen Verluste von ca. 20 %.

Der Code von Simon ist wirklich super. Da sieht man, dass viel Hirnschmalz in die Automatisierung investiert wurde. Als Semi-Profi ist der Code für mich zu Overdosed. Ich hatte das Problem, dass die Automation nicht solide genug lief und ein Verhalten aufwies, das ich nicht rekonstruieren und beheben konnte. Einerseits unterbrach die automatische Akkuentladung vor dem eigentlichen Sollwert, andererseits wurde durch Schwankungen im Energieverbrauch zu viel Strom eingespeist. Das ist nun mal die Herausforderung, wenn man den Code von anderen übernimmt und nicht alles übernehmen kann oder die Überlegungen der anderen nicht vollständig versteht und nachvollziehen kann. Und nach zwei Wochen weiß ich schon nicht mehr genau, wie alles funktioniert. Ich musste die Automation also so weit anpassen, dass ich meine Überlegungen möglichst einfach nachvollziehen kann und für mein Setting zuverlässig läuft.

Ich habe mich als Auslöser den Time-Patter entschieden. Der Triggert alle 15 Sekunden. Dies ist wichtig, für conditions und actions. Ich erstellte mir auch einen Datums-Zeit-Helfer. Wichtig hier, setzt eine Zeitwert ein.

Zudem habe ich noch einen Trigger, wenn sollte es kurzfristig zu einer Netzeinspeisung von über 5 W kommen.

alias: Anker "Nulleinspeisung" – PV‑Deckel - Timer
description: Stellt die Einspeisevorgabe auf min(PV‑Leistung, Haushaltsbedarf)
triggers:
  - trigger: time_pattern
    seconds: /15
    id: Timer
  - trigger: numeric_state
    entity_id:
      - sensor.power_netzeinspeisung
    above: 5
    id: Netzeinspeisung

Bedingung 1: Die API muss aktiv sein und analog zu Simons Automation eingestellt werden.

Bedingung 2: Der Account muss Systemadministrator sein.

Bedingung 3: Hier nutze ich eine Oder-Bedingung. Damit stelle ich sicher, dass der Abstand für die API-Abfragen eingehalten wird. Hierzu verwende ich den Datums-Zeit-Helfer. „API Normal” hat eine Zeitdauer von 90 Sekunden und benötigt zusätzlich die ID „Timer”. Die Bedingung „API Minimal” hat eine minimale Zeitdauer von 60 Sekunden und benötigt zusätzlich die ID „Netzeinspeisung”.

conditions:
  - condition: state
    entity_id: switch.ank_ch_api_nutzung
    state: "on"
  - condition: state
    entity_id: binary_sensor.system_anker_solarbank_terrasse_systemadministration
    state: "on"
  - alias: Cooldown
    condition: or
    conditions:
      - condition: and
        conditions:
          - condition: template
            value_template: >-
              {{ (now().astimezone() -
              (states('input_datetime.automation_anker_nulleinspeisung_pv_deckel')
              | as_datetime | as_local)).total_seconds() > 90 }}
            alias: Cool-Down API Zeitschutz
          - condition: trigger
            id:
              - Timer
        alias: API Normal
      - alias: API minimal
        condition: and
        conditions:
          - alias: Cool-Down API Zeitschutz
            condition: template
            value_template: >-
              {{ (now().astimezone() -
              (states('input_datetime.automation_anker_nulleinspeisung_pv_deckel')
              | as_datetime | as_local)).total_seconds() > 60 }}
          - condition: trigger
            id:
              - Netzeinspeisung

Die Auswahl wird von oben nach unten abgearbeitet. Entsprechend ist die Gliederung entscheidend.

Option 1: Zunächst wird geprüft, ob eine Netzeinspeisung vorhanden ist. Wenn ja, werden die Variablen für PV und Netzeinspeisung definiert. Das Ziel besteht darin, dass die Einspeise­vorgabe 10 % unter dem tatsächlichen Bedarf liegt. Load ist der Sensor, der den Netzbezug sowie die Messsteckdose von Anker-Solix misst. Feed enthält nur den positiven Überschuss. New Target zieht für die Einspeise­vorgabe 10 % ab und der Wert darf maximal 600 und minimal 0 betragen.

      - conditions:
          - condition: numeric_state
            entity_id: sensor.power_netzeinspeisung
            above: 5
        sequence:
          - variables:
              pv: >-
                {{
                states('sensor.solarbank_2_e1600_plus_solarleistung')|float(0)
                }}
              load: "{{ states('sensor.power_verbrauch_haushalt')|float(0) }}"
              feed: "{{ max(0, pv - load) }}"
              new_target: >-
                {{ [ [ (pv - feed * 1.1) , 600 ] | min , 0 ] | max | round(0)
                }} 
            alias: ziehe 110 % des Überschusses ab, klemme 0‑600 W
          - action: number.set_value
            metadata: {}
            data:
              value: "{{new_target}}"
            target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe

Option 2: Die automatische Entladung wird abgeschaltet, wenn der Akkustand unter 20 % fällt. Für diese Option und die Entladungsoption habe ich außerdem den Helfer-Schalter input_boolean.anker_discharge_prio erstellt. Hier wird er ausgeschaltet und bei Option 3 eingeschaltet.

      - conditions:
          - condition: numeric_state
            entity_id: sensor.solarbank_2_e1600_plus_ladestand
            below: 20
          - condition: time
            after: "20:30:00"
            before: "05:00:00"
          - condition: state
            entity_id: input_boolean.anker_discharge_prio
            state: "on"
          - condition: not
            conditions:
              - condition: state
                entity_id: sensor.solarbank_2_e1600_plus_einspeisevorgabe
                state: "0"
        sequence:
          - target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe
            data:
              value: "0"
            action: number.set_value
          - action: input_boolean.turn_off
            metadata: {}
            data: {}
            target:
              entity_id: input_boolean.anker_discharge_prio
        alias: Entladen Akku aus bei 20%

Option 3 startet die Entladung. Der Akku muss über 20 % geladen sein und die Zeit muss stimmen.

      - conditions:
          - condition: time
            after: "20:30:00"
            before: "05:00:00"
          - condition: numeric_state
            entity_id: sensor.solarbank_2_e1600_plus_ladestand
            above: 20
          - condition: state
            entity_id: input_boolean.anker_discharge_prio
            state: "off"
          - condition: not
            conditions:
              - condition: state
                entity_id: sensor.solarbank_2_e1600_plus_einspeisevorgabe
                state: "200"
        sequence:
          - action: number.set_value
            metadata: {}
            data:
              value: "130"
            target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe
          - action: input_boolean.turn_on
            metadata: {}
            data: {}
            target:
              entity_id: input_boolean.anker_discharge_prio
        alias: Entladen Akku ein

Der erste Template‑Abschnitt sorgt dafür, dass die Automation nur dann einen neuen Wert an die Solarbank sendet, wenn sich die Leistung wirklich spürbar geändert hat. Zuerst holt er sich drei aktuelle Zahlen: die Momentanleistung der Module, den Minimal‑ und den Durchschnittsverbrauch der letzten fünf Minuten. Aus den beiden Verbrauchswerten wird ein geglätteter Bedarf berechnet, der Schwankungen abfedert. Anschließend vergleicht das Skript diesen Bedarf mit der gerade verfügbaren PV‑Leistung und wählt das kleinere der beiden Werte als „neues Ziel“.

Nun prüft es, wie stark dieses Ziel vom aktuell eingestellten Sollwert der Solarbank abweicht. Liegt einer der beiden Werte bei null, genügt schon die kleinste Differenz, weil der Wechselrichter in dem Moment ein‑ oder ausgeschaltet werden müsste. In allen anderen Fällen muss die Abweichung deutlich sein: Entweder weicht der neue Zielwert um mehr als zehn Prozent vom aktuellen Sollwert ab oder die PV‑Leistung selbst hat sich um mehr als fünf Prozent verschoben. Erst wenn mindestens eine dieser Bedingungen stimmt, liefert das Template ein „wahr“ zurück und die nachfolgenden Aktionen dürfen den neuen Sollwert setzen. Bleibt die Änderung unter diesen Schwellen, wertet das Template sie als unwichtig und die Automation lässt alles unverändert – genau so verhindert es ein ständiges Hin‑ und Hergeregle bei kleinen Leistungsschwankungen. Die anderen Bedingungen sind selbsterklärend.

Im Aktions­block wird der neue Sollwert für die Solarbank berechnet und anschließend direkt an das Number‑Entity übergeben. Zunächst zieht sich das Template drei frische Werte: die momentane PV‑Leistung der Anlage, den Fünf‑Minuten‑Minimal­verbrauch des Haushalts und den dazugehörigen gleitenden Durchschnitt. Aus Minimal‑ und Durchschnitts­verbrauch wird ein geglätteter Leistungs­bedarf ermittelt, indem der Minimalwert doppelt, der Mittelwert einfach gewichtet wird. Danach vergleicht das Skript diesen Bedarf mit der aktuell verfügbaren Modul­leistung und nimmt den kleineren der beiden Zahlen als Obergrenze – so kann die Solix “nie” mehr abgeben, als die Module tatsächlich liefern oder als das Haus realistisch zieht. Zur Sicherheit wird dieser Wert noch auf den Hardware­bereich der Wechselrichter gekappt, also mindestens auf null und höchstens auf 600 W.

Jetzt entscheidet die PV‑Leistung darüber, wie viel Reserve für den Netzbezug stehen­bleibt: Liegt der PV‑Ertrag unter 20 W, wird der Sollwert auf null gesetzt, weil sich das Einspeisen dann ohnehin nicht mehr lohnt und der Wechselrichter sauber abschalten soll. Produziert die Anlage über 200 W, reduziert das Template den gekappten Wert nur um fünf Prozent – so bleibt ein ganz kleiner Netzbezug als Puffer. Bei allen Zwischenwerten (21 bis 199 W) wird um zehn Prozent gekürzt, wodurch ein etwas größerer Puffer entsteht und kleine Schwankungen abgefangen werden. Das Ergebnis wird auf volle Watt gerundet und mit number.set_value an die Entität number.solarbank_2_e1600_plus_system_einspeisevorgabe geschickt; die Solarbank drosselt daraufhin ihre Ausgangsleistung genau auf diesen berechneten Wert.

      - conditions:
          - alias: 10‑Prozent‑Diff
            condition: template
            value_template: >-
              {% set pv      =
              states('sensor.solarbank_2_e1600_plus_solarleistung')|float(0) %}

              {% set min5    =
              states('sensor.power_verbrauch_haushalt_min_5min')|float(0) %}

              {% set mean    =
              states('sensor.power_verbrauch_haushalt_mittelwert_5min')|float(0)
              %}

              {% set target  = ([ ((min5*2)+mean)/3 , pv ] | min) | round(0) %}

              {% set current =
              states('number.solarbank_2_e1600_plus_system_einspeisevorgabe')|float(0)
              %}

              {% if current == 0 or target == 0 %}
                {{ current != target }}
              {% else %}
                {{
                     ( (target - current) | abs > current * 0.1 )
                  or ( (pv     - current) | abs > current * 0.05 )
                }}
              {% endif %}
            enabled: true
          - condition: not
            conditions:
              - condition: time
                after: "20:30:00"
                before: input_datetime.nas_zeit_aus
          - condition: state
            entity_id: input_boolean.anker_discharge_prio
            state: "off"
        sequence:
          - target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe
            data:
              value: >-
                {% set pv   =
                states('sensor.solarbank_2_e1600_plus_solarleistung')|float(0)
                %} 

                {% set min5 =
                states('sensor.power_verbrauch_haushalt_min_5min')|float(0) %} 

                {% set mean =
                states('sensor.power_verbrauch_haushalt_mittelwert_5min')|float(0)
                %} 

                {% set raw  = ((min5*2) + mean) / 3 %} 

                {% set deckel = [ raw , pv ] | min %}

                {% set capped = [ [ deckel , 0 ] | max , 600 ] | min %}


                {% if pv < 20 %}
                  0
                {% elif pv > 200 %}
                  {{ (capped * 0.95) | round(0) }}
                {% else %}
                  {{ (capped * 0.90) | round(0) }}
                {% endif %}
            action: number.set_value
        alias: PV-Standardeinstellung mit 10% Differenz

Dies ist der wichtigste Teil. Hier wird die Automation gestoppt, wenn keine der Optionen zutrifft. Der Datumszeit-Helfer wird nicht neu eingestellt. Sollte sich eine der Bedingungen in den Optionen ändern, wird diese nach Ablauf der Zeit innerhalb von 15 Sekunden ausgelöst.

    default:
      - stop: Keine Aktion
        error: false

Wenn eine Option zutrifft, wird der Datumszeit-Helfer neu gesetzt.

  - action: input_datetime.set_datetime
    metadata: {}
    target:
      entity_id: input_datetime.automation_anker_nulleinspeisung_pv_deckel
    data:
      datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
    alias: Zeitstempel für Cool-Down-Bedingung setzen

Hier ist noch die vollständige Automation:

alias: Anker Nulleinspeisung – PV‑Deckel - Timer
description: Stellt die Einspeisevorgabe auf min(PV‑Leistung, Haushaltsbedarf)
triggers:
  - trigger: time_pattern
    seconds: /15
    id: Timer
  - trigger: numeric_state
    entity_id:
      - sensor.power_netzeinspeisung
    above: 5
    id: Netzeinspeisung
conditions:
  - condition: state
    entity_id: switch.ank_ch_api_nutzung
    state: "on"
  - condition: state
    entity_id: binary_sensor.system_anker_solarbank_terrasse_systemadministration
    state: "on"
  - alias: Cooldown
    condition: or
    conditions:
      - condition: and
        conditions:
          - condition: template
            value_template: >-
              {{ (now().astimezone() -
              (states('input_datetime.automation_anker_nulleinspeisung_pv_deckel')
              | as_datetime | as_local)).total_seconds() > 90 }}
            alias: Cool-Down API Zeitschutz
          - condition: trigger
            id:
              - Timer
        alias: API Normal
      - alias: API minimal
        condition: and
        conditions:
          - alias: Cool-Down API Zeitschutz
            condition: template
            value_template: >-
              {{ (now().astimezone() -
              (states('input_datetime.automation_anker_nulleinspeisung_pv_deckel')
              | as_datetime | as_local)).total_seconds() > 60 }}
          - condition: trigger
            id:
              - Netzeinspeisung
actions:
  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: sensor.power_netzeinspeisung
            above: 5
        sequence:
          - variables:
              pv: >-
                {{
                states('sensor.solarbank_2_e1600_plus_solarleistung')|float(0)
                }}
              load: "{{ states('sensor.power_verbrauch_haushalt')|float(0) }}"
              feed: "{{ max(0, pv - load) }}"
              new_target: >-
                {{ [ [ (pv - feed * 1.1) , 600 ] | min , 0 ] | max | round(0)
                }} 
            alias: ziehe 110 % des Überschusses ab, klemme 0‑600 W
          - action: number.set_value
            metadata: {}
            data:
              value: "{{new_target}}"
            target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe
      - conditions:
          - condition: numeric_state
            entity_id: sensor.solarbank_2_e1600_plus_ladestand
            below: 20
          - condition: time
            after: "20:30:00"
            before: "05:00:00"
          - condition: state
            entity_id: input_boolean.anker_discharge_prio
            state: "on"
          - condition: not
            conditions:
              - condition: state
                entity_id: sensor.solarbank_2_e1600_plus_einspeisevorgabe
                state: "0"
        sequence:
          - target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe
            data:
              value: "0"
            action: number.set_value
          - action: input_boolean.turn_off
            metadata: {}
            data: {}
            target:
              entity_id: input_boolean.anker_discharge_prio
        alias: Entladen Akku aus bei 20%
      - conditions:
          - condition: time
            after: "20:30:00"
            before: "05:00:00"
          - condition: numeric_state
            entity_id: sensor.solarbank_2_e1600_plus_ladestand
            above: 20
          - condition: state
            entity_id: input_boolean.anker_discharge_prio
            state: "off"
          - condition: not
            conditions:
              - condition: state
                entity_id: sensor.solarbank_2_e1600_plus_einspeisevorgabe
                state: "200"
        sequence:
          - action: number.set_value
            metadata: {}
            data:
              value: "130"
            target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe
          - action: input_boolean.turn_on
            metadata: {}
            data: {}
            target:
              entity_id: input_boolean.anker_discharge_prio
        alias: Entladen Akku ein
      - conditions:
          - alias: 10‑Prozent‑Diff
            condition: template
            value_template: >-
              {% set pv      =
              states('sensor.solarbank_2_e1600_plus_solarleistung')|float(0) %}

              {% set min5    =
              states('sensor.power_verbrauch_haushalt_min_5min')|float(0) %}

              {% set mean    =
              states('sensor.power_verbrauch_haushalt_mittelwert_5min')|float(0)
              %}

              {% set target  = ([ ((min5*2)+mean)/3 , pv ] | min) | round(0) %}

              {% set current =
              states('number.solarbank_2_e1600_plus_system_einspeisevorgabe')|float(0)
              %}

              {% if current == 0 or target == 0 %}
                {{ current != target }}
              {% else %}
                {{
                     ( (target - current) | abs > current * 0.1 )
                  or ( (pv     - current) | abs > current * 0.05 )
                }}
              {% endif %}
            enabled: true
          - condition: not
            conditions:
              - condition: time
                after: "20:30:00"
                before: input_datetime.nas_zeit_aus
          - condition: state
            entity_id: input_boolean.anker_discharge_prio
            state: "off"
        sequence:
          - target:
              entity_id: number.solarbank_2_e1600_plus_system_einspeisevorgabe
            data:
              value: >-
                {% set pv   =
                states('sensor.solarbank_2_e1600_plus_solarleistung')|float(0)
                %} 

                {% set min5 =
                states('sensor.power_verbrauch_haushalt_min_5min')|float(0) %} 

                {% set mean =
                states('sensor.power_verbrauch_haushalt_mittelwert_5min')|float(0)
                %} 

                {% set raw  = ((min5*2) + mean) / 3 %} 

                {% set deckel = [ raw , pv ] | min %}

                {% set capped = [ [ deckel , 0 ] | max , 600 ] | min %}


                {% if pv < 20 %}
                  0
                {% elif pv > 200 %}
                  {{ (capped * 0.95) | round(0) }}
                {% else %}
                  {{ (capped * 0.90) | round(0) }}
                {% endif %}
            action: number.set_value
        alias: PV-Standardeinstellung mit 10% Differenz
    default:
      - stop: Keine Aktion
        error: false
  - action: input_datetime.set_datetime
    metadata: {}
    target:
      entity_id: input_datetime.automation_anker_nulleinspeisung_pv_deckel
    data:
      datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
    alias: Zeitstempel für Cool-Down-Bedingung setzen
mode: single
trace:
  stored_traces: 30

Hier sind viele meiner Gedanken eingeflossen, daher kann es sein, dass meine Überlegungen nicht zu deinem Setting passen. Vielleicht helfen sie dir aber dabei, die Einstellungen nach deinen Bedürfnissen anzupassen und zu optimieren. Ich habe gut zwei Wochen lang justiert und experimentiert. So passt die Automation für mich.

THX Mike