Bewegungsgesteuertes Licht ist wahrscheinlich eine der ältesten „Smart Home“ Lösungen, die es gibt. Mit modernen smarten Leuchtmitteln und Steuerzentralen lässt sich allerdings noch viel mehr umsetzen, als nur an oder aus. Eine populäre Lösung ist hier Philips Hue, das allerdings (auch nach der Einführung der Hue Essentials) nach wie vor eher teuer ist – aber dankbarerweise kann man mit universellen Steuerzentralen wie Home Assistant Leuchtmittel verschiedenster Hersteller verwenden.
In meinem Fall habe ich bei Ikea während des Abverkaufs der „alten“ Zigbee-Produkte einige „Tradfri“ Tunable White Leuchtmittel erworben, die zusammen mit einem Zigbee-Bewegungsmelder als automatische Badezimmerbeleuchtung dienen sollte. Allerdings (schließlich soll das Licht ja so smart wie möglich sein) sollte die Automation mehr können als nur an oder aus:
- Weiches Dimmen
- Unterschiedliche Helligkeits- und Farbwerte basierend auf der Uhrzeit (Tag/Nachtmodus)
- „Warndimmen“, sodass man nicht plötzlich auf der Toilette im Dunkeln sitzt
Tatsächlich bin ich selbstverständlich nicht der Erste mit dieser Idee, weshalb es bereits einige Automations-Blueprints mit diesem Funktionsumfang (und mehr!) gibt, z.B. dieses hier vom HA-Forum Nutzer „Blacky“. Theoretisch sollte damit also alles getan sein: Blueprint in HA importieren, Einstellungen anpassen und fertig? Leider nein, denn wie ich auch schon hier beschrieben habe, unterstützen die Tradfri Leuchtmittel leider keine gleichzeitige Anpassung der Helligkeit und Farbtemperatur, sodass solche Blueprints bzw. Automationen leider nicht wie gewünscht funktionieren. Des Weiteren hatte ich gelegentlich das Problem, dass eine der Leuchten einen der Befehle nicht empfangen oder erfolgreich umgesetzt hatte, was nicht schön aussieht und auch nicht förderlich für das Vertrauen der anderen Hausbewohner in die Automation ist. Also habe ich einen Automations-Blueprint erstellt, der diese Probleme löst – selbstverständlich funktioniert der auch nicht ausschließelich für Ikea Tradfri Leuchtmittel, sondern müsste für alle Tunable White Leuchten in Home Assistant funktionieren.
Der Blueprint setzt das wie folgt um:
- Senden des Einschaltbefehls mit der gewünschten Helligkeit und Dimm-Dauer
- Senden der gewünschten Farbtemperatur nach einstellbarer Verzögerung
- Prüfen, ob alle Leuchten den gewünschten Status haben
- Falls nein: Befehle erneut an dieses Leuchtmittel senden
- Nach einstellbarer Dauer: Warndimmen
- Nach einstellbarer Dauer: Lichter ausschalten
- Prüfen, ob Lampen ausgeschaltet, falls nicht, erneut Ausschaltbefehl senden
Darüber hinaus bleibt der Status des Lichts konstant, solange es eingeschaltet ist, das heißt beim Erreichen der Umschaltzeit zwischen Tag oder Nacht passiert nichts, solange das Licht bereits eingeschaltet ist (damit man nicht plötzlich im Dunkeln steht).
Bei der Handhabung des Blueprints habe ich mich an dem oben erwähnten Skript von Blacky orientiert und ein ähnliches Einstellungsmenü eingebaut, welches alle für diesen Zweck relevanten Einstellungen enthält. Dabei habe ich mir von Codex helfen lassen, Teile des Skripts geschrieben hat. Das erschien mir deutlich zielführender, als eine Modifikation des bestehenden Skripts aus dem Forum, welches mehr als 12.000 Zeilen lang ist – meins kommt hingegen nur auf 824 Zeilen. Natürlich bietet es auch einen kleineren Funktionsumfang, aber es deckt alles ab was ich haben wollte und was nicht da ist, kann auch nicht zu unvorhergesehenen Edge-Cases und Problemen führen.
Um den Blueprint in Home Assistant zu importieren, müsst ihr unter Einstellungen -> Automationen & Szenen > Blueprints den Button „Blueprint importieren“ wählen. Leider könnt ihr hier nicht einfach Copy/Pasten, sondern müsst zwangsläufig eine URL angeben, unter welcher der Blueprint liegt. Hierzu habe ich den Blueprint auf meinen Server geladen, ihr könnt den Blueprint daher unter „http://silentnet.de/download/motion-light_tradfri_new.yaml“ abrufen. Alternativ findet ihr den Code auch hier drunter im Spoiler, sodass ihr ihn kopieren und anderswo hochladen könnt.
Blueprint Code
blueprint:
name: Motion Light – Tradfri Tunable White Kompatibel
description: >
Bewegungslicht-Blueprint für Tunable-White-Leuchten.
Helligkeit und Kelvin werden für IKEA Tradfri getrennt gesendet.
Ein Smart-Reassert korrigiert anschließend nur tatsächlich abweichende
Lampen. Der optionale Nacht-Helper hat Vorrang vor der Uhrzeit. Der beim
Einschalten gewählte Tag-/Nachtmodus bleibt bis zum finalen Ausschalten
gespeichert.
domain: automation
homeassistant:
min_version: 2024.10.0
input:
entities:
name: Geräte und Entitäten
icon: mdi:motion-sensor
collapsed: false
input:
motion_sensors:
name: Bewegungs- oder Präsenzsensoren
description: >
Ein oder mehrere binary_sensor-Entities; aktiv ist der Zustand
"on". Erst wenn alle Sensoren exakt "off" sind, wird ausgeschaltet.
selector:
entity:
filter:
- domain: binary_sensor
multiple: true
light_entities:
name: Lampen
description: >
Eine oder mehrere light-Entities; einzelne Lampen und
Home-Assistant-Light-Groups sind zulässig.
selector:
entity:
filter:
- domain: light
multiple: true
enable_helper:
name: Enable-/Disable-Helper (optional)
description: >
Wenn ausgewählt, arbeitet die Automation nur bei "on".
Leer bedeutet ausdrücklich: kein Helper.
default: ""
selector:
entity:
filter:
- domain: input_boolean
night_mode:
name: Tag- und Nachtmodus
icon: mdi:weather-night
collapsed: false
input:
night_mode_enabled:
name: Zeitgesteuerten Nachtmodus aktivieren
description: >
Gilt ohne Nacht-Helper. Ein ausgewählter Nacht-Helper hat Vorrang.
default: true
selector:
boolean:
night_start:
name: Nachtmodus beginnt
description: >
24:00 beziehungsweise 24:00:00 wird intern als 00:00:00 behandelt.
default: "22:00:00"
selector:
time:
night_end:
name: Nachtmodus endet
description: >
24:00 beziehungsweise 24:00:00 wird intern als 00:00:00 behandelt.
default: "06:00:00"
selector:
time:
night_mode_helper:
name: Nachtmodus-Helper (optional)
description: >
Wenn ausgewählt, bedeutet "on" Nacht und "off" Tag; die Uhrzeit
wird dann nicht ausgewertet.
default: ""
selector:
entity:
filter:
- domain: input_boolean
normal_light:
name: Normales Licht
icon: mdi:lightbulb-on
collapsed: true
input:
normal_brightness_pct:
name: Helligkeit
default: 80
selector:
number:
min: 1
max: 100
step: 1
mode: slider
unit_of_measurement: "%"
normal_color_temp_kelvin:
name: Farbtemperatur
default: 3500
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
normal_transition:
name: Einschalt-Transition
default: 1
selector:
number:
min: 0
max: 10
step: 0.1
mode: box
unit_of_measurement: s
night_light:
name: Nachtlicht
icon: mdi:lightbulb-night
collapsed: true
input:
night_brightness_pct:
name: Helligkeit
default: 15
selector:
number:
min: 1
max: 100
step: 1
mode: slider
unit_of_measurement: "%"
night_color_temp_kelvin:
name: Farbtemperatur
default: 2200
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
night_transition:
name: Einschalt-Transition
default: 1
selector:
number:
min: 0
max: 10
step: 0.1
mode: box
unit_of_measurement: s
turn_off:
name: Ausschaltverhalten
icon: mdi:lightbulb-off
collapsed: false
input:
no_motion_delay:
name: Zeit ohne Bewegung
default:
minutes: 5
selector:
duration:
warning_enabled:
name: Warn-Dimmen aktivieren
default: true
selector:
boolean:
warning_duration:
name: Dauer des Warn-Dimmens
default:
seconds: 10
selector:
duration:
warning_transition:
name: Transition beim Warn-Dimmen
default: 1
selector:
number:
min: 0
max: 10
step: 0.1
mode: box
unit_of_measurement: s
warning_level_pct:
name: Warn-Helligkeit
description: >
Prozent der Zielhelligkeit des gespeicherten Tag-/Nachtmodus.
default: 25
selector:
number:
min: 1
max: 100
step: 1
mode: slider
unit_of_measurement: "% des Zielwerts"
off_transition:
name: Ausschalt-Transition
default: 2
selector:
number:
min: 0
max: 30
step: 0.1
mode: box
unit_of_measurement: s
off_retry_delay:
name: Verzögerung bis zur Ausschaltkontrolle
description: >
Danach werden standardmäßig nur Lampen erneut ausgeschaltet, die
Home Assistant noch als eingeschaltet meldet.
default:
seconds: 3
selector:
duration:
force_off_retry:
name: Zweiten Ausschaltbefehl immer senden
description: >
Sendet den zweiten Off-Befehl auch an bereits als "off" gemeldete
Lampen. Das kann Restglimmen beheben, bei manchen Tradfri-Lampen
aber ein kurzes Aufblitzen verursachen.
default: false
selector:
boolean:
tradfri:
name: Tradfri-Kompatibilität
icon: mdi:zigbee
collapsed: false
input:
color_command_delay:
name: Verzögerung Helligkeit → Kelvin
description: >
Der Kelvin-Befehl enthält keine erneute Helligkeitsangabe.
default:
seconds: 1
selector:
duration:
brightness_check_delay:
name: Verzögerung bis zur Helligkeitskontrolle
description: >
Ab dem ersten Helligkeitsbefehl gemessen. Mindestens so lang wie
Kelvin-Verzögerung und gewünschte Einschalt-Transition wählen.
default:
seconds: 3
selector:
duration:
brightness_tolerance:
name: Helligkeitstoleranz
description: Home-Assistant-Skala von 0 bis 255.
default: 10
selector:
number:
min: 0
max: 50
step: 1
mode: slider
trigger_variables:
motion_trigger_entities: !input motion_sensors
# Ein Aggregat-Trigger feuert nur beim Übergang
# "kein Sensor on" -> "mindestens ein Sensor on".
triggers:
- trigger: template
value_template: >-
{% set ns = namespace(any_on=false) %}
{% for entity_id in motion_trigger_entities %}
{% if is_state(entity_id, 'on') %}
{% set ns.any_on = true %}
{% endif %}
{% endfor %}
{{ ns.any_on }}
variables:
motion_entities: !input motion_sensors
lights: !input light_entities
enable_helper_entity: !input enable_helper
night_helper_entity: !input night_mode_helper
use_timed_night_mode: !input night_mode_enabled
night_start_time: !input night_start
night_end_time: !input night_end
normal_brightness: !input normal_brightness_pct
normal_kelvin: !input normal_color_temp_kelvin
normal_on_transition: !input normal_transition
night_brightness: !input night_brightness_pct
night_kelvin: !input night_color_temp_kelvin
night_on_transition: !input night_transition
no_motion_duration: !input no_motion_delay
use_warning: !input warning_enabled
warning_transition_seconds: !input warning_transition
warning_factor: !input warning_level_pct
brightness_tolerance_value: !input brightness_tolerance
off_transition_seconds: !input off_transition
force_off_retry_enabled: !input force_off_retry
conditions: []
# Ein einzelner Lauf hält den gewählten Modus bis zum vollständigen Ausschalten.
# Bewegung während Warte-/Warnphasen wird intern verarbeitet.
mode: single
max_exceeded: silent
actions:
# Leere Helper-Auswahl ist kein versehentlich erfüllter State-Test.
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ not selected or is_state(enable_helper_entity, 'on') }}
# Pflichtauswahlen zusätzlich absichern: niemals mit leeren Targets laufen.
- condition: template
value_template: >-
{{ motion_entities | count > 0 and lights | count > 0 }}
# Modus-Snapshot: Helper vor Uhrzeit; start=end bedeutet kein Zeitfenster.
- variables:
night_mode_active: >-
{% set selected = night_helper_entity is string
and night_helper_entity | length > 0 %}
{% if selected %}
{{ is_state(night_helper_entity, 'on') }}
{% elif not (use_timed_night_mode | bool) %}
{{ false }}
{% else %}
{# today_at() akzeptiert 24:00 nicht; Mitternacht normalisieren. #}
{% set normalized_start = '00:00:00'
if night_start_time in ['24:00', '24:00:00']
else night_start_time %}
{% set normalized_end = '00:00:00'
if night_end_time in ['24:00', '24:00:00']
else night_end_time %}
{% set start = today_at(normalized_start) %}
{% set end = today_at(normalized_end) %}
{% set current = now() %}
{% if start < end %}
{{ start <= current < end }}
{% elif start > end %}
{{ current >= start or current < end }}
{% else %}
{{ false }}
{% endif %}
{% endif %}
resolved_lights: >-
{% set ns = namespace(ids=[]) %}
{% for entity_id in lights %}
{% set members = state_attr(entity_id, 'entity_id') %}
{% if members is iterable and members is not string
and members | count > 0 %}
{% set ns.ids = ns.ids + (members | list) %}
{% else %}
{% set ns.ids = ns.ids + [entity_id] %}
{% endif %}
{% endfor %}
{{ expand(ns.ids)
| selectattr('domain', 'eq', 'light')
| map(attribute='entity_id')
| unique
| list }}
- variables:
target_brightness_pct: >-
{{ (night_brightness if night_mode_active | bool
else normal_brightness) | int }}
target_color_temp_kelvin: >-
{{ (night_kelvin if night_mode_active | bool
else normal_kelvin) | int }}
target_on_transition: >-
{{ (night_on_transition if night_mode_active | bool
else normal_on_transition) | float }}
warning_brightness_pct: >-
{% set value =
(target_brightness_pct | float * warning_factor | float / 100)
| round(0) | int %}
{{ [1, value] | max }}
- variables:
initial_brightness_targets: >-
{% set target = target_brightness_pct | float * 255 / 100 %}
{% set tolerance = brightness_tolerance_value | float %}
{% set ns = namespace(ids=[]) %}
{% for entity_id in resolved_lights %}
{% set reported = state_attr(entity_id, 'brightness') %}
{% if states(entity_id) != 'on'
or reported is none
or not is_number(reported)
or (((reported | float) - target) | abs) > tolerance %}
{% set ns.ids = ns.ids + [entity_id] %}
{% endif %}
{% endfor %}
{{ ns.ids }}
# Sofort: nur Helligkeit und Transition, niemals Kelvin.
- if:
- condition: template
value_template: "{{ initial_brightness_targets | count > 0 }}"
then:
- action: light.turn_on
target:
entity_id: "{{ initial_brightness_targets }}"
data:
brightness_pct: "{{ target_brightness_pct }}"
transition: "{{ target_on_transition }}"
# Beide Delays beginnen am selben Punkt: Kelvin bei t=1 s, Check bei t=3 s.
- parallel:
- sequence:
- delay: !input color_command_delay
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ (not selected or is_state(enable_helper_entity, 'on'))
and resolved_lights | count > 0 }}
# Tradfri: Kelvin und Transition, aber weiterhin keine Helligkeit.
- action: light.turn_on
target:
entity_id: "{{ resolved_lights }}"
data:
color_temp_kelvin: "{{ target_color_temp_kelvin }}"
transition: "{{ target_on_transition }}"
- sequence:
- delay: !input brightness_check_delay
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ not selected or is_state(enable_helper_entity, 'on') }}
- variables:
reassert_targets: >-
{% set target = target_brightness_pct | float * 255 / 100 %}
{% set tolerance = brightness_tolerance_value | float %}
{% set ns = namespace(ids=[]) %}
{% for entity_id in resolved_lights %}
{% set reported = state_attr(entity_id, 'brightness') %}
{% if states(entity_id) != 'on'
or reported is none
or not is_number(reported)
or (((reported | float) - target) | abs) > tolerance %}
{% set ns.ids = ns.ids + [entity_id] %}
{% endif %}
{% endfor %}
{{ ns.ids }}
# Nur abweichende Lampen: wie beim ersten Einschalten zunächst
# Helligkeit plus Transition, danach Kelvin als separater Befehl.
- if:
- condition: template
value_template: "{{ reassert_targets | count > 0 }}"
then:
- action: light.turn_on
target:
entity_id: "{{ reassert_targets }}"
data:
brightness_pct: "{{ target_brightness_pct }}"
transition: "{{ target_on_transition }}"
- delay: !input color_command_delay
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ (not selected
or is_state(enable_helper_entity, 'on'))
and reassert_targets | count > 0 }}
then:
# Tradfri: Kelvin bleibt auch beim Reassert getrennt.
- action: light.turn_on
target:
entity_id: "{{ reassert_targets }}"
data:
color_temp_kelvin: "{{ target_color_temp_kelvin }}"
transition: "{{ target_on_transition }}"
# Mode-Latch: Dieser Lauf umfasst den kompletten Beleuchtungszyklus.
# Zeit- oder Helper-Wechsel ändern target_* deshalb nicht mehr. Neue Bewegung
# wird innerhalb der Schleife behandelt und startet keinen neuen Lauf.
- repeat:
while:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ not selected or is_state(enable_helper_entity, 'on') }}
sequence:
# Erst alle exakt "off"; unknown/unavailable blockiert das Ausschalten.
- wait_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{% set disabled = selected
and not is_state(enable_helper_entity, 'on') %}
{% set ns = namespace(all_off=(motion_entities | count > 0)) %}
{% for entity_id in motion_entities %}
{% if not is_state(entity_id, 'off') %}
{% set ns.all_off = false %}
{% endif %}
{% endfor %}
{{ disabled or ns.all_off }}
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ not selected or is_state(enable_helper_entity, 'on') }}
then:
# Bereits verstrichene Inaktivität während der Tradfri-Prüfung
# wird von der konfigurierten Nachlaufzeit abgezogen.
- variables:
no_motion_remaining_seconds: >-
{% set d = no_motion_duration %}
{% set total =
(d.get('days', 0) | float * 86400)
+ (d.get('hours', 0) | float * 3600)
+ (d.get('minutes', 0) | float * 60)
+ (d.get('seconds', 0) | float)
+ (d.get('milliseconds', 0) | float / 1000) %}
{% set ns = namespace(latest=0) %}
{% for sensor in expand(motion_entities) %}
{% set changed = as_timestamp(sensor.last_changed, 0) %}
{% if changed > ns.latest %}
{% set ns.latest = changed %}
{% endif %}
{% endfor %}
{% set elapsed = as_timestamp(now()) - ns.latest
if ns.latest > 0 else 0 %}
{% set remaining = total - elapsed %}
{{ 0 if remaining <= 0
else remaining | round(0, 'ceil') | int }}
# Bewegung beendet den Nachlauf sofort; die Schleife beginnt erneut.
- wait_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{% set disabled = selected
and not is_state(enable_helper_entity, 'on') %}
{% set ns = namespace(any_on=false) %}
{% for entity_id in motion_entities %}
{% if is_state(entity_id, 'on') %}
{% set ns.any_on = true %}
{% endif %}
{% endfor %}
{{ disabled or ns.any_on }}
timeout:
seconds: "{{ no_motion_remaining_seconds }}"
continue_on_timeout: true
# wait.completed bleibt auch dann true, wenn ein sehr kurzer
# Bewegungsimpuls beim nächsten Schritt schon wieder "off" ist.
- variables:
no_motion_interrupted: "{{ wait.completed }}"
# Bei Bewegung im normalen Nachlauf werden nur Lampen korrigiert,
# deren Rückmeldung wirklich vom gespeicherten Ziel abweicht.
- variables:
motion_restore_targets: >-
{% set target = target_brightness_pct | float * 255 / 100 %}
{% set tolerance = brightness_tolerance_value | float %}
{% set ns = namespace(ids=[]) %}
{% for entity_id in resolved_lights %}
{% set reported = state_attr(entity_id, 'brightness') %}
{% if states(entity_id) != 'on'
or reported is none
or not is_number(reported)
or (((reported | float) - target) | abs) > tolerance %}
{% set ns.ids = ns.ids + [entity_id] %}
{% endif %}
{% endfor %}
{{ ns.ids }}
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ (not selected or is_state(enable_helper_entity, 'on'))
and (no_motion_interrupted | bool)
and motion_restore_targets | count > 0 }}
then:
- action: light.turn_on
target:
entity_id: "{{ motion_restore_targets }}"
data:
brightness_pct: "{{ target_brightness_pct }}"
- delay: !input color_command_delay
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ (not selected
or is_state(enable_helper_entity, 'on'))
and motion_restore_targets | count > 0 }}
then:
- action: light.turn_on
target:
entity_id: "{{ motion_restore_targets }}"
data:
color_temp_kelvin: "{{ target_color_temp_kelvin }}"
transition: "{{ target_on_transition }}"
# Nur ein echter Timeout bei weiterhin vollständig inaktiven Sensoren
# darf Warn-Dimmen und den finalen Ausschaltpfad betreten.
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{% set ns = namespace(all_off=(motion_entities | count > 0)) %}
{% for entity_id in motion_entities %}
{% if not is_state(entity_id, 'off') %}
{% set ns.all_off = false %}
{% endif %}
{% endfor %}
{{ (not selected or is_state(enable_helper_entity, 'on'))
and not (no_motion_interrupted | bool)
and ns.all_off }}
then:
# Ohne Warn-Dimmen bleibt dieses Flag false. Die Variable wird
# vor dem verschachtelten If angelegt, damit dessen Update im
# gesamten Script-Lauf sichtbar bleibt.
- variables:
warning_interrupted: false
# Warn-Dimmen basiert direkt auf dem gespeicherten Zielwert.
- if:
- condition: template
value_template: "{{ use_warning | bool }}"
then:
- variables:
warning_targets: >-
{% set ns = namespace(ids=[]) %}
{% for entity_id in resolved_lights %}
{% if is_state(entity_id, 'on') %}
{% set ns.ids = ns.ids + [entity_id] %}
{% endif %}
{% endfor %}
{{ ns.ids }}
- if:
- condition: template
value_template: >-
{{ warning_targets | count > 0 }}
then:
- action: light.turn_on
target:
entity_id: "{{ warning_targets }}"
data:
brightness_pct: "{{ warning_brightness_pct }}"
transition: "{{ warning_transition_seconds }}"
- wait_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{% set disabled = selected
and not is_state(enable_helper_entity, 'on') %}
{% set ns = namespace(any_on=false) %}
{% for entity_id in motion_entities %}
{% if is_state(entity_id, 'on') %}
{% set ns.any_on = true %}
{% endif %}
{% endfor %}
{{ disabled or ns.any_on }}
timeout: !input warning_duration
continue_on_timeout: true
- variables:
warning_interrupted: "{{ wait.completed }}"
# Bei Bewegung im Warnfenster wird exakt der gespeicherte Modus
# wiederhergestellt, nicht aus der Warnhelligkeit zurückgerechnet.
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ use_warning | bool
and (not selected
or is_state(enable_helper_entity, 'on'))
and (warning_interrupted | bool)
and resolved_lights | count > 0 }}
then:
- action: light.turn_on
target:
entity_id: "{{ resolved_lights }}"
data:
brightness_pct: "{{ target_brightness_pct }}"
transition: "{{ warning_transition_seconds }}"
- delay: !input color_command_delay
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ (not selected
or is_state(enable_helper_entity, 'on'))
and resolved_lights | count > 0 }}
then:
# Tradfri: auch beim Restore Helligkeit und Kelvin trennen.
- action: light.turn_on
target:
entity_id: "{{ resolved_lights }}"
data:
color_temp_kelvin: >-
{{ target_color_temp_kelvin }}
transition: "{{ warning_transition_seconds }}"
# Ohne neue Bewegung beginnt jetzt der einzige finale Off-Pfad.
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{% set ns = namespace(all_off=(motion_entities | count > 0)) %}
{% for entity_id in motion_entities %}
{% if not is_state(entity_id, 'off') %}
{% set ns.all_off = false %}
{% endif %}
{% endfor %}
{{ (not selected
or is_state(enable_helper_entity, 'on'))
and not (warning_interrupted | bool)
and ns.all_off and lights | count > 0 }}
then:
- action: light.turn_off
target:
entity_id: "{{ lights }}"
data:
transition: "{{ off_transition_seconds }}"
# Auch der Off-Retry ist bewegungsreaktiv. Bewegung erhält
# den alten Snapshot, solange der Off-Pfad nicht vollständig
# abgeschlossen wurde.
- wait_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{% set disabled = selected
and not is_state(enable_helper_entity, 'on') %}
{% set ns = namespace(any_on=false) %}
{% for entity_id in motion_entities %}
{% if is_state(entity_id, 'on') %}
{% set ns.any_on = true %}
{% endif %}
{% endfor %}
{{ disabled or ns.any_on }}
timeout: !input off_retry_delay
continue_on_timeout: true
- variables:
off_retry_interrupted: "{{ wait.completed }}"
- if:
- condition: template
value_template: >-
{% set selected = enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ (not selected
or is_state(enable_helper_entity, 'on'))
and (off_retry_interrupted | bool)
and resolved_lights | count > 0 }}
then:
- action: light.turn_on
target:
entity_id: "{{ resolved_lights }}"
data:
brightness_pct: "{{ target_brightness_pct }}"
- delay: !input color_command_delay
- if:
- condition: template
value_template: >-
{% set selected =
enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{{ (not selected
or is_state(enable_helper_entity, 'on'))
and resolved_lights | count > 0 }}
then:
- action: light.turn_on
target:
entity_id: "{{ resolved_lights }}"
data:
color_temp_kelvin: >-
{{ target_color_temp_kelvin }}
transition: "{{ target_on_transition }}"
else:
- if:
- condition: template
value_template: >-
{% set selected =
enable_helper_entity is string
and enable_helper_entity | length > 0 %}
{% set ns = namespace(
all_off=(motion_entities | count > 0)) %}
{% for entity_id in motion_entities %}
{% if not is_state(entity_id, 'off') %}
{% set ns.all_off = false %}
{% endif %}
{% endfor %}
{{ (not selected
or is_state(enable_helper_entity, 'on'))
and not (off_retry_interrupted | bool)
and ns.all_off and lights | count > 0 }}
then:
- variables:
off_retry_targets: >-
{% if force_off_retry_enabled | bool %}
{{ lights }}
{% else %}
{% set ns = namespace(ids=[]) %}
{% for entity_id in resolved_lights %}
{% if is_state(entity_id, 'on') %}
{% set ns.ids = ns.ids + [entity_id] %}
{% endif %}
{% endfor %}
{{ ns.ids }}
{% endif %}
- if:
- condition: template
value_template: >-
{{ off_retry_targets | count > 0 }}
then:
- action: light.turn_off
target:
entity_id: "{{ off_retry_targets }}"
# Auch ohne Retry-Target muss die Repeat-Schleife enden.
- stop: Beleuchtungszyklus vollständig beendet
Anschließend könnt ihr mit dem Blueprint eine Automation anlegen und die entsprechenden Einstellungen vornehmen, welche Lampen ihr wie steuern wollt. Das war’s schon! Falls ihr die Automation benutzt, lasst mich gerne wissen, wie es funktioniert.
