Hallo
Ich habe auch einen ESP32 zum Auslesen des Marstek B2500 per Bluetooth genommen. So braucht man nicht in das Gerät eingreifen und bekommt auch keine Probleme mit Garantie/Gewährleistung. Funktioniert einwandfrei. Wie das geht steht hier: GitHub - tomquist/esphome-b2500 und hier: ESPHome for B2500.
Die Automation für die Nulleinspeisung (ich hab das aber auf Restbezug 20 Watt eingestellt, um ja nichts zu verschenken) hab ich mir von ChatGPT bauen lassen. Und ich muss sagen, was die KI hier geliefert hat, funktioniert seeeehr gut. Das ganze hat ein paar Stunden gedauert, aber selber hätte ich sowas niemals ! hinbekommen.
alias: Nulleinspeisung - inkrementeller PI mit Akku-Schutz (3s)
description: >
Inkrementeller PI-Regler: regelt alle 3s, max 200 W Sprungweite, 0–800 W. Nur
aktiv bei Akku > 20 %. Unter 20 % Akku wird auf festen Wert 125 W gesetzt.
triggers:
- seconds: /3
trigger: time_pattern
conditions:
- condition: state
entity_id: input_boolean.nulleinspeisung
state: “on”
- condition: template
value_template: >-
{{ states(‘sensor.energy_netzbezug’) not in
\[‘unknown’,‘unavailable’,‘uninitialized’\] }}
actions:
- choose:
- conditions:
- condition: template
value_template: “{{ soc <= 20 }}”
sequence:
- target:
entity_id: number.b2500_01_b2500_1_b2500_01_timer_1_output_power
data:
value: “{{ output_final | round(0) }}”
action: number.set_value
- data:
entity_id: input_number.nulleinspeisung_integral
value: “{{ integral_final }}”
action: input_number.set_value
- conditions:
- condition: template
value_template: “{{ soc > 20 }}”
sequence:
- target:
entity_id: number.b2500_01_b2500_1_b2500_01_timer_1_output_power
data:
value: “{{ output_final | round(0) }}”
action: number.set_value
- data:
entity_id: input_number.nulleinspeisung_integral
value: “{{ integral_final | round(3) }}”
action: input_number.set_value
variables:
sensor: “{{ states(‘sensor.energy_netzbezug’) | float(0) }}”
setpoint: 10
soc: “{{ states(‘sensor.b2500_01_b2500_1_b2500_01_battery_level’) | float(0) }}”
prev_output: >-
{{ states(‘number.b2500_01_b2500_1_b2500_01_timer_1_output_power’) |
float(0) }}
prev_integral: “{{ states(‘input_number.nulleinspeisung_integral’) | float(0) }}”
kp: 0.5
ki: 0.02
dt: 3
max_step: 200
out_min: 0
out_max: 800
error: “{{ (sensor - setpoint) }}”
integral_raw: “{{ (prev_integral + (error \* dt)) }}”
delta_raw: “{{ (kp \* error) + (ki \* integral_raw) }}”
delta_step: |
{% if delta_raw > max_step %}
{{ max_step }}
{% elif delta_raw < -max_step %}
{{ -max_step }}
{% else %}
{{ delta_raw }}
{% endif %}
tentative_output: “{{ (prev_output | float) + (delta_step | float) }}”
clamped_output: “{{ \[out_min, \[tentative_output | float, out_max\] | min\] | max }}”
integral_new: >
{% set tentative = tentative_output | float %} {% set clamped =
clamped_output | float %} {% if (clamped == tentative) and (clamped >
out_min) and (clamped < out_max) %}
{{ integral_raw }}
{% else %}
{{ prev_integral }}
{% endif %}
output_final: |
{% if soc <= 20 %}
125
{% else %}
{{ clamped_output }}
{% endif %}
integral_final: |
{% if soc <= 20 %}
0
{% else %}
{{ integral_new }}
{% endif %}
mode: single
by HarryP: Code-/Logzeilen formatiert (bitte immer in </> einbinden)
s.a.: (Neues Update & Features - Hier in der Community 🫶)