Skip to content

Instantly share code, notes, and snippets.

@psychowood
Forked from BoBBer446/sunrise-wake-up.yaml
Last active February 9, 2026 21:57
Show Gist options
  • Select an option

  • Save psychowood/94ae85bca884c6973cbdcf1032273a0c to your computer and use it in GitHub Desktop.

Select an option

Save psychowood/94ae85bca884c6973cbdcf1032273a0c to your computer and use it in GitHub Desktop.
Wake-up Sunrise (Kelvin, smooth & smart)
blueprint:
name: Wake-up Sunrise (Kelvin, smooth & smart)
description: >
Modern alarm clock with sunrise effect. Default: warm white (≈2200 K) → daylight white (≈6500 K),
automatically adapted to device limits. Optionally use an RGB color curve from start to end color instead of Kelvin.
Future-proof thanks to color_temp_kelvin. For multiple lamps, please select a Light Group.
domain: automation
input:
light_entity:
name: Target Light
description: >
Light or Light Group to be woken up.
selector:
entity:
domain: light
# --- Alarm Source ---
timestamp_sensor:
name: Timestamp Entity (optional)
description: >
Sensor with device_class=timestamp (e.g. phone alarm). If 'none', manual time is used.
default: none
selector:
entity:
device_class: timestamp
manual_time:
name: Manual Wake Time
description: >
Daily wake time when no timestamp sensor is set (format HH:MM:SS).
default: "07:00:00"
selector:
time: {}
# --- Duration & Brightness ---
sunrise_duration_min:
name: Sunrise Duration (Minutes)
description: >
Total duration of the ramp for brightness and color.
default: 25
selector:
number:
min: 5
max: 120
step: 5
unit_of_measurement: min
mode: slider
start_brightness_pct:
name: Start Brightness (%)
description: >
Initial brightness at the beginning of the ramp. 1–10% recommended (some lights ignore 1%).
default: 3
selector:
number:
min: 1
max: 100
step: 1
mode: slider
end_brightness_pct:
name: End Brightness (%)
description: >
Target brightness at the end of the ramp.
default: 100
selector:
number:
min: 5
max: 100
step: 1
mode: slider
# --- Kelvin Ramp (Default) ---
start_kelvin:
name: Start Kelvin (Warm)
description: >
Color temperature at the beginning of the ramp (e.g. 2200 K = warm). Automatically adjusted to device limits.
default: 2200
selector:
number:
min: 1500
max: 9000
step: 50
unit_of_measurement: K
mode: slider
end_kelvin:
name: End Kelvin (Cooler / Daylight)
description: >
Color temperature at the end of the ramp (e.g. 6500 K = daylight white). Automatically adjusted to device limits.
default: 6500
selector:
number:
min: 1500
max: 9000
step: 50
unit_of_measurement: K
mode: slider
# --- Alternative: RGB Color Curve ---
enable_color_ramp:
name: Color Gradient (RGB) instead of Kelvin
description: >
Enable to use an RGB color gradient from start to end color instead of Kelvin.
default: false
selector:
boolean: {}
start_color_rgb:
name: Start Color (RGB)
description: >
Start color for the RGB gradient (only relevant if color gradient is enabled).
default:
r: 255
g: 120
b: 20
selector:
color_rgb: {}
end_color_rgb:
name: End Color (RGB)
description: >
End color for the RGB gradient (only relevant if color gradient is enabled).
default:
r: 255
g: 255
b: 255
selector:
color_rgb: {}
ramp_steps:
name: Step Count (Smoothness)
description: >
Number of intermediate steps for brightness/color (more = smoother ramp, but more service calls).
default: 60
selector:
number:
min: 10
max: 240
step: 10
# --- Conditions (optional) ---
check_entity:
name: Check Entity
description: >
Must be 'on' or 'home' for the alarm to start (e.g. Workday, Device Tracker, or Person entity).
Leave empty (= none) if no condition is desired.
default: none
selector:
entity: {}
require_workday:
name: Workdays Only
description: >
Execute only when the Workday sensor is 'on'.
default: false
selector:
boolean: {}
workday_sensor:
name: Workday Sensor
description: >
Workday sensor to check when "Workdays Only" is active.
default: binary_sensor.workday
selector:
entity:
domain: binary_sensor
respect_quiet_hours:
name: Respect Quiet Hours
description: >
Prevents startup if the current time is within the quiet hours window.
default: false
selector:
boolean: {}
quiet_start:
name: Quiet Hours Start
description: >
Start of the quiet hours window (format HH:MM:SS).
default: "22:00:00"
selector:
time: {}
quiet_end:
name: Quiet Hours End
description: >
End of the quiet hours window (format HH:MM:SS).
default: "06:00:00"
selector:
time: {}
off_cancels:
name: Turn Off Cancels
description: >
If the light is turned off during the ramp, the process is cancelled.
default: true
selector:
boolean: {}
# --- Pre/Post Actions ---
pre_actions:
name: Pre-Actions
description: >
Actions executed immediately before the ramp starts (e.g. increase heating).
default: []
selector:
action: {}
post_actions:
name: Post-Actions
description: >
Actions at the end of the ramp (e.g. start music).
default: []
selector:
action: {}
variables:
le: !input light_entity
sensor_ts: !input timestamp_sensor
manual_time: !input manual_time
duration_min: !input sunrise_duration_min
seconds: "{{ (duration_min | float(25)) * 60 }}"
start_pct: !input start_brightness_pct
end_pct: !input end_brightness_pct
range_pct: "{{ (end_pct | float) - (start_pct | float) }}"
steps: !input ramp_steps
enable_rgb: !input enable_color_ramp
off_cancels: !input off_cancels
check_entity: !input check_entity
require_workday: !input require_workday
workday_sensor: !input workday_sensor
respect_quiet: !input respect_quiet_hours
quiet_start: !input quiet_start
quiet_end: !input quiet_end
# Device limits for Kelvin (prefer native, otherwise derive from Mired)
minK_native: "{{ state_attr(le, 'min_color_temp_kelvin') }}"
maxK_native: "{{ state_attr(le, 'max_color_temp_kelvin') }}"
minM: "{{ state_attr(le, 'min_mireds') }}"
maxM: "{{ state_attr(le, 'max_mireds') }}"
device_k_min: >-
{% if minK_native is number %}
{{ minK_native | int }}
{% elif maxM is number %}
{{ (1000000 / (maxM | float)) | int }}
{% else %}
2000
{% endif %}
device_k_max: >-
{% if maxK_native is number %}
{{ maxK_native | int }}
{% elif minM is number %}
{{ (1000000 / (minM | float)) | int }}
{% else %}
6500
{% endif %}
# User values (Kelvin) → clamp to device limits and ensure order (start <= end)
startK_in: !input start_kelvin
endK_in: !input end_kelvin
startK_clamped: >-
{% set s = startK_in | int(2200) %}
{% set s1 = [s, device_k_min] | max %}
{{ [s1, device_k_max] | min }}
endK_clamped: >-
{% set e = endK_in | int(6500) %}
{% set e1 = [e, device_k_min] | max %}
{{ [e1, device_k_max] | min }}
start_kelvin_final: "{{ [startK_clamped, endK_clamped] | min }}"
end_kelvin_final: "{{ [startK_clamped, endK_clamped] | max }}"
# Tick time (seconds), minimum 1 s
tick: >-
{% set t = (seconds | float) / (steps | float) %}
{{ [ t, 1 ] | max | int }}
# RGB start/end (+ components)
rgb_start: !input start_color_rgb
rgb_end: !input end_color_rgb
sr: "{{ (rgb_start.r | int) }}"
sg: "{{ (rgb_start.g | int) }}"
sb: "{{ (rgb_start.b | int) }}"
er: "{{ (rgb_end.r | int) }}"
eg: "{{ (rgb_end.g | int) }}"
eb: "{{ (rgb_end.b | int) }}"
# Device capabilities
supported_modes: "{{ state_attr(le, 'supported_color_modes') | default([]) }}"
can_rgb: >-
{{ 'hs' in supported_modes or 'rgb' in supported_modes or 'rgbw' in supported_modes or 'rgbww' in supported_modes or 'xy' in supported_modes }}
can_ct: >-
{{ 'color_temp' in supported_modes or minM is number or minK_native is number or maxM is number or maxK_native is number }}
# Conditions
cond_check_entity_ok: >-
{{ check_entity == 'none' or states(check_entity) in ['on','home','unknown'] }}
cond_workday_ok: >-
{{ (not require_workday) or (is_state(workday_sensor, 'on')) }}
cond_quiet_ok: >-
{% if not respect_quiet %}true
{% else %}
{% set nowt = now().time() %}
{% set qs = strptime(quiet_start, '%H:%M:%S').time() %}
{% set qe = strptime(quiet_end, '%H:%M:%S').time() %}
{% if qs <= qe %}
{{ not (nowt >= qs and nowt < qe) }}
{% else %}
{{ not (nowt >= qs or nowt < qe) }}
{% endif %}
{% endif %}
trigger:
- platform: time_pattern
minutes: "*"
condition: []
action:
# 1) Wait for valid alarm basis (timestamp present OR manual time)
- wait_template: >-
{{ sensor_ts == 'none' or as_timestamp(states(sensor_ts), None) != None }}
# 2) Start window (0 < Alarm - now <= Duration) + conditions met
- wait_template: >-
{% set alarm_ts = as_timestamp(sensor_ts != 'none'
and states(sensor_ts)
or (states('sensor.date') ~ ' ' ~ manual_time)) %}
{% set now_ts = as_timestamp(states('sensor.date_time_iso')) %}
{{ 0 < (alarm_ts - now_ts) <= (seconds | float) and
cond_check_entity_ok and cond_workday_ok and cond_quiet_ok }}
# 3) Pre-actions
- choose: []
default: !input pre_actions
# 4) Recheck conditions shortly before start
- condition: template
value_template: "{{ cond_check_entity_ok and cond_workday_ok and cond_quiet_ok }}"
# 5) Initial turn on
- choose:
# 5a) RGB – if enabled and supported
- conditions: "{{ enable_rgb and can_rgb }}"
sequence:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ start_pct | int }}"
rgb_color: ["{{ sr }}","{{ sg }}","{{ sb }}"]
transition: 1
# 5b) Kelvin – Default (if supported)
- conditions: "{{ can_ct }}"
sequence:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ start_pct | int }}"
color_temp_kelvin: "{{ start_kelvin_final | int }}"
transition: 1
default:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ start_pct | int }}"
transition: 1
# 6) Running ramp
- repeat:
while:
- >-
{% set alarm_ts = as_timestamp(sensor_ts != 'none'
and states(sensor_ts)
or (states('sensor.date') ~ ' ' ~ manual_time)) %}
{{ 0 < (alarm_ts - as_timestamp(now())) <= (seconds | float) }}
- "{{ not (off_cancels and is_state(le, 'off')) }}"
sequence:
- delay:
seconds: "{{ tick | int }}"
- variables:
alarm_ts: >-
{{ as_timestamp(sensor_ts != 'none'
and states(sensor_ts)
or (states('sensor.date') ~ ' ' ~ manual_time)) }}
remain: "{{ (alarm_ts - as_timestamp(now())) | float }}"
frac: "{{ (remain / (seconds | float)) | float }}" # 1 → 0 über die Laufzeit
bri_now: >-
{{ ((end_pct | float) - ((range_pct | float) * frac)) | round(0) | int }}
# RGB linear: start + (1 - frac) * (end - start)
r_now: "{{ (sr + (er - sr) * (1 - frac)) | round(0) | int }}"
g_now: "{{ (sg + (eg - sg) * (1 - frac)) | round(0) | int }}"
b_now: "{{ (sb + (eb - sb) * (1 - frac)) | round(0) | int }}"
# Kelvin linear: start → end
kelv_now: "{{ ((end_kelvin_final | float) - ((end_kelvin_final | float - start_kelvin_final | float) * frac)) | round(0) | int }}"
- choose:
- conditions: "{{ enable_rgb and can_rgb }}"
sequence:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ [bri_now, 1] | max }}"
rgb_color: ["{{ r_now }}","{{ g_now }}","{{ b_now }}"]
transition: "{{ [ (tick | int) - 1, 0 ] | max }}"
- conditions: "{{ can_ct }}"
sequence:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ [bri_now, 1] | max }}"
color_temp_kelvin: "{{ kelv_now }}"
transition: "{{ [ (tick | int) - 1, 0 ] | max }}"
default:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ [bri_now, 1] | max }}"
transition: "{{ [ (tick | int) - 1, 0 ] | max }}"
# 7) Set final state
- choose:
- conditions: "{{ enable_rgb and can_rgb }}"
sequence:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ end_pct | int }}"
rgb_color: ["{{ er }}","{{ eg }}","{{ eb }}"]
transition: 1
- conditions: "{{ can_ct }}"
sequence:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ end_pct | int }}"
color_temp_kelvin: "{{ end_kelvin_final | int }}"
transition: 1
default:
- service: light.turn_on
entity_id: !input light_entity
data:
brightness_pct: "{{ end_pct | int }}"
transition: 1
# 8) Post-actions
- choose: []
default: !input post_actions
mode: single
max_exceeded: silent
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment