Lösung zur smarten Heizungssteuerung in Home Assistant mit Bosch Thermostaten (Generation 2) und Zigbee2MQTT ohne 3. Anbieter Plugins (HACS)

Da ich online keine Gesamtlösung zu meiner Anforderung gefunden habe, möchte ich der Community etwas zurückgeben und präsentiere hier meine Lösung.

Die Ausgangssituation:

Ein Raum mit mehreren Heizkörpern soll synchronisiert und sauber gesteuert werden. Das Ziel ist es, dass eine Temperaturänderung an einem Heizkörper automatisch auf alle anderen übertragen wird. Zusätzlich erhalten die Thermostate die Temperatur von einem externen Sonoff-Sensor.

Ein wichtiges Anliegen ist es, Über- oder Untersteuerungen zu vermeiden. Wenn ich beispielsweise 21°C einstelle, soll die Temperatur möglichst konstant bleiben, mit einer minimalen Abweichung von maximal ±0,5°C. Normal steuern die Thermostate zu pessimistisch. Soll bedeuten, dass die Ventile nach erreichen der Zieltemperatur nur langsam geschlossen werden und somit das Nachheizen der Heizkörper durch Ihre Trägheit wieder befeuert wird = Der Raum wird zu warm.

Die gesamte Steuerung basiert auf Home Assistant Automationen ohne Drittanbieter-Plugins wie Better Thermostat, um Fehler zu minimieren und eine robuste, flache Struktur zu gewährleisten. So stelle ich sicher, dass das System auch bei Ausfällen, wie einer leeren Batterie des externen Sensors, weiterhin funktioniert.

Insgesamt habe ich drei Automationen entwickelt, die ich im Folgenden detailliert erklären werde.

Lösung:

  1. Automation

Hier meine erste Automation, die den 4 Thermostaten in meinem Raum die Raumtemperatur übergibt. Die Bosch Thermostate haben ein integriertes Fallback: Nach 30 Minuten ohne Temperatur-Update kehren sie zur selbst gemessenen Temperatur zurück. Das bietet zwar eine Ausfallsicherheit, wenn der externe Sensor keine Daten sendet, kann aber problematisch sein, wenn es nicht in der Automation beachtet wird. Um dies zu vermeiden, übermittelt meine Automation bei jeder Temperaturänderung die neuen Werte an die Thermostate. Zusätzlich wird alle 25 Minuten die Temperatur erneut übertragen, auch wenn Sie sich nicht geändert hat, um sicherzustellen, dass die Thermostate stets aktuelle Daten erhalten.

alias: Heizung UG Raumtemperatur Sensor Sonoff
description: >-
  Überträgt die Sonoff Temperatur an die Thermostate bei Änderung oder alle 25
  Minuten
triggers:
  - entity_id: sensor.sonoff_temperature
    alias: External Temperature Sensor Change
    trigger: state
  - minutes: /25
    alias: 25 Minuten Trigger
    trigger: time_pattern
conditions:
  - condition: template
    value_template: >-
      {{ states('sensor.sonoff_temperature') !=
      state_attr('number.heizung_wohnzimmer_remote_temperature',
      'current_value') }}
actions:
  - data:
      value: "{{ states('sensor.sonoff_temperature') }}"
    target:
      entity_id: number.heizung_wohnzimmer_remote_temperature
    action: number.set_value
  - data:
      value: "{{ states('sensor.sonoff_temperature') }}"
    target:
      entity_id: number.heizung_badezimmer_ug_remote_temperature
    action: number.set_value
  - data:
      value: "{{ states('sensor.sonoff_temperature') }}"
    target:
      entity_id: number.heizung_kuche_remote_temperature
    action: number.set_value
  - data:
      value: "{{ states('sensor.sonoff_temperature') }}"
    target:
      entity_id: number.heizung_esszimmer_remote_temperature
    action: number.set_value
mode: single

  1. Automation

Diese Automation sorgt dafür, dass eine Temperaturänderung an einem der vier Thermostate automatisch auf die anderen drei übertragen wird. Dabei wird sichergestellt, dass kein Loop entsteht und unnötige Befehle vermieden werden. Die Änderung muss mindestens 10 Sekunden bestehen bleiben, bevor die Automation ausgelöst wird. Dies verhindert, dass kurzzeitige oder versehentliche Änderungen (z. B. durch ein Interface-Lag oder kurzes Verstellen) direkt übernommen werden.

alias: Thermostate synchronisieren Neu
description: ""
triggers:
  - entity_id:
      - climate.heizung_badezimmer_ug
      - climate.heizung_esszimmer
      - climate.heizung_kuche
      - climate.heizung_wohnzimmer
    attribute: temperature
    for: "00:00:10"
    trigger: state
actions:
  - variables:
      source_entity: "{{ trigger.entity_id }}"
      new_temperature: "{{ trigger.to_state.attributes.temperature }}"
  - repeat:
      for_each:
        - climate.heizung_badezimmer_ug
        - climate.heizung_esszimmer
        - climate.heizung_kuche
        - climate.heizung_wohnzimmer
      sequence:
        - if:
            - condition: template
              value_template: "{{ repeat.item != source_entity }}"
          then:
            - data:
                entity_id: "{{ repeat.item }}"
                temperature: "{{ new_temperature }}"
              action: climate.set_temperature
mode: restart

  1. Automation - schwerste in der Umsetzung :smiley:

Die dritte Automation in meinem Setup sorgt dafür, dass die Ventilöffnung präzise gesteuert wird, wenn die Zieltemperatur erreicht ist. Zum Glück kann das Boschthermostat das Ventil fein steuern und kennt nicht nur an und aus.
Es würde übrigens auch schon alles ohne diese Automation gehen, nur schwankt dann die Temperatur im Raum zu stark. Bei 21 Grad Zieltemperatur z.B. von 20,5 bis 22,5 Grad.

alias: Dynamische Temperaturregelung (mit individueller Boost-Ausnahme)
description: >
  Schließt das Ventil, wenn das Thermostat Heizanforderungen stellt, obwohl die
  Zieltemperatur erreicht oder überschritten wurde, außer der Boost-Modus ist
  für diesen Heizkörper aktiv.
triggers:
  - value_template: |
      {{
        states('sensor.heizung_badezimmer_ug_pi_heating_demand') | int > 10 and
        state_attr('climate.heizung_badezimmer_ug', 'current_temperature') >=
        state_attr('climate.heizung_badezimmer_ug', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/Heizung Badezimmer UG/set
      boost: switch.heizung_badezimmer_ug_boost_heating
    trigger: template
  - value_template: |
      {{
        states('sensor.heizung_esszimmer_pi_heating_demand') | int > 10 and
        state_attr('climate.heizung_esszimmer', 'current_temperature') >=
        state_attr('climate.heizung_esszimmer', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/Heizung Esszimmer/set
      boost: switch.heizung_esszimmer_boost_heating
    trigger: template
  - value_template: |
      {{
        states('sensor.heizung_kuche_pi_heating_demand') | int > 10 and
        state_attr('climate.heizung_kuche', 'current_temperature') >=
        state_attr('climate.heizung_kuche', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/Heizung Küche/set
      boost: switch.heizung_kuche_boost_heating
    trigger: template
  - value_template: |
      {{
        states('sensor.heizung_wohnzimmer_pi_heating_demand') | int > 10 and
        state_attr('climate.heizung_wohnzimmer', 'current_temperature') >=
        state_attr('climate.heizung_wohnzimmer', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/Heizung Wohnzimmer/set
      boost: switch.heizung_wohnzimmer_boost_heating
    trigger: template
conditions:
  - condition: template
    value_template: |
      {{ is_state(boost, 'off') }}
actions:
  - data:
      topic: "{{ topic }}"
      payload: "{ \"pi_heating_demand\": 10 }"
    action: mqtt.publish
mode: queued

Also was macht die Automation:

  • Ventilsteuerung: Wenn die Zieltemperatur im Raum erreicht wird, wird das Ventil automatisch auf 10% geschlossen, um ein Nachheizen bestmöglich zu verhindern.
  • Temperaturüberwachung: Die Automation sorgt dafür, dass das Ventil bei Erreichen oder Überschreiten der Zieltemperatur auf 10% bleibt. Nur wenn die Temperatur unter den Zielwert fällt, übernimmt das Thermostat wieder die Steuerung.
  • Boost-Modus: Eine zusätzliche Bedingung stellt sicher, dass der Boost-Modus, der das Ventil auf 80% öffnet, nicht durch die Automation eingeschränkt wird. Wenn man z.B. im Bad beim Duschen die Temperatur kurzfristig erhöhen möchte.

Zusammenfassung:

Insgesamt bin ich sehr zufrieden mit der Lösung, besonders die erste und zweite Automation funktionieren hervorragend. Bei der dritten Automation habe ich noch etwas Bauchschmerzen, da das Thermostat alle 15 Minuten die Ventilsteuerung wieder übernehmen möchte. Das führt dazu, dass bei einer Zieltemperatur von 21°C, die Automation zwar greift, aber nach 15 Minuten das Thermostat das Ventil wieder weiter öffnet (z.B. 35%), obwohl die Raumtemperatur mit 21,5°C erreicht/überschritten wurde. Meine 3. Automation greift dann zwar und fährt das Ventil wieder auf 10% zurück, aber irgendwie müsset es da doch eine bessere Lösung geben.

Falls jemand Ideen hat, wie man das optimieren kann, freue ich mich über Vorschläge.

3 „Gefällt mir“

Super…das klingt ziemlich exakt nach dem was ich mir vorgestellt habe!!
Werde ich die Tage testen. 1000 Dank schonmal

Habe es jetzt getestet. Die zweite Automation funktioniert bei mir perfekt! Vielen Dank, habe länger danach gesucht. Die erste klappt so bei mir leider nicht, da meine Sonoff Thermostate (bis jetzt?) keine externe Temperatur unterstützen. Angeblich steht ein update an…mal schauen.
Hatte jetzt eine Automation gefunden, welche jeweils die Kallibration so anpassen soll, das entsprechend die extern gemessene Temperatur abgebildet wird…aber das haut nicht hin.
Mal sehen, vielleicht finde ich da noch eine Lösung.
Besten Dank!

Inzwischen unterstützen die Sonoff TRVs ein externes Thermostat, man muss es nur einstellen in Z2M und den Wert dort “füttern”.

Ich mache es über eine Automation, die bei einer Temperaturänderung den Wert überschreibt. Sollte es keine Temperaturänderung geben, wird der Wert alle 30 Minuten geändert.

alias: Externe Temperatur in TRV's setzen
description: ""
triggers:
  - trigger: state
    entity_id:
      - sensor.badezimmer_temperature
    id: BAZ
  - trigger: state
    entity_id:
      - sensor.kinderzimmer_temperature
    id: KiZ
  - trigger: state
    entity_id:
      - sensor.schlafzimmer_temperature
    id: ScZ
  - trigger: state
    entity_id:
      - sensor.wohnzimmer_temperature
    id: WoZ
  - trigger: state
    entity_id:
      - sensor.arbeitszimmer_temperature
    id: ArZ
  - trigger: time_pattern
    id: Timer
    minutes: /30
conditions: []
actions:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - BAZ
              - Timer
        sequence:
          - action: mqtt.publish
            data:
              topic: zigbee2mqtt/IEEE-123456789/set/external_temperature_input
              payload: "{{ states('sensor.badezimmer_temperature') | float }}"
      - conditions:
          - condition: trigger
            id:
              - KiZ
              - Timer
        sequence:
          - action: mqtt.publish
            data:
              topic: zigbee2mqtt/IEEE-123456789/set/external_temperature_input
              payload: "{{ states('sensor.kinderzimmer_temperature') | float }}"
      - conditions:
          - condition: trigger
            id:
              - ScZ
              - Timer
        sequence:
          - action: mqtt.publish
            data:
              topic: zigbee2mqtt/IEEE-123456789/set/external_temperature_input
              payload: "{{ states('sensor.schlafzimmer_temperature') | float }}"
      - conditions:
          - condition: trigger
            id:
              - WoZ
              - Timer
        sequence:
          - action: mqtt.publish
            data:
              topic: zigbee2mqtt/IEEE-123456789/set/external_temperature_input
              payload: "{{ states('sensor.wohnzimmer_temperature') | float }}"
          - action: mqtt.publish
            data:
              topic: zigbee2mqtt/IEEE-123456789/set/external_temperature_input
              payload: "{{ states('sensor.wohnzimmer_temperature') | float }}"
      - conditions:
          - condition: trigger
            id:
              - Timer
              - ArZ
        sequence:
          - action: mqtt.publish
            data:
              topic: zigbee2mqtt/IEEE-123456789/set/external_temperature_input
              payload: "{{ states('sensor.arbeitszimmer_temperature') | float }}"
mode: parallel
max: 10

Du musst natürlich die Sensoren anpassen und ‘IEEE-123456789’ mit der IEEE Adresse Deines Ventiles ersetzen.

alias: Dynamische Temperaturregelung (mit individueller Boost-Ausnahme)
description: >
  Schließt das Ventil, wenn das Thermostat Heizanforderungen stellt, obwohl die
  Zieltemperatur erreicht oder überschritten wurde, außer der Boost-Modus ist
  für diesen Heizkörper aktiv.
triggers:
  - value_template: |
      {{
        states('sensor.bosch_hkt_schlafen_pi_heating_demand') | int > 10 and
        state_attr('climate.bosch_hkt_schlafen', 'current_temperature') >=
        state_attr('climate.bosch_hkt_schlafen', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/BOSCH HKT Schlafen/set
      boost: switch.bosch_hkt_schlafen_boost_heating
    trigger: template
  - value_template: |
      {{
        states('sensor.bosch_hkt_bad_pi_heating_demand') | int > 10 and
        state_attr('climate.bosch_hkt_bad', 'current_temperature') >=
        state_attr('climate.bosch_hkt_bad', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/BOSCH HKT Bad/set
      boost: switch.bosch_hkt_bad_boost_heating
    trigger: template
  - value_template: |
      {{
        states('sensor.bosch_hkt_sofa_pi_heating_demand') | int > 10 and
        state_attr('climate.bosch_hkt_sofa', 'current_temperature') >=
        state_attr('climate.bosch_hkt_sofa', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/BOSCH HKT Sofa/set
      boost: switch.bosch_hkt_sofa_boost_heating
    trigger: template
  - value_template: |
      {{
        states('sensor.bosch_hkt_kuche_pi_heating_demand') | int > 10 and
        state_attr('climate.bosch_hkt_kuche', 'current_temperature') >=
        state_attr('climate.bosch_hkt_kuche', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/BOSCH HKT Kuche/set
      boost: switch.bosch_hkt_kuche_boost_heating
    trigger: template
  - value_template: |
      {{
        states('sensor.bosch_hkt_flur_pi_heating_demand') | int > 10 and
        state_attr('climate.bosch_hkt_flur', 'current_temperature') >=
        state_attr('climate.bosch_hkt_flur', 'temperature')
      }}
    variables:
      topic: zigbee2mqtt/BOSCH HKT Flur/set
      boost: switch.bosch_hkt_flur_boost_heating
    trigger: template
conditions:
  - condition: template
    value_template: |
      {{ is_state(boost, 'off') }}
actions:
  - data:
      topic: "{{ topic }}"
      payload: "{ \"pi_heating_demand\": 10 }"
    action: mqtt.publish
mode: queued

Hi, ich glaube bei mir stimmt noch etwas nicht.

(PS: DANKE Für deinen Post!!)

Kannst du mir da evtl. bei Helfen? LG