Skip to content

Instantly share code, notes, and snippets.

@BoBBer446
Created October 27, 2025 17:28
Show Gist options
  • Select an option

  • Save BoBBer446/e94b479d06713f25ca5d6003dd48c132 to your computer and use it in GitHub Desktop.

Select an option

Save BoBBer446/e94b479d06713f25ca5d6003dd48c132 to your computer and use it in GitHub Desktop.
blueprint:
name: Wake-up Sunrise (Kelvin, smooth & smart)
description: >
Modern wake-up light with sunrise effect. Default: warm white (≈2200 K) → daylight white (≈6500 K),
automatically adjusted to device limits. Optionally, use an RGB color ramp 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: >
The light or light group that should be used for the wake-up sequence.
selector:
entity:
domain: light
# --- Alarm Source ---
timestamp_sensor:
name: Timestamp Entity (optional)
description: >
Sensor with device_class=timestamp (e.g., smartphone alarm). If 'none', the manual time will be used.
default: none
selector:
entity:
device_class: timestamp
manual_time:
name: Manual Wake Time
description: >
Daily wake-up 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 brightness and color ramp.
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 lamps 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 clamped to device limits.
default: 2200
selector:
number:
min: 1500
max: 9000
step: 50
unit_of_measurement: K
mode: slider
end_kelvin:
name: End Kelvin (cool/daylight)
description: >
Color temperature at the end of the ramp (e.g., 6500 K = daylight white). Automatically clamped to device limits.
default: 6500
selector:
number:
min: 1500
max: 9000
step: 50
unit_of_measurement: K
mode: slider
# --- Alternative: RGB Color Ramp ---
enable_color_ramp:
name: Use Color Ramp (RGB) Instead of Kelvin
description: >
Enable this to use an RGB color ramp from start to end color instead of Kelvin temperature.
default: false
selector:
boolean: {}
start_color_rgb:
name: Start Color (RGB)
description: >
Starting color for the RGB ramp (only relevant if color ramp is enabled).
default:
r: 255
g: 120
b: 20
selector:
color_rgb: {}
end_color_rgb:
name: End Color (RGB)
description: >
Ending color for the RGB ramp (only relevant if color ramp is enabled).
default:
r: 255
g: 255
b: 255
selector:
color_rgb: {}
ramp_steps:
name: Ramp Steps (Smoothness)
description: >
Number of intermediate steps for brightness and 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 required.
default: none
selector:
entity: {}
require_workday:
name: Only on Workdays
description: >
Execute only when the specified workday sensor is 'on'.
default: false
selector:
boolean: {}
workday_sensor:
name: Workday Sensor
description: >
Workday sensor to be checked when "Only on Workdays" is enabled.
default: binary_sensor.workday
selector:
entity:
domain: binary_sensor
respect_quiet_hours:
name: Respect Quiet Hours
description: >
Prevents the wake-up sequence 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: Turning Off Cancels Ramp
description: >
If the light is turned off during the ramp, the wake-up process will be cancelled.
default: true
selector:
boolean: {}
# --- Pre/Post Actions ---
pre_actions:
name: Pre-Actions
description: >
Actions to execute immediately before starting the ramp (e.g., increasing room temperature).
default: []
selector:
action: {}
post_actions:
name: Post-Actions
description: >
Actions to execute after the ramp completes (e.g., start music).
default: []
selector:
action: {}
# --- Variables, Triggers, Actions ---
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 color temperature limits (prefer native Kelvin, else derive from mireds)
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 %}
# Clamp user Kelvin values to device range and ensure 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), at least 1 s
tick: >-
{% set t = (seconds | float) / (steps | float) %}
{{ [ t, 1 ] | max | int }}
# RGB start/end
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 base (timestamp present OR manual time)
- wait_template: >-
{{ sensor_ts == 'none' or as_timestamp(states(sensor_ts), None) != None }}
# 2) Wait until within wake-up window (0 < alarm - now <= duration) + all 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) Re-check conditions right before start
- condition: template
value_template: "{{ cond_check_entity_ok and cond_workday_ok and cond_quiet_ok }}"
# 5) Initial light turn-on
- choose:
# 5a) RGB mode – 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 mode – 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) Main ramp loop
- 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 }}"
bri_now: >-
{{ ((end_pct | float) - ((range_pct | float) * frac)) | round(0) | int }}
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 }}"
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) Ensure 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