SmartGrid-Steuerung an Wärmepumpe (hier: Dimplex LI 16I-TUR)

Ich bastel gerade an einer Lösung, wie ich das den PV-Überschuss für ein Überheizen des Warmwasserspeichers nutzen kann.

Die Automationen und Hardware sind fertig. jetzt fehlt noch der finale Anschluss durch den Heizungsbauer. Falls jemand entweder Inspiration sucht oder nochmal gegenlesen möchte, ob ich was übersehen habe: Bitteschön. :slight_smile:

Systemumgebung:

  • Zentrale Steuerung: Home Assistant OS (Core 2025.8.0) auf Proxmox-Host

  • Laufzeitumgebung: Proxmox LXC-Container

  • Kommunikation: Lokale Ansteuerung via MQTT und REST, keine Cloud-Abhängigkeit

  • Energie-Hardware: PV-Anlage mit SolarLog, Varta-Batteriesystem (Modbus-Anbindung), SmartMeter für Netzbezug/-einspeisung

  • Heizungsanbindung: Überschussheizen der Wärmepumpe Dimplex LI 16I-TUR über SG-Ready-Eingänge

  • Warmwasserspeicher: 300 Liter

  • Schaltkomponenten: 2 × Shelly Plus 1 Relais (SG1, SG2) für die SG-Ready-Stufen

Umgesetzte Logik:

  • Hilfsschalter für das Überschussheizen
    input_boolean.schalter_uberschussheizen

    • Trennung von Automations- und Schaltlogik

    • Automationen setzen nur den Hilfsschalter; eine separate Logik schaltet daraus die Relais.

    • Vorteil: Trennung von Entscheidungs- und Ausführungsebene, leichter zu debuggen.

  • Überwachung der Shellys

    • Automatische Deaktivierung, falls ein Shelly nicht erreichbar ist.
  • Zustandssynchronisation nach HA-Neustart

    • Stellt sicher, dass Relais und Hilfsschalter synchron sind.
  • Relaissteuerung anhand Helferzustand

    • Hilfsschalter „an“ → SG1 an, SG2 bleibt aus (derzeit nicht genutzt)

    • Hilfsschalter „aus“ → SG1 aus, SG2 bleibt aus

  • Überschussheizen EIN

    • Schaltet den Hilfsschalter nur, wenn seit dem letzten Ausschalten ≥ 15 min vergangen sind.

    • Vermeidet Sofort-Wiederanläufe bei kurzen PV-Spitzen.

    • Berücksichtigt PV-Überschuss, Batteriestatus u. a.

  • Überschussheizen AUS

    • Schaltet erst nach einer Mindestlaufzeit von 22 min.

    • Verhindert unnötiges Takten bei kurzen PV-Leistungseinbrüchen.

Anmerkungen:

  • SG2 wird aktuell (noch) nicht benötigt – ist aber vorbereitet.

  • FI im Verteilerkasten ist nur verbaut, weil er verfügbar war.

  • Leitungsschutzschalter dienen primär Wartungszwecken (Relais-Tausch ohne Raumabschaltung).

  • Einfachere Alternative wäre ein Anschluss per Schuko-Stecker – birgt aber das Risiko vertauschter L/N.

Hier die Automationen dazu:

alias: Shelly-Verfügbarkeit überwachen
description: Schalte beide SG-Relais aus, wenn mindestens einer nicht erreichbar ist
triggers:
  - entity_id:
      - switch.sg1
      - switch.sg2
    to: unavailable
    trigger: state
actions:
  - target:
      entity_id:
        - switch.sg1
        - switch.sg2
    action: switch.turn_off
    data: {}
  - data:
      title: Shelly-Ausfall
      message: >-
        Mindestens einer der Shellys ist nicht erreichbar – beide SG-Relais
        wurden ausgeschaltet.
    action: persistent_notification.create
mode: single
initial_state: true
alias: Steuerung SG1/SG2 bei Start
description: Synchronisiert SG1/SG2 bei Start entsprechend dem Helferzustand
triggers:
  - event: start
    trigger: homeassistant
actions:
  - target:
      entity_id: input_boolean.schalter_uberschussheizen
    action: homeassistant.update_entity
    data: {}
  - delay: "00:00:03"
  - target:
      entity_id: automation.steuerung_sg1_sg2_nach_helferzustand
    action: automation.trigger
    data:
      skip_condition: true
mode: single

alias: Überschussheizen EIN (setzt nur Helfer)
description: >-
  Aktiviert das Warmwasser nur bei PV-Überschuss am Tag und vollem Akku (ohne
  Tageslimit)
triggers:
  - entity_id: sensor.grid_power_out_varta
    above: 3000
    for:
      minutes: 5
    trigger: numeric_state
conditions:
  - condition: numeric_state
    entity_id: sensor.varta_ladestand_gefiltert
    above: 95
  - condition: state
    entity_id: input_boolean.schalter_uberschussheizen
    state: "off"
    for:
      hours: 0
      minutes: 15
      seconds: 0
actions:
  - target:
      entity_id: input_boolean.schalter_uberschussheizen
    action: input_boolean.turn_on
    data: {}
mode: single

alias: Überschussheizen AUS (schaltet nur Helfer)
description: "Stufe 1: SoC < 75% (2min) immer AUS. Stufe 2: SoC < 85% nur bei <500 W (5min)"
triggers:
  - id: soc75
    entity_id: sensor.varta_ladestand_gefiltert
    below: 75
    for:
      minutes: 2
    trigger: numeric_state
  - id: gridlow
    entity_id: sensor.grid_power_out_varta
    below: 500
    for:
      minutes: 5
    trigger: numeric_state
conditions:
  - condition: state
    entity_id: input_boolean.schalter_uberschussheizen
    state: "on"
    for:
      hours: 0
      minutes: 22
      seconds: 0
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: soc75
        sequence:
          - target:
              entity_id: input_boolean.schalter_uberschussheizen
            action: input_boolean.turn_off
            data: {}
      - conditions:
          - condition: trigger
            id: gridlow
          - condition: numeric_state
            entity_id: sensor.varta_ladestand_gefiltert
            below: 98
        sequence:
          - target:
              entity_id: input_boolean.schalter_uberschussheizen
            action: input_boolean.turn_off
            data: {}
  - stop: "Fertig: Überschussheizen ausgeschaltet"
initial_state: true
mode: single

alias: Steuerung SG1/SG2 nach Helferzustand
description: >-
  Setzt SG1/SG2 abhängig vom Helferzustand
  input_boolean.schalter_uberschussheizen
triggers:
  - entity_id:
      - input_boolean.schalter_uberschussheizen
    trigger: state
actions:
  - choose:
      - conditions:
          - condition: state
            entity_id: input_boolean.schalter_uberschussheizen
            state: "on"
        sequence:
          - target:
              entity_id: switch.sg1
            action: switch.turn_on
            data: {}
          - target:
              entity_id: switch.sg2
            action: switch.turn_off
            data: {}
      - conditions:
          - condition: state
            entity_id: input_boolean.schalter_uberschussheizen
            state: "off"
        sequence:
          - target:
              entity_id:
                - switch.sg1
                - switch.sg2
            action: switch.turn_off
            data: {}
mode: single
initial_state: true

Update AN- und AUS-Automationen angepasst.

An-Automation mit Träger alle 10 Minuten:

alias: Überschussheizen EIN (setzt nur Helfer)
description: >-
  Aktiviert das Warmwasser nur bei PV-Überschuss am Tag und vollem Akku (ohne
  Tageslimit). Mit Verzögerung zum letzten Ausschalten und maximal 3 Stunden am
  Tag.
triggers:
  - entity_id: sensor.grid_power_out_varta
    above: 3000
    for:
      minutes: 5
    trigger: numeric_state
  - trigger: time_pattern
    minutes: /10
conditions:
  - condition: state
    entity_id: sun.sun
    state: above_horizon
  - condition: numeric_state
    entity_id: sensor.varta_ladestand_gefiltert
    above: 95
  - condition: state
    entity_id: input_boolean.schalter_uberschussheizen
    state: "off"
    for:
      hours: 0
      minutes: 15
      seconds: 0
  - condition: template
    value_template: |
      {{ states('sensor.uberschussheizen_dauer_heute_h')|float(0) < 3 }}
actions:
  - target:
      entity_id: input_boolean.schalter_uberschussheizen
    action: input_boolean.turn_on
    data: {}
mode: single

Aus-Automation mit hartem aus bei SOC unter 75%:

alias: Überschussheizen AUS (schaltet nur Helfer)
description: >
  Stufe 1: SoC < 75% (2min) immer AUS. Stufe 2: SoC < 85% nur bei <500 W (5min).
  Stufe 3: >3h Tageslaufzeit => AUS.
triggers:
  - id: soc75
    entity_id: sensor.varta_ladestand_gefiltert
    below: 75
    for:
      minutes: 2
    trigger: numeric_state
  - id: gridlow
    entity_id: sensor.grid_power_out_varta
    below: 500
    for:
      minutes: 5
    trigger: numeric_state
  - id: maxruntime
    entity_id: sensor.uberschussheizen_dauer_heute_h
    above: 3
    trigger: numeric_state
conditions:
  - condition: state
    entity_id: input_boolean.schalter_uberschussheizen
    state: "on"
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: soc75
        sequence:
          - target:
              entity_id: input_boolean.schalter_uberschussheizen
            action: input_boolean.turn_off
            data: {}
      - conditions:
          - condition: trigger
            id: gridlow
          - condition: numeric_state
            entity_id: sensor.varta_ladestand_gefiltert
            below: 98
          - condition: state
            entity_id: input_boolean.schalter_uberschussheizen
            state: "on"
            for:
              minutes: 22
        sequence:
          - target:
              entity_id: input_boolean.schalter_uberschussheizen
            action: input_boolean.turn_off
            data: {}
      - conditions:
          - condition: trigger
            id: maxruntime
        sequence:
          - target:
              entity_id: input_boolean.schalter_uberschussheizen
            action: input_boolean.turn_off
            data: {}
  - stop: "Fertig: Überschussheizen ausgeschaltet"
initial_state: true
mode: single

Ergänzung und vollständige Snippets:

ich habe noch einen Urlaubsmodus und einen Failsafe eingebaut. Hier der komplette Beitrag noch einmal inklusive dieser Features:

Herausforderung

Die Dimplex LI 16I‑TUR soll per SGready über Home Assistant geschaltet werden: Normalbetrieb, PV‑Überschuss (Warmwasser “Überschussheizen”) und ein Urlaubsmodus mit abgesenkter Heizkurve.


Lösungsansatz

Ich setze zwei Shelly 1 Plus als SG‑Relais (SG1/SG2) ein. Home Assistant entscheidet anhand PV‑Netzeinspeisung und Batterieladestand, welcher Modus aktiv ist, begrenzt die Überschussdauer pro Tag und synchronisiert SG1/SG2 zum gewählten Modus. Die Struktur basiert auf einem input_select für den Modus, History‑Stat‑Sensor für die Tagesdauer, optionalem input_number für die maximale Überschusszeit und mehreren Automationen (Moduslogik, SG‑Relais‑Mapping, Failsafe).

Wichtige Punkte in der Umsetzung

  • Failsafe (beide Relais AUS):
    Fällt einer der Shellys aus oder wird „unavailable“, schalte SG1 und SG2 AUS. Dadurch wird der SGready‑Zustand dunkelgrün vermieden. Optional (oder ergänzend): Wärmepumpe so konfigurieren, dass dunkelgrün keine Wirkung hat (neutral).
  • Schaltverzögerungen gegen Taktung:
    Die Automationen enthalten Mindestlaufzeiten (z. B. Modus mindestens 15–22 Minuten stabil), bevor gewechselt wird.
  • Urlaubsmodus (SGready “rot” zweckentfremdet):
    „Rot“ wird als Urlaubsmodus genutzt. Neben dem Deaktivieren der Auto‑Überschusslogik wurden ergänzend in der Wärmepumpe abgesenkte Heiz‑ und Warmwassertemperaturen hinterlegt. Beim Verlassen des Urlaubsmodus werden die Normalwerte wiederhergestellt.
  • WP richtig konfigurieren:
    Ordne in der Dimplex‑Steuerung die SGready‑Zustände passend zu:
    Hellgrün = Normalbetrieb,
    Gelb = PV‑/WW‑Priorität (Überschussheizen),
    Rot = Urlaubsmodus (abgesenkte Kurven),
    Dunkelgrün = „neutral/ohne Wirkung“ (Failsafe).
    Prüfe die Belegung im Dimplex‑Handbuch deiner Anlage.

Bausteine

  • 2× Shelly 1 Plus (SG1/SG2)
  • input_select für den Modus (WP SmartGrid)
  • (Optional) input_number für max. Tagesdauer Überschussheizen
  • History‑Stat‑Sensor für Tagesdauer im Modus „Überschussheizen“
  • Sensoren für Netzeinspeisung & Akku‑SoC (bei mir via Varta/Modbus)
  • Automationen: Ein/Aus‑Logik, SG‑Relais‑Mapping, Urlaubsmodus, Failsafe

YAML zum Kopieren

  1. configuration.yaml – Modusauswahl
input_select:
  wp_smartgrid_mode:  # entity_id: input_select.wp_smartgrid_mode
    name: Wärmepumpe SmartGrid Modus
    icon: mdi:traffic-light
    options:
      - Überschussheizen
      - Normalbetrieb
      - Urlaubsmodus
    initial: Normalbetrieb

  1. input_number.yaml – optionale Maximaldauer
input_number:
  ueberschussheizen_max_tagesdauer_min:  # entity_id: input_number.ueberschussheizen_max_tagesdauer_min
    name: Maximale Tagesdauer Überschussheizen
    min: 0
    max: 600
    step: 10
    unit_of_measurement: "min"
    mode: box
    icon: mdi:timer-outline
    initial: 120

  1. sensors.yaml – Tagesdauer „Überschussheizen“
- platform: history_stats
  name: "Überschussheizen Dauer heute (h)"
  unique_id: ueberschussheizen_dauer_heute_h
  entity_id: input_select.wp_smartgrid_mode  # -> eigene Entität hier einfügen
  state: 'Überschussheizen'
  type: time
  start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
  end: "{{ now() }}"

  1. automations.yaml – Logik inkl. Failsafe und Urlaubsmodus
# =======================================
#  ÜBERSCHUSSHEIZEN EIN  (setzt nur Helfer)
# =======================================
- id: ueberschussheizen_ein
  alias: Überschussheizen EIN (setzt nur Helfer)
  description: Aktiviert Warmwasser nur bei PV-Überschuss und hohem Akku-SoC.
  trigger:
    - platform: numeric_state
      entity_id: sensor.grid_power_export          # -> eigene Entität hier einfügen
      above: 3000
      for: { minutes: 5 }
  condition:
    - condition: state
      entity_id: input_select.wp_smartgrid_mode
      state: Normalbetrieb
      for: { minutes: 15 }
    - condition: numeric_state
      entity_id: sensor.battery_soc                # -> eigene Entität hier einfügen
      above: 95
    - condition: template
      value_template: >
        {{ states('sensor.ueberschussheizen_dauer_heute_h')|float(0)
           < ( states('input_number.ueberschussheizen_max_tagesdauer_min')|float(180) / 60 ) }}
  action:
    - service: input_select.select_option
      target: { entity_id: input_select.wp_smartgrid_mode }
      data: { option: Überschussheizen }
  mode: single
  initial_state: true

# =======================================
#  ÜBERSCHUSSHEIZEN AUS  (schaltet nur Helfer)
# =======================================
- id: ueberschussheizen_aus
  alias: Überschussheizen AUS (schaltet nur Helfer)
  description: Schaltet Überschussheizen nach Bedingungen aus.
  trigger:
    - id: soc_low
      platform: numeric_state
      entity_id: sensor.battery_soc                # -> eigene Entität hier einfügen
      below: 75
      for: { minutes: 2 }
    - id: grid_low
      platform: numeric_state
      entity_id: sensor.grid_power_export          # -> eigene Entität hier einfügen
      below: 500
      for: { minutes: 5 }
    - id: max_runtime
      platform: template
      value_template: >
        {{ states('sensor.ueberschussheizen_dauer_heute_h')|float(0)
           >= ( states('input_number.ueberschussheizen_max_tagesdauer_min')|float(180) / 60 ) }}
  condition:
    - condition: state
      entity_id: input_select.wp_smartgrid_mode
      state: Überschussheizen
  action:
    - choose:
        - conditions: [{ condition: trigger, id: soc_low }]
          sequence:
            - service: input_select.select_option
              target: { entity_id: input_select.wp_smartgrid_mode }
              data: { option: Normalbetrieb }
        - conditions:
            - condition: trigger
              id: grid_low
            - condition: state
              entity_id: input_select.wp_smartgrid_mode
              state: Überschussheizen
              for: { minutes: 22 }
          sequence:
            - service: input_select.select_option
              target: { entity_id: input_select.wp_smartgrid_mode }
              data: { option: Normalbetrieb }
        - conditions: [{ condition: trigger, id: max_runtime }]
          sequence:
            - service: input_select.select_option
              target: { entity_id: input_select.wp_smartgrid_mode }
              data: { option: Normalbetrieb }
  mode: single
  initial_state: true

# =======================================
#  SG-RELAIS-MAPPING NACH MODUS (SG1/SG2)
# =======================================
- id: sgready_steuerung
  alias: Steuerung SG1/SG2 nach Modus
  trigger:
    - platform: state
      entity_id: input_select.wp_smartgrid_mode
  action:
    - choose:
        - conditions:
            - condition: state
              entity_id: input_select.wp_smartgrid_mode
              state: Überschussheizen
          sequence:
            - service: switch.turn_on
              target: { entity_id: switch.sg1 }      # -> eigene Entität hier einfügen
            - service: switch.turn_off
              target: { entity_id: switch.sg2 }      # -> eigene Entität hier einfügen
        - conditions:
            - condition: state
              entity_id: input_select.wp_smartgrid_mode
              state: Normalbetrieb
          sequence:
            - service: switch.turn_off
              target:
                entity_id:
                  - switch.sg1                       # -> eigene Entität hier einfügen
                  - switch.sg2                       # -> eigene Entität hier einfügen
        - conditions:
            - condition: state
              entity_id: input_select.wp_smartgrid_mode
              state: Urlaubsmodus
          sequence:
            - service: switch.turn_off
              target: { entity_id: switch.sg1 }      # -> eigene Entität hier einfügen
            - service: switch.turn_on
              target: { entity_id: switch.sg2 }      # -> eigene Entität hier einfügen
  mode: single
  initial_state: true

# ===================================================
#  URLAUBSMODUS: AUTO-LOGIK AUS + ABGESENKTE KURVEN
# ===================================================
- id: urlaubsmodus_aktiv_auto_aus_abgesenkt
  alias: Urlaubsmodus aktiv: Auto-Überschussheizen AUS + abgesenkt
  trigger:
    - platform: state
      entity_id: input_select.wp_smartgrid_mode
      to: "Urlaubsmodus"
  action:
    - service: automation.turn_off
      target:
        entity_id:
          - automation.ueberschussheizen_ein
          - automation.ueberschussheizen_aus
    - choose:
        - conditions:
            - condition: state
              entity_id: climate.wp_heating          # -> eigene Climate-Entität
              state: "heat"
          sequence:
            - service: climate.set_preset_mode
              target: { entity_id: climate.wp_heating }
              data: { preset_mode: eco }             # -> passendes Preset
    - service: water_heater.set_temperature
      target: { entity_id: water_heater.wp_dhw }     # -> eigene WW-Entität
      data: { temperature: 45 }                      # -> Urlaubstemperatur
  mode: single
  initial_state: true

- id: urlaubsmodus_beendet_auto_an_normal
  alias: Urlaubsmodus beendet: Auto-Überschussheizen EIN + normal
  trigger:
    - platform: state
      entity_id: input_select.wp_smartgrid_mode
      from: "Urlaubsmodus"
      to: ["Normalbetrieb","Überschussheizen"]
  action:
    - service: automation.turn_on
      target:
        entity_id:
          - automation.ueberschussheizen_ein
          - automation.ueberschussheizen_aus
    - choose:
        - conditions:
            - condition: state
              entity_id: climate.wp_heating
              state: "heat"
          sequence:
            - service: climate.set_preset_mode
              target: { entity_id: climate.wp_heating }
              data: { preset_mode: none }            # -> z. B. comfort/none
    - service: water_heater.set_temperature
      target: { entity_id: water_heater.wp_dhw }
      data: { temperature: 50 }                      # -> Normaltemperatur
  mode: single
  initial_state: true

# ===========================
#  START-SYNC SG1/SG2
# ===========================
- id: sgready_sync_bei_start
  alias: Steuerung SG1/SG2 bei Start
  trigger:
    - platform: homeassistant
      event: start
  action:
    - delay: "00:00:10"
    - service: automation.trigger
      data: { skip_condition: true }
      target: { entity_id: automation.sgready_steuerung }
  mode: single
  initial_state: true

# ===========================
#  FAILSAFE: BEIDE AUS
# ===========================
- id: shelly_failsafe_beide_aus
  alias: Shelly-Verfügbarkeit überwachen (Failsafe: beide AUS)
  trigger:
    - platform: state
      entity_id:
        - switch.sg1                                   # -> eigene Entität
        - switch.sg2                                   # -> eigene Entität
      to: unavailable
  action:
    - service: switch.turn_off
      target:
        entity_id:
          - switch.sg1
          - switch.sg2
    - service: persistent_notification.create
      data:
        title: "Shelly-Ausfall"
        message: "Mindestens einer der Shellys ist nicht erreichbar – beide SG-Relais wurden ausgeschaltet (Failsafe)."
  mode: single
  initial_state: true
1 „Gefällt mir“