588 lines
22 KiB
YAML
588 lines
22 KiB
YAML
mode: queued
|
|
max: 10
|
|
blueprint:
|
|
name: Bosch BTH-RA Radiator Control
|
|
homeassistant:
|
|
min_version: "2024.6.0"
|
|
description: >
|
|
Eine Automation zur automatischen Steuerung eines Bosch TRV Heizkörperthermostats.
|
|
domain: automation
|
|
author: Me
|
|
input:
|
|
trv:
|
|
name: Thermostat
|
|
description: Thermostat muss ein Bosch BTH-RA sein
|
|
selector:
|
|
entity:
|
|
multiple: false
|
|
filter:
|
|
- domain: climate
|
|
temperature_sensor:
|
|
name: Temperatursensor
|
|
selector:
|
|
entity:
|
|
multiple: false
|
|
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). Temperatur wird weiterhin spätestens nach 25 Minuten synchronisiert.
|
|
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)
|
|
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.
|
|
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 eingestellt wird, wenn der Alarm im Abwesendmodus ist (Default = 18°C)
|
|
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:
|
|
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
|
|
is_heating_period: >
|
|
{% if heating_period_switch is none or heating_period_switch == '' %}
|
|
true
|
|
{% else %}
|
|
{{ is_state(heating_period_switch, 'on') }}
|
|
{% endif %}
|
|
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 }}
|
|
current_remote_temperature: >
|
|
{% if remote_temperature_entity and states(remote_temperature_entity) not in ['unknown', 'unavailable', none]%}
|
|
{{ states(remote_temperature_entity) | float(0) }}
|
|
{% else %}
|
|
{{ 0 }}
|
|
{% endif %}
|
|
remote_temperature_last_change: >
|
|
{% if remote_temperature_entity and states(remote_temperature_entity) not in ['unknown', 'unavailable', none] %}
|
|
{{ as_datetime(states[remote_temperature_entity].last_changed) }}
|
|
{% else %}
|
|
{{ none }}
|
|
{% endif %}
|
|
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 }}
|
|
setpoint_change_source_entity: >
|
|
{% set entities = device_entities(device_id(trv)) %}
|
|
{% set setpoint_change_source_entity_id = namespace(id='') %}
|
|
{% for entity in entities %}
|
|
{% if 'setpoint_change_source' in entity %}
|
|
{% set setpoint_change_source_entity_id.id = entity %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{{ setpoint_change_source_entity_id.id }}
|
|
scheduled_temperature: >
|
|
{% set ns = namespace(current_temperature = none) %}
|
|
{% 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 %}
|
|
{% endfor %}
|
|
{% endif %}
|
|
{{ ns.current_temperature }}
|
|
target_temperature: >
|
|
{% if alarm_control_panel and is_state(alarm_control_panel, 'armed_away') %}
|
|
{{ away_temperature }}
|
|
{% elif scheduled_temperature is not none %}
|
|
{{ scheduled_temperature }}
|
|
{% 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: >
|
|
{{ 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 diff | abs >= 0.4 %}
|
|
true
|
|
{% else %}
|
|
false
|
|
{% endif %}
|
|
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 %}
|
|
{% if diff >= sensor_sync_threshold %}
|
|
true
|
|
{% elif new_sensor_temp < target_temp and current_remote_temperature >= target_temp %}
|
|
true
|
|
{% elif new_sensor_temp > target_temp and current_remote_temperature <= target_temp %}
|
|
true
|
|
{% else %}
|
|
false
|
|
{% endif %}
|
|
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 %}
|
|
is_manual_override: >
|
|
{% set current_temp = state_attr(trv, 'temperature') | float(0) %}
|
|
{% set target_temp = safe_temperature | float(0) %}
|
|
{% set diff = (current_temp - target_temp) | abs %}
|
|
{{ diff >= 0.4 }}
|
|
override_reset_duration_seconds: >
|
|
{% if override_reset_duration is mapping %}
|
|
{{ (override_reset_duration.hours | default(0) | int) * 3600 + (override_reset_duration.minutes | default(0) | int) * 60 + (override_reset_duration.seconds | default(0) | int) }}
|
|
{% else %}
|
|
0
|
|
{% endif %}
|
|
override_last_change: >
|
|
{% if setpoint_change_source_entity and states(setpoint_change_source_entity) not in ['unknown', 'unavailable', none] %}
|
|
{{ as_datetime(states[setpoint_change_source_entity].last_updated) }}
|
|
{% else %}
|
|
{{ none }}
|
|
{% endif %}
|
|
override_duration_exceeded: >
|
|
{% if override_last_change != none and override_reset_duration_seconds > 0 and setpoint_change_source_entity and states(setpoint_change_source_entity) == 'manual' %}
|
|
{% set elapsed = (now() - override_last_change).total_seconds() %}
|
|
{{ elapsed > override_reset_duration_seconds }}
|
|
{% else %}
|
|
false
|
|
{% endif %}
|
|
|
|
triggers:
|
|
- platform: state
|
|
entity_id:
|
|
- !input window_sensor
|
|
from: "off"
|
|
to: "on"
|
|
for: !input window_delay_open
|
|
id: FENSTER_OPEN
|
|
- platform: state
|
|
entity_id:
|
|
- !input window_sensor
|
|
from: "on"
|
|
to: "off"
|
|
for: !input window_delay_close
|
|
id: FENSTER_CLOSED
|
|
- platform: time_pattern
|
|
# Periodische Überprüfung alle 5 Minuten (Temperatursynchronisation, Override-Check)
|
|
minutes: "/5"
|
|
id: PERIODIC_CHECK
|
|
- platform: state
|
|
entity_id: !input temperature_sensor
|
|
id: TEMP_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"
|
|
to: "disarmed"
|
|
id: ALARM_DISARMED_AWAY
|
|
- platform: state
|
|
entity_id: !input heating_period_switch
|
|
to: "on"
|
|
id: HEATING_PERIOD_ON
|
|
- platform: state
|
|
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:
|
|
# Heizperiode Switch Aktionen
|
|
- conditions:
|
|
- condition: trigger
|
|
id:
|
|
- HEATING_PERIOD_ON
|
|
- HEATING_PERIOD_OFF
|
|
sequence:
|
|
- choose:
|
|
- conditions:
|
|
- condition: trigger
|
|
id:
|
|
- HEATING_PERIOD_ON
|
|
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 }}
|
|
then:
|
|
- 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_OFF
|
|
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:
|
|
- FENSTER_OPEN
|
|
- 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:
|
|
- FENSTER_CLOSED
|
|
- condition: template
|
|
value_template: >
|
|
{{ is_state(window_detection_entity, 'on') }}
|
|
sequence:
|
|
- service: switch.turn_off
|
|
target:
|
|
entity_id: "{{ window_detection_entity }}"
|
|
- if:
|
|
- condition: template
|
|
value_template: >
|
|
{{ is_valid_temperature and is_temperature_change_needed }}
|
|
then:
|
|
- 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:
|
|
- TEMP_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: >
|
|
{{ remote_temperature_last_change != none }}
|
|
- condition: template
|
|
value_template: >
|
|
{{ (now() - remote_temperature_last_change).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 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:
|
|
- 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_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
|
|
value_template: >
|
|
{{ is_valid_temperature and is_temperature_change_needed }}
|
|
then:
|
|
- 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 |