Maximaler PV Eigenverbrauch der Wärmepumpe (2/2)
Meine Wärmepumpe war alles andere als Smart, bis ich sie mit einem ESP32 - Mikrocontroller verheiratet habe. Seit der Hochzeit wird sie von Home Assistant überwacht und über eine Automatisierung gesteuert. Kombiniert mit den Leistungsdaten der PV-Anlage ist Home Assistant in der Lage, den produzierten Strom möglichst direkt für das Heizen zu verwenden und somit den Eigenverbrauch zu steigern:
Die Automatisierung in Aktion: in Grün die PV-Produktion; in Gelb: der Stromverbrauch der Heizung.
Die Heizung wird möglichst nur zu Zeiten aktiviert, an denen die PV-Anlage liefert.
Estrich als Pufferspeicher
In meiner Heizung ist für die hydraulische Entkopplung ein kleiner Pufferspeicher mit 300 Liter verbaut. Betreibe ich die Umwälzpumpen für die Fußbodenheizung auf der kleinsten Stufe, ist der Speicher in ca. einer Stunde entladen. Dank der sehr trägen Fußbodenheizung kann die Heizung dennoch für einige Stunden deaktiviert werden, ohne die Raumtemperatur zu beeinflussen. Der Grund dafür ist, dass der Estrich ein enormes Speicherpotential besitzt. Idealerweise ist die Heizung nach Sonnenuntergang nicht mehr aktiv, wodurch die Batterie der PV-Anlage abends und in der Nacht rein für den Haushaltsstrom verwendet werden kann.
Umsetzung - Verfügbarkeit
Die benötigte Hardware für die hier vorgestellte Lösung ist günstig und funktioniert vom Prinzip her mit nahezu jeder Heizung. Ergänzend zum vorherigen Beitrag "Heizung steuern: PV Überschuss > ESP32 & Home Assistant" behandelt dieser Beitrag meine Umsetzung im Detail.
Da zusätzliche Komponenten zusätzliche Abhängigkeiten schaffen, habe ich bei der Umsetzung ein Augenmerk darauf gelegt, dass die Heizung bei einem möglichen Ausfall der zugebauten Komponenten normal weiter funktioniert. Sollte Home Assistant, das WLAN oder der ESP ausfallen, übernimmt die eigentliche Heizung wieder das Kommando.
Mir ist bewusst, dass meine Umsetzung auf meine Heizung zugeschnitten ist und daher wahrscheinlich nicht eins zu eins, oder nur teilweise für andere Projekte kopiert werden kann: dennoch hoffe ich, dass sich der ein oder andere ein paar Ideen holen kann.
Ziel: Wärmepumpe für Heizung und Warmwasser sowie Heizstab, abhängig vom PV-Überschuss steuern
Wie im vorherigen Betrag bereits erwähnt, handelt es sich bei meiner Heizung um eine alte Wärmepumpe mit Pufferspeicher und Boiler. Für das (Über)steuern meiner Heizung verwende ich Home Assistant und einen ESP32 mit einem 4-Kanal-Relay-Board und mehreren Temperatursensoren.
Die Heizung wird abhängig von der Raumtemperatur übersteuert: wenn die PV-Anlage gerade genügend Strom produziert und im Batteriespeicher genügend Kapazität vorhanden ist.
Zwar läuft der ESP super stabil, dennoch habe ich die Relais so verbaut, dass ein Ausfall des ESP die Relais fallen lässt und dadurch die Temperatursensoren der eigentlichen Heizung aktiviert werden: Die Heizung funktioniert im Fehlerfall wie zuvor. Zudem ist der ESP in der Lage, eigenständig bestimmte Aktionen auszulösen, auch wenn das WLAN oder Home Assistant nicht verfügbar sein sollten.
Zusätzlich zur Wärmepumpe ermöglicht eine Zigbee-Steckdose das Aktivieren eines Heizstabs im Boiler und somit höhere Warmwassertemperaturen. Um die Steckdose nicht zu überfordern, habe ich den Heizstab mit nur 2kW angeschlossen: funktionell zumindest für die Warmwasseraufbereitung ein Low-Cost Ersatz für den Fronius Ohmpilot.
Mein Setup kombiniert die Informationen der folgenden Artikel:
- Hardware für Home Assistant? Varianten: HAOS vs. Docker
- Home-Assistant + DIY Mikrocontroller + ESP Home (Docker)
- Heizung steuern: PV Überschuss > ESP32 & Home Assistant
- PV Überschuss, Warmwasser: Heizstab
Das Herzstück: ESP-Home Projekt
Der ESP32 Mikrocontroller integriert sich nicht direkt in die Steuerung der Heizung, auch ersetzt er diese nicht. Ich habe den ESP32-Mikrocontroller vielmehr um die bestehende Steuerung herumgebaut: mit eigenen Sensoren und eigenen Relais für das Manipulieren der bestehenden Heizungssteuerung. Vom ESP32 für die Heizungs(über)steuerung führen dutzende Kabel zu mehreren Temperatursensoren: verbaut an den unterschiedlichsten Stellen der Heizung und des Hauses. Noch fehlt mir der Mut ein Bild zur Verkabelung zu posten, da diese aktuell mehr einem Testaufbau gleicht, daher habe ich diese schematisch dargestellt:
Hier ein grober Überblick der wesentlichen Komponenten:
- DS18B20 Temperatursensoren für die Heizung und Außentemperatur, siehe: DS18B20 - Temperatur-Sensoren in ESP-Home
verbaute DS18B20 Temperatursensoren:
- direkt in der Wärmepumpe im Wärmetauscher
- Boiler
- Pufferspeicher
- Vorlauftemperatur
- Rücklauftemperatur
- Außentemperatur
- ...
- DHT11-Temperatur-Sensoren für bestimmte Wohnräume: ESPHome: Temperatur- und Feuchtigkeitssensoren DHT11/22
- Relay-Board für das Steuern der Heizung: Relay Board ESP32 - ESPHome
- Relay1: Warmwassertemperatur vortäuschen: ohne Relay2: warmes Wasser
- Relay2: Warmwasser kalt vortäuschen
- Relay3: Pufferspeichertemperatur vortäuschen: ohne Relay4: warmer Pufferspeicher
- Relay4: Pufferspeicher kalt vortäuschen
Das komplette ESP-Home-Projekt für den ESP schaut wie folgt aus:
esphome:
name: heating
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "???"
ota:
password: "???"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Heating Fallback Hotspot"
password: "???"
on_disconnect:
if:
condition:
switch.is_on: relay4
then:
- switch.turn_off: relay3
- switch.turn_off: relay4
captive_portal:
dallas:
- pin: GPIO13
update_interval: 22s
# Relais
switch:
- platform: gpio
pin: GPIO32
name: Relay-Heizung-Wasser-fake
id: relay1
inverted: true
- platform: gpio
pin: GPIO33
name: Relay-Heizung-Wasser-kalt
id: relay2
inverted: true
- platform: gpio
pin: GPIO25
name: Relay-Heizung-Puffer-fake
id: relay3
inverted: true
- platform: gpio
pin: GPIO26
name: Relay-Heizung-Puffer-kalt
id: relay4
inverted: true
# Individual sensors
sensor:
- platform: uptime
name: heating.uptime
- platform: dht
pin: GPIO23
model: DHT11
temperature:
name: "Kueche.Temperature"
id: kueche_temperature
humidity:
name: "Kueche.Humidity"
- platform: dht
pin: GPIO1
model: DHT11
temperature:
name: "Wohnzimmer.Temperature"
id: wohnzimmer_temperature
humidity:
name: "Wohnzimmer.Humidity"
- platform: dht
pin: GPIO21
model: DHT11
temperature:
name: "Buero.Temperature"
id: buero_temperature
humidity:
name: "Buero.Humidity"
- platform: dht
pin: GPIO19
model: DHT11
temperature:
name: "Vorhaus.Temperature"
id: vorhaus_temperature
humidity:
name: "Vorhaus.Humidity"
- platform: dht
pin: GPIO3
model: DHT11
temperature:
name: "KloEG.Temperature"
id: kloeg_temperature
humidity:
name: "KloEG.Humidity"
- platform: dallas
address: 0x??
name: "heating.Vorlauf"
- platform: dallas
address: 0x??
name: "heating.Ruecklauf"
- platform: dallas
address: 0x??
name: "heating.Waermetauscher"
on_value_range:
- above: 60.0
then:
- switch.turn_off: relay1
- switch.turn_off: relay2
- switch.turn_off: relay3
- switch.turn_off: relay4
- platform: dallas
address: 0x??
name: "heating.Warmwasser"
on_value_range:
- below: 40.0
then:
- switch.turn_off: relay1
- switch.turn_off: relay2
- above: 48
then:
- switch.turn_on: relay1
- switch.turn_off: relay2
- platform: dallas
address: 0x??
name: "heating.Pufferspeicher"
- platform: dallas
address: 0x??
name: "heating.Pufferspeicher2"
- platform: dallas
address: 0x??
name: "aussen.temperature"
- platform: dallas
address: 0x??
name: "heating.PufferVorlauf"
- platform: dallas
address: 0x??
name: "heating.BoilerVorlauf"
- platform: dallas
address: 0x??
name: "heating.BoilerPufferRuecklauf"
- platform: combination
type: median
name: "EG.Temperatur"
sources:
- source: kueche_temperature
- source: wohnzimmer_temperature
- source: buero_temperature
- source: kloeg_temperature
- source: vorhaus_temperature
Wie aus dem Projekt ersichtlich ist, habe ich bestimmte Aktionen direkt über Trigger des ESP32 umgesetzt. Damit ist der ESP32 in der Lage beim Ausfall des WLANs, oder falls Home Assistant einmal nicht verfügbar ist, die Kontrolle an die eigentliche Steuerung der Heizung zu übergeben. Als Beispiel werden alle Relais deaktiviert, wenn der Wärmetauscher 60 °C erreicht, das Warmwasser seine Zieltemperatur erreicht, oder einen bestimmten Wert unterschreitet.
Eine Temperatur von über 60 °C im Wärmetauscher tritt bei meiner Anlage im Normalbetrieb nicht auf und falls diese erreicht wird, schaltet der ESP32 und die Heizung übernimmt mit deren eigener Temperatursensoren die Kontrolle. Selbiges gilt für das Warmwasser: Die Anlage kann nicht mehr als 48 °C für das Warmwasser und ab dem Erreichen dieser Temperatur, werden die Relais deaktiviert und die Wärmepumpe nicht weiter mit falschen Temperaturwerten getäuscht. Zudem gibt der ESP32 die Kontrolle wieder an die Heizung zurück, wenn das Wasser unter 40 °C fällt: Eine Art Notfall, damit das Wasser nicht ganz kalt wird.
Falls die Heizung über den vorgetäuschten Temperaturwert des Relais für den Pufferspeicher aktiviert wurde und die WLAN-Verbindung verloren geht, lässt der ESP ebenfalls die Heizungsrelais fallen.
Die Temperaturwerte der Wohnräume werden über einen "Combination-Sensor" vom Typ Median zu einem Wert kombiniert und dienen als Basis für die in Home Assistant umgesetzte Heizungsübersteuerung. (Der Median kann im Gegensatz zum Mittelwert besser mit einzelnen Ausreißern umgehen)
Home Assistant Automatisierung
Damit die HA-Automatisierung besser auf einen PV-Überschuss reagieren kann, habe ich einen eigenen Template-Sensor-Helfer erstellt:
{% set remaining_load = (states("sensor.byd_battery_box_premium_hv_maximale_kapazitat") | default(0) | float *
(1-((states("sensor.byd_battery_box_premium_hv_ladezustand") | default(0) |float))/100))/1000 %}
{% set before_onehourbeforesunset= as_timestamp(now()) | timestamp_custom('%H%M') | float < (states.sun.sun.attributes.next_setting |as_timestamp - (60*60))| timestamp_custom("%H%M", True) | float %}
{% set pvsumkw = states("sensor.pv_sum_kw") | default(0) | float %}
{% set bat_gt_2 = states("sensor.byd_available_capacity") | default(0) | float > 2 %}
{{ states("sensor.pv_remaining_today") | default(0) | float > (remaining_load+3)
and before_onehourbeforesunset and pvsumkw > 1 and bat_gt_2 }}
Der Templatesensor wechselt auf eingeschaltet, wenn über 1kW von der PV-Anlage produziert werden, der Füllstand der PV-Batterie mindestens 2 kWh beträgt und die PV-Prognose für den verbleibenden Tag größer als die restliche Kapazität des Akkus ist. Ok, ich merke schon: Der Satz ist schwer zu verdauen, vielleicht hilft ein Beispiel weiter: Angenommen der 10 kWh Akku ist zu 50 % geladen, fehlen noch 5 kWh bis dieser voll ist. Ist die PV-Vorhersage für den verbleibenden Tag größer als 5 kWh, löst das Template aus: Es besteht ein potenzieller Überschuss und die Zieltemperatur der Räume wird über die folgende Automatisierung angehoben. Damit kann die Heizung bereits aktiviert werden, bevor der Akku zu 100 % geladen ist. Und sollte die PV-Vorhersage stimmen, wird der Akku an diesem Tag dennoch später noch voll. Nachdem die PV-Vorhersage der Home Assistant Integration: "Forecast.Solar" nur jede Stunde aktualisiert wird, addiert das Template an dieser Stelle sicherheitshalber 3 kWh (remaining_load+3).
Aufbauend auf dieses Template, den Scripts für die Relays und der bereits umgesetzten Logik des ESP, lässt sich die HA-Automatisierung etwas vereinfachen, hier der komplette YAML-Code:
description: ""
mode: parallel
trigger:
- platform: time_pattern
minutes: /30
- platform: numeric_state
entity_id:
- sensor.pv_sum
for:
hours: 0
minutes: 10
seconds: 0
below: 3500
- platform: numeric_state
entity_id:
- sensor.byd_battery_box_premium_hv_ladezustand
above: 70
- platform: state
entity_id:
- sensor.pv
- platform: numeric_state
entity_id:
- sensor.grid_power_kw
for:
hours: 0
minutes: 1
seconds: 0
above: 0
- platform: numeric_state
entity_id:
- sensor.grid_power_kw
for:
hours: 0
minutes: 2
seconds: 0
below: -2
- platform: numeric_state
entity_id:
- sensor.heating_warmwasser
above: 60
- platform: numeric_state
entity_id:
- sensor.heating_warmwasser
below: 43
- platform: numeric_state
entity_id:
- sensor.grid_power_kw
for:
hours: 0
minutes: 1
seconds: 0
above: 0
- platform: numeric_state
entity_id:
- sensor.grid_power_kw
for:
hours: 0
minutes: 2
seconds: 0
below: -2
condition: []
action:
- alias: Warmwasser automatisch
if:
- condition: time
after: "13:00:00"
- condition: time
before: "23:00:00"
- condition: numeric_state
entity_id: sensor.heating_warmwasser
below: 43
then:
- service: script.warmwasser_automatisch
data: {}
- alias: Warmwasser ein
if:
- condition: state
entity_id: sensor.pv
state: "True"
- condition: state
entity_id: switch.relay_heizung_wasser_kalt
state: "off"
- condition: numeric_state
entity_id: sensor.heating_warmwasser
below: 44
then:
- service: script.warmwasser_ein
data: {}
- alias: Warmwasser Heizstab
if:
- condition: or
conditions:
- alias: grid_power_kw < -2
condition: template
value_template: >-
{{( states("sensor.grid_power_kw") | float -
(states("sensor.warmwasser_active_power") | default(0) | float /
1000)) < - 2 }}
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.byd_battery_box_premium_hv_ladezustand
above: 99
- alias: grid_power_kw < -0.5
condition: template
value_template: >-
{{( states("sensor.grid_power_kw") | float -
(states("sensor.warmwasser_active_power") | default(0) | float
/ 1000)) < - 0.5 }}
alias: Battery full & Überschuss
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.pv_remaining_today
above: 50
- condition: numeric_state
entity_id: sensor.pv_sum_kw
above: 6
alias: remaining > 50 & pv_sum > 6
- condition: numeric_state
entity_id: sensor.heating_warmwasser2
above: 44
below: 60
- condition: state
entity_id: switch.relay_heizung_wasser_kalt
state: "off"
then:
- type: turn_on
device_id: 0a9f1a5a5a02fbf55cc577ac6c882b35
entity_id: switch.warmwasser_switch
domain: switch
- service: script.warmwasser_aus
metadata: {}
data: {}
else:
- type: turn_off
device_id: 0a9f1a5a5a02fbf55cc577ac6c882b35
entity_id: d31c0573e9c9d10aa19826e6a7631057
domain: switch
- alias: Puffer
if:
- condition: or
conditions:
- condition: numeric_state
entity_id: sensor.aussen_temperature
above: 22
- alias: vor 2:30 -> 21°C
condition: and
conditions:
- condition: or
conditions:
- condition: time
after: "21:00:00"
enabled: true
- condition: time
before: "02:30:00"
- condition: numeric_state
entity_id: sensor.eg_temperatur
above: 20.99
- alias: vor 3:30 -> 21.3°C
condition: and
conditions:
- condition: time
before: "03:30:00"
- condition: numeric_state
entity_id: sensor.eg_temperatur
above: 21.3
- alias: 5:00 - 2h nach Sonnenaufgang und PV Forecast > 25kWh -> 21.3°C
condition: and
conditions:
- condition: numeric_state
entity_id: sensor.pv_remaining_today
above: 25
- condition: numeric_state
entity_id: sensor.eg_temperatur
above: 21.3
- condition: time
after: "05:00:00"
- condition: sun
before: sunrise
before_offset: "2:00:00"
- condition: state
entity_id: sensor.pv
state: "False"
- condition: state
entity_id: input_boolean.pv_snow
state: "off"
- condition: and
conditions:
- condition: state
entity_id: sensor.pv
state: "False"
- condition: numeric_state
entity_id: sensor.eg_temperatur
above: 21.99
alias: PV- 21.99°C
- condition: numeric_state
entity_id: sensor.eg_temperatur
above: 22.99
then:
- if:
- alias: >-
Heizung kürzlich erst aktiviert? warte 60 Minuten mit dem
Ausschalten
condition: template
value_template: |-
{% set timeSinceStateChanged = ((as_timestamp(now()) -
as_timestamp(states.sensor.heating.last_changed)) / 60) | int %}
{{
false if (states("sensor.heating") == "Heizen" and
timeSinceStateChanged < 30)
else true
}}
- alias: Warte mit dem Ausschalten wenn Batterie lädt
condition: and
conditions:
- condition: or
conditions:
- condition: numeric_state
entity_id: sensor.byd_power_kw
below: 0.5
- condition: numeric_state
entity_id: sensor.byd_available_capacity
below: 1.5
then:
- service: script.pufferspeicher_aus
data: {}
else:
- if:
- condition: numeric_state
entity_id: sensor.pv_sum
above: 3500
- device_id: ""
domain: ""
entity_id: ""
condition: device
- alias: warte mind. 1h mit dem Wiedereinschalten
condition: template
value_template: >-
{% set timeSinceStateChanged = ((as_timestamp(now()) -
as_timestamp(states.binary_sensor.heating_active.last_changed)) /
60) | int %} {{ false if (states("sensor.heating") != "Heizen" and
timeSinceStateChanged < 60) else true }}
then:
- service: script.pufferspeicher_heizen
data: {}
else:
- service: script.pufferspeicher_automatisch
data: {}
trace:
stored_traces: 100
max: 10
Die Automatisierung wird alle 30 Minuten gestartet und für eine schnellere Reaktion zusätzlich beim Eintreten bestimmter Ereignisse. Die Aktionen setzten sich wie folgt zusammen:
Aktion: Warmwasser automatisch
Zwischen 13:00 und 23:00 Uhr und bei einer Warmwassertemperatur unter 43 °C soll die Wärmepumpe die Steuerung übernehmen: "Skript Warmwasser automatisch". Die Schwellwerte in der eigentlichen Heizungssteuerung werden unter 43 °C aktiv, daher startet die Warmwasseraufbereitung in der Regel beim Auslösen des Skripts.
Aktion: Warmwasser ein
Bei einem PV-Überschuss (PV+ Template-Sensor löst aus) und bei einer Warmwassertemperatur unter 44 °C, soll das Wasser durch das Vortäuschen einer niedrigen Boilertemperatur aktiviert werden: "Skript: Warmasser ein".
Das Ausschalten beim Erreichen der Zieltemperatur über 48 °C übernimmt der ESP und dessen Trigger.
Aktion: Warmwasser Heizstab
Bei einer Wassertemperatur von über 44 °C, einem vollgeladenen Akku und zusätzlichem PV-Überschuss ("sensor.grid_power_kw") wird der Heizstab aktiviert. Der Heizstab ist natürlich nicht so effizient wie die Wärmepumpe, ermöglicht aber das Erwärmen des Wassers auf Temperaturen jenseits des Bereichs der Wärmepumpe. Nachdem die Einspeisevergütung für einen Stromüberschuss aktuell sehr überschaubar ist, steigert das Anheben der Warmwassertemperatur die Chance, dass die Wärmepumpe abends nicht mehr aktiviert werden muss.
Aktion: Heizung
Die Aktion für die Heizung verwendet zunächst Bedingungen, bei denen die Heizung deaktiviert werden soll:
- Bei einer Außentemperatur von über 22 °C wird die Heizung deaktiviert.
- Abhängig von der Uhrzeit und der Raumtemperatur wird die Heizung beim Eintreten einer der folgenden Ereignisse deaktiviert:
- Nachts vor 2:30 Uhr, bei einer Raumtemperatur über 21 °C,
- vor 3:30 Uhr bei einer Raumtemperatur über 21,3 °C und
- je nach PV-Vorhersage über 22 °C
- Bei einem PV-Überschuss: Template PV+: wird die Heizung erst bei einer Raumtemperatur von über 23 °C deaktiviert.
- Abhängig vom Füllstand des Akkus wird die Heizung ab 70 % erzwungen. Unter 70 % Ladezustand sind beide Heizungsrelais deaktiviert und die eigentliche Heizungssteuerung entscheidet, ob die Heizung aktiviert werden soll.
In der Praxis - PV Eigenverbrauch
Hier ein Beispiel der Automatisierung Anfang März:
Die Automatisierung arbeitet erst dann so richtig, wenn die Sonne scheint: An Tagen an denen genügend PV-Strom erzeugt wird (in grün), kann die Heizung (in gelb) während des Tages gestartet werden. Bei einer niedrigen PV-Prognose erlaubt die Automatisierung bereits vor Sonnenaufgang eine Zieltemperatur von bis zu 22 °C, wodurch die Heizung bereits früher aktiviert wird. Hier ein weiteres Beispiel einiger Tage mit 100% Eigenverbrauch inkl. Batteriestatus:
Die Energieverteilung unseres Hauses für Anfang 2024:
Monat | Selbstversorgung |
Stromverbrauch: |
---|---|---|
Jänner |
Im Jänner hatte die Steuerung noch relativ wenig Effekt, da der produzierte PV-Strom bei weitem nicht reicht. |
|
Februar |
Der relativ milde Februar ermöglichte der Steuerung an sehr vielen Tagen den Großteil des PV-Stromes direkt mit der Wärmepumpe zu verbrauchen. |
|
März |
Im März entfaltete die Steuerung ihr volles Potential und konnte den Stromverbrauch zu 89 % mit dem PV-Strom decken. |
|
April |
Bereits im April konnte nahezu der gesamte Strombedarf mit PV-Strom gedeckt werden. |
|
Mai |
||
Juni |
Fazit
Zugegeben ist mein Setup weit von Plug-and-Play entfernt, auch funktionierte die Automatisierung erst nach einigem Tuning. Wer sich aber gerne mit automatischen Abläufen beschäftigt kann sich mit ESP-Home und Home Assistant ordentlich austoben und damit richtig Strom sparen.
{{percentage}} % positiv