Compare commits

...

91 Commits

Author SHA1 Message Date
edc4cabde5 fix yaml 2026-02-04 20:21:11 +01:00
bf5409d36e use random delay before set temp 2026-02-04 20:17:43 +01:00
3491ac64db fix reset of target temp when disarming alarm 2026-01-25 15:58:31 +01:00
c6301b9f76 do not set target temp when windows is open 2026-01-15 14:21:03 +01:00
b8839bac3a wait between two signals to same trv 2026-01-15 14:16:36 +01:00
2eba16f2e7 Add description to inputs 2026-01-08 19:19:32 +01:00
2c838a85a1 add comments 2026-01-08 19:14:07 +01:00
7dd83511a3 simplify scheduled_temperature 2026-01-08 19:08:38 +01:00
1bac8c5e4c rename trigger ids 2026-01-07 20:59:43 +01:00
106d6215b1 restructure variables 2026-01-07 20:52:15 +01:00
e99c3aa967 refactoring - naming 2026-01-07 20:46:41 +01:00
90f895dbe5 add comment 2026-01-07 20:45:18 +01:00
2e6fcefca4 secure access to entities 2026-01-07 20:42:46 +01:00
69c65c844f refactor if-style 2026-01-07 20:39:30 +01:00
a2343237c3 fail fast for complext calculation 2026-01-07 20:35:46 +01:00
5dd43bbd89 reduce duplications 2026-01-07 20:30:10 +01:00
a797acf3ab extract magic numbers 2026-01-07 20:26:29 +01:00
e72365df0a fix again 2026-01-07 19:32:08 +01:00
0bb4ced299 fix after refactoring 2026-01-07 19:25:07 +01:00
3ff5db1cbf refactoring 2026-01-07 19:23:53 +01:00
5895c7c9dc try to fix reset of temp 2026-01-07 18:35:26 +01:00
aee08c12cc refactoring 2026-01-07 18:18:52 +01:00
2fdf78ac75 try to fix error 2026-01-06 19:30:53 +01:00
d58ea02aeb fix datetime usage 2026-01-06 19:22:36 +01:00
1d0b0ff241 fix state check 2026-01-06 19:17:44 +01:00
413eea13e0 extract current_remote_temperature 2026-01-06 19:00:42 +01:00
a170d7d0e2 extract remote_temperature_last_change 2026-01-06 18:57:47 +01:00
336f8e2e74 split override reset variables for better debugging 2026-01-06 18:52:15 +01:00
59edb3265c fix dict error 2026-01-05 19:25:47 +01:00
1759929d9f fix default and set to 0 2026-01-04 18:55:27 +01:00
6ccbe1839b collapse sections 2026-01-04 18:52:46 +01:00
f719294bcf add optional override section for automatic reset, some refactoring 2026-01-04 18:50:40 +01:00
9e9dbf8e5f make sync threshold temp adjustable 2026-01-02 10:37:34 +01:00
1f0cc41a58 align target temp increase change 2026-01-02 10:30:39 +01:00
146e4e6ab5 try to fix not setting target temp for half degree difference 2026-01-02 10:22:39 +01:00
f6fe2f9ab9 increase max queue 2025-12-23 19:54:34 +01:00
55dff75ce5 use current target temp as reference 2025-12-22 19:42:26 +01:00
5fdc338194 refactoring and fix 2025-12-22 19:33:39 +01:00
4de21e35ec fix temp sync 2025-12-22 19:16:35 +01:00
ddc8408a73 use correct state of remote temperature 2025-12-22 17:58:33 +01:00
7042453358 reduce temp syncs 2025-12-21 18:36:34 +01:00
d3beca60b6 limit temps 2025-12-21 18:28:21 +01:00
d68a173de6 refactoring and warning notification 2025-12-21 18:09:37 +01:00
ab94183c65 add trigger for schedule changes 2025-12-21 17:23:57 +01:00
576b6fa98a use schedule entity and use active scheduler for determination of temp 2025-12-21 13:44:08 +01:00
e200814de6 use friendly name 2025-12-21 13:30:19 +01:00
9cb8b30fca check if schedule exists in selector 2025-12-21 13:28:25 +01:00
640681ce43 add warning for input_select 2025-12-21 12:24:37 +01:00
e40e2b3e90 add first version of schedule selector 2025-12-21 11:53:46 +01:00
34c7cc90b9 change order of conditions 2025-12-14 16:43:10 +01:00
49263eb1ca reduce sync to 5 minutes - is only synced if last sync is older than 20 minutes 2025-12-14 16:43:10 +01:00
0ca8f4a97d remove remaining null defaults 2025-12-14 16:43:09 +01:00
1b6d967d80 require input_boolean 2025-12-14 16:43:09 +01:00
acb4c180ad try non empty default 2025-12-14 16:43:09 +01:00
cd2053c999 use input_boolean for heating period 2025-12-14 16:43:09 +01:00
16b721814b add heating period switch 2025-12-14 16:43:09 +01:00
b3126d5cc7 fix name 2025-12-13 18:59:19 +01:00
714bde1ee1 remove dev file 2025-12-13 18:57:43 +01:00
f830176260 reduce unnecessary sync of temperature 2025-12-13 18:57:01 +01:00
094beba3d6 move dev to prod script 2025-12-13 15:03:38 +01:00
770e5a1884 use platform isntead of trigger 2025-12-13 15:00:07 +01:00
9e1909e690 fix target temp 2025-12-13 14:42:51 +01:00
3dd3671591 fix input 2025-12-13 14:32:28 +01:00
3ce1d58b7c some refactoring 2025-12-13 14:30:22 +01:00
01416a3751 add away_section, set target_temperature when (dis-)arming alarm_control_panel 2025-12-12 22:24:56 +01:00
aadf1c2860 add alarm_control_panel (WIP) 2025-12-07 18:25:21 +01:00
de2c1e778f fix name after merge 2025-12-07 18:07:49 +01:00
88ce76ea0e move dev to prod script 2025-12-07 18:06:39 +01:00
ba6f37abab wording 2025-12-07 18:06:39 +01:00
8ab15228fe set default for schedules 2025-12-07 18:06:39 +01:00
10f7e76d20 adjust wording 2025-12-07 18:06:39 +01:00
c3daab46cb only set scheduled temperatur if available 2025-12-07 18:06:39 +01:00
bdefd74801 use value template instead of state 2025-12-07 18:06:39 +01:00
2ce92eb995 set scheduled temp when closing window 2025-12-07 18:06:39 +01:00
5e15b77f75 do net sync temp when ope/closing window 2025-12-07 18:06:39 +01:00
0cb33f18a3 fix type 2025-12-07 18:06:39 +01:00
51f038dc22 re-introduce sections and use entity instead of device 2025-12-07 18:06:39 +01:00
8a9e355d74 remove sections 2025-12-07 18:06:39 +01:00
888ab99b3e remove falsy key 2025-12-07 18:06:39 +01:00
cf027bc4fd another fix 2025-12-07 18:06:39 +01:00
5da0623b72 move section variables above triggers 2025-12-07 18:06:39 +01:00
d9738bec8f another fix 2025-12-07 18:06:39 +01:00
423771ed25 move homeassistant to blueprint section 2025-12-07 18:06:39 +01:00
df2ae53616 fix min_version 2025-12-07 18:06:39 +01:00
1dfcb7ba57 some renaming 2025-12-07 18:06:39 +01:00
d3691a5e4b try to fix error 2025-12-07 18:06:39 +01:00
10d3cb0bda try to fix errors 2025-12-07 18:06:39 +01:00
95a42a8f5f change name of dev blueprint 2025-12-07 18:06:39 +01:00
f0eb9a8c25 refactoring: use sections and use choose instead of if 2025-12-07 18:06:39 +01:00
dfa2d15b27 add schedule component 2025-12-07 18:06:39 +01:00
edc263e311 move dev to non-dev blueprint 2025-12-06 11:33:21 +01:00
3 changed files with 826 additions and 229 deletions

View File

@@ -1,11 +1,20 @@
mode: queued
max: 10
blueprint:
name: Bosch BTH-RA Radiator Control
homeassistant:
min_version: "2024.6.0"
description: >
An automation to set Bosch TRV radiator valve to a desired temperature.
Eine Automation zur automatischen Steuerung eines Bosch TRV Heizkörperthermostats.
**Temperatur-Prioritätslogik:**
1. Alarm armed_away → Abwesenheitstemperatur
2. Aktiver Zeitplan → Temperatur aus Schedule
3. Fallback → Abwesenheitstemperatur
domain: automation
author: Me
input:
radiator_entity:
trv:
name: Thermostat
description: Thermostat muss ein Bosch BTH-RA sein
selector:
@@ -13,35 +22,6 @@ blueprint:
multiple: false
filter:
- domain: climate
window_sensor:
name: Fenster-/Türsensor (oder Gruppe)
selector:
entity:
multiple: false
filter:
- domain: binary_sensor
window_delay_open:
name: Fenster-/Türsensor Verzögerung
description: Zeit die das Fenster offen bleiben muss, um die den Radiator in den "Fenster offen"-Modus zu versetzen (Default = 30s)
default: 30
selector:
number:
mode: box
min: 0.0
max: 3600.0
unit_of_measurement: seconds
step: 1.0
window_delay_close:
name: Fenster-/Türsensor Verzögerung
description: Zeit die das Fenster geschlossen bleiben muss, um die den Radiator in den "Fenster geschlossen"-Modus zu versetzen (Default = 5s)
default: 5
selector:
number:
mode: box
min: 0.0
max: 3600.0
unit_of_measurement: seconds
step: 1.0
temperature_sensor:
name: Temperatursensor
selector:
@@ -50,84 +30,567 @@ blueprint:
filter:
- domain: sensor
device_class: temperature
sensor_sync_threshold:
name: Sensor-Synchronisations-Schwellenwert
description: >
Minimale Temperaturdifferenz in °C, um eine Sensor-Synchronisation auszulösen (Default = 0.5°C).
**Synchronisation erfolgt bei:**
1. Differenz zwischen Sensor und TRV ≥ Schwellenwert
2. Sensor kreuzt Solltemperatur von unten (verhindert zu spätes Aufheizen)
3. Sensor kreuzt Solltemperatur von oben (verhindert Überhitzung)
4. Spätestens nach 25 Minuten ohne Änderung
default: 0.5
selector:
number:
mode: box
min: 0.1
max: 1.0
unit_of_measurement: "°C"
step: 0.1
heating_period_switch:
name: Heizperiode Switch
description: Optional - Input Boolean der angibt ob Heizperiode aktiv ist. Wenn nicht gesetzt, ist die Heizperiode immer aktiv.
selector:
entity:
multiple: false
filter:
- domain: input_boolean
min_temperature:
name: Minimale Temperatur
description: >
Minimale erlaubte Solltemperatur als Sicherheitsgrenze (Default = 15°C).
Alle berechneten Temperaturen werden auf diesen Minimalwert begrenzt (Clamping).
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 = 23°C).
Alle berechneten Temperaturen werden auf diesen Maximalwert begrenzt (Clamping).
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.
collapsed: true
input:
window_sensor:
name: Fenster-/Türsensor (oder Gruppe)
selector:
entity:
multiple: false
filter:
- domain: binary_sensor
window_delay_open:
name: Fenster-/Türsensor Verzögerung
description: Zeit die das Fenster offen bleiben muss, um die den Radiator in den "Fenster offen"-Modus zu versetzen (Default = 30s)
default: 30
selector:
number:
mode: box
min: 0.0
max: 3600.0
unit_of_measurement: seconds
step: 1.0
window_delay_close:
name: Fenster-/Türsensor Verzögerung
description: Zeit die das Fenster geschlossen bleiben muss, um die den Radiator in den "Fenster geschlossen"-Modus zu versetzen (Default = 5s)
default: 5
selector:
number:
mode: box
min: 0.0
max: 3600.0
unit_of_measurement: seconds
step: 1.0
schedule_section:
name: Heizplan Konfiguration
description: Konfiguration der Heizpläne die für den Radiator berücksichtigt werden sollen
collapsed: true
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 einer gültigen Temperatur verwendet.
default: []
selector:
entity:
multiple: true
filter:
- 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.
**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:
filter:
- domain: input_select
away_section:
name: Konfiguration für Abwesenheitsmodus
description: Konfiguration für das Absenken der Heizung im Abwesenheitsmodus eines Alarmsystems
collapsed: true
input:
alarm_control_panel:
name: Alarm Control Panel
description: Optional - Alarm Control Panel um Heizung abzusenken, wenn Alarm im Abwesendmodus ist
selector:
entity:
multiple: false
filter:
- domain: alarm_control_panel
away_temperature:
name: Abwesenheitstemperatur
description: >
Temperatur die verwendet wird, wenn der Alarm im Abwesendmodus ist oder kein Zeitplan aktiv ist (Default = 18°C).
**Verwendung:**
- Primär: Alarm Control Panel im Modus 'armed_away' (höchste Priorität)
- Fallback: Wenn kein aktiver Zeitplan eine Temperatur liefert
default: 18
selector:
number:
mode: box
min: 15.0
max: 25.0
unit_of_measurement: "°C"
step: 0.5
override_section:
name: Override Reset Konfiguration
description: Automatisches Zurücksetzen der Solltemperatur nach manueller Änderung am Thermostat
collapsed: true
input:
override_reset_duration:
name: Override Reset Zeitraum
description: >
Zeit nach der eine manuelle Temperaturänderung automatisch auf den Heizplan zurückgesetzt wird.
**Feature deaktiviert wenn auf 00:00:00 gesetzt** (Default = 00:00:00)
**Verhalten:**
- Nur manuelle Änderungen am Thermostat werden zurückgesetzt (nicht über Home Assistant)
- Timer startet neu bei jeder weiteren manuellen Änderung
- Reset erfolgt nur wenn Solltemperatur vom Heizplan abweicht
- Wenn kein Schedule aktiv ist, wird auf Abwesenheitstemperatur zurückgesetzt
- Alarm-Modus hat Priorität: Bei aktivem Alarm erfolgt kein Reset (Abwesenheitstemperatur gilt)
Beispiel: Nach 2 Stunden wird die Temperatur wieder auf den aktiven Heizplan-Wert gesetzt.
default:
hours: 0
minutes: 0
seconds: 0
selector:
duration:
enable_day: false
variables:
# Input-Variablen
trv: !input trv
temperature_sensor: !input temperature_sensor
sensor_sync_threshold: !input sensor_sync_threshold
heating_period_switch: !input heating_period_switch
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
override_reset_duration: !input override_reset_duration
# Konstanten
temperature_change_tolerance: 0.4 # °C - Minimale Differenz für Temperaturänderung
sensor_sync_max_age: 1499 # Sekunden (25 Minuten - 1 Sekunde) - Max Alter für Sensor-Sync
random_delay_max_seconds: 30
# Zufalls-Delay für climate.set_temperature
random_delay_seconds: "{{ range(0, random_delay_max_seconds + 1) | random }}"
# Entity-Discovery
remote_temperature_entity: >
{{ device_entities(device_id(trv)) | select('search', 'remote_temperature') | list | first | default('') }}
window_detection_entity: >
{{ device_entities(device_id(trv)) | select('search', 'window_detection') | list | first | default('') }}
setpoint_change_source_entity: >
{{ device_entities(device_id(trv)) | select('search', 'setpoint_change_source') | list | first | default('') }}
# Entity-Validierung
remote_temperature_entity_valid: >
{{ remote_temperature_entity and states(remote_temperature_entity) not in ['unknown', 'unavailable', none] }}
window_detection_entity_valid: >
{{ window_detection_entity and window_detection_entity | length > 0 }}
setpoint_change_source_entity_valid: >
{{ setpoint_change_source_entity and states(setpoint_change_source_entity) not in ['unknown', 'unavailable', none] }}
# Basis-Berechnungen
is_heating_period: >
{{ heating_period_switch in [none, ''] or is_state(heating_period_switch, 'on') }}
current_remote_temperature: >
{{ states(remote_temperature_entity) | float(0) if remote_temperature_entity_valid else 0 }}
remote_temperature_last_change: >
{{ states[remote_temperature_entity].last_changed if remote_temperature_entity_valid else none }}
scheduled_temperature: >
{% set selected = states(active_scheduler_selector) %}
{% if selected not in [none, 'unknown', ''] %}
{% for schedule in radiator_schedules if state_attr(schedule, 'friendly_name') == selected and is_state(schedule, 'on') %}
{% set temp = state_attr(schedule, 'temp') %}
{% if temp is not none and temp | is_number %}
{{ temp }}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
# Temperatur-Berechnungen
# Priorität: 1. Alarm armed_away → away_temperature, 2. Aktiver Zeitplan → scheduled_temperature, 3. Fallback → away_temperature
target_temperature: >
{% if alarm_control_panel and is_state(alarm_control_panel, 'armed_away') %}
{{ away_temperature }}
{% elif scheduled_temperature != none %}
{{ scheduled_temperature }}
{% else %}
{{ away_temperature }}
{% endif %}
# Begrenzt target_temperature auf den Bereich [min_temperature, max_temperature] (Clamping)
safe_temperature: >
{{ [min_temperature, [max_temperature, target_temperature | float(18)] | min] | max }}
# Status-Prüfungen
is_valid_temperature: >
{{ safe_temperature is not none and safe_temperature | is_number }}
is_temperature_change_needed: >
{{ (safe_temperature | float(0) - state_attr(trv, 'temperature') | float(0)) | abs >= temperature_change_tolerance }}
# Synchronisation nötig wenn: 1) Differenz > Schwelle, 2) Sensor kreuzt Soll-Temp von unten, 3) Sensor kreuzt Soll-Temp von oben
is_sensor_sync_needed: >
{% set new_sensor_temp = states(temperature_sensor) | float(0) %}
{% set target_temp = state_attr(trv, 'temperature') | float(0) %}
{% set diff = (new_sensor_temp - current_remote_temperature) | abs %}
{{ diff >= sensor_sync_threshold or
(new_sensor_temp < target_temp and current_remote_temperature >= target_temp) or
(new_sensor_temp > target_temp and current_remote_temperature <= target_temp) }}
scheduler_mismatch: >
{% set selected = states(active_scheduler_selector) %}
{% if selected not in ['unknown', '', none] %}
{% set schedule_found = namespace(value=false) %}
{% for schedule in radiator_schedules %}
{% if state_attr(schedule, 'friendly_name') == selected %}
{% set schedule_found.value = true %}
{% endif %}
{% endfor %}
{{ not schedule_found.value }}
{% else %}
false
{% endif %}
is_manual_override: >
{{ (state_attr(trv, 'temperature') | float(0) - safe_temperature | float(0)) | abs >= temperature_change_tolerance }}
# Override Reset Berechnungen
# Konvertiert duration-Dictionary {hours, minutes, seconds} in Gesamtsekunden
override_reset_duration_seconds: >
{{ (override_reset_duration.hours | default(0) | int) * 3600 +
(override_reset_duration.minutes | default(0) | int) * 60 +
(override_reset_duration.seconds | default(0) | int) if override_reset_duration is mapping else 0 }}
override_last_change: >
{{ states[setpoint_change_source_entity].last_updated if setpoint_change_source_entity_valid else none }}
is_setpoint_manual: >
{{ setpoint_change_source_entity_valid and states(setpoint_change_source_entity) == 'manual' }}
# Early exits: 1) Keine Änderung/Timeout=0 → false, 2) Nicht manuell → false, 3) Berechne Zeitdifferenz
override_duration_exceeded: >
{% if override_last_change == none or override_reset_duration_seconds == 0 %}
false
{% elif not is_setpoint_manual %}
false
{% else %}
{% set last_change = as_datetime(override_last_change) %}
{{ last_change != none and (now() - last_change).total_seconds() > override_reset_duration_seconds }}
{% endif %}
triggers:
- trigger: state
- platform: state
entity_id:
- !input window_sensor
from: "off"
to: "on"
for: !input window_delay_open
id: FENSTER_OPEN
- trigger: state
id: WINDOW_OPENED
- platform: state
entity_id:
- !input window_sensor
from: "on"
to: "off"
for: !input window_delay_close
id: FENSTER_CLOSED
id: WINDOW_CLOSED
- platform: time_pattern
# Synce Temperatur alle 20 Minuten (Teiler von 60)
minutes: "/20"
id: SYNC_TEMPERATURE
# Periodische Überprüfung alle 5 Minuten (Temperatursynchronisation, Override-Check)
minutes: "/5"
id: PERIODIC_CHECK
- platform: state
entity_id: !input temperature_sensor
id: TEMP_CHANGED
variables:
trv: !input radiator_entity
remote_temperature_entity: >
{% set entities = device_entities(device_id(trv)) %}
{% set remote_temperature_entity_id = namespace(id='') %}
{% for s in entities %}
{% if 'remote_temperature' in s %}
{% set remote_temperature_entity_id.id = s %}
{% endif %}
{% endfor %}
{{ remote_temperature_entity_id.id }}
window_detection_entity: >
{% set entities = device_entities(device_id(trv)) %}
{% set window_detection_entity_id = namespace(id='') %}
{% for s in entities %}
{% if 'window_detection' in s %}
{% set window_detection_entity_id.id = s %}
{% endif %}
{% endfor %}
{{ window_detection_entity_id.id }}
current_temperature: !input temperature_sensor
id: SENSOR_TEMPERATURE_CHANGED
- platform: state
entity_id: !input alarm_control_panel
to: "armed_away"
id: ALARM_ARMED_AWAY
- platform: state
entity_id: !input alarm_control_panel
from: "armed_away"
id: ALARM_DISARMED
- platform: state
entity_id: !input heating_period_switch
to: "on"
id: HEATING_PERIOD_STARTED
- platform: state
entity_id: !input heating_period_switch
to: "off"
id: HEATING_PERIOD_ENDED
- platform: state
entity_id: !input radiator_schedules
attribute: temp
id: SCHEDULE_TEMPERATURE_CHANGED
- platform: state
entity_id: !input active_scheduler_selector
id: SCHEDULE_SELECTOR_CHANGED
# Hinweis zur Wartbarkeit: climate.set_temperature wird an 4 Stellen verwendet:
# 1. Heizperiode aktiviert - Setzt Temperatur beim Einschalten der Heizung
# 2. Fenster geschlossen - Setzt Temperatur nach Fensterschließung
# 3. Override Reset - Setzt Temperatur zurück nach manuellem Override
# 4. Schedule/Alarm-Änderungen - Setzt Temperatur bei Zeitplan- oder Alarmänderungen
# Standardformat: service: climate.set_temperature | target: !input trv | data: safe_temperature
actions:
- if:
- condition: trigger
id:
- FENSTER_OPEN
then:
- service: switch.turn_on
target:
entity_id: "{{ window_detection_entity }}"
alias: Setze Fenster auf offen
- if:
- condition: trigger
id:
- FENSTER_CLOSED
then:
- service: switch.turn_off
target:
entity_id: "{{ window_detection_entity }}"
alias: Setze Fenster auf geschlossen
# temperature sensor sync
- if:
- condition: trigger
id:
- FENSTER_OPEN
- FENSTER_CLOSED
- SYNC_TEMPERATURE
- TEMP_CHANGED
then:
- service: number.set_value
data:
value: "{{ states(current_temperature) | float }}"
target:
entity_id: "{{ remote_temperature_entity }}"
alias: Sync remote temperature sensor
alias: Synce Temperatur zum TRV
- choose:
# Heizperiode Switch Aktionen
- conditions:
- condition: trigger
id:
- HEATING_PERIOD_STARTED
- HEATING_PERIOD_ENDED
sequence:
- choose:
- conditions:
- condition: trigger
id:
- HEATING_PERIOD_STARTED
sequence:
- service: climate.set_hvac_mode
target:
entity_id: !input trv
data:
hvac_mode: "heat"
- if:
- condition: state
entity_id: !input window_sensor
state: "on"
then:
- service: switch.turn_on
target:
entity_id: "{{ window_detection_entity }}"
- if:
- condition: template
value_template: >
{{ is_valid_temperature and is_temperature_change_needed and is_state(window_detection_entity, 'off') }}
then:
- delay:
seconds: "{{ random_delay_seconds }}"
- service: climate.set_temperature
target:
entity_id: !input trv
data:
temperature: "{{ safe_temperature | float }}"
alias: Heizperiode aktiviert - Setze Modus auf heat, synchronisiere Fensterstatus und Solltemperatur
- conditions:
- condition: trigger
id:
- HEATING_PERIOD_ENDED
sequence:
- service: switch.turn_off
target:
entity_id: "{{ window_detection_entity }}"
- service: climate.set_hvac_mode
target:
entity_id: !input trv
data:
hvac_mode: "off"
alias: Heizperiode deaktiviert - Setze Fenster geschlossen und Modus auf off
alias: Heizperiode Switch Änderung
alias: Heizperiode Switch Aktionen
# Alle bestehenden Aktionen nur ausführen wenn Heizperiode aktiv ist
- conditions:
- condition: template
value_template: "{{ is_heating_period }}"
sequence:
# setze Fenster auf offen/geschlossen
- choose:
- conditions:
- condition: trigger
id:
- WINDOW_OPENED
- condition: template
value_template: >
{{ is_state(window_detection_entity, 'off') }}
sequence:
- service: switch.turn_on
target:
entity_id: "{{ window_detection_entity }}"
alias: Setze Fenster auf offen
- conditions:
- condition: trigger
id:
- WINDOW_CLOSED
- condition: template
value_template: >
{{ is_state(window_detection_entity, 'on') }}
sequence:
- service: switch.turn_off
target:
entity_id: "{{ window_detection_entity }}"
- delay:
minutes: 1
- if:
- condition: template
value_template: >
{{ is_valid_temperature and is_temperature_change_needed }}
then:
- delay:
seconds: "{{ random_delay_seconds }}"
- service: climate.set_temperature
target:
entity_id: !input trv
data:
temperature: "{{ safe_temperature | float }}"
alias: Setze Fenster auf geschlossen und setze Solltemperatur auf Wert aus Zeitplan (wenn vorhanden)
alias: Fensterstatus Änderung
# temperature sensor sync
- choose:
- conditions:
- condition: trigger
id:
- SENSOR_TEMPERATURE_CHANGED
- 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:
value: "{{ states(temperature_sensor) | float }}"
target:
entity_id: "{{ remote_temperature_entity }}"
alias: Synchronisiere Temperatur am TRV (bei Änderung)
- conditions:
- condition: trigger
id:
- PERIODIC_CHECK
- condition: template
value_template: >
{{ temperature_sensor is defined and states(temperature_sensor) | is_number }}
- condition: template
value_template: >
{% if remote_temperature_last_change != none %}
{% set last_change = as_datetime(remote_temperature_last_change) %}
{{ last_change != none and (now() - last_change).total_seconds() > sensor_sync_max_age }}
{% else %}
false
{% endif %}
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 25min unverändert)
alias: Temperatursynchronisation
# override reset
- choose:
- conditions:
- condition: trigger
id:
- PERIODIC_CHECK
- condition: template
value_template: "{{ override_reset_duration_seconds > 0 }}"
- condition: template
value_template: "{{ is_manual_override }}"
- condition: template
value_template: "{{ override_duration_exceeded }}"
sequence:
- delay:
seconds: "{{ random_delay_seconds }}"
- service: climate.set_temperature
target:
entity_id: !input trv
data:
temperature: "{{ safe_temperature | float }}"
alias: Setze Solltemperatur zurück nach manuellem Override
alias: Override Reset
# setze Solltemperatur bei Schedule und Alarm-Status-Änderungen
- choose:
- conditions:
- condition: trigger
id:
- ALARM_ARMED_AWAY
- ALARM_DISARMED
- SCHEDULE_TEMPERATURE_CHANGED
- SCHEDULE_SELECTOR_CHANGED
- condition: template
value_template: >
{% if trigger.id == 'SCHEDULE_TEMPERATURE_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
value_template: >
{{ is_valid_temperature and is_temperature_change_needed and is_state(window_detection_entity, 'off') }}
then:
- delay:
seconds: "{{ random_delay_seconds }}"
- service: climate.set_temperature
target:
entity_id: !input trv
data:
temperature: "{{ safe_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: {{ safe_temperature }}°C
(Original: {{ target_temperature }}°C, Limits: {{ min_temperature }}-{{ max_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
alias: Hauptsteuerung

View File

@@ -1,134 +0,0 @@
mode: single
blueprint:
name: Bosch BTH-RA Radiator Control
description: >
An automation to set Bosch TRV radiator valve to a desired temperature.
domain: automation
author: Me
input:
radiator_entity:
name: Thermostat
description: Thermostat muss ein Bosch BTH-RA sein
selector:
entity:
multiple: false
filter:
- domain: climate
window_sensor:
name: Fenster-/Türsensor (oder Gruppe)
selector:
entity:
multiple: false
filter:
- domain: binary_sensor
window_delay_open:
name: Fenster-/Türsensor Verzögerung
description: Zeit die das Fenster offen bleiben muss, um die den Radiator in den "Fenster offen"-Modus zu versetzen (Default = 30s)
default: 30
selector:
number:
mode: box
min: 0.0
max: 3600.0
unit_of_measurement: seconds
step: 1.0
window_delay_close:
name: Fenster-/Türsensor Verzögerung
description: Zeit die das Fenster geschlossen bleiben muss, um die den Radiator in den "Fenster geschlossen"-Modus zu versetzen (Default = 5s)
default: 5
selector:
number:
mode: box
min: 0.0
max: 3600.0
unit_of_measurement: seconds
step: 1.0
temperature_sensor:
name: Temperatursensor
selector:
entity:
multiple: false
filter:
- domain: sensor
device_class: temperature
triggers:
- trigger: state
entity_id:
- !input window_sensor
from: "off"
to: "on"
for: !input window_delay_open
id: FENSTER_OPEN
- trigger: state
entity_id:
- !input window_sensor
from: "on"
to: "off"
for: !input window_delay_close
id: FENSTER_CLOSED
- platform: time_pattern
# Synce Temperatur alle 20 Minuten (Teiler von 60)
minutes: "/20"
id: SYNC_TEMPERATURE
- platform: state
entity_id: !input temperature_sensor
id: TEMP_CHANGED
variables:
trv: !input radiator_entity
remote_temperature_entity: >
{% set entities = device_entities(device_id(trv)) %}
{% set remote_temperature_entity_id = namespace(id='') %}
{% for entity in entities %}
{% if 'remote_temperature' in entity %}
{% set remote_temperature_entity_id.id = entity %}
{% endif %}
{% endfor %}
{{ remote_temperature_entity_id.id }}
window_detection_entity: >
{% set entities = device_entities(device_id(trv)) %}
{% set window_detection_entity_id = namespace(id='') %}
{% for entity in entities %}
{% if 'window_detection' in entity %}
{% set window_detection_entity_id.id = entity %}
{% endif %}
{% endfor %}
{{ window_detection_entity_id.id }}
current_temperature: !input temperature_sensor
actions:
- if:
- condition: trigger
id:
- FENSTER_OPEN
then:
- service: switch.turn_on
target:
entity_id: "{{ window_detection_entity }}"
alias: Setze Fenster auf offen
- if:
- condition: trigger
id:
- FENSTER_CLOSED
then:
- service: switch.turn_off
target:
entity_id: "{{ window_detection_entity }}"
alias: Setze Fenster auf geschlossen
# temperature sensor sync
- if:
- condition: trigger
id:
- FENSTER_OPEN
- FENSTER_CLOSED
- SYNC_TEMPERATURE
- TEMP_CHANGED
then:
- service: number.set_value
data:
value: "{{ states(current_temperature) | float }}"
target:
entity_id: "{{ remote_temperature_entity }}"
alias: Sync remote temperature sensor
alias: Synce Temperatur zum TRV

View File

@@ -0,0 +1,268 @@
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.
**WICHTIG**: Die Options der input_select müssen vorbereitet werden!
Sie müssen die Friendly Names folgender Entities enthalten:
- Alle Scheduler aus der Scheduler-Liste
- Den Standard-Scheduler
Beispiel Options:
["Urlaub Heizplan", "Home Office Heizplan", "Normal Heizplan", "Standard Heizplan"]
Die Blueprint schreibt den Friendly Name (nicht die Entity-ID) in die Helper-Entity.
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:
- if:
- condition: template
value_template: >
{{ 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: "{{ state_attr(matched_scheduler, 'friendly_name') }}"
else:
# Matched Scheduler nicht vorhanden - Fallback auf Default
- if:
- condition: template
value_template: >
{{ 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: "{{ state_attr(default_scheduler, 'friendly_name') }}"
- service: persistent_notification.create
data:
title: "⚠️ Heizplan Selector - Konfigurationsfehler"
message: >
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 '{{ state_attr(default_scheduler, 'friendly_name') }}' ({{ 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 '{{ 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!
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)
- conditions:
- condition: trigger
id: CALENDAR_END
- condition: template
value_template: "{{ not other_event_active }}"
sequence:
- if:
- condition: template
value_template: >
{{ 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: "{{ state_attr(default_scheduler, 'friendly_name') }}"
else:
- service: persistent_notification.create
data:
title: "❌ Heizplan Selector - Konfigurationsfehler"
message: >
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!
Bitte input_select Options korrigieren.
Event: {{ trigger.calendar_event.summary }}
notification_id: "schedule_selector_default_error"
alias: Event-Ende - Standard-Scheduler aktivieren
default: []