YAML – Beschattung¶
Dokumentation: Beschattung
Package: Skripte & Hitzeschutz¶
Quelle in HA: /config/packages/beschattung.yaml
Enthält:
- script.beschattung_cover_setzen — generischer Baustein für alle Rollläden/Markisen
- script.beschattung_fenster_offen — Position sichern & Lüftungsposition bei offenem Fenster
- script.beschattung_komfortsperren_loeschen — alle Komfortsperren löschen und Abwesenheitslogik auslösen
- binary_sensor.beschattung_hitzeschutz_noetig — Trigger-Template mit Forecast-Auswertung und Selbsthaltung
script:
# =====================================================================
# 🌤️ BESCHATTUNG – Wiederverwendbare Skripte
# =====================================================================
# Dieses Package buendelt Skripte, die von mehreren Beschattungs-
# Automationen (Wohnzimmer, Martha, Schlafzimmer, ...) gemeinsam
# genutzt werden, damit die eigentliche Cover-Ansteuerungs-Logik
# nur an EINER Stelle gepflegt werden muss.
# =====================================================================
beschattung_cover_setzen:
alias: "Beschattung: Cover Position setzen"
icon: mdi:blinds
mode: parallel
max: 10
description: >-
Generischer Baustein fuer die Beschattungs-Automationen: setzt ein
Cover (Rollo/Markise/Terrassentuer) auf eine Zielposition. Optional
werden dabei ein Lauf-Marker (input_boolean, das waehrend der Fahrt
z.B. Fenstersensor-Events ausblendet) gesetzt/zurueckgesetzt und ein
Rueckkehrwert-Helper (input_number) mit der Zielposition befuellt,
damit eine spaetere Rueckkehr-Automation weiss, wohin sie fahren
soll. Danach folgt ein Logbook-Eintrag. Nur wenn ein Lauf-Marker
angegeben wurde, folgt zusaetzlich ein Nachlauf-Delay, bevor der
Marker wieder zurueckgesetzt wird -- ohne Marker kehrt das Skript
sofort nach dem Logbook-Eintrag zurueck, damit aufrufende
Automationen (die auf das Skript-Ende warten) nicht unnoetig
ausgebremst werden. Marker und Rueckkehrwert sind rein optional --
werden sie beim Aufruf nicht angegeben, macht das Skript einfach
nur "Position setzen + loggen" (z.B. fuer einfaches Markise-Oeffnen
ohne Fenstersensor-Interferenz).
fields:
cover_entity:
name: Cover
description: "Das Cover (Rollo, Markise, Terrassentuer), das positioniert
werden soll."
required: true
selector:
entity:
domain: cover
ziel_position:
name: Zielposition
description: "Zielposition in Prozent. 0 = geschlossen, 100 = offen."
required: true
selector:
number:
min: 0
max: 100
mode: slider
step: 1
unit_of_measurement: "%"
marker_entity:
name: "Lauf-Marker (optional)"
description: >-
input_boolean, das waehrend der Cover-Fahrt auf 'on' gesetzt
wird, damit andere Automationen Fenstersensor-Events waehrend
der Fahrt ignorieren koennen. Nur wenn dieses Feld gesetzt ist,
wartet das Skript anschliessend das Nachlauf-Delay ab. Leer
lassen, wenn kein Marker benoetigt wird -- das Skript kehrt
dann sofort nach dem Logbook-Eintrag zurueck.
required: false
selector:
entity:
domain: input_boolean
rueckkehr_entity:
name: "Rueckkehrwert-Helper (optional)"
description: >-
input_number, in dem die gesetzte Zielposition gespeichert
wird, damit eine spaetere Rueckkehr-Automation weiss, wohin
sie zurueckfahren soll. Leer lassen, wenn nicht benoetigt.
required: false
selector:
entity:
domain: input_number
log_nachricht:
name: "Logbook-Nachricht (optional)"
description: >-
Eigener Text fuer den Logbook-Eintrag. Wenn leer, wird
automatisch "<Cover-Name> -> <Position>%" erzeugt.
required: false
selector:
text:
log_label:
name: "Logbook-Absender (optional)"
description: "Name, unter dem der Logbook-Eintrag erscheint. Standard: 'Beschattung'."
required: false
selector:
text:
delay_sekunden:
name: "Nachlauf-Delay in Sekunden (optional)"
description: >-
Wartezeit, bevor der Lauf-Marker wieder zurueckgesetzt wird.
Wirkt sich nur aus, wenn auch ein Lauf-Marker angegeben ist.
Standard: 30 Sekunden.
required: false
selector:
number:
min: 0
max: 300
mode: box
step: 5
unit_of_measurement: "s"
sequence:
- alias: "Hilfsvariablen berechnen (Marker, Rueckkehrwert, Label, Delay, Logbook-Text)"
variables:
_marker: "{{ marker_entity | default('') }}"
_rueckkehr: "{{ rueckkehr_entity | default('') }}"
_label: "{{ log_label | default('Beschattung', true) }}"
_delay: "{{ (delay_sekunden | default(30)) | int }}"
_nachricht: >-
{{ log_nachricht if (log_nachricht is defined and log_nachricht | trim != '')
else (state_attr(cover_entity, 'friendly_name') ~ ' → ' ~ (ziel_position | int) ~ '%') }}
- alias: "Lauf-Marker aktivieren, falls angegeben"
if:
- condition: template
value_template: "{{ _marker != '' }}"
then:
- alias: "Marker einschalten"
action: input_boolean.turn_on
target:
entity_id: "{{ _marker }}"
- alias: "Zielposition im Rueckkehrwert-Helper speichern, falls angegeben"
if:
- condition: template
value_template: "{{ _rueckkehr != '' }}"
then:
- alias: "Rueckkehrwert setzen"
action: input_number.set_value
target:
entity_id: "{{ _rueckkehr }}"
data:
value: "{{ ziel_position | int }}"
- alias: "Cover auf Zielposition fahren"
action: cover.set_cover_position
target:
entity_id: "{{ cover_entity }}"
data:
position: "{{ ziel_position | int }}"
- alias: "Logbuch-Eintrag schreiben"
action: logbook.log
data:
name: "{{ _label }}"
entity_id: "{{ cover_entity }}"
message: "{{ _nachricht }}"
- alias: "Nachlauf-Delay abwarten und Lauf-Marker zuruecksetzen, falls angegeben"
if:
- condition: template
value_template: "{{ _marker != '' }}"
then:
- alias: "Nachlauf-Delay abwarten"
delay:
seconds: "{{ _delay }}"
- alias: "Marker ausschalten"
action: input_boolean.turn_off
target:
entity_id: "{{ _marker }}"
beschattung_komfortsperren_loeschen:
alias: "Beschattung: Komfortsperren löschen"
description: >
Löscht alle manuellen Komfortsperren.
Fährt die Markise ein wenn niemand zuhause ist.
icon: mdi:lock-open-remove
mode: single
sequence:
- action: input_boolean.turn_off
target:
entity_id:
- input_boolean.beschattung_sperre_kueche
- input_boolean.beschattung_sperre_wohnzimmer_links
- input_boolean.beschattung_sperre_wohnzimmer_rechts
- input_boolean.beschattung_sperre_martha
- input_boolean.beschattung_sperre_schlafzimmer
- if:
- condition: state
entity_id: binary_sensor.beschattung_jemand_zuhause
state: "off"
- condition: numeric_state
entity_id: cover.markise
attribute: current_position
below: 10
then:
- action: cover.open_cover
target:
entity_id: cover.markise
- action: logbook.log
data:
name: Beschattung
entity_id: cover.markise
message: "Markise eingefahren: Abwesenheitsmodus"
- action: logbook.log
data:
name: Beschattung
entity_id: input_select.beschattung_betriebsmodus
message: "Alle Komfortsperren gelöscht"
beschattung_fenster_offen:
alias: "Beschattung: Fenster geöffnet – Position sichern & Lüftungsposition"
icon: mdi:window-open-variant
mode: parallel
max: 10
description: >-
Generische Fensterlogik fuer die Beschattung: Wird ein Fenster
geoeffnet oder gekippt, sichert dieses Skript zuerst IMMER die
aktuelle Cover-Position in einem input_number-Helper. Steht das
Cover tiefer als die gewuenschte Lueftungsposition (minus Toleranz),
faehrt es auf die Lueftungsposition hoch.
fields:
cover_entity:
name: Cover
required: true
selector:
entity:
domain: cover
position_speicher_entity:
name: Positions-Speicher
required: true
selector:
entity:
domain: input_number
lueftungs_position:
name: Lüftungsposition
required: true
selector:
number:
min: 0
max: 100
mode: slider
step: 1
unit_of_measurement: "%"
toleranz:
name: "Toleranz (optional)"
required: false
selector:
number:
min: 0
max: 20
mode: box
step: 1
marker_entity:
name: "Lauf-Marker (optional)"
required: false
selector:
entity:
domain: input_boolean
log_nachricht:
name: "Logbook-Nachricht (optional)"
required: false
selector:
text:
log_label:
name: "Logbook-Absender (optional)"
required: false
selector:
text:
delay_sekunden:
name: "Nachlauf-Delay in Sekunden (optional)"
required: false
selector:
number:
min: 0
max: 300
mode: box
step: 5
unit_of_measurement: "s"
sequence:
- alias: "Hilfsvariablen berechnen"
variables:
_marker: "{{ marker_entity | default('') }}"
_label: "{{ log_label | default('Beschattung', true) }}"
_delay: "{{ (delay_sekunden | default(30)) | int }}"
_toleranz: "{{ (toleranz | default(3)) | int }}"
_pos_aktuell: "{{ state_attr(cover_entity, 'current_position') | int(100) }}"
_nachricht: >-
{{ log_nachricht if (log_nachricht is defined and log_nachricht | trim != '')
else (state_attr(cover_entity, 'friendly_name') ~ ' → Lüftungsposition '
~ (lueftungs_position | int) ~ '% (Fenster geöffnet)') }}
- alias: "Lauf-Marker aktivieren, falls angegeben"
if:
- condition: template
value_template: "{{ _marker != '' }}"
then:
- action: input_boolean.turn_on
target:
entity_id: "{{ _marker }}"
- alias: "Aktuelle Position im Speicher-Helper sichern"
action: input_number.set_value
target:
entity_id: "{{ position_speicher_entity }}"
data:
value: "{{ _pos_aktuell }}"
- alias: "Auf Lüftungsposition hochfahren, falls Cover zu weit unten"
if:
- condition: template
value_template: "{{ _pos_aktuell < (lueftungs_position | int - _toleranz | int) }}"
then:
- action: cover.set_cover_position
target:
entity_id: "{{ cover_entity }}"
data:
position: "{{ lueftungs_position | int }}"
- action: logbook.log
data:
name: "{{ _label }}"
entity_id: "{{ cover_entity }}"
message: "{{ _nachricht }}"
- alias: "Nachlauf-Delay abwarten und Lauf-Marker zuruecksetzen, falls angegeben"
if:
- condition: template
value_template: "{{ _marker != '' }}"
then:
- delay:
seconds: "{{ _delay }}"
- action: input_boolean.turn_off
target:
entity_id: "{{ _marker }}"
template:
# Hitzeschutz-Binary-Sensor: EIN wenn Forecast >= 7 Stunden >= Schwelle bis Sonnenuntergang.
# Selbsthaltung bis Abkühlung (30 Min durchgehend 1°C unter Schwelle) oder Sonnenuntergang.
- triggers:
- trigger: time_pattern
id: zeit
minutes: "/15"
- trigger: state
id: forecast_update
entity_id: weather.forecast_home
- trigger: event
id: reload
event_type: event_template_reloaded
- trigger: homeassistant
id: start
event: start
- trigger: state
id: schwelle_geaendert
entity_id: input_number.beschattung_schwelle_aussen_hitzeschutz
- trigger: template
id: abkuehlung
value_template: >-
{{ states('sensor.beschattung_aussentemperatur') | float(99)
<= states('input_number.beschattung_schwelle_aussen_hitzeschutz') | float(27)
- 1 }}
for:
minutes: 30
actions:
- action: weather.get_forecasts
target:
entity_id: weather.forecast_home
data:
type: hourly
response_variable: forecast_data
binary_sensor:
- name: "Beschattung Hitzeschutz Noetig"
unique_id: beschattung_hitzeschutz_noetig
icon: mdi:sun-thermometer
state: >-
{% set benoetigt = 7 %}
{% set schwelle = states('input_number.beschattung_schwelle_aussen_hitzeschutz') | float(27) %}
{% set puffer_min = states('input_number.beschattung_hitzeschutz_sonnenuntergang_puffer') | int(60) %}
{% set sunset_raw = state_attr('sun.sun', 'next_setting') %}
{% set sunset = as_datetime(sunset_raw) if sunset_raw else now() %}
{% set sonne_ok = state_attr('sun.sun', 'elevation') | float(0) > 0
and now().timestamp() < sunset.timestamp() - puffer_min * 60 %}
{% set ns = namespace(n=0) %}
{% for h in (forecast_data | default({})).get('weather.forecast_home', {}).get('forecast', []) %}
{% if as_datetime(h.datetime) < sunset and h.temperature | float(-99) >= schwelle %}
{% set ns.n = ns.n + 1 %}
{% endif %}
{% endfor %}
{% set war_an = this.state == 'on' %}
{{ trigger.id | default('') != 'abkuehlung'
and sonne_ok
and (ns.n >= benoetigt or war_an) }}
attributes:
heisse_stunden: >-
{% set schwelle = states('input_number.beschattung_schwelle_aussen_hitzeschutz') | float(27) %}
{% set sunset_raw = state_attr('sun.sun', 'next_setting') %}
{% set sunset = as_datetime(sunset_raw) if sunset_raw else now() %}
{% set ns = namespace(n=0) %}
{% for h in (forecast_data | default({})).get('weather.forecast_home', {}).get('forecast', []) %}
{% if as_datetime(h.datetime) < sunset and h.temperature | float(-99) >= schwelle %}
{% set ns.n = ns.n + 1 %}
{% endif %}
{% endfor %}
{{ ns.n }}