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 Einspeisevorgabe der Anker-Solarbank dynamisch anzupassen – unter Berücksichtigung von PV-Ertrag, Haushaltsbedarf, Netzeinspeisung, Akku-SoC und einer zeitgesteuerten Abendentladung. 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 Einspeisevorgabe 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 Einspeisevorgabe 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 Aktionsblock 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‑Minimalverbrauch des Haushalts und den dazugehörigen gleitenden Durchschnitt. Aus Minimal‑ und Durchschnittsverbrauch wird ein geglätteter Leistungsbedarf ermittelt, indem der Minimalwert doppelt, der Mittelwert einfach gewichtet wird. Danach vergleicht das Skript diesen Bedarf mit der aktuell verfügbaren Modulleistung 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 Hardwarebereich 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 stehenbleibt: 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