From e40e2b3e905f630dcf8763a423e5bbd2bec198c6 Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 11:53:46 +0100 Subject: [PATCH 01/14] add first version of schedule selector --- Climate/schedule_selector.yaml | 199 +++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 Climate/schedule_selector.yaml diff --git a/Climate/schedule_selector.yaml b/Climate/schedule_selector.yaml new file mode 100644 index 0000000..9d52674 --- /dev/null +++ b/Climate/schedule_selector.yaml @@ -0,0 +1,199 @@ +blueprint: + name: Heizplan Selector basierend auf Kalenderereignissen + description: > + Wählt automatisch einen Heizplan basierend auf Kalenderereignissen aus. + + Der Blueprint überwacht einen Kalender und wechselt zwischen verschiedenen + Schedule-Helfern basierend auf Keywords im Event-Titel oder der Beschreibung. + + **WICHTIG**: Die Anzahl der Scheduler und Keywordlisten muss übereinstimmen! + Jede Zeile in den Keywordlisten entspricht einem Scheduler an der gleichen Position. + + Bei Event-Ende wird zum Standard-Scheduler zurückgekehrt, außer ein anderes + Event ist bereits aktiv. + + Required = * + domain: automation + homeassistant: + min_version: "2024.6.0" + author: Me + input: + calendar_section: + name: Kalender-Einstellungen + description: Kalender und zeitlicher Offset für Events + collapsed: false + input: + calendar_entity: + name: Kalender * + description: Der Kalender, der für Event-Erkennung überwacht wird + selector: + entity: + filter: + - domain: calendar + + event_offset: + name: Zeitlicher Offset + description: > + Optional: Zeitlicher Versatz für Event-Start und -Ende. + Beispiel: "-00:15:00" startet 15 Minuten vor dem Event. + default: "00:00:00" + selector: + duration: + enable_day: false + + scheduler_section: + name: Scheduler-Konfiguration + description: Schedule-Helfer und zugeordnete Keywords + collapsed: false + input: + scheduler_list: + name: Scheduler-Liste * + description: > + Liste der Schedule-Helfer (schedule entities). + Die Reihenfolge muss mit den Keywordlisten übereinstimmen! + selector: + entity: + multiple: true + filter: + - domain: schedule + + keyword_lists: + name: Keywordlisten * + description: > + Eine Keywordliste pro Zeile (komma-separiert). + Beispiel: + urlaub,vacation,holiday + homeoffice,wfh,remote + normal,standard + + **Position in der Liste = Position im Scheduler!** + Zeile 1 → Scheduler 1, Zeile 2 → Scheduler 2, etc. + selector: + text: + multiline: true + type: text + + default_scheduler: + name: Standard-Scheduler * + description: > + Dieser Scheduler wird aktiviert, wenn kein Keyword matched + oder ein Event endet (und kein anderes aktiv ist). + selector: + entity: + filter: + - domain: schedule + + helper_section: + name: Helper-Entity + description: Entity zum Speichern des aktiven Schedulers + collapsed: false + input: + helper_entity: + name: Input Select Helper * + description: > + Die input_select Helper-Entity, in die der aktive Scheduler + geschrieben wird. Optionen sollten die Entity-IDs der Scheduler enthalten. + selector: + entity: + filter: + - domain: input_select + +mode: single +max_exceeded: silent + +triggers: + - platform: calendar + event: start + entity_id: !input calendar_entity + offset: !input event_offset + id: CALENDAR_START + + - platform: calendar + event: end + entity_id: !input calendar_entity + offset: !input event_offset + id: CALENDAR_END + +variables: + schedulers: !input scheduler_list + keywords_raw: !input keyword_lists + default_scheduler: !input default_scheduler + helper: !input helper_entity + calendar: !input calendar_entity + + # Keywords in Liste umwandeln (eine pro Zeile) + keyword_lists: > + {{ keywords_raw.split('\n') | select() | list }} + + # Bei Event-Start: Keywords matchen + matched_scheduler: > + {% set ns = namespace(scheduler = none) %} + {% if trigger.id == 'CALENDAR_START' %} + {% set event_text = (trigger.calendar_event.summary | lower) ~ ' ' ~ (trigger.calendar_event.description | default('') | lower) %} + {% for i in range(keyword_lists | length) %} + {% set keywords = keyword_lists[i].split(',') | map('trim') | map('lower') | list %} + {% set matched = namespace(found = false) %} + {% for keyword in keywords %} + {% if keyword in event_text %} + {% set matched.found = true %} + {% endif %} + {% endfor %} + {% if matched.found and i < (schedulers | length) %} + {% set ns.scheduler = schedulers[i] %} + {% break %} + {% endif %} + {% endfor %} + {% endif %} + {{ ns.scheduler }} + + # Bei Event-Ende: Prüfen ob ein anderes Event aktiv ist + other_event_active: > + {% if trigger.id == 'CALENDAR_END' %} + {% set start = state_attr(calendar, 'start_time') %} + {% set end = state_attr(calendar, 'end_time') %} + {% if start and end %} + {% set now_ts = now().timestamp() %} + {% set start_ts = as_timestamp(start) %} + {% set end_ts = as_timestamp(end) %} + {{ start_ts <= now_ts and now_ts <= end_ts }} + {% else %} + false + {% endif %} + {% else %} + false + {% endif %} + +conditions: [] + +actions: + - choose: + # Event-Start: Scheduler aktivieren wenn Keyword matched + - conditions: + - condition: trigger + id: CALENDAR_START + - condition: template + value_template: "{{ matched_scheduler is not none and matched_scheduler != '' }}" + sequence: + - service: input_select.select_option + target: + entity_id: !input helper_entity + data: + option: "{{ matched_scheduler }}" + alias: Event-Start - Scheduler aktivieren + + # Event-Ende: Zu Standard zurückkehren (nur wenn kein anderes Event aktiv) + - conditions: + - condition: trigger + id: CALENDAR_END + - condition: template + value_template: "{{ not other_event_active }}" + sequence: + - service: input_select.select_option + target: + entity_id: !input helper_entity + data: + option: "{{ default_scheduler }}" + alias: Event-Ende - Standard-Scheduler aktivieren + + default: [] + -- 2.49.1 From 640681ce43e8fe32cba02d11b56dae56e7d63aa5 Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 12:24:37 +0100 Subject: [PATCH 02/14] add warning for input_select --- Climate/schedule_selector.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Climate/schedule_selector.yaml b/Climate/schedule_selector.yaml index 9d52674..3fd728c 100644 --- a/Climate/schedule_selector.yaml +++ b/Climate/schedule_selector.yaml @@ -91,8 +91,17 @@ blueprint: helper_entity: name: Input Select Helper * description: > - Die input_select Helper-Entity, in die der aktive Scheduler - geschrieben wird. Optionen sollten die Entity-IDs der Scheduler enthalten. + Die input_select Helper-Entity, in die der aktive Scheduler geschrieben wird. + + **WICHTIG**: Die Options der input_select müssen vorbereitet werden! + Sie müssen folgende Entity-IDs enthalten: + - Alle Scheduler aus der Scheduler-Liste + - Den Standard-Scheduler + + Beispiel Options: + ["schedule.urlaub_heizplan", "schedule.homeoffice_heizplan", "schedule.normal_heizplan", "schedule.standard_heizplan"] + + Die Blueprint schreibt die Entity-ID (nicht den Friendly Name) in die Helper-Entity. selector: entity: filter: -- 2.49.1 From 9cb8b30fcaa8c369be1f11467a0f9a14b4607143 Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 13:28:25 +0100 Subject: [PATCH 03/14] check if schedule exists in selector --- Climate/schedule_selector.yaml | 80 +++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/Climate/schedule_selector.yaml b/Climate/schedule_selector.yaml index 3fd728c..3878c47 100644 --- a/Climate/schedule_selector.yaml +++ b/Climate/schedule_selector.yaml @@ -183,11 +183,53 @@ actions: - condition: template value_template: "{{ matched_scheduler is not none and matched_scheduler != '' }}" sequence: - - service: input_select.select_option - target: - entity_id: !input helper_entity - data: - option: "{{ matched_scheduler }}" + - if: + - condition: template + value_template: > + {{ matched_scheduler in state_attr(helper, 'options') }} + then: + # Matched Scheduler ist in Options vorhanden + - service: input_select.select_option + target: + entity_id: !input helper_entity + data: + option: "{{ matched_scheduler }}" + else: + # Matched Scheduler nicht vorhanden - Fallback auf Default + - if: + - condition: template + value_template: > + {{ default_scheduler in state_attr(helper, 'options') }} + then: + - service: input_select.select_option + target: + entity_id: !input helper_entity + data: + option: "{{ default_scheduler }}" + - service: persistent_notification.create + data: + title: "⚠️ Heizplan Selector - Konfigurationsfehler" + message: > + Der gematchte Scheduler '{{ matched_scheduler }}' ist nicht in den Options der input_select '{{ helper }}' vorhanden. + + Fallback auf Standard-Scheduler '{{ default_scheduler }}'. + + Event: {{ trigger.calendar_event.summary }} + notification_id: "schedule_selector_config_error" + else: + # Auch Default-Scheduler nicht vorhanden + - service: persistent_notification.create + data: + title: "❌ Heizplan Selector - Kritischer Konfigurationsfehler" + message: > + Weder der gematchte Scheduler '{{ matched_scheduler }}' noch der Standard-Scheduler '{{ default_scheduler }}' sind in den Options der input_select '{{ helper }}' vorhanden. + + Kein Scheduler wurde aktiviert! + + Bitte input_select Options korrigieren. + + Event: {{ trigger.calendar_event.summary }} + notification_id: "schedule_selector_critical_error" alias: Event-Start - Scheduler aktivieren # Event-Ende: Zu Standard zurückkehren (nur wenn kein anderes Event aktiv) @@ -197,11 +239,29 @@ actions: - condition: template value_template: "{{ not other_event_active }}" sequence: - - service: input_select.select_option - target: - entity_id: !input helper_entity - data: - option: "{{ default_scheduler }}" + - if: + - condition: template + value_template: > + {{ default_scheduler in state_attr(helper, 'options') }} + then: + - service: input_select.select_option + target: + entity_id: !input helper_entity + data: + option: "{{ default_scheduler }}" + else: + - service: persistent_notification.create + data: + title: "❌ Heizplan Selector - Konfigurationsfehler" + message: > + Der Standard-Scheduler '{{ default_scheduler }}' ist nicht in den Options der input_select '{{ helper }}' vorhanden. + + Kein Scheduler wurde aktiviert! + + Bitte input_select Options korrigieren. + + Event: {{ trigger.calendar_event.summary }} + notification_id: "schedule_selector_default_error" alias: Event-Ende - Standard-Scheduler aktivieren default: [] -- 2.49.1 From e200814de6915eb4249a7e1bd2c8ea9ab085ca6d Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 13:30:19 +0100 Subject: [PATCH 04/14] use friendly name --- Climate/schedule_selector.yaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Climate/schedule_selector.yaml b/Climate/schedule_selector.yaml index 3878c47..92f87b4 100644 --- a/Climate/schedule_selector.yaml +++ b/Climate/schedule_selector.yaml @@ -94,14 +94,14 @@ blueprint: Die input_select Helper-Entity, in die der aktive Scheduler geschrieben wird. **WICHTIG**: Die Options der input_select müssen vorbereitet werden! - Sie müssen folgende Entity-IDs enthalten: + Sie müssen die Friendly Names folgender Entities enthalten: - Alle Scheduler aus der Scheduler-Liste - Den Standard-Scheduler Beispiel Options: - ["schedule.urlaub_heizplan", "schedule.homeoffice_heizplan", "schedule.normal_heizplan", "schedule.standard_heizplan"] + ["Urlaub Heizplan", "Home Office Heizplan", "Normal Heizplan", "Standard Heizplan"] - Die Blueprint schreibt die Entity-ID (nicht den Friendly Name) in die Helper-Entity. + Die Blueprint schreibt den Friendly Name (nicht die Entity-ID) in die Helper-Entity. selector: entity: filter: @@ -186,33 +186,33 @@ actions: - if: - condition: template value_template: > - {{ matched_scheduler in state_attr(helper, 'options') }} + {{ state_attr(matched_scheduler, 'friendly_name') in state_attr(helper, 'options') }} then: # Matched Scheduler ist in Options vorhanden - service: input_select.select_option target: entity_id: !input helper_entity data: - option: "{{ matched_scheduler }}" + option: "{{ state_attr(matched_scheduler, 'friendly_name') }}" else: # Matched Scheduler nicht vorhanden - Fallback auf Default - if: - condition: template value_template: > - {{ default_scheduler in state_attr(helper, 'options') }} + {{ state_attr(default_scheduler, 'friendly_name') in state_attr(helper, 'options') }} then: - service: input_select.select_option target: entity_id: !input helper_entity data: - option: "{{ default_scheduler }}" + option: "{{ state_attr(default_scheduler, 'friendly_name') }}" - service: persistent_notification.create data: title: "⚠️ Heizplan Selector - Konfigurationsfehler" message: > - Der gematchte Scheduler '{{ matched_scheduler }}' ist nicht in den Options der input_select '{{ helper }}' vorhanden. + Der gematchte Scheduler '{{ state_attr(matched_scheduler, 'friendly_name') }}' ({{ matched_scheduler }}) ist nicht in den Options der input_select '{{ helper }}' vorhanden. - Fallback auf Standard-Scheduler '{{ default_scheduler }}'. + Fallback auf Standard-Scheduler '{{ state_attr(default_scheduler, 'friendly_name') }}' ({{ default_scheduler }}). Event: {{ trigger.calendar_event.summary }} notification_id: "schedule_selector_config_error" @@ -222,7 +222,7 @@ actions: data: title: "❌ Heizplan Selector - Kritischer Konfigurationsfehler" message: > - Weder der gematchte Scheduler '{{ matched_scheduler }}' noch der Standard-Scheduler '{{ default_scheduler }}' sind in den Options der input_select '{{ helper }}' vorhanden. + Weder der gematchte Scheduler '{{ state_attr(matched_scheduler, 'friendly_name') }}' ({{ matched_scheduler }}) noch der Standard-Scheduler '{{ state_attr(default_scheduler, 'friendly_name') }}' ({{ default_scheduler }}) sind in den Options der input_select '{{ helper }}' vorhanden. Kein Scheduler wurde aktiviert! @@ -242,19 +242,19 @@ actions: - if: - condition: template value_template: > - {{ default_scheduler in state_attr(helper, 'options') }} + {{ state_attr(default_scheduler, 'friendly_name') in state_attr(helper, 'options') }} then: - service: input_select.select_option target: entity_id: !input helper_entity data: - option: "{{ default_scheduler }}" + option: "{{ state_attr(default_scheduler, 'friendly_name') }}" else: - service: persistent_notification.create data: title: "❌ Heizplan Selector - Konfigurationsfehler" message: > - Der Standard-Scheduler '{{ default_scheduler }}' ist nicht in den Options der input_select '{{ helper }}' vorhanden. + Der Standard-Scheduler '{{ state_attr(default_scheduler, 'friendly_name') }}' ({{ default_scheduler }}) ist nicht in den Options der input_select '{{ helper }}' vorhanden. Kein Scheduler wurde aktiviert! -- 2.49.1 From 576b6fa98a22c0ab30afb7893d880792ecd9065f Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 13:44:08 +0100 Subject: [PATCH 05/14] use schedule entity and use active scheduler for determination of temp --- Climate/bosch_bth-ra_control.yaml | 38 +++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index 2b29fd4..bcfe725 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -73,13 +73,27 @@ blueprint: input: radiator_schedules: name: Heizpläne - description: Alle Heizpläne die für den Radiator berücksichtigt werden sollen. Sind mehrere Schedules aktiv, wird der erste mit einem gültigen Slot verwendet. + description: Alle Heizpläne die für den Radiator berücksichtigt werden sollen. Sind mehrere Schedules aktiv, wird der erste mit einer gültigen Temperatur verwendet. default: [] selector: entity: multiple: true filter: - - domain: switch + - domain: schedule + + active_scheduler_selector: + name: Aktiver Scheduler Selector + description: > + Input Select der den aktuell aktiven Scheduler enthält (per Friendly Name). + Wird verwendet um zu bestimmen welcher Schedule für die Temperatur verwendet werden soll. + + **WICHTIG**: Die Options müssen die Friendly Names der Scheduler aus der Scheduler-Liste enthalten. + + Beispiel: Wird typischerweise vom "Heizplan Selector" Blueprint gesteuert. + selector: + entity: + filter: + - domain: input_select away_section: name: Konfiguration für Abwesenheitsmodus description: Konfiguration für das Absenken der Heizung im Abwesenheitsmodus eines Alarmsystems @@ -110,6 +124,7 @@ variables: temperature_sensor: !input temperature_sensor heating_period_switch: !input heating_period_switch radiator_schedules: !input radiator_schedules + active_scheduler_selector: !input active_scheduler_selector away_temperature: !input away_temperature alarm_control_panel: !input alarm_control_panel is_heating_period: > @@ -138,19 +153,18 @@ variables: {{ window_detection_entity_id.id }} scheduled_temperature: > {% set ns = namespace(current_temperature = none) %} - {% for schedule in radiator_schedules %} - {% if states(schedule) == 'on' and state_attr(schedule, 'current_slot') is not none %} - {% set current_slot = state_attr(schedule, 'current_slot') %} - {% set actions = state_attr(schedule, 'actions') %} - {% if actions is not none and actions | length > current_slot %} - {% set action = actions[current_slot] %} - {% if action.data.temperature is defined and action.data.temperature | is_number %} - {% set ns.current_temperature = action.data.temperature %} + {% set selected_friendly_name = states(active_scheduler_selector) %} + {% if selected_friendly_name is not none and selected_friendly_name != 'unknown' %} + {% for schedule in radiator_schedules %} + {% if state_attr(schedule, 'friendly_name') == selected_friendly_name and states(schedule) == 'on' %} + {% set temp = state_attr(schedule, 'temp') %} + {% if temp is not none and temp | is_number %} + {% set ns.current_temperature = temp %} {% break %} {% endif %} {% endif %} - {% endif %} - {% endfor %} + {% endfor %} + {% endif %} {{ ns.current_temperature }} target_temperature: > {% if alarm_control_panel and is_state(alarm_control_panel, 'armed_away') %} -- 2.49.1 From ab94183c65ffbd474391b101ebe1097eb523bea5 Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 17:23:57 +0100 Subject: [PATCH 06/14] add trigger for schedule changes --- Climate/bosch_bth-ra_control.yaml | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index bcfe725..a69ec6c 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -1,4 +1,5 @@ -mode: single +mode: queued +max: 5 blueprint: name: Bosch BTH-RA Radiator Control homeassistant: @@ -89,6 +90,9 @@ blueprint: **WICHTIG**: Die Options müssen die Friendly Names der Scheduler aus der Scheduler-Liste enthalten. + **HINWEIS**: Wenn der angezeigte Scheduler inaktiv ist (off), wird automatisch + auf die Abwesenheitstemperatur zurückgegriffen. + Beispiel: Wird typischerweise vom "Heizplan Selector" Blueprint gesteuert. selector: entity: @@ -214,6 +218,13 @@ triggers: entity_id: !input heating_period_switch to: "off" id: HEATING_PERIOD_OFF + - platform: state + entity_id: !input radiator_schedules + attribute: temp + id: SCHEDULE_TEMP_CHANGED + - platform: state + entity_id: !input active_scheduler_selector + id: SCHEDULER_CHANGED actions: - choose: @@ -346,13 +357,23 @@ actions: entity_id: "{{ remote_temperature_entity }}" alias: Synchronisiere Temperatur am TRV (zeitbasiert, wenn länger als 20min unverändert) alias: Temperatursynchronisation - # setze Solltemperatur basierend auf Alarm-Status + # setze Solltemperatur bei Schedule und Alarm-Status-Änderungen - choose: - conditions: - condition: trigger id: - ALARM_ARMED_AWAY - ALARM_DISARMED_AWAY + - SCHEDULE_TEMP_CHANGED + - SCHEDULER_CHANGED + - condition: template + value_template: > + {% if trigger.id == 'SCHEDULE_TEMP_CHANGED' %} + {% set selected_friendly_name = states(active_scheduler_selector) %} + {{ state_attr(trigger.entity_id, 'friendly_name') == selected_friendly_name }} + {% else %} + true + {% endif %} sequence: - if: - condition: template @@ -364,7 +385,7 @@ actions: entity_id: !input trv data: temperature: "{{ target_temperature | float }}" - alias: Setze Solltemperatur basierend auf Alarm-Status - alias: Solltemperatur setzen + alias: Setze Solltemperatur + alias: Solltemperatur bei Änderungen alias: Aktionen während Heizperiode alias: Hauptsteuerung \ No newline at end of file -- 2.49.1 From d68a173de6261a90ecae53015d247ab47a29eacd Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 18:09:37 +0100 Subject: [PATCH 07/14] refactoring and warning notification --- Climate/bosch_bth-ra_control.yaml | 37 ++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index a69ec6c..3112209 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -178,6 +178,21 @@ variables: {% else %} {{ away_temperature }} {% endif %} + is_valid_temperature: > + {{ target_temperature is not none and target_temperature | is_number }} + scheduler_mismatch: > + {% set selected_friendly_name = states(active_scheduler_selector) %} + {% if selected_friendly_name not in ['unknown', '', none] %} + {% set found = namespace(value=false) %} + {% for schedule in radiator_schedules %} + {% if state_attr(schedule, 'friendly_name') == selected_friendly_name %} + {% set found.value = true %} + {% endif %} + {% endfor %} + {{ not found.value }} + {% else %} + false + {% endif %} triggers: - platform: state @@ -257,7 +272,7 @@ actions: - if: - condition: template value_template: > - {{ target_temperature is not none and target_temperature | is_number }} + {{ is_valid_temperature }} then: - service: climate.set_temperature target: @@ -314,7 +329,7 @@ actions: - if: - condition: template value_template: > - {{ target_temperature is not none and target_temperature | is_number }} + {{ is_valid_temperature }} then: - service: climate.set_temperature target: @@ -378,13 +393,29 @@ actions: - if: - condition: template value_template: > - {{ target_temperature is not none and target_temperature | is_number }} + {{ is_valid_temperature }} then: - service: climate.set_temperature target: entity_id: !input trv data: temperature: "{{ target_temperature | float }}" + # Notification bei Scheduler-Mismatch + - if: + - condition: template + value_template: "{{ scheduler_mismatch and trigger.id == 'SCHEDULER_CHANGED' }}" + then: + - service: persistent_notification.create + data: + title: "⚠️ Radiator Control - Scheduler nicht gefunden" + message: > + Der ausgewählte Scheduler '{{ states(active_scheduler_selector) }}' wurde nicht in der + Scheduler-Liste gefunden. + + TRV '{{ state_attr(trv, 'friendly_name') }}' nutzt Fallback-Temperatur: {{ away_temperature }}°C + + Bitte Konfiguration überprüfen. + notification_id: "radiator_control_scheduler_mismatch_{{ trv }}" alias: Setze Solltemperatur alias: Solltemperatur bei Änderungen alias: Aktionen während Heizperiode -- 2.49.1 From d3beca60b683596ab8a3e93e333200c9ad15c62e Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 18:28:21 +0100 Subject: [PATCH 08/14] limit temps --- Climate/bosch_bth-ra_control.yaml | 61 +++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index 3112209..e5166ef 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -33,6 +33,28 @@ blueprint: multiple: false filter: - domain: input_boolean + min_temperature: + name: Minimale Temperatur + description: Minimale erlaubte Solltemperatur als Sicherheitsgrenze (Default = 15°C) + default: 15 + selector: + number: + mode: box + min: 10.0 + max: 20.0 + unit_of_measurement: "°C" + step: 0.5 + max_temperature: + name: Maximale Temperatur + description: Maximale erlaubte Solltemperatur als Sicherheitsgrenze (Default = 28°C) + default: 23 + selector: + number: + mode: box + min: 20.0 + max: 25.0 + unit_of_measurement: "°C" + step: 0.5 window_section: name: Fenster-/Türsensor Konfiguration description: Konfiguration für den Fenster-/Türsensor. @@ -130,6 +152,8 @@ variables: radiator_schedules: !input radiator_schedules active_scheduler_selector: !input active_scheduler_selector away_temperature: !input away_temperature + min_temperature: !input min_temperature + max_temperature: !input max_temperature alarm_control_panel: !input alarm_control_panel is_heating_period: > {% if heating_period_switch is none or heating_period_switch == '' %} @@ -178,8 +202,28 @@ variables: {% else %} {{ away_temperature }} {% endif %} + safe_temperature: > + {% set temp = target_temperature | float(18) %} + {% if temp < min_temperature %} + {{ min_temperature }} + {% elif temp > max_temperature %} + {{ max_temperature }} + {% else %} + {{ temp }} + {% endif %} is_valid_temperature: > - {{ target_temperature is not none and target_temperature | is_number }} + {{ safe_temperature is not none and safe_temperature | is_number }} + is_temperature_change_needed: > + {% set current = state_attr(trv, 'temperature') | float(0) %} + {% set new = safe_temperature | float(0) %} + {% set diff = new - current %} + {% if new > current %} + true + {% elif diff | abs >= 0.5 %} + true + {% else %} + false + {% endif %} scheduler_mismatch: > {% set selected_friendly_name = states(active_scheduler_selector) %} {% if selected_friendly_name not in ['unknown', '', none] %} @@ -272,13 +316,13 @@ actions: - if: - condition: template value_template: > - {{ is_valid_temperature }} + {{ is_valid_temperature and is_temperature_change_needed }} then: - service: climate.set_temperature target: entity_id: !input trv data: - temperature: "{{ target_temperature | float }}" + temperature: "{{ safe_temperature | float }}" alias: Heizperiode aktiviert - Setze Modus auf heat, synchronisiere Fensterstatus und Solltemperatur - conditions: - condition: trigger @@ -329,13 +373,13 @@ actions: - if: - condition: template value_template: > - {{ is_valid_temperature }} + {{ is_valid_temperature and is_temperature_change_needed }} then: - service: climate.set_temperature target: entity_id: !input trv data: - temperature: "{{ target_temperature | float }}" + temperature: "{{ safe_temperature | float }}" alias: Setze Fenster auf geschlossen und setze Solltemperatur auf Wert aus Zeitplan (wenn vorhanden) alias: Fensterstatus Änderung # temperature sensor sync @@ -393,13 +437,13 @@ actions: - if: - condition: template value_template: > - {{ is_valid_temperature }} + {{ is_valid_temperature and is_temperature_change_needed }} then: - service: climate.set_temperature target: entity_id: !input trv data: - temperature: "{{ target_temperature | float }}" + temperature: "{{ safe_temperature | float }}" # Notification bei Scheduler-Mismatch - if: - condition: template @@ -412,7 +456,8 @@ actions: Der ausgewählte Scheduler '{{ states(active_scheduler_selector) }}' wurde nicht in der Scheduler-Liste gefunden. - TRV '{{ state_attr(trv, 'friendly_name') }}' nutzt Fallback-Temperatur: {{ away_temperature }}°C + TRV '{{ state_attr(trv, 'friendly_name') }}' nutzt Fallback-Temperatur: {{ safe_temperature }}°C + (Original: {{ target_temperature }}°C, Limits: {{ min_temperature }}-{{ max_temperature }}°C) Bitte Konfiguration überprüfen. notification_id: "radiator_control_scheduler_mismatch_{{ trv }}" -- 2.49.1 From 7042453358130204fed0a47e987dbcd66df031cc Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Sun, 21 Dec 2025 18:36:34 +0100 Subject: [PATCH 09/14] reduce temp syncs --- Climate/bosch_bth-ra_control.yaml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index e5166ef..fe1d44e 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -224,6 +224,20 @@ variables: {% else %} false {% endif %} + is_sensor_sync_needed: > + {% set current_remote = state_attr(trv, device_class ~ '_temperature') | default(state_attr(remote_temperature_entity, 'state'), none) | float(0) %} + {% set new_sensor = states(temperature_sensor) | float(0) %} + {% set target = safe_temperature | float(0) %} + {% set diff = (new_sensor - current_remote) | abs %} + {% if diff >= 0.5 %} + true + {% elif new_sensor < target and current_remote >= target %} + true + {% elif new_sensor > target and current_remote <= target %} + true + {% else %} + false + {% endif %} scheduler_mismatch: > {% set selected_friendly_name = states(active_scheduler_selector) %} {% if selected_friendly_name not in ['unknown', '', none] %} @@ -391,6 +405,8 @@ actions: - condition: template value_template: > {{ temperature_sensor is defined and states(temperature_sensor) | is_number }} + - condition: template + value_template: "{{ is_sensor_sync_needed }}" sequence: - service: number.set_value data: @@ -407,14 +423,14 @@ actions: {{ temperature_sensor is defined and states(temperature_sensor) | is_number }} - condition: template value_template: > - {{ (now() - states[temperature_sensor].last_changed).total_seconds() > 1199 }} + {{ (now() - states[temperature_sensor].last_changed).total_seconds() > 1499 }} sequence: - service: number.set_value data: value: "{{ states(temperature_sensor) | float }}" target: entity_id: "{{ remote_temperature_entity }}" - alias: Synchronisiere Temperatur am TRV (zeitbasiert, wenn länger als 20min unverändert) + alias: Synchronisiere Temperatur am TRV (zeitbasiert, wenn länger als 25min unverändert) alias: Temperatursynchronisation # setze Solltemperatur bei Schedule und Alarm-Status-Änderungen - choose: -- 2.49.1 From ddc8408a738923e91a3bb54c4361c1dc5c86fe8b Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Mon, 22 Dec 2025 17:58:33 +0100 Subject: [PATCH 10/14] use correct state of remote temperature --- Climate/bosch_bth-ra_control.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index fe1d44e..05051f6 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -225,7 +225,7 @@ variables: false {% endif %} is_sensor_sync_needed: > - {% set current_remote = state_attr(trv, device_class ~ '_temperature') | default(state_attr(remote_temperature_entity, 'state'), none) | float(0) %} + {% set current_remote = states('number.heizthermostat_pierre_buro_remote_temperature') | float(0) %} {% set new_sensor = states(temperature_sensor) | float(0) %} {% set target = safe_temperature | float(0) %} {% set diff = (new_sensor - current_remote) | abs %} -- 2.49.1 From 4de21e35ec6d241b98fe7587c5b8227e2853a9d5 Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Mon, 22 Dec 2025 19:16:35 +0100 Subject: [PATCH 11/14] fix temp sync --- Climate/bosch_bth-ra_control.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index 05051f6..516082c 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -225,7 +225,7 @@ variables: false {% endif %} is_sensor_sync_needed: > - {% set current_remote = states('number.heizthermostat_pierre_buro_remote_temperature') | float(0) %} + {% set current_remote = states('remote_temperature_entity') | float(0) %} {% set new_sensor = states(temperature_sensor) | float(0) %} {% set target = safe_temperature | float(0) %} {% set diff = (new_sensor - current_remote) | abs %} @@ -423,7 +423,7 @@ actions: {{ temperature_sensor is defined and states(temperature_sensor) | is_number }} - condition: template value_template: > - {{ (now() - states[temperature_sensor].last_changed).total_seconds() > 1499 }} + {{ (now() - states[remote_temperature_entity].last_changed).total_seconds() > 1499 }} sequence: - service: number.set_value data: -- 2.49.1 From 5fdc33819443903c149b462479c3775ea55c925b Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Mon, 22 Dec 2025 19:33:39 +0100 Subject: [PATCH 12/14] refactoring and fix --- Climate/bosch_bth-ra_control.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index 516082c..79b1d34 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -225,15 +225,15 @@ variables: false {% endif %} is_sensor_sync_needed: > - {% set current_remote = states('remote_temperature_entity') | float(0) %} - {% set new_sensor = states(temperature_sensor) | float(0) %} + {% set current_remote_temp = states(remote_temperature_entity) | float(0) %} + {% set new_sensor_temp = states(temperature_sensor) | float(0) %} {% set target = safe_temperature | float(0) %} - {% set diff = (new_sensor - current_remote) | abs %} + {% set diff = (new_sensor_temp - current_remote_temp) | abs %} {% if diff >= 0.5 %} true - {% elif new_sensor < target and current_remote >= target %} + {% elif new_sensor_temp < target and current_remote_temp >= target %} true - {% elif new_sensor > target and current_remote <= target %} + {% elif new_sensor_temp > target and current_remote_temp <= target %} true {% else %} false -- 2.49.1 From 55dff75ce5fd97ade3544c180958e86630a10afb Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Mon, 22 Dec 2025 19:42:26 +0100 Subject: [PATCH 13/14] use current target temp as reference --- Climate/bosch_bth-ra_control.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index 79b1d34..74e2454 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -227,13 +227,13 @@ variables: is_sensor_sync_needed: > {% set current_remote_temp = states(remote_temperature_entity) | float(0) %} {% set new_sensor_temp = states(temperature_sensor) | float(0) %} - {% set target = safe_temperature | float(0) %} + {% set target_temp = state_attr(trv, 'temperature') | float(0) %} {% set diff = (new_sensor_temp - current_remote_temp) | abs %} {% if diff >= 0.5 %} true - {% elif new_sensor_temp < target and current_remote_temp >= target %} + {% elif new_sensor_temp < target_temp and current_remote_temp >= target_temp %} true - {% elif new_sensor_temp > target and current_remote_temp <= target %} + {% elif new_sensor_temp > target_temp and current_remote_temp <= target_temp %} true {% else %} false -- 2.49.1 From f6fe2f9ab9d1e6168d78d01999bd4fc48c5e5551 Mon Sep 17 00:00:00 2001 From: Pierre Eisenbrandt Date: Tue, 23 Dec 2025 19:54:34 +0100 Subject: [PATCH 14/14] increase max queue --- Climate/bosch_bth-ra_control.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Climate/bosch_bth-ra_control.yaml b/Climate/bosch_bth-ra_control.yaml index 74e2454..0fd1160 100644 --- a/Climate/bosch_bth-ra_control.yaml +++ b/Climate/bosch_bth-ra_control.yaml @@ -1,5 +1,5 @@ mode: queued -max: 5 +max: 10 blueprint: name: Bosch BTH-RA Radiator Control homeassistant: -- 2.49.1