Bosch Wärmepumpe in Home Assistant integrieren und steuern dieser mit Leistungsüberschuss der PV-Anlage

Einrückung

#load packages
homeassistant:
  packages: !include_dir_merge_named packages
1 „Gefällt mir“

Danke dir. Kleine Korrektur für meine Umgebung:

#load packages
homeassistant:
packages: !include_dir_named packages

:wrench: Update meines Energy-Manager Home-Assistant Packages (Bosch 5800i / PV-optimierte WP-Steuerung)

Ich habe mein bestehendes Energy-Manager-Package erweitert, nachdem es zu einem einmaligen, aber deutlichen Problem gekommen ist:

:backhand_index_pointing_right: In einer Nacht lief der elektrische Zusatzheizer meiner Bosch Compress 5800i AW für mehrere Stunden durch und hat dabei ca. 16,6 kWh verbraucht sowie den Batteriespeicher vollständig entladen.

Die Analyse hat gezeigt:

  • switch.boiler_aux_heater_only war über die gesamte Zeit aktiv

  • dadurch lief die Anlage im reinen Heizstabbetrieb

  • die bestehende Logik hatte keine ausreichende Sicherheitsabschaltung


:brain: Ursache im Systemdesign

Der Fehler war kein Hardwareproblem, sondern ein fehlender Fail-Safe in der Automationslogik:

  • Aktivierung des Heizstabs war möglich (über PV-/Temperaturlogik)

  • aber es gab keine harte Begrenzung der Laufzeit

  • und keine zuverlässige Abschaltung bei fehlendem PV-Überschuss

Dadurch konnte ein einmaliger falscher Zustand über Stunden bestehen bleiben.


:hammer_and_wrench: Eingeführte Sicherheits-Updates

Ich habe das Package um zwei zentrale Schutzmechanismen erweitert:


1. :stopwatch: Maximal-Laufzeit für Heizstab (Dead Man Switch)

Der Heizstab wird jetzt automatisch abgeschaltet, wenn er zu lange aktiv ist:

  - 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"
    action:
      - service: switch.turn_off
        target:
          entity_id: switch.boiler_aux_heater_only

:backhand_index_pointing_right: Ziel:

  • verhindert Dauerbetrieb durch Fehltrigger oder vergessene Zustände

  • begrenzt mögliche Schäden auf wenige Minuten


2. :high_voltage: Automatische Abschaltung bei fehlendem PV-Überschuss

Der Heizstab wird nun konsequent deaktiviert, wenn keine ausreichende PV-Leistung vorhanden ist:

  - 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"
    action:
      - condition: state
        entity_id: switch.boiler_aux_heater_only
        state: "on"
      - service: switch.turn_off
        target:
          entity_id: switch.boiler_aux_heater_only

:backhand_index_pointing_right: Ziel:

  • verhindert nächtlichen Netzbezug durch Heizstab

  • schützt Batteriespeicher zuverlässig vor Entladung

  • sorgt für PV-priorisierte Betriebsweise


:pushpin: Ergebnis der Anpassung

Mit diesen beiden Ergänzungen ist das System jetzt deutlich robuster:

  • :cross_mark: kein unbegrenzter Heizstabbetrieb mehr möglich

  • :cross_mark: kein unkontrolliertes Entladen des Stromspeichers

  • :white_check_mark: automatische Rückfallebene bei Fehlern oder falschen States

  • :white_check_mark: PV-optimierter Betrieb wird konsequent eingehalten


:receipt: Fazit

Der Vorfall hat gezeigt, dass nicht die Optimierungslogik selbst das Problem war, sondern das Fehlen einer harten Sicherheitsbegrenzung für den Heizstabbetrieb.

Mit den neuen Failsafe-Automationen ist das System jetzt deutlich stabiler und fehlertoleranter aufgebaut.

1 „Gefällt mir“

:high_voltage: Energy Manager Update – zuverlässiger Sommer/Winter-Modus + neues Admin UI

Weil ich gerade dabei war, habe noch eine Sommer-/Winterumschaltung eingebaut**.**


:counterclockwise_arrows_button: Was wurde verbessert?

:check_mark: 1. Zuverlässiger Moduswechsel

Der Sommer-/Wintermodus wird jetzt:

  • über den input_select.energy_manager_mode gesteuert

  • zentral über das Script script.em_set_mode

  • automatisch bei Änderung des Selects ausgelöst

:backhand_index_pointing_right: Dadurch ist sichergestellt:

  • keine „halb aktiven“ Zustände

  • vollständiges Ein-/Ausschalten aller relevanten Automationen


:check_mark: 2. Klare Systemlogik pro Modus

:snowflake: Wintermodus

  • Energy Manager aktiv

  • Temperaturprüfung aktiv

  • alle Automationen eingeschaltet:

    • Vorlaufregelung

    • DHW Steuerung

    • Aux-Heater Steuerung

:sun: Sommermodus

  • Energy Manager deaktiviert

  • DHW & Aux-Heater AUS

  • Automationen deaktiviert

  • System wird in definierten Grundzustand zurückgesetzt


:check_mark: 3. Reset-Funktion verbessert

Beim Wechsel in den Sommermodus erfolgt jetzt ein kompletter Reset:

  • alle Input-Booleans zurückgesetzt

  • alle relevanten Input-Numbers auf Default

  • Wärmepumpe geht in definierten Ausgangszustand

  • sichere Abschaltung aller Verbraucher


:brain: Neue Automation: Moduswechsel

Zusätzlich gibt es jetzt eine dedizierte Automation:

  • reagiert direkt auf input_select.energy_manager_mode

  • ruft automatisch script.em_set_mode auf

  • keine manuelle Script-Auslösung mehr notwendig


:control_knobs: Neues UI – Mode Button

Der neue Button zeigt jetzt klar den aktuellen Status und erlaubt direkten Wechsel:

  • :snowflake: Winter = blau

  • :sun: Sommer = rot

  • visuelle Rückmeldung + klare Zustandsanzeige

:camera_with_flash: Screenshots:

  • Screenshot Wintermodus

  • Screenshot Sommermodus


:package: Komplettes aktualisiertes Package

# 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

      # 📣 Feedback
      - service: notify.persistent_notification
        data:
          title: "EM Reset"
          message: "System wurde vollständig zurückgesetzt und in Wintermodus initialisiert"
          
# --- 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

Da ich das hier schon einige Male gelesen habe.

Warmwassertemperaturen von 60 und mehr sind nicht zu empfehlen. Zum einen belasten Sie die Wärmepumpe bzw. den Kompressor unnötig und zum anderen fördern sie die Verkalkung des Wärmetauschers.

@Thomassh Danke für den Hinweis :+1:
Grundsätzlich hast du recht – dauerhaft hohe Temperaturen sind für Effizienz und Lebensdauer nicht optimal.

Bei mir geht es aber eher um PV-Überschussnutzung, d. h. ich fahre höhere Temperaturen nur gezielt, wenn genug Solarleistung da ist. Im Normalbetrieb bleibt die Temperatur deutlich niedriger.

wie groß ist den dein WW Speicher ?

Ich würde, statt den Wärmetauscher durch Kalkausfall zu riskieren bei der normalen WW Bereitung, die bei einem entsprechend gedämmten Speicher und der passenden Temperatur ja sowieso nur einmal pro Tag bzw. sogar nur alle 2 Tage stattfindet, den Zeitpunkt so legen das PV Überschuss vorhanden ist und dann mit dem Heizstab unterstützen/arbeiten.

Die in den kleinen Wassermengen eines Speichers einlagerbare Energie ist sowieso begrenzt.

PV mit einer WP einspeichern funktioniert eigentlich nur gut mit einer FBH da dort der Beton mit entsprechend großer Masse als Speicher funktioniert.

Dazu reicht es aus die VL Temperatur zu erhöhen.

Weitere Eingriffe in das System halte ich bei einer richtig eingestellten FBH und WP sogar für kontraproduktiv.

Das Zusammenspiel von Außentemperatur geführter WP und der Heizkurve klappt normalerweise sehr gut und bewirkt im Winter Laufzeiten von Monaten. Das ist für die WP gesünder als ständige Eingriffe und häufige Starts.

1 „Gefällt mir“

Das funktioniert bei mir tatsächlich.

Nur jetzt speise ich Tag für Tag durchschnittlich 10 kWh ein, obwohl ich den Akku voll habe und den 290 Liter Speicher auf 65° aufgeheizt habe. Das letztere geht automatisch: Fonius meldet Überschuss von 5000W über eine halbe Stunde, dann kriegt die WP per Bus die Information, dass die PV genügend produziert und geht automatisch in die erhöhte Warmwasserbereitung. Wenn ich jetzt noch den Heizstab mit 3kW dazu schalten könnte, wäre ich ja einen entscheidenden Schritt weiter.

Ich würde auf keinen Fall die WP benutzen um den Speicher auf 65 Grad zu erhitzen.

Das belastet zum einen die WP sehr stark und fördert zum anderen die Verkalkung des Wärmetauschers.

Wenn du deinen Speicher um 20 Grad erhitzt kannst du ungefähr

6,74 kw/h speichern.

Die Bosch sollte eigentlich einen Menüpunkt bieten um nur mit Heizstab aufzuheizen, wie der genau heißt kann ich dir nicht sagen weil ich keine habe.

Hej Thomassh, Danke für den Hinweis, bisher habe ich nichts gefunden, aber jetzt suche ich weiter!

Die Frage ist ja auch ob du dadurch überhaupt sparst.

Wenn einmal am Tag WW gemacht wird und zwar während sowieso Überschuss da ist der für die WP genutzt wird ,macht es keinen Sinn den WW Speicher zu überhöhen da sowieso nur PV Energie benutzt wird.

Die einzige Konstellation in der es Sinn machen würde wäre wenn nachgeheizt werden müsste während kein PV Überschuss da ist und durch das überhöhen dies verhindert würde.

Schau da mal Evt hilft das ja

https://community.simon42.com/t/bosch-buderus-junkers-heizungs-integration-in-ha-ueber-km200-oder-ems-eps/8954/106?page=6

1 „Gefällt mir“

Hallo @Reluca
erst einmal vielen Dank für deine Arbeit und dass wir sie nutzen dürfen. Ich konnte mich jetzt recht gut durch die Materie arbeiten, entsprechend auch die Variablen an meine Umgebung anpassen - aber was ich überhaupt nicht verstehe ist folgendes aus deinem Dashboard:

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)


Hier bekomme ich immer die Meldung die Entity sensor.wp_laufzeit_bei_uberschuss_heute_neu ist unbekannt. In deiner energy_manager.yaml finde ich aber auch nichts, was ich irgendwie in Zusammenhang bringen könnte. Ich verstehe daher nicht so recht, woher du die Daten für diese Berechnung erhältst. Vielleicht kannst du mir da eine kleine Hilfestellung geben?

Beste Grüße

Marcus

Hallo Marcus,

entschuldige bitte meine etwas verspätete Antwort.

Die von dir angesprochene Entity
sensor.wp_laufzeit_bei_uberschuss_heute_neu
kommt nicht aus dem Dashboard und ist auch kein Bestandteil der energy_manager.yaml.
Sie wird stattdessen als eigener Sensor per YAML definiert, genauer gesagt als history_stats‑Sensor.

Ich habe diesen Sensor in einer separaten Datei angelegt:

/homeassistant/integration_sensors/laufzeit_wp.yaml

mit folgendem Inhalt:

- 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() }}"

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).

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

# 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.

Ich hoffe, damit ist klarer, woher die Daten kommen und warum die Entity bei dir als „unbekannt“ auftaucht, wenn dieser Sensor nicht angelegt ist.

Viele Grüße
Rene

1 „Gefällt mir“

Vielen lieben Dank für die ausführliche Antwort. Da werde ich mich dann auch einmal reinwuchsen :slight_smile: