Schlagwort-Archiv: Bosch

Intelligentes Energiemanagement für Bosch Wärmepumpe mit Home Assistant

In diesem Beitrag teile ich meine Erfahrungen und die Umsetzung eines Energy Managers in Home Assistant (HA) für meine Bosch Wärmepumpe Compress 5800i AW 12 ORE‑T in Kombination mit einer PV‑Anlage und einer Tesla Powerwall.

🎉Energiemanagement – Motivation

Bosch bietet einen kostenpflichtigen Energiemanager an, der die Wärmepumpe abhängig vom PV-Überschuss steuert:

Viel PV-Strom-Überschuss → Wärmepumpe läuft intensiver
Kein PV-Überschuss → Wärmepumpe im Normalmodus

Ich wollte das in Home Assistant selbst umsetzen: Ein umfassendes System, das PV-Überschuss, Hausverbrauch und Powerwall-Ladestatus intelligent auswertet und steuert. Dabei sollten auch Temperaturbedingungen berücksichtigt werden. Das System sollte flexibel über Schalter und Zahlen steuerbar sein und automatisch die Wärmepumpe je nach Restüberschuss regeln.

🧰Ausgangslage

Seit etwa einem Jahr habe ich die Bosch Wärmepumpe und eine PV-Anlage installiert. Die PV-Anlage und die Tesla Powerwall ließen sich problemlos in HA integrieren (z.B. mit der „SMA Solar“ und der “Tesla Powerwall” Integration). Die Wärmepumpe von Bosch war allerdings schwieriger: Bosch stellt keine offizielle Schnittstelle für die über 150 Entitäten der Wärmepumpe zur Verfügung.

👑Lösung: EMS-Bus-Gateway von BBQKees Electronics

Nach längerer Recherche bin ich auf das EMS-Bus-Gateway von BBQKees Electronics gestoßen. Dieses Gateway bietet eine umfangreiche Knowledge Base und lässt sich gut über Matter in HA integrieren. So habe ich endlich Zugriff auf die Wärmepumpen-Daten bekommen.

📟Dashboard

Mein erstes Ziel war ein Dashboard mit den wichtigsten Wärmepumpen-Daten. Dabei hat mir die Webseite bosch-buderus-wp.github.io sehr geholfen.

Hier der zugehörige Auszug aus der RAW-Konfigurationsdatei:

YAML
  - type: custom:grid-layout
    path: waermepumpe
    title: Wärmepumpe
    theme: Nordic
    cards:
      - square: false
        type: grid
        cards:
          - square: false
            type: grid
            cards:
              - graph: line
                type: sensor
                entity: sensor.boiler_burner_current_power
                detail: 1
                name: Modulation
                icon: mdi:fan
              - graph: line
                type: sensor
                entity: sensor.boiler_current_flow_temperature
                detail: 1
                name: Vorlauf
                icon: mdi:thermometer-lines
              - graph: line
                type: sensor
                entity: sensor.boiler_return_temperature
                detail: 1
                name: Rücklauf
                icon: mdi:thermometer-lines
              - graph: line
                type: sensor
                entity: sensor.shellyhtg3_543204575c94_temperature
                detail: 1
                name: Innentemperatur
                icon: mdi:home-thermometer-outline
              - graph: line
                type: sensor
                entity: sensor.boiler_air_inlet_temperature_tl2
                detail: 1
                name: Außentemperatur
                icon: mdi:home-thermometer-outline
                limits:
                  min: 0
                  max: 10
              - type: gauge
                unit: kW
                name: WP Gesamt Extern
                needle: true
                grid_options:
                  columns: 6
                  rows: auto
                severity:
                  green: 0
                  yellow: 10
                  red: 25
                entity: sensor.warmepumpen_verbrauch_gesamt_taglich
                max: 40
              - type: gauge
                entity: sensor.ems_verbrauchte_energie_taglich
                unit: kW
                name: WP Heizung
                min: 0
                needle: true
                grid_options:
                  columns: 6
                  rows: auto
                severity:
                  green: 0
                  yellow: 10
                  red: 25
                max: 40
              - type: gauge
                entity: sensor.ems_erzeugte_energie_taglich
                unit: kW
                min: 0
                name: Energieabgabe
                needle: true
                max: 100
                severity:
                  green: 0
                  yellow: 100
                  red: 100
              - type: gauge
                entity: sensor.warmepumpe_cop
                min: 0
                name: COP
                needle: true
                unit: ' '
                max: 20
              - type: gauge
                entity: sensor.starts_pro_kwh
                min: 0
                name: Starts pro kWh
                severity:
                  green: 0
                  yellow: 5
                  red: 8
                max: 10
            columns: 5
        columns: 1
      - type: history-graph
        entities:
          - entity: sensor.boiler_compressor_activity
            name: ' '
        hours_to_show: 24
        grid_options:
          columns: full
          rows: auto
        logarithmic_scale: false
        title: Heute
      - chart_type: bar
        period: hour
        type: statistics-graph
        entities:
          - sensor.ems_erzeugte_energie_taglich
          - sensor.ems_verbrauchte_energie_taglich
        stat_types:
          - change
        days_to_show: 1
    visible:
      - user: 9c3a30d6645649c386acf6485e8d39e3
    badges:
      - type: entity
        show_name: true
        show_state: true
        show_icon: true
        entity: sensor.boiler_compressor_activity
        name: Heizen
        icon: mdi:heat-pump
      - type: entity
        show_name: true
        show_state: true
        show_icon: true
        entity: select.boiler_aux_heater_mode
        name: Modus
        icon: mdi:auto-mode
    icon: mdi:heat-pump
    subview: true
  - type: sections
    max_columns: 3
    title: System
    path: system
    icon: mdi:archive-cog
    sections:
      - type: grid
        cards:
          - type: heading
            heading: System
            heading_style: title
          - type: tile
            entity: update.home_assistant_operating_system_update
            features_position: bottom
            vertical: false
            name: Operating System
          - type: tile
            entity: sensor.home_assistant_operating_system_newest_version
            features_position: bottom
            vertical: false
            name: Neueste Version
            icon: mdi:cogs
          - type: tile
            features_position: bottom
            vertical: false
            color: yellow
            tap_action:
              action: toggle
            entity: button.ignore_all_issues
            name: Fehler löschen
          - type: tile
            entity: sensor.active_issues
          - type: tile
            entity: sensor.ignored_issues
          - type: tile
            entity: sensor.issues
          - type: tile
            features_position: bottom
            vertical: false
            name: 'Neu starten '
            color: red
            tap_action:
              action: toggle
            entity: button.homeassistant_restart
          - type: tile
            features_position: bottom
            vertical: false
            entity: button.homeassistant_reload
            name: 'Neu laden '
            color: red
            tap_action:
              action: toggle
      - type: grid
        cards:
          - type: heading
            heading: FRITZ!Box
            heading_style: title
          - type: tile
            entity: update.fritz_box_7530_fritz_os
            features_position: bottom
            vertical: false
            name: Firmware
          - type: tile
            entity: binary_sensor.fritz_box_7530_verbindung
            features_position: bottom
            vertical: false
            name: Verbunden
          - type: tile
            entity: sensor.fritz_box_7530_link_download_durchsatz
            features_position: bottom
            vertical: false
            name: Download Rate
          - type: tile
            entity: sensor.fritz_box_7530_link_upload_durchsatz
            features_position: bottom
            vertical: false
            name: Upload Rate
          - type: tile
            features_position: bottom
            vertical: false
            name: 'Neu starten '
            color: red
            tap_action:
              action: toggle
            entity: button.fritz_box_7530_neu_starten
          - type: tile
            features_position: bottom
            vertical: false
            entity: button.fritz_box_7530_neu_verbinden
            name: 'Neu verbinden '
            color: red
            tap_action:
              action: toggle
          - type: tile
            features_position: bottom
            vertical: false
            color: yellow
            tap_action:
              action: toggle
            entity: button.fritz_box_7530_firmware_update
            name: 'Firmware Update '
          - type: tile
            features_position: bottom
            vertical: false
            color: yellow
            tap_action:
              action: toggle
            entity: button.fritz_box_7530_aufraumen
            name: 'Aufräumen '
      - type: grid
        cards:
          - type: heading
            icon: mdi:fridge
            heading: NAS-Server
            heading_style: title
          - type: tile
            entity: update.diskstation_dsm_update
            features_position: bottom
            vertical: false
            name: DSM Update
          - type: tile
            entity: sensor.diskstation_temperatur
            features_position: bottom
            vertical: false
            name: Temperatur
          - type: tile
            features_position: bottom
            vertical: false
            color: red
            tap_action:
              action: toggle
            entity: button.diskstation_reboot
            name: Reboot
          - type: tile
            features_position: bottom
            vertical: false
            color: red
            tap_action:
              action: toggle
            entity: button.diskstation_shutdown
            name: Shutdown
          - type: gauge
            entity: sensor.diskstation_volume_1_belegter_speicherplatz
            max: 2.7
            name: Belegter Speicherplatz V1
            theme: Github Dark Theme
            grid_options:
              columns: 6
              rows: auto
            severity:
              green: 0
              yellow: 2
              red: 2.4
          - type: gauge
            entity: sensor.diskstation_volume_2_belegter_speicherplatz
            max: 2.7
            name: Belegter Speicherplatz V2
            grid_options:
              columns: 6
              rows: auto
            severity:
              green: 0
              yellow: 2
              red: 2.4
      - type: grid
        cards:
          - type: heading
            heading_style: title
            heading: Kamerabilder
          - show_name: true
            show_icon: true
            type: button
            name: Snapshots löschen
            icon: mdi:delete
            tap_action:
              action: call-service
              service: automation.trigger
              service_data:
                entity_id: automation.snapshots_sofort_loschen
            grid_options:
              columns: 12
              rows: 2
          - type: heading
            heading_style: title
            heading: Lichtsteuerung Urlaub
            grid_options:
              columns: 6
              rows: 1
          - type: markdown
            content: |
              **Status:** {{ states('automation.zeitsteuerung_flur_zufall') }}
            text_only: true
            grid_options:
              columns: 12
              rows: auto
          - show_name: true
            show_icon: true
            type: button
            name: Automationen einschalten
            icon: mdi:power-cycle
            tap_action:
              action: perform-action
              target:
                entity_id:
                  - automation.zeitsteuerung_lichterkette_garten
                  - automation.zeitsteuerung_schlafzimmer
                  - automation.zufall_steuerung_spitzboden
                  - automation.zeitsteuerung_flur_zufall
                  - automation.zufalls_steuerung_licht_wohnzimmer_schraenke
                  - automation.zufalls_steuerung_stehlampe_wz
                  - automation.zufall_steuerung_schreibtisch_buro_rene
                  - automation.zufall_steuerung_licht_treppenhaus
              perform_action: automation.turn_on
              data: {}
            show_state: false
            grid_options:
              columns: 6
              rows: 2
          - show_name: true
            show_icon: true
            type: button
            name: Automationen ausschalten
            icon: mdi:power-off
            tap_action:
              action: perform-action
              target:
                entity_id:
                  - automation.zeitsteuerung_lichterkette_garten
                  - automation.zeitsteuerung_schlafzimmer
                  - automation.zufall_steuerung_spitzboden
                  - automation.zeitsteuerung_flur_zufall
                  - automation.zufalls_steuerung_licht_wohnzimmer_schraenke
                  - automation.zufalls_steuerung_stehlampe_wz
                  - automation.zufall_steuerung_schreibtisch_buro_rene
                  - automation.zufall_steuerung_licht_treppenhaus
              perform_action: automation.turn_off
              data:
                stop_actions: true
      - type: grid
        cards:
          - type: heading
            heading_style: title
            heading: Backup
          - type: tile
            features_position: bottom
            vertical: false
            entity: sensor.backup_nachstes_geplantes_automatisches_backup
            name: Nächste auto. Backup
          - type: tile
            features_position: bottom
            vertical: false
            entity: sensor.backup_letztes_erfolgreiches_automatisches_backup
            name: Letztes auto. Backup
          - show_name: true
            show_icon: true
            type: button
            name: Voll-Backup starten
            icon: mdi:backup-restore
            tap_action:
              action: call-service
              service: automation.trigger
              service_data:
                entity_id: automation.backup_manuell_komplett
          - show_name: true
            show_icon: true
            type: button
            name: Teil-Backup starten
            icon: mdi:backup-restore
            tap_action:
              action: call-service
              service: automation.trigger
              service_data:
                entity_id: automation.backup_manuell_teilbackup
      - type: grid
        cards:
          - type: heading
            heading_style: title
            heading: EMS-ESP
          - type: tile
            entity: sensor.system_ems_bus
          - type: tile
            entity: binary_sensor.system_status
          - type: tile
            entity: sensor.system_version
            features_position: bottom
            vertical: false
            name: Firmware
          - type: tile
            entity: sensor.system_uptime
            features_position: bottom
            vertical: false
            name: Uptime
          - type: tile
            entity: sensor.system_rx_fails
          - type: tile
            entity: sensor.system_rx_received
            features_position: bottom
            vertical: false
            name: RX Received
          - type: tile
            entity: sensor.system_tx_reads
          - type: tile
            entity: sensor.system_tx_fails
    theme: Nordic
    visible:
      - user: xxx
    cards: []
    subview: true

🧱Bestandteile und Funktionen meines Energy Manager Packages

1) Helpers / Controls – Was ist definiert und wofür?

Im Package werden diverse Input‑Helper verwendet, um den Energy Manager flexibel steuerbar zu machen. Ich erkläre sie nacheinander:

Input Booleans

  • input_boolean.energy_manager_enabled

    Beschreibt, ob der Energy Manager grundsätzlich aktiv ist. Wird verwendet als Hauptschalter: viele Automationen und Logik prüfen diesen Zustand.
  • input_boolean.energy_manager_temp_ok

    Signalisiert, ob die Außentemperatur (bzw. der Luftansaugtemperatur der WP) in einem Bereich liegt, in dem das Energiemanagement arbeiten soll. Das verhindert sinnlose Heizaktivitäten bei sehr niedrigen Außentemperaturen.
  • input_boolean.energy_manager_force_zero

    Ein Start‑/Initialisierungs‑Flag: beim Start von HA wird temporär gesetzt, damit die gemittelten Sensoren nicht wegen initialer fehlender Rohdaten fälschlicherweise Werte liefern. Wird automatisch wieder ausgeschaltet.
  • input_boolean.em_allow_aux

    Erlaubt die Nutzung des Aux‑Heaters (Nachheizung). Standardmäßig aus, kann manuell eingeschaltet werden, wenn Zusatzheizung erwünscht ist.
  • input_boolean.em_dhw_charging

    Kennzeichnet, ob eine gezielte Boiler‑/DHW‑Ladung via PV‑Überschuss aktuell aktiv ist.
  • input_boolean.em_safe_mode

    Ein Schutzschalter, um den Manager in einen sicheren Zustand zu versetzen (Stopschalter, z. B. während Reset oder Wartung).

Input Select

  • input_select.energy_manager_mode

    Umschaltbar zwischen „Sommer“ und „Winter“. In Sommer wird der Energiemanager weitestgehend deaktiviert (z. B. keine Heizaktivitäten), in Winter wird er aktiviert und die zugehörigen Automationen eingeschaltet.

Input Numbers

Diese Zahlen erlauben die Feinabstimmung des Managers ohne Anpassung der YAML‑Logik:

  • pw_max_charge – max. gewünschte Ladeleistung für die Powerwall (kW).
  • energy_manager_temp_start / energy_manager_temp_stop – Temperatur‑Hysterese für energy_manager_temp_ok (Start‑ und Stop‑Schwellen).
  • em_flow_temp_min / em_flow_temp_max – zulässiger Bereich für die Vorlauftemperatur, in dem der Manager die WP steuert.
  • em_flow_temp_step_k – Schrittgröße (°C) für das schrittweise Erhöhen der ausgewählten Vorlauftemperatur (Ramping).
  • em_min_runtime – minimale Kompressor‑Laufzeit (Minuten) (in der aktuellen Config als Parameter geplant, wird aber nicht direkt in allen Automationen verwendet).
  • em_pv_threshold_start / em_pv_threshold_stop – Grenzwerte (kW) für bestimmte PV‑Aktionen (Start / Stop), z. B. für DHW‑Ladung (werden aber in weiteren Logiken die tatsächlichen Avail‑Schwellwerte verwendet).
  • em_dhw_max_temp / em_dhw_min_temp – maximale/minimale Boiler‑Temperatur für DHW‑Ladung und für Blockierung des Aux‑Heaters.
  • em_dhw_charge_min_duration – minimale Ladezeit für DHW‑Ladevorgänge (Minuten) zur Vermeidung zu kurzer Schaltzyklen.
  • em_avail_start / em_avail_stop – Schwellen zur Aktivierung/Deaktivierung der Wärmepumpen‑Regelung basierend auf verfügbarer überschüssiger Leistung (kW).

2) Filter Sensoren (Glättung)

Rohdaten der PV‑Anlage, des Hausverbrauchs und der Batterie können stark schwanken. Um „flattern“ (häufiges An‑/Abschalten) zu vermeiden, werden diese Werte geglättet:

  • sensor.em_pv_power_smoothed

    Quelle: sensor.my_home_solar_energie

    Filter: lowpass (time_constant: 2, precision: 2).

    Zweck: Glättet kurzzeitige PV‑Spitzen/-Tiefen. Niedriger Time Constant (2s) sorgt für relativ schnelle Reaktion bei gleichzeitigem Dämpfen von kurzen Ausschlägen.
  • sensor.em_home_load_smoothed

    Quelle: sensor.my_home_last_leistung

    Filter: time_simple_moving_average (window_size: 3).

    Zweck: Glättet den Hausverbrauch mit einfachem gleitendem Mittelwert über 3 Messpunkte (reduziert kurzzeitige Peaks).
  • sensor.em_powerwall_battery_power_smoothed

    Quelle: sensor.my_home_batterie_leistung

    Filter: lowpass (time_constant: 6).

    Zweck: Batterieströme können stark schwanken (z. B. Wechsel zwischen Laden/Entladen). Ein längeres Lowpass (6s) sorgt für stabile Erkennung der Ladeleistung.

Warum Glätten? Weil Steuerentscheidungen (z. B. Vorlauf‑Temp erhöhen, DHW starten) bittere Konsequenzen haben können (häufiges Schalten, unnötige Laufzeiten). Glättung reduziert Noise und setzt auf „echte“ Trends.


3) Template Sensors (Kernlogik)

Die Template‑Sensoren bilden das Herz des EM: Sie berechnen korrigierte Werte, Überschuss, verfügbarer Anteil für die WP und daraus das Ziel für die Vorlauftemperatur.

  • em_pv_power_corrected (kW)

    Logik: Nimmt entweder den geglätteten PV‑Wert (sensor.em_pv_power_smoothed) oder 0, falls das Force‑Zero‑Flag aktiv ist (input_boolean.energy_manager_force_zero) oder der rohe Wert nahe 0 liegt. Damit schützt man vor falschen Messungen beim Systemstart und vermeidet winzige Rauschwerte.
  • em_home_load_corrected (kW)

    Entspricht dem obigen Prinzip für Hausverbrauch (sensor.em_home_load_smoothed). Ziel: saubere Basis für Überschussberechnung.
  • em_powerwall_charge_corrected (kW)

    Korrigiert die Powerwall‑Leistung: returns 0 wenn Force‑Zero gesetzt oder wenn die absolute Rohleistung < 0.02 kW (Schwellenwert). Ansonsten verwendet es den geglätteten Wert. Zusätzlich wird in em_pw_current_charge der absolute Wert genommen (positive Ladeleistung).
  • em_energy_surplus (kW)

    Überschuss = em_pv_power_corrected − em_home_load_corrected. Der zentrale Kennwert, der anzeigt, wieviel Leistung theoretisch zur Verfügung steht.
  • em_pw_max_charge (kW)

    Schlicht die Eingabe (input_number.pw_max_charge). Praktisch Referenzwert zur Begrenzung der Batterieladung durch andere Logiken (derzeit als Informativ verwendet).
  • em_pw_current_charge (kW)

    Absoluter (gegenseitiger) Ladewert der Batterie – wieviel gerade in die Powerwall fließt (kW). Wichtig beim Verteilen des Überschusses: wenn die Batterie schon lädt, reduziert sie den Überschuss, der der WP zur Verfügung steht.
  • em_available_for_heatpump (kW)

    Zentrale Logik: Verfügbarer Überschuss für die Wärmepumpe = max(0, surplus − current_battery_charge). Implementiert eine einfache Regel: Wird PV‑Überschuss erzeugt, nutzt zuerst die Batterie das Laden (wenn sie gerade lädt). Der Rest geht an die WP. Negative oder fehlende Überschüsse werden als 0 betrachtet. Es gibt ein implizites kleines Threshold‑Handling (pw_curr > 0.02 wird als „nutzt Batterie“ angenommen).
  • em_target_flow_temp (°C)

    Hier wird aus em_available_for_heatpump eine Zielvorlauftemperatur berechnet. Formel: linearer Interpolationsansatz zwischen em_flow_temp_min und em_flow_temp_max, basierend auf einem angenommenen maximalen Heizleistungsbudget (in der Config ist max_power = 6 kW als Referenz). Ratio = clamp(avail / max_power, 0, 1). Zieltemp = min_t + (max_t − min_t) * ratio. Ergebnis ist stufenlos und wird in 0.1°C gerundet.
  • em_current_flow_temp (°C)

    Liest die aktuelle WP‑Vorlauftemperatur (sensor.boiler_current_flow_temperature). Wird benötigt, um zu entscheiden, ob erhöht werden soll.
  • em_dhw_boiler_temp (°C)

    Aktuelle Boiler‑Temperatur (für Warmwasser). Grundlage für DHW‑Ladungsentscheidungen.
  • em_dhw_charge_state

    Ein einfacher on/off‑Sensor, abgeleitet vom input_boolean.em_dhw_charging, dient dem Dashboard und anderen Automationen als bequeme Sicht auf den DHW‑Lademodus.

Diese Sensoren liefern zusammen die Basis, auf der Automationen entscheiden, ob die WP angehoben wird, ob Boiler geladen wird oder ob Aux‑Heater blockiert wird.


4) Binary Sensor: wp_laufzeit_startbedingung

Der Binary Sensor fasst mehrere Bedingungen zusammen und liefert ein klares „laufen / nicht laufen“ Signal, ob die WP laufen (im Sinne: die Vorlauftemperatur erhöht werden soll).

Zustand (true), wenn:

  • Energy Manager ist aktiviert (energy_manager_enabled = on)
  • Temperatur‑Bedingung ist ok (energy_manager_temp_ok = on)
  • Safe Mode ist aus (em_safe_mode = off)
  • Verfügbare Überschussleistung (em_available_for_heatpump) ist größer als em_avail_start
  • Target Vorlauftemperatur ist deutlich (> current + hysteresis 0.2°C) über der aktuellen Vorlauftemperatur

Der Sensor hat zahlreiche Attribute, z. B. em_enabled, avail_for_hp_kW, target_flow_temp_C, current_flow_temp_C und eine berechnete “would_set_to_C” Angabe die zeigt, auf welche Temperatur die WP gesetzt würde (current + step, mit Cap auf target und max). Außerdem liefert das Attribut “reason” eine hilfreiche Diagnosen‑Kette (z. B. warum gerade nicht gestartet wird), was fürs Troubleshooting sehr praktisch ist.


5) Integration Sensor: wp_laufzeit_bei_uberschuss_heute_neu

Hinweis: Nicht bestandteil des Packages!

Dieser history_stats‑Sensor wertet die Laufzeit eines Binary‑Sensors aus (in meinem Fall
binary_sensor.wp_laufzeit_startbedingung) und berechnet daraus, wie lange dieser heute bzw. im aktuellen Monat auf on stand.
Das Ergebnis ist eine Laufzeit in Dezimalstunden (z. B. 1.75 = 1 h 45 min).

Ich habe diesen Sensor in einer separaten Datei angelegt:

/homeassistant/integration_sensors/laufzeit_wp.yaml

mit folgendem Inhalt:

YAML
- platform: history_stats
  name: "WP Laufzeit bei Überschuss heute neu"
  unique_id: wp_laufzeit_bei_uberschuss_heute_neu
  entity_id: binary_sensor.wp_laufzeit_startbedingung
  state: "on"
  type: time
  start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
  end: "{{ now() }}"

- platform: history_stats
  name: "WP Laufzeit bei Überschuss Monat"
  unique_id: wp_laufzeit_bei_uberschuss_monat
  entity_id: binary_sensor.wp_laufzeit_startbedingung
  state: "on"
  type: time
  start: "{{ now().replace(day=1, hour=0, minute=0, second=0, microsecond=0) }}"
  end: "{{ now() }}"

Damit Home Assistant diesen Sensor überhaupt kennt, habe ich die Datei zusätzlich in der configuration.yaml eingebunden:

YAML
# Allgemeine Sensoren (verschiedene Integrationen)
sensor: !include_dir_merge_list integration_sensors/

Erst dadurch wird  sensor.wp_laufzeit_bei_uberschuss_heute_neu erzeugt und kann anschließend im Dashboard (z. B. in der button-card) verwendet werden.
Die JavaScript‑Logik in der Card dient dann nur noch dazu, den Stundenwert optisch als hh:mm:ss darzustellen (siehe Scrennshot unten)

6) Automatisierungen – Funktionen im Detail

Das Package enthält mehrere Automationen. Ich erläutere kurz jede einzelne samt Triggern, Bedingungen und Zweck.

EM Temp OK Status aktualisieren

Zweck: Setzt input_boolean.energy_manager_temp_ok abhängig von der gemessenen Lufteinlasstemperatur sensor.boiler_air_inlet_temperature_tl2 mit Debounce (60 s).

Logik:

  • Auslöser: jede Änderung der inlet‑Temp.
  • Wenn Temp < energy_manager_temp_start → nach 60 s nochmals prüfen und dann input_boolean.energy_manager_temp_ok = on (Manager darf arbeiten).
  • Wenn Temp > energy_manager_temp_stop → nach 60 s prüfen und input_boolean.energy_manager_temp_ok = off (zu warm / nicht heizrelevant).

Hinweis: Der Start/Stop ist hystereseartig gestaltet (separierte Start/Stop‑Schwellen), um Flattern zu vermeiden.

EM: Vorlauftemperatur regeln

Zweck: Erhöht stufenweise die ausgewählte Vorlauftemperatur (number.boiler_selected_flow_temperature) – nur Erhöhungen, kein aktives Absenken. Das vermeidet unnötige Absenk‑Schaltungen der WP und ermöglicht sanftes Ramping.

Trigger: Änderung von em_available_for_heatpump, em_current_flow_temp oder den Eingangs‑Booleans.

Voraussetzungen: Manager aktiv, Temp OK, Safe Mode off, avail > em_avail_start.

Aktion:

  • Berechnet target_temp_raw und current_temp sowie step und max_t.
  • Wenn target > current → Kandidat = current + step; cappe auf target und max_t → setze number.boiler_selected_flow_temperature auf new_temp_capped.
  • Schreibt einen Logeintrag mit Änderung.

Warum nur Erhöhungen? Absenken sollte – je nach WP – eher vom Gerät selbst oder durch separate Strategien übernommen werden, um kurze zyklische Belastungen zu vermeiden.

EM: DHW Ladung steuern

Zweck: Startet / Stoppt Boiler‑DHW‑Ladung basierend auf PV‑Überschuss, Boiler‑Temperatur, Hysterese und Mindestladezeit.

Trigger: Änderungen an em_available_for_heatpump, Boiler‑Temp, Manager‑Flags.

Logik:

  • Start: Wenn pv_avail > em_avail_start UND Boiler Temp < em_dhw_max_temp UND (noch nicht ladend oder bereits ladend, aber Mindestdauer erfüllt) → input_boolean.em_dhw_charging = on, switch.boiler_dhw_one_time_charging = on.
  • Stop: Wenn Boiler Temp >= em_dhw_max_temp ODER pv_avail < em_avail_stop UND Mindestladezeit erfüllt → stoppe DHW‑Ladung und setze Flag off.

Die Mindestladezeit verhindert zu kurzes Ein/Aus‑Schalten; die Start/Stop‑Schwellen gewährleisten saubere Schaltbedingungen mit Hysterese.

EM: Aux‑Heater Steuerung

Zweck: Blockiert oder erlaubt den Aux‑Heater basierend auf em_allow_aux, Boiler Temp, DHW‑Ladestatus und PV‑Verfügbarkeit.

Logik:

  • Wenn Aux nicht erlaubt (em_allow_aux = off), Boiler Temp über em_dhw_min_temp, DHW charging aktiv und PV avail > em_avail_stop → schalte Aux‑Heater aus (switch.boiler_aux_heater_only = off).
  • Wenn em_allow_aux = on → schalte Aux‑Heater an.

Zusätzlich werden Logeinträge geschrieben – praktisch beim Debugging und zur Nachverfolgung der Steuerung.

EM: Force smoothed zero on startup

Zweck: Diese Automation verhindert initiale Fehlinterpretationen der geglätteten Sensoren direkt nach einem Home Assistant Neustart. Oft sind Rohsensoren zu Beginn noch „unknown“ oder liefern spurige Werte — das Force‑Zero‑Flag sorgt dafür, dass die auf Glättung basierenden Sensoren zunächst 0 melden, bis verlässliche Rohdaten vorliegen.

Logik im Ablauf:

  • Beim Start von Home Assistant wird input_boolean.energy_manager_force_zero kurz gesetzt.
  • Die Automation wartet, bis mindestens einer der Rohsensoren (PV, Hauslast, Batterie) initialisiert ist; Timeout sind 20 Sekunden.
  • Nach einer kleinen Verzögerung werden die geglätteten Sensoren sowie die wichtigsten Template‑Sensoren einmalig per homeassistant.update_entity aktualisiert, um konsistente Startwerte zu erzwingen.
  • Anschließend wird das Force‑Zero‑Flag wieder deaktiviert und das System arbeitet mit echten Werten weiter.

Nutzen: Robustheit beim Systemstart — keine spontanen Aktionen (z. B. Boiler‑Start) aufgrund falscher Anfangswerte.

EM: Aux‑Heater max Laufzeit

Zweck: Failsafe gegen dauerhaft eingeschalteten Aux‑Heater. Wenn der Aux‑Heater manuell oder durch andere Logiken eingeschaltet wurde, schaltet diese Automation ihn nach 20 Minuten automatisch aus.

Warum notwendig: Aux‑Heater sind in der Regel elektrische Zusatzheizungen mit hohem Verbrauch. Ein unbeabsichtigtes oder klemmendes Relais könnte ansonsten unnötig hohe Kosten verursachen oder die Energieeffizienz komplett zunichte machen.

EM: Aux‑Heater aus bei keinem PV-Überschuss

Zweck: Verhindert das Einschalten des Aux‑Heaters, wenn kein ausreichender PV‑Überschuss mehr vorhanden ist.

Logik:

  • Wenn der verfügbare PV-Überschuss (sensor.em_available_for_heatpump) unter 0,3 kW fällt und für mindestens 2 Minuten bleibt, wird der Aux‑Heater automatisch ausgeschaltet.
  • Das schützt vor unnötiger Netzstromaufnahme durch den Aux‑Heater, der nur bei Überschuss laufen soll.

EM: Moduswechsel ausführen

Zweck: Schaltet den Energy Manager automatisch in den Sommer‑ oder Wintermodus um, wenn die Auswahl im input_select.energy_manager_mode geändert wird.

In der Automation wird das Script em_set_mode ausgelöst, welches die notwendigen Input Booleans, Automationen und Werte entsprechend setzt.

EM: HARD Restore Modus nach Neustart

Zweck: Nach einem Home Assistant Neustart wird der zuletzt gewählte Modus aus dem input_select.energy_manager_mode gelesen und über das Script em_set_mode hart wiederhergestellt.

So wird sichergestellt, dass der Manager nicht in einem unerwarteten Zustand startet.


💾Package Struktur

YAML
# config/packages/energy_manager.yaml

# --- Helpers / Controls ---
input_boolean:
  energy_manager_enabled:
    name: "Energy Manager aktiv"
    initial: on

  energy_manager_temp_ok:
    name: "Energy Manager: Temp OK"
    initial: on

  energy_manager_force_zero:
    name: "Energy Manager: Force smoothed zero"
    initial: off
    icon: mdi:power-off

  em_allow_aux:
    name: "EM: Aux-Heater erlauben"
    initial: off

  em_dhw_charging:
    name: "EM DHW Ladung aktiv"
    initial: off

  em_safe_mode:
    name: "EM Safe Mode"
    initial: off
    icon: mdi:shield-alert


input_select:
  energy_manager_mode:
    name: "Energy Manager Modus"
    options:
      - "Sommer"
      - "Winter"


input_number:
  pw_max_charge:
    name: "Powerwall max Ladeleistung (kW)"
    initial: 4.6
    min: 0
    max: 11.0
    step: 0.1
    unit_of_measurement: kW

  energy_manager_temp_start:
    name: "EM Temp Start (°C)"
    initial: 14
    min: -40
    max: 40
    step: 0.5

  energy_manager_temp_stop:
    name: "EM Temp Stop (°C)"
    initial: 17
    min: -40
    max: 40
    step: 0.5

  em_flow_temp_min:
    name: "EM Vorlauf Minimum (°C)"
    initial: 38
    min: 20
    max: 45
    step: 0.5

  em_flow_temp_max:
    name: "EM Vorlauf Maximum (°C)"
    initial: 50
    min: 40
    max: 60
    step: 0.5

  em_flow_temp_step_k:
    name: "EM Vorlauf Änderung pro Intervall (°C)"
    initial: 0.5
    min: 0.1
    max: 2
    step: 0.1

  em_min_runtime:
    name: "EM Kompressor Mindestlaufzeit (Minuten)"
    initial: 15
    min: 5
    max: 60
    step: 1

  em_pv_threshold_start:
    name: "EM PV Überschuss Startschwelle (kW)"
    initial: 3
    min: 0
    max: 5
    step: 0.1

  em_pv_threshold_stop:
    name: "EM PV Überschuss Stoppschwelle (kW)"
    initial: 1
    min: 0
    max: 5
    step: 0.1

  em_dhw_max_temp:
    name: "EM DHW Max Temperatur (°C)"
    initial: 55
    min: 45
    max: 70
    step: 0.5

  em_dhw_min_temp:
    name: "EM DHW Min Temperatur (°C)"
    initial: 45
    min: 35
    max: 55
    step: 0.5

  em_dhw_charge_min_duration:
    name: "EM DHW Mindestladezeit (Minuten)"
    initial: 20
    min: 5
    max: 60
    step: 1

  em_avail_start:
    name: "EM Avail Start (kW)"
    initial: 0.6
    min: 0
    max: 10
    step: 0.1

  em_avail_stop:
    name: "EM Avail Stop (kW)"
    initial: 0.4
    min: 0
    max: 10
    step: 0.1


# --- Scripts ---
script:

  em_set_mode:
    alias: "EM: Modus anwenden"
    mode: single
    sequence:

      - variables:
          mode: "{{ states('input_select.energy_manager_mode') }}"

      - choose:

          - conditions:
              - condition: template
                value_template: "{{ mode == 'Winter' }}"
            sequence:
              - service: input_boolean.turn_on
                target:
                  entity_id: input_boolean.energy_manager_enabled

              - service: input_boolean.turn_on
                target:
                  entity_id: input_boolean.energy_manager_temp_ok

              - service: input_boolean.turn_off
                target:
                  entity_id: input_boolean.em_safe_mode

              - service: number.set_value
                target:
                  entity_id: number.boiler_selected_flow_temperature
                data:
                  value: "{{ states('input_number.em_flow_temp_min') | float(38) }}"
              
              - delay: "00:00:02"
              
              - service: automation.turn_on
                target:
                  entity_id:
                    - automation.em_vorlauftemperatur_regeln
                    - automation.em_dhw_ladung_steuern
                    - automation.em_aux_heater_steuerung

              - service: system_log.write
                data:
                  message: "EM: Wintermodus aktiviert"
                  level: info

          - conditions:
              - condition: template
                value_template: "{{ mode == 'Sommer' }}"
            sequence:
              - service: input_boolean.turn_off
                target:
                  entity_id:
                    - input_boolean.energy_manager_enabled
                    - input_boolean.em_dhw_charging
                    - input_boolean.em_allow_aux

              - service: automation.turn_off
                target:
                  entity_id:
                    - automation.em_vorlauftemperatur_regeln
                    - automation.em_dhw_ladung_steuern
                    - automation.em_aux_heater_steuerung

              - service: script.em_reset_to_defaults

              - service: system_log.write
                data:
                  message: "EM: Sommermodus aktiviert"
                  level: warning

  em_toggle_mode:
    alias: "EM: Modus umschalten"
    mode: single
    sequence:
      - variables:
          current: "{{ states('input_select.energy_manager_mode') }}"

      - service: input_select.select_option
        target:
          entity_id: input_select.energy_manager_mode
        data:
          option: >
            {% if current == 'Winter' %}
              Sommer
            {% else %}
              Winter
            {% endif %}
            
  em_reset_to_defaults:
    alias: "EM: Reset auf Standardwerte"
    mode: single
    sequence:

      # 🛡 Safe Mode aktivieren (verhindert Zwischenzustände)
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.em_safe_mode

      - delay: "00:00:01"

      # 🧹 Grundsystem deaktivieren
      - service: input_boolean.turn_off
        target:
          entity_id:
            - input_boolean.energy_manager_enabled
            - input_boolean.energy_manager_force_zero
            - input_boolean.em_allow_aux
            - input_boolean.em_dhw_charging

      # ✔ Temp Status initialisieren
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.energy_manager_temp_ok

      # 🔧 Input Numbers Reset
      - service: input_number.set_value
        data:
          entity_id: input_number.pw_max_charge
          value: 4.6

      - service: input_number.set_value
        data:
          entity_id: input_number.energy_manager_temp_start
          value: 14

      - service: input_number.set_value
        data:
          entity_id: input_number.energy_manager_temp_stop
          value: 17

      - service: input_number.set_value
        data:
          entity_id: input_number.em_flow_temp_min
          value: 38

      - service: input_number.set_value
        data:
          entity_id: input_number.em_flow_temp_max
          value: 50

      - service: input_number.set_value
        data:
          entity_id: input_number.em_flow_temp_step_k
          value: 0.5

      - service: input_number.set_value
        data:
          entity_id: input_number.em_min_runtime
          value: 15

      - service: input_number.set_value
        data:
          entity_id: input_number.em_pv_threshold_start
          value: 3

      - service: input_number.set_value
        data:
          entity_id: input_number.em_pv_threshold_stop
          value: 1

      - service: input_number.set_value
        data:
          entity_id: input_number.em_dhw_max_temp
          value: 55

      - service: input_number.set_value
        data:
          entity_id: input_number.em_dhw_min_temp
          value: 45

      - service: input_number.set_value
        data:
          entity_id: input_number.em_dhw_charge_min_duration
          value: 20

      - service: input_number.set_value
        data:
          entity_id: input_number.em_avail_start
          value: 0.6

      - service: input_number.set_value
        data:
          entity_id: input_number.em_avail_stop
          value: 0.4

      # 🌡 Boiler Startwert setzen
      - service: number.set_value
        data:
          entity_id: number.boiler_selected_flow_temperature
          value: "{{ states('input_number.em_flow_temp_min') | float(38) }}"

      # 🔌 Aktoren sicher aus
      - service: switch.turn_off
        target:
          entity_id:
            - switch.boiler_dhw_one_time_charging
            - switch.boiler_aux_heater_only

# --- Filter Sensoren (Glättung) ---
sensor:
  - platform: filter
    name: "em_pv_power_smoothed"
    entity_id: sensor.my_home_solar_energie
    filters:
      - filter: lowpass
        time_constant: 2
        precision: 2

  - platform: filter
    name: "em_home_load_smoothed"
    entity_id: sensor.my_home_last_leistung
    filters:
      - filter: time_simple_moving_average
        window_size: 3
        precision: 2

  - platform: filter
    name: "em_powerwall_battery_power_smoothed"
    entity_id: sensor.my_home_batterie_leistung
    filters:
      - filter: lowpass
        time_constant: 6
        precision: 2

# --- Template Sensors & Binary Sensors ---
template:
  - sensor:
      - name: "em_pv_power_corrected"
        unit_of_measurement: "kW"
        state: >-
          {% set force = is_state('input_boolean.energy_manager_force_zero', 'on') %}
          {% set raw = states('sensor.my_home_solar_energie') | float(0) %}
          {% set sm = states('sensor.em_pv_power_smoothed') | float(0) %}
          {% if force or raw < 0.02 %}
            0
          {% else %}
            {{ sm | round(2) }}
          {% endif %}

      - name: "em_home_load_corrected"
        unit_of_measurement: "kW"
        state: >-
          {% set force = is_state('input_boolean.energy_manager_force_zero', 'on') %}
          {% set raw = states('sensor.my_home_last_leistung') | float(0) %}
          {% set sm = states('sensor.em_home_load_smoothed') | float(0) %}
          {% if force or raw < 0.02 %}
            0
          {% else %}
            {{ sm | round(2) }}
          {% endif %}

      - name: "em_powerwall_charge_corrected"
        unit_of_measurement: "kW"
        state: >-
          {% set force = is_state('input_boolean.energy_manager_force_zero', 'on') %}
          {% set raw = states('sensor.my_home_batterie_leistung') | float(0) %}
          {% set sm = states('sensor.em_powerwall_battery_power_smoothed') | float(0) %}
          {% if force or (raw | abs) < 0.02 %}
            0
          {% else %}
            {{ sm | round(2) }}
          {% endif %}

      - name: "em_energy_surplus"
        unit_of_measurement: "kW"
        state: >-
          {{ (states('sensor.em_pv_power_corrected') | float(0) - states('sensor.em_home_load_corrected') | float(0)) | round(2) }}

      - name: "em_pw_max_charge"
        unit_of_measurement: "kW"
        state: >-
          {{ states('input_number.pw_max_charge') | float(4.6) }}

      - name: "em_pw_current_charge"
        unit_of_measurement: "kW"
        state: >-
          {{ (states('sensor.em_powerwall_charge_corrected') | float(0)) | abs | round(2) }}

      - name: "em_available_for_heatpump"
        unit_of_measurement: "kW"
        state: >-
          {% set surplus = states('sensor.em_energy_surplus') | float(0) %}
          {% set pw_curr = states('sensor.em_pw_current_charge') | float(0) %}
          {% set threshold = 0.02 %}
          {% if surplus > 0 %}
            {% set used = pw_curr if pw_curr > threshold else 0 %}
            {{ [surplus - used, 0] | max | round(2) }}
          {% else %}
            0
          {% endif %}
        attributes:
          note: "Surplus minus current battery charge (threshold 0.02 kW)"

      - name: "em_target_flow_temp"
        unit_of_measurement: "°C"
        state: >-
          {% set min_t = states('input_number.em_flow_temp_min') | float(0) %}
          {% set max_t = states('input_number.em_flow_temp_max') | float(0) %}
          {% set avail = states('sensor.em_available_for_heatpump') | float(0) %}
          {% set max_power = 6 %}
          {% set ratio = (avail / max_power) | float(0) %}
          {% set ratio = [ratio, 0] | max %}
          {% set ratio = [ratio, 1] | min %}
          {% set target = min_t + (max_t - min_t) * ratio %}
          {{ target | round(1) }}

      - name: "em_current_flow_temp"
        unit_of_measurement: "°C"
        state: "{{ states('sensor.boiler_current_flow_temperature') | float(0) }}"

      - name: "em_dhw_boiler_temp"
        unit_of_measurement: "°C"
        state: "{{ states('sensor.boiler_heat_carrier_forward_tc1') | float(0) }}"

      - name: "em_dhw_charge_state"
        state: >-
          {{ 'on' if is_state('input_boolean.em_dhw_charging', 'on') else 'off' }}

  - binary_sensor:
    - name: "wp_laufzeit_startbedingung"
      unique_id: wp_laufzeit_startbedingung
      device_class: running
      state: >-
        {% set enabled    = is_state('input_boolean.energy_manager_enabled','on') %}
        {% set temp_ok    = is_state('input_boolean.energy_manager_temp_ok','on') %}
        {% set safe_off   = is_state('input_boolean.em_safe_mode','off') %}
        {% set avail      = states('sensor.em_available_for_heatpump') | float(0) %}
        {% set start_th   = states('input_number.em_avail_start') | float(0) %}
        {% set target_t   = states('sensor.em_target_flow_temp') | float(0) %}
        {% set current_t  = states('sensor.em_current_flow_temp') | float(0) %}
        {% set hysteresis = 0.2 %}
        {{ enabled and temp_ok and safe_off and (avail > start_th) and (target_t > current_t + hysteresis) }}
        {# Optional zusätzlich: or is_state('input_boolean.em_dhw_charging','on') #}
      attributes:
        em_enabled: "{{ states('input_boolean.energy_manager_enabled') }}"
        em_temp_ok: "{{ states('input_boolean.energy_manager_temp_ok') }}"
        em_safe_mode: "{{ states('input_boolean.em_safe_mode') }}"
        avail_for_hp_kW: "{{ states('sensor.em_available_for_heatpump') }}"
        avail_start_kW: "{{ states('input_number.em_avail_start') }}"
        target_flow_temp_C: "{{ states('sensor.em_target_flow_temp') }}"
        current_flow_temp_C: "{{ states('sensor.em_current_flow_temp') }}"
        would_set_to_C: >-
          {% set step  = states('input_number.em_flow_temp_step_k') | float(0.5) %}
          {% set max_t = states('input_number.em_flow_temp_max')   | float(50) %}
          {% set cand  = (states('sensor.em_current_flow_temp') | float(0)) + step %}
          {% set tgt   = states('sensor.em_target_flow_temp')  | float(0) %}
          {% set capped = [cand, tgt, max_t] | min %}
          {{ capped | round(1) }}
        reason: >-
          {# Variablen in Attribut-Template erneut setzen, da separat gerendert #}
          {% set enabled    = is_state('input_boolean.energy_manager_enabled','on') %}
          {% set temp_ok    = is_state('input_boolean.energy_manager_temp_ok','on') %}
          {% set safe_off   = is_state('input_boolean.em_safe_mode','off') %}
          {% set avail      = states('sensor.em_available_for_heatpump') | float(0) %}
          {% set start_th   = states('input_number.em_avail_start') | float(0) %}
          {% set target_t   = states('sensor.em_target_flow_temp') | float(0) %}
          {% set current_t  = states('sensor.em_current_flow_temp') | float(0) %}
          {% set hysteresis = 0.2 %}
          {% set reasons = [] %}
          {% if not enabled %}{% set reasons = reasons + ['disabled'] %}{% endif %}
          {% if not temp_ok %}{% set reasons = reasons + ['temp_not_ok'] %}{% endif %}
          {% if not safe_off %}{% set reasons = reasons + ['safe_mode_on'] %}{% endif %}
          {% if not (avail > start_th) %}{% set reasons = reasons + ['avail_below_start'] %}{% endif %}
          {% if not (target_t > current_t + hysteresis) %}{% set reasons = reasons + ['target_not_above_current'] %}{% endif %}
          {{ 'all_conditions_met' if reasons|length == 0 else reasons|join(',') }}
      availability: >-
        {{
          (states('sensor.em_available_for_heatpump') not in ['unknown','unavailable','']) and
          (states('sensor.em_target_flow_temp') not in ['unknown','unavailable','']) and
          (states('sensor.em_current_flow_temp') not in ['unknown','unavailable',''])
        }}

# --- Automationen (alle in einem Block) ---
automation:
  - alias: "EM Temp OK Status aktualisieren"
    description: "Setzt input_boolean.energy_manager_temp_ok basierend auf Temperatur mit Debounce 60s"
    mode: restart
    trigger:
      - platform: state
        entity_id: sensor.boiler_air_inlet_temperature_tl2
    condition: []
    action:
      - choose:
          - conditions:
              - condition: template
                value_template: >-
                  {% set temp = states('sensor.boiler_air_inlet_temperature_tl2') %}
                  {% set start = states('input_number.energy_manager_temp_start') %}
                  {{ temp not in ['unavailable', 'unknown', ''] and
                     start not in ['unavailable', 'unknown', ''] and
                     (temp | float(0)) < (start | float(0)) }}
            sequence:
              - delay: "00:01:00"
              - condition: template
                value_template: >-
                  {% set temp = states('sensor.boiler_air_inlet_temperature_tl2') %}
                  {% set start = states('input_number.energy_manager_temp_start') %}
                  {{ temp not in ['unavailable', 'unknown', ''] and
                     start not in ['unavailable', 'unknown', ''] and
                     (temp | float(0)) < (start | float(0)) }}
              - service: input_boolean.turn_on
                target:
                  entity_id: input_boolean.energy_manager_temp_ok
          - conditions:
              - condition: template
                value_template: >-
                  {% set temp = states('sensor.boiler_air_inlet_temperature_tl2') %}
                  {% set stop = states('input_number.energy_manager_temp_stop') %}
                  {{ temp not in ['unavailable', 'unknown', ''] and
                     stop not in ['unavailable', 'unknown', ''] and
                     (temp | float(0)) > (stop | float(0)) }}
            sequence:
              - delay: "00:01:00"
              - condition: template
                value_template: >-
                  {% set temp = states('sensor.boiler_air_inlet_temperature_tl2') %}
                  {% set stop = states('input_number.energy_manager_temp_stop') %}
                  {{ temp not in ['unavailable', 'unknown', ''] and
                     stop not in ['unavailable', 'unknown', ''] and
                     (temp | float(0)) > (stop | float(0)) }}
              - service: input_boolean.turn_off
                target:
                  entity_id: input_boolean.energy_manager_temp_ok

  # Automation: EM: Vorlauftemperatur regeln (nur Erhöhungen, kein Absenken)
  - alias: "EM: Vorlauftemperatur regeln"
    description: "Erhöht Selected flow temperature stufenlos mit Ramping (nur Anheben, kein aktives Absenken)"
    trigger:
      - platform: state
        entity_id:
          - sensor.em_available_for_heatpump
          - sensor.em_current_flow_temp
          - input_boolean.energy_manager_enabled
          - input_boolean.energy_manager_temp_ok
    condition:
      - condition: state
        entity_id: input_boolean.energy_manager_enabled
        state: 'on'
      - condition: state
        entity_id: input_boolean.energy_manager_temp_ok
        state: 'on'
      - condition: state
        entity_id: input_boolean.em_safe_mode
        state: 'off'
      # Hysterese-Bedingung: nur wenn verfügbar > Start-Schwelle
      - condition: template
        value_template: >-
          {{ (states('sensor.em_available_for_heatpump') | float(0)) > (states('input_number.em_avail_start') | float(0)) }}
    action:
      - variables:
          target_temp_raw: "{{ states('sensor.em_target_flow_temp') | float(0) }}"
          current_temp: "{{ states('sensor.em_current_flow_temp') | float(0) }}"
          step: "{{ states('input_number.em_flow_temp_step_k') | float(0) }}"
          max_t: "{{ states('input_number.em_flow_temp_max') | float(0) }}"
          last_change: "{{ state_attr('number.boiler_selected_flow_temperature', 'last_changed') }}"
          time_since_change_min: >-
            {% if last_change is none %}
              9999
            {% else %}
              ((as_timestamp(now()) - as_timestamp(last_change)) / 60)
            {% endif %}
      - choose:
          - conditions:
              # Nur erhöhen, wenn Ziel > Ist
              - condition: template
                value_template: >-
                  {{ target_temp_raw > current_temp }}
            sequence:
              - variables:
                  # Kandidat ist Ist + Schritt, aber nicht über Ziel
                  candidate_temp: >-
                    {% set cand = current_temp + step %}
                    {% if cand > target_temp_raw %}
                      {{ target_temp_raw }}
                    {% else %}
                      {{ cand }}
                    {% endif %}
                  # Kappe auf maximalen erlaubten Wert
                  new_temp_capped: >-
                    {{ [ (candidate_temp | float), (max_t | float) ] | min | round(1) }}
              - service: number.set_value
                data:
                  entity_id: number.boiler_selected_flow_temperature
                  value: "{{ new_temp_capped }}"
              - service: system_log.write
                data:
                  message: "EM: Vorlaufregelung erhöht von {{ current_temp }}°C auf {{ new_temp_capped }}°C (Ziel {{ target_temp_raw }}°C, Max {{ max_t }}°C)"
                  level: info
        default: []

  - alias: "EM: DHW Ladung steuern"
    description: "Steuert DHW Charging basierend auf PV Überschuss und Boiler Temperatur, mit Hysterese"
    trigger:
      - platform: state
        entity_id:
          - sensor.em_available_for_heatpump
          - sensor.em_dhw_boiler_temp
          - input_boolean.energy_manager_enabled
          - input_boolean.energy_manager_temp_ok
          - input_boolean.em_dhw_charging
    condition:
      - condition: state
        entity_id: input_boolean.energy_manager_enabled
        state: 'on'
      - condition: state
        entity_id: input_boolean.energy_manager_temp_ok
        state: 'on'
      - condition: state
        entity_id: input_boolean.em_safe_mode
        state: 'off'
    action:
      - variables:
          pv_avail: "{{ states('sensor.em_available_for_heatpump') | float(0) }}"
          boiler_temp: "{{ states('sensor.boiler_heat_carrier_forward_tc1') | float(0) }}"
          dhw_charging: "{{ is_state('input_boolean.em_dhw_charging', 'on') }}"
          max_temp: "{{ states('input_number.em_dhw_max_temp') | float(0) }}"
          min_temp: "{{ states('input_number.em_dhw_min_temp') | float(0) }}"
          min_duration: "{{ states('input_number.em_dhw_charge_min_duration') | int }}"
          last_on: "{{ state_attr('input_boolean.em_dhw_charging', 'last_changed') }}"
          time_on_min: >-
            {% if last_on is none %}
              9999
            {% else %}
              ((as_timestamp(now()) - as_timestamp(last_on)) / 60)
            {% endif %}
          start_thresh: "{{ states('input_number.em_avail_start') | float(0) }}"
          stop_thresh: "{{ states('input_number.em_avail_stop') | float(0) }}"
      - choose:
          # Start DHW Charging wenn PV Überschuss über Startschwelle und Boiler Temp < max_temp
          - conditions:
              - condition: template
                value_template: >-
                  {{ pv_avail > start_thresh }}
              - condition: template
                value_template: >-
                  {{ boiler_temp < max_temp }}
              - condition: template
                value_template: >-
                  {{ not dhw_charging or (dhw_charging and time_on_min > min_duration) }}
            sequence:
              - service: input_boolean.turn_on
                target:
                  entity_id: input_boolean.em_dhw_charging
              - service: switch.turn_on
                target:
                  entity_id: switch.boiler_dhw_one_time_charging
              - service: system_log.write
                data:
                  message: "EM: DHW Charging gestartet (Boiler {{ boiler_temp }}°C, PV Überschuss {{ pv_avail }} kW)"
                  level: info

          # Stop DHW Charging wenn PV unter Stop-Schwelle oder Boiler Temp >= max_temp und Mindestladezeit erfüllt
          - conditions:
              - condition: template
                value_template: >-
                  {{ (boiler_temp >= max_temp) or (pv_avail < stop_thresh) }}
              - condition: template
                value_template: >-
                  {{ dhw_charging and time_on_min > min_duration }}
            sequence:
              - service: input_boolean.turn_off
                target:
                  entity_id: input_boolean.em_dhw_charging
              - service: switch.turn_off
                target:
                  entity_id: switch.boiler_dhw_one_time_charging
              - service: system_log.write
                data:
                  message: "EM: DHW Charging gestoppt (Boiler {{ boiler_temp }}°C, PV Überschuss {{ pv_avail }} kW)"
                  level: info

  - alias: "EM: Aux-Heater Steuerung"
    description: "Blockiert Aux-Heater wenn nicht erlaubt oder Boiler Temp ausreichend, mit Hysterese"
    trigger:
      - platform: state
        entity_id:
          - input_boolean.em_allow_aux
          - sensor.boiler_heat_carrier_forward_tc1
          - input_boolean.em_dhw_charging
          - sensor.em_available_for_heatpump
    condition:
      - condition: state
        entity_id: input_boolean.em_safe_mode
        state: 'off'
    action:
      - variables:
          allow_aux: "{{ is_state('input_boolean.em_allow_aux', 'on') }}"
          boiler_temp: "{{ states('sensor.boiler_heat_carrier_forward_tc1') | float(0) }}"
          min_temp: "{{ states('input_number.em_dhw_min_temp') | float(0) }}"
          dhw_charging: "{{ is_state('input_boolean.em_dhw_charging', 'on') }}"
          pv_avail: "{{ states('sensor.em_available_for_heatpump') | float(0) }}"
          start_thresh: "{{ states('input_number.em_avail_start') | float(0) }}"
          stop_thresh: "{{ states('input_number.em_avail_stop') | float(0) }}"
      - choose:
          - conditions:
              - condition: template
                value_template: >-
                  {{ not allow_aux and boiler_temp > min_temp and dhw_charging and pv_avail > stop_thresh }}
            sequence:
              - service: switch.turn_off
                target:
                  entity_id: switch.boiler_aux_heater_only
              - service: system_log.write
                data:
                  message: "EM: Aux-Heater deaktiviert wegen Energiemanager"
                  level: info

          - conditions:
              - condition: template
                value_template: "{{ allow_aux }}"
            sequence:
              - service: switch.turn_on
                target:
                  entity_id: switch.boiler_aux_heater_only
              - service: system_log.write
                data:
                  message: "EM: Aux-Heater erlaubt durch Energiemanager"
                  level: info

  - alias: "EM: Force smoothed zero on startup"
    description: "Setzt beim HA-Start kurzzeitig das Force-Zero-Flag, bis Rohsensoren initialisiert sind"
    mode: single
    trigger:
      - platform: homeassistant
        event: start
    action:
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.energy_manager_force_zero
      - wait_for_trigger:
          - platform: template
            value_template: >-
              {% set s1 = states('sensor.my_home_solar_energie') %}
              {% set s2 = states('sensor.my_home_last_leistung') %}
              {% set s3 = states('sensor.my_home_batterie_leistung') %}
              {{ [s1, s2, s3] | select('match', '^(?!unknown$|^unavailable$|^$).+') | list | count > 0 }}
        timeout: "00:00:20"
      - delay: "00:00:02"
      - service: homeassistant.update_entity
        target:
          entity_id:
            - sensor.em_pv_power_smoothed
            - sensor.em_home_load_smoothed
            - sensor.em_powerwall_battery_power_smoothed
            - sensor.em_pv_power_corrected
            - sensor.em_home_load_corrected
            - sensor.em_powerwall_charge_corrected
            - sensor.em_energy_surplus
            - sensor.em_available_for_heatpump
            - sensor.em_pw_current_charge
            - sensor.em_pw_max_charge
            - sensor.em_target_flow_temp
            - sensor.em_current_flow_temp
            - sensor.boiler_heat_carrier_forward_tc1
      - delay: "00:00:01"
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.energy_manager_force_zero

  - alias: "EM: Aux-Heater max Laufzeit"
    description: "Schaltet den Aux-Heater automatisch nach definierter Zeit wieder aus (Failsafe)"
    mode: restart
    trigger:
      - platform: state
        entity_id: switch.boiler_aux_heater_only
        to: "on"
        for: "00:20:00"
    condition: []
    action:
      - service: switch.turn_off
        target:
          entity_id: switch.boiler_aux_heater_only
      - service: system_log.write
        data:
          message: "EM: Aux-Heater wurde nach 20 Minuten automatisch abgeschaltet (Failsafe)"
          level: warning

  - alias: "EM: Aux-Heater aus bei keinem PV-Überschuss"
    description: "Schaltet den Aux-Heater aus, wenn kein ausreichender PV-Überschuss vorhanden ist"
    mode: restart
    trigger:
      - platform: numeric_state
        entity_id: sensor.em_available_for_heatpump
        below: 0.3
        for: "00:02:00"
    condition: []
    action:
      - condition: state
        entity_id: switch.boiler_aux_heater_only
        state: "on"
      - service: switch.turn_off
        target:
          entity_id: switch.boiler_aux_heater_only
      - service: system_log.write
        data:
          message: "EM: Aux-Heater deaktiviert wegen fehlendem PV-Überschuss"
          level: warning
          
  - alias: "EM: Moduswechsel ausführen"
    mode: single
    trigger:
      - platform: state
        entity_id: input_select.energy_manager_mode
    action:
      - service: script.em_set_mode
      
  - alias: "EM: HARD Restore Modus nach Neustart"
    mode: single

    trigger:
      - platform: homeassistant
        event: start

    action:
      # ⏱ wirklich warten bis alles stabil ist
      - delay: "00:00:20"

      # 🧠 Modus hart lesen (mit Fallback!)
      - variables:
          mode: "{{ states('input_select.energy_manager_mode') | default('Winter') }}"

      # 🧯 Debug zur Kontrolle
      - service: system_log.write
        data:
          message: "EM START: gespeicherter Modus = {{ mode }}"
          level: warning

      # 🔥 HARTE WIEDERHERSTELLUNG
      - choose:
          - conditions:
              - condition: template
                value_template: "{{ mode == 'Sommer' }}"
            sequence:
              - service: script.em_set_mode

          - conditions:
              - condition: template
                value_template: "{{ mode == 'Winter' }}"
            sequence:
              - service: script.em_set_mode

🕶️Überblick zur Funktionsweise des Energy Managers

Das Energiemanagement arbeitet in mehreren, klar strukturierten Schritten:

Datenaufnahme & Glättung

Rohdaten aus den Quellen PV-Anlage, Hausverbrauch und Powerwall-Batterie werden zunächst mit Filtern geglättet. Dadurch werden kurzfristige Schwankungen und Messrauschen reduziert, um stabile Steuerentscheidungen zu ermöglichen.

Überschussberechnung & Verteilung

Aus den geglätteten Werten wird ein Energieüberschuss berechnet (PV-Erzeugung minus Hausverbrauch). Von diesem Überschuss wird sukzessive der aktuelle Ladebedarf der Powerwall subtrahiert, da die Batterie Vorrang beim Laden hat.

Der verbleibende Überschuss steht dann der Wärmepumpe zur Verfügung – dieser Wert steuert, wie intensiv die Wärmepumpe beheizt wird.

Temperaturabhängige Aktivierung

Der Manager berücksichtigt über einen eigenen Input Boolean, ob die Temperaturbedingungen (bspw. Außentemperatur am Luftansaugpunkt) geeignet sind. Nur wenn die Temperatur im definierten Bereich ist, aktiviert er Heizprozesse – das erhöht Effizienz und schützt die Anlage.

Automatische Steuerung

Die Wärmepumpe wird nur dann hochgefahren, wenn ein Restüberschuss nach Batterieladung verbleibt. Die Vorlauftemperatur der WP wird sanft in kleinen Schritten erhöht, um eine zu schnelle Hysterese zu vermeiden.

Die Powerwall wird vorrangig mit Überschussstrom geladen, wobei eine maximale Ladeleistung definiert ist.

Außerdem steuert der Manager die Warmwasserbereitung (DHW), indem er das Boilerladen abhängig von PV-Überschuss, Boiler-Temperatur und definierten Zeiträumen ein- und ausschaltet.

Der Aux-Heater wird je nach Einstellung blockiert oder zugelassen, um unnötigen Stromverbrauch zu verhindern.

Manuelle Steuerung

Über definierte Input Booleans kann der Nutzer den Manager manuell ein- oder ausschalten. Ein Force-Flag sorgt beim Start für stabile Initialisierung, Safe Mode verhindert temporär alle automatischen Aktionen, und verschiedene Schalter erlauben die manuelle Aktivierung von DHW-Ladung oder Aux-Heater.

Mit dieser Kombination aus Helpern, Glättung, Logiksensoren und Automationen entsteht ein flexibles, robuster Energiemanager, der PV-Überschuss gezielt für Heizungs- und Warmwasserbetrieb nutzt und gleichzeitig Hausverbrauch und Batterie berücksichtigt. So wird der Eigenverbrauch erhöht, Stromkosten gesenkt und der Komfort gewahrt.


🔭Visualisierung im Dashboard

Im Hauptdashboard zeige ich aktuell nur den Betriebsmodus der Wärmepumpe mit einer custom:button-card an:

Hier der zugehörige YAML-Code:

YAML
type: custom:button-card
entity: binary_sensor.wp_laufzeit_startbedingung
name: Wärmepumpe
icon: |
  [[[ return entity.state === 'on'
      ? 'mdi:fire'
      : 'mdi:fire-off'; ]]]
show_icon: true
show_name: true
show_state: true
tap_action:
  action: navigate
  navigation_path: /lovelace/WPDaten
state_display: |
  [[[ return entity.state === 'on'
      ? 'Aufheizen aktiv'
      : 'Normaler Betrieb'; ]]]
styles:
  grid:
    - grid-template-areas: "\"i n\" \"i s\""
    - grid-template-columns: 48px 1fr
    - grid-template-rows: auto auto
  card:
    - padding: 10px 12px
    - border-radius: 8px
    - box-shadow: none
    - text-align: left
  icon:
    - width: 32px
    - height: 32px
  name:
    - font-size: 16px
    - font-weight: 600
  state:
    - font-size: 13px
state:
  - value: "on"
    styles:
      card:
        - background-color: "#FF8C00"
        - border-color: "#FF8C00"
      icon:
        - color: white
      name:
        - color: white
      state:
        - color: white

Zusätzlich habe ich ein rudimentäres Dashboard mit allen relevanten Entitäten zur Steuerung der Wärmepumpe erstellt (rein zur Übersicht und prüfen der Daten die alles steuern):

  • Screenshot Sommermodus
  • Screenshot Wintermodus

Hier der zugehörige Auszug aus der RAW-Konfigurationsdatei:

YAML
  - title: WP Automatisierung
    path: WPDaten
    icon: mdi:heat-pump-outline
    theme: Nordic
    cards: []
    type: sections
    layout:
      width: 400
      max_cols: 3
    visible:
      - user: 9c3a30d6645649c386acf6485e8d39e3
    sections:
      - type: grid
        cards:
          - type: heading
            heading_style: title
            heading: Wärmepumpe - Betriebsmodus
            icon: mdi:heat-pump
          - type: custom:button-card
            entity: input_select.energy_manager_mode
            name: Energy Manager
            show_icon: true
            show_name: true
            show_state: false
            tap_action:
              action: call-service
              service: input_select.select_option
              target:
                entity_id: input_select.energy_manager_mode
              data:
                option: |
                  [[[
                    return entity.state === "Winter" ? "Sommer" : "Winter";
                  ]]]
            state:
              - value: Winter
                icon: mdi:snowflake
                name: Wintermodus
                styles:
                  card:
                    - background: '#4DA3FF'
                    - border-radius: 8px
                    - box-shadow: none
                  name:
                    - color: white
                    - font-weight: 600
                  icon:
                    - color: white
              - value: Sommer
                icon: mdi:white-balance-sunny
                name: Sommermodus
                styles:
                  card:
                    - background: '#D32F2F'
                    - border-radius: 8px
                    - box-shadow: none
                  name:
                    - color: white
                    - font-weight: 600
                  icon:
                    - color: white
            styles:
              card:
                - padding: 12px
                - font-size: 16px
                - text-align: center
                - border-radius: 6px
              icon:
                - width: 32px
                - height: 32px
              name:
                - font-size: 16px
          - type: custom:button-card
            entity: binary_sensor.wp_laufzeit_startbedingung
            name: Extra Heißmodus
            show_icon: true
            icon: mdi:fire
            show_name: true
            show_state: false
            state:
              - value: 'on'
                color: white
                icon: mdi:fire
                name: Aufheiz-Betrieb
                styles:
                  card:
                    - background: '#FF8C00'
                    - border-radius: 8px
                    - box-shadow: none
                  name:
                    - color: white
                    - font-weight: 600
                  state:
                    - color: white
              - value: 'off'
                color: white
                icon: mdi:fire-off
                name: Normal-Betrieb
                styles:
                  card:
                    - background: '#2E8B57'
                    - border-radius: 8px
                    - box-shadow: none
                  name:
                    - color: white
                    - font-weight: 600
                  state:
                    - color: white
            styles:
              card:
                - padding: 12px
                - font-size: 16px
                - text-align: center
                - color: white
                - border-radius: 6px
                - box-shadow: none
              icon:
                - width: 32px
                - height: 32px
              name:
                - font-size: 16px
              state:
                - font-size: 14px
          - square: false
            type: grid
            cards:
              - type: custom:button-card
                entity: sensor.wp_laufzeit_bei_uberschuss_heute_neu
                name: WP Laufzeit bei Überschuss heute
                icon: mdi:clock-time-five-outline
                show_icon: false
                show_name: true
                show_state: false
                show_label: true
                label: |
                  [[[
                    // raw = Stunden im Dezimalformat (z.B. 1.75 = 1h45min)
                    const raw = states['sensor.wp_laufzeit_bei_uberschuss_heute_neu']?.state;
                    const val = parseFloat(raw);
                    if (isNaN(val)) return "–";

                    // Umrechnung: Dezimalstunden → Sekunden
                    const totalSeconds = Math.round(val * 3600);

                    const hours   = Math.floor(totalSeconds / 3600);
                    const minutes = Math.floor((totalSeconds % 3600) / 60);
                    const seconds = totalSeconds % 60;

                    // Format hh:mm:ss
                    return `${hours.toString().padStart(2,'0')}:` +
                           `${minutes.toString().padStart(2,'0')}:` +
                           `${seconds.toString().padStart(2,'0')}`;
                  ]]]
                styles:
                  card:
                    - font-size: 18px
                    - text-align: center
                    - padding: 8px
                  label:
                    - font-weight: bold
                    - color: var(--primary-text-color)
            columns: 1
          - square: false
            type: grid
            cards:
              - type: custom:button-card
                entity: sensor.wp_laufzeit_bei_uberschuss_monat
                name: WP Laufzeit bei Überschuss Monat
                icon: mdi:clock-time-five-outline
                show_icon: false
                show_name: true
                show_state: false
                show_label: true
                label: |
                  [[[
                    // raw = Stunden im Dezimalformat (z.B. 1.75 = 1h45min)
                    const raw = states['sensor.wp_laufzeit_bei_uberschuss_monat']?.state;
                    const val = parseFloat(raw);
                    if (isNaN(val)) return "–";

                    // Umrechnung: Dezimalstunden → Sekunden
                    const totalSeconds = Math.round(val * 3600);

                    const hours   = Math.floor(totalSeconds / 3600);
                    const minutes = Math.floor((totalSeconds % 3600) / 60);
                    const seconds = totalSeconds % 60;

                    // Format hh:mm:ss
                    return `${hours.toString().padStart(2,'0')}:` +
                           `${minutes.toString().padStart(2,'0')}:` +
                           `${seconds.toString().padStart(2,'0')}`;
                  ]]]
                styles:
                  card:
                    - font-size: 18px
                    - text-align: center
                    - padding: 8px
                  label:
                    - font-weight: bold
                    - color: var(--primary-text-color)
            columns: 1
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                entity: input_boolean.energy_manager_enabled
                name: EM aktiv
                show_state: true
                icon: mdi:flash
                tap_action:
                  action: toggle
                styles:
                  card:
                    - padding: 12px
                    - font-size: 16px
                    - text-align: center
                    - color: white
                    - border-radius: 6px
                    - box-shadow: none
              - type: custom:button-card
                entity: input_boolean.em_safe_mode
                name: SAFE MODE
                show_state: true
                styles:
                  card:
                    - padding: 12px
                    - font-size: 16px
                    - text-align: center
                    - color: white
                    - border-radius: 6px
                    - box-shadow: none
                state:
                  - value: 'on'
                    color: red
                    icon: mdi:shield-alert
                    name: SAFE MODE ON
                  - value: 'off'
                    color: green
                    icon: mdi:shield-check
                    name: Safe Mode
                tap_action:
                  action: toggle
                hold_action:
                  action: more-info
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                entity: sensor.em_dhw_charge_state
                name: DHW State
                show_state: true
                show_icon: false
                tap_action:
                  action: toggle
                styles:
                  card:
                    - padding: 12px
                    - font-size: 16px
                    - text-align: center
                    - color: white
                    - border-radius: 6px
                    - box-shadow: none
              - type: custom:button-card
                entity: input_boolean.em_dhw_charging
                name: DHW Charging
                show_state: true
                show_icon: false
                tap_action:
                  action: toggle
                styles:
                  card:
                    - padding: 12px
                    - font-size: 16px
                    - text-align: center
                    - color: white
                    - border-radius: 6px
                    - box-shadow: none
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                entity: input_boolean.energy_manager_force_zero
                name: Force Zero
                show_state: true
                icon: mdi:power-off
                tap_action:
                  action: toggle
                styles:
                  card:
                    - padding: 12px
                    - font-size: 16px
                    - text-align: center
                    - color: white
                    - border-radius: 6px
                    - box-shadow: none
              - type: custom:button-card
                entity: input_boolean.em_allow_aux
                name: Aux erlauben
                show_state: true
                icon: mdi:radiator
                tap_action:
                  action: toggle
                styles:
                  card:
                    - padding: 12px
                    - font-size: 16px
                    - text-align: center
                    - color: white
                    - border-radius: 6px
                    - box-shadow: none
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                entity: script.em_reset_to_defaults
                name: Reset Defaults
                show_state: false
                icon: mdi:restart-alert
                styles:
                  card:
                    - padding: 12px
                    - font-size: 16px
                    - text-align: center
                    - color: white
                    - border-radius: 6px
                    - box-shadow: none
                tap_action:
                  action: call-service
                  service: script.turn_on
                  service_data:
                    entity_id: script.em_reset_to_defaults
                hold_action:
                  action: more-info
                show_icon: true
                show_name: true
                confirmation:
                  text: Reset auf Standardwerte ausführen?
                  confirm: true
            grid_options:
              columns: 6
              rows: auto
      - type: grid
        cards:
          - type: heading
            heading_style: title
            heading: 'Aktueller Status:'
            icon: mdi:list-status
          - type: vertical-stack
            cards:
              - type: vertical-stack
                cards:
                  - type: horizontal-stack
                    cards:
                      - type: tile
                        entity: sensor.em_pv_power_corrected
                        name: PV (kW)
                        vertical: false
                        features_position: bottom
                      - type: tile
                        entity: sensor.em_home_load_corrected
                        name: Hausverbrauch (kW)
                        vertical: false
                        features_position: bottom
                  - type: horizontal-stack
                    cards:
                      - type: tile
                        entity: sensor.em_pw_current_charge
                        name: Batterie (kW)
                        vertical: false
                        features_position: bottom
                      - type: tile
                        entity: sensor.my_home_ladung
                        name: Powerwall Aufladung
                        vertical: false
                        features_position: bottom
                  - type: horizontal-stack
                    cards:
                      - type: tile
                        entity: sensor.em_target_flow_temp
                        name: Ziel Vorlauf (°C)
                        vertical: false
                        features_position: bottom
                      - type: tile
                        entity: sensor.em_current_flow_temp
                        name: Ist Vorlauf (°C)
                        vertical: false
                        features_position: bottom
                  - type: horizontal-stack
                    cards:
                      - type: tile
                        entity: sensor.em_dhw_boiler_temp
                        name: Boiler (°C)
                        vertical: false
                        features_position: bottom
                      - type: tile
                        entity: number.thermostat_hc1_selected_room_temperature
                        name: Soll-Temp
                        vertical: false
                        features_position: bottom
          - type: tile
            entity: sensor.boiler_air_inlet_temperature_tl2
            name: Außentemp.
            vertical: false
            features_position: bottom
          - type: tile
            entity: input_number.em_flow_temp_step_k
            name: Vorlauf Step (°C)
            vertical: false
            features_position: bottom
          - type: heading
            heading_style: title
            heading: 'Vorgaben:'
            icon: mdi:heat-pump
          - type: vertical-stack
            cards:
              - type: vertical-stack
                cards:
                  - type: horizontal-stack
                    cards:
                      - type: tile
                        entity: input_number.em_pv_threshold_start
                        name: PV Start (kW)
                        vertical: false
                        features_position: bottom
                      - type: tile
                        entity: input_number.em_pv_threshold_stop
                        name: PV Stop (kW)
                        vertical: false
                        features_position: bottom
                  - type: horizontal-stack
                    cards:
                      - type: tile
                        entity: input_number.pw_max_charge
                        name: PW Max (kW)
                        vertical: false
                        features_position: bottom
                      - type: tile
                        entity: input_number.em_flow_temp_step_k
                        name: Vorlauf Step (°C)
                        vertical: false
                        features_position: bottom
                  - type: horizontal-stack
                    cards:
                      - type: tile
                        entity: input_number.em_flow_temp_min
                        name: Vorlauf Min (°C)
                        vertical: false
                        features_position: bottom
                      - type: tile
                        entity: input_number.em_flow_temp_max
                        name: Vorlauf Max (°C)
                        vertical: false
                        features_position: bottom
          - type: tile
            entity: input_number.energy_manager_temp_start
            vertical: false
            features_position: bottom
          - type: tile
            entity: input_number.energy_manager_temp_stop
            vertical: false
            features_position: bottom
      - type: grid
        cards:
          - type: heading
            heading: Geglättete Werte
            heading_style: title
          - type: tile
            entity: sensor.em_pv_power_smoothed
            name: 'Smoothed PV Power '
            vertical: false
            features_position: bottom
          - type: tile
            entity: sensor.em_home_load_smoothed
            name: 'Smoothed Home Load '
            vertical: false
            features_position: bottom
          - type: tile
            entity: sensor.em_powerwall_battery_power_smoothed
            name: Smoothed Ladestrom Batterie
            vertical: false
            features_position: bottom
          - type: heading
            heading: >-
              Aktuelle Ladeleistung der Powerwall (negativer Wert bedeutet
              Laden, wird positiv ausgegeben)
            heading_style: title
          - type: tile
            entity: sensor.em_pw_current_charge
            vertical: false
            features_position: bottom
          - type: heading
            heading: >-
              Überschussleistung, die nach Batterieladung für die Wärmepumpe
              verfügbar bleibt
            heading_style: title
            grid_options:
              columns: full
              rows: 1
          - type: tile
            entity: sensor.em_available_for_heatpump
            vertical: false
            features_position: bottom
          - type: heading
            heading: >-
              Überschussleistung = PV-Leistung minus Hausverbrauch (beide
              geglättet)
            heading_style: title
            grid_options:
              columns: full
              rows: 1
          - type: tile
            entity: sensor.em_energy_surplus
            vertical: false
            features_position: bottom
    max_columns: 3
    header: {}
    subview: true

Ich hoffe, die ausführliche Beschreibung und Struktur hilft dir, die Lösung nachzuvollziehen und bei eigenen Projekten anzupassen.