Compare commits
9 Commits
e72365df0a
...
1bac8c5e4c
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bac8c5e4c | |||
| 106d6215b1 | |||
| e99c3aa967 | |||
| 90f895dbe5 | |||
| 2e6fcefca4 | |||
| 69c65c844f | |||
| a2343237c3 | |||
| 5dd43bbd89 | |||
| a797acf3ab |
@@ -184,6 +184,7 @@ blueprint:
|
||||
enable_day: false
|
||||
|
||||
variables:
|
||||
# Input-Variablen
|
||||
trv: !input trv
|
||||
temperature_sensor: !input temperature_sensor
|
||||
sensor_sync_threshold: !input sensor_sync_threshold
|
||||
@@ -195,89 +196,107 @@ variables:
|
||||
max_temperature: !input max_temperature
|
||||
alarm_control_panel: !input alarm_control_panel
|
||||
override_reset_duration: !input override_reset_duration
|
||||
is_heating_period: >
|
||||
{{ heating_period_switch in [none, ''] or is_state(heating_period_switch, 'on') }}
|
||||
|
||||
# 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
|
||||
|
||||
# Entity-Discovery
|
||||
remote_temperature_entity: >
|
||||
{{ device_entities(device_id(trv)) | select('search', 'remote_temperature') | list | first | default('') }}
|
||||
current_remote_temperature: >
|
||||
{{ states(remote_temperature_entity) | float(0) if remote_temperature_entity and
|
||||
states(remote_temperature_entity) not in ['unknown', 'unavailable', none] else 0 }}
|
||||
remote_temperature_last_change: >
|
||||
{{ states[remote_temperature_entity].last_changed if remote_temperature_entity and
|
||||
states(remote_temperature_entity) not in ['unknown', 'unavailable', none] else none }}
|
||||
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 ns = namespace(current_temperature = none) %}
|
||||
{% set schedule_finder = 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 %}
|
||||
{% set schedule_finder.current_temperature = temp %}
|
||||
{% break %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ ns.current_temperature }}
|
||||
{{ schedule_finder.current_temperature }}
|
||||
|
||||
# Temperatur-Berechnungen
|
||||
target_temperature: >
|
||||
{{ scheduled_temperature if scheduled_temperature != none and not (alarm_control_panel and is_state(alarm_control_panel, 'armed_away')) else away_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 %}
|
||||
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 >= 0.4 }}
|
||||
{{ (safe_temperature | float(0) - state_attr(trv, 'temperature') | float(0)) | abs >= temperature_change_tolerance }}
|
||||
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 %}
|
||||
{{ 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 found = namespace(value=false) %}
|
||||
{% set schedule_found = namespace(value=false) %}
|
||||
{% for schedule in radiator_schedules %}
|
||||
{% if state_attr(schedule, 'friendly_name') == selected %}
|
||||
{% set found.value = true %}
|
||||
{% set schedule_found.value = true %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ not found.value }}
|
||||
{{ not schedule_found.value }}
|
||||
{% else %}
|
||||
false
|
||||
{% endif %}
|
||||
is_manual_override: >
|
||||
{{ (state_attr(trv, 'temperature') | float(0) - safe_temperature | float(0)) | abs >= 0.4 }}
|
||||
{{ (state_attr(trv, 'temperature') | float(0) - safe_temperature | float(0)) | abs >= temperature_change_tolerance }}
|
||||
|
||||
# Override Reset Berechnungen
|
||||
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 and
|
||||
states(setpoint_change_source_entity) not in ['unknown', 'unavailable', none] else none }}
|
||||
{{ 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' }}
|
||||
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 last_change = as_datetime(override_last_change) %}
|
||||
{% if last_change != none %}
|
||||
{% set elapsed = (now() - last_change).total_seconds() %}
|
||||
{{ elapsed > override_reset_duration_seconds }}
|
||||
{% else %}
|
||||
false
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% 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:
|
||||
@@ -287,21 +306,21 @@ triggers:
|
||||
from: "off"
|
||||
to: "on"
|
||||
for: !input window_delay_open
|
||||
id: FENSTER_OPEN
|
||||
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
|
||||
# Periodische Überprüfung alle 5 Minuten (Temperatursynchronisation, Override-Check)
|
||||
minutes: "/5"
|
||||
id: PERIODIC_CHECK
|
||||
- platform: state
|
||||
entity_id: !input temperature_sensor
|
||||
id: TEMP_CHANGED
|
||||
id: SENSOR_TEMPERATURE_CHANGED
|
||||
- platform: state
|
||||
entity_id: !input alarm_control_panel
|
||||
to: "armed_away"
|
||||
@@ -310,37 +329,43 @@ triggers:
|
||||
entity_id: !input alarm_control_panel
|
||||
from: "armed_away"
|
||||
to: "disarmed"
|
||||
id: ALARM_DISARMED_AWAY
|
||||
id: ALARM_DISARMED
|
||||
- platform: state
|
||||
entity_id: !input heating_period_switch
|
||||
to: "on"
|
||||
id: HEATING_PERIOD_ON
|
||||
id: HEATING_PERIOD_STARTED
|
||||
- platform: state
|
||||
entity_id: !input heating_period_switch
|
||||
to: "off"
|
||||
id: HEATING_PERIOD_OFF
|
||||
id: HEATING_PERIOD_ENDED
|
||||
- platform: state
|
||||
entity_id: !input radiator_schedules
|
||||
attribute: temp
|
||||
id: SCHEDULE_TEMP_CHANGED
|
||||
id: SCHEDULE_TEMPERATURE_CHANGED
|
||||
- platform: state
|
||||
entity_id: !input active_scheduler_selector
|
||||
id: SCHEDULER_CHANGED
|
||||
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:
|
||||
- choose:
|
||||
# Heizperiode Switch Aktionen
|
||||
- conditions:
|
||||
- condition: trigger
|
||||
id:
|
||||
- HEATING_PERIOD_ON
|
||||
- HEATING_PERIOD_OFF
|
||||
- HEATING_PERIOD_STARTED
|
||||
- HEATING_PERIOD_ENDED
|
||||
sequence:
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: trigger
|
||||
id:
|
||||
- HEATING_PERIOD_ON
|
||||
- HEATING_PERIOD_STARTED
|
||||
sequence:
|
||||
- service: climate.set_hvac_mode
|
||||
target:
|
||||
@@ -369,7 +394,7 @@ actions:
|
||||
- conditions:
|
||||
- condition: trigger
|
||||
id:
|
||||
- HEATING_PERIOD_OFF
|
||||
- HEATING_PERIOD_ENDED
|
||||
sequence:
|
||||
- service: switch.turn_off
|
||||
target:
|
||||
@@ -392,7 +417,7 @@ actions:
|
||||
- conditions:
|
||||
- condition: trigger
|
||||
id:
|
||||
- FENSTER_OPEN
|
||||
- WINDOW_OPENED
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ is_state(window_detection_entity, 'off') }}
|
||||
@@ -404,7 +429,7 @@ actions:
|
||||
- conditions:
|
||||
- condition: trigger
|
||||
id:
|
||||
- FENSTER_CLOSED
|
||||
- WINDOW_CLOSED
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ is_state(window_detection_entity, 'on') }}
|
||||
@@ -429,7 +454,7 @@ actions:
|
||||
- conditions:
|
||||
- condition: trigger
|
||||
id:
|
||||
- TEMP_CHANGED
|
||||
- SENSOR_TEMPERATURE_CHANGED
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ temperature_sensor is defined and states(temperature_sensor) | is_number }}
|
||||
@@ -453,7 +478,7 @@ actions:
|
||||
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() > 1499 }}
|
||||
{{ last_change != none and (now() - last_change).total_seconds() > sensor_sync_max_age }}
|
||||
{% else %}
|
||||
false
|
||||
{% endif %}
|
||||
@@ -491,12 +516,12 @@ actions:
|
||||
- condition: trigger
|
||||
id:
|
||||
- ALARM_ARMED_AWAY
|
||||
- ALARM_DISARMED_AWAY
|
||||
- SCHEDULE_TEMP_CHANGED
|
||||
- SCHEDULER_CHANGED
|
||||
- ALARM_DISARMED
|
||||
- SCHEDULE_TEMPERATURE_CHANGED
|
||||
- SCHEDULE_SELECTOR_CHANGED
|
||||
- condition: template
|
||||
value_template: >
|
||||
{% if trigger.id == 'SCHEDULE_TEMP_CHANGED' %}
|
||||
{% 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 %}
|
||||
|
||||
Reference in New Issue
Block a user