Modern Sunrise Wake-Up Blueprint (Kelvin/RGB): Start/End Sliders, Quiet Hours, Workday — Evolution of Sbyx

This blueprint is a modern evolution of Sbyx’s “Wake-up light alarm with sunrise effect” (Dec 2020).

Original gist: https://gist.github.com/sbyx/96c43b13b90ae1c35b872313ba1d2d2d

Huge thanks to Sbyx for the initial concept and implementation. The core idea—ramping brightness and color temperature towards the alarm time—remains, while this version modernizes the internals and expands usability.

Use this new Version now

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Why a new version?

  • Future-proof color control
    Home Assistant is deprecating the old color_temp (mired) service parameter.
    This blueprint uses color_temp_kelvin end-to-end (with on-the-fly conversion when devices only expose mired ranges) to avoid warnings and ensure forward compatibility.

  • Direct, user-friendly controls
    Two dedicated Kelvin sliders (Start and End) model a natural sunrise: warm (~2200 K)daylight (~6500 K). Values are auto-clamped to each light’s supported range.

  • Optional RGB sunrise
    Prefer color over Kelvin? Enable the RGB gradient and set Start/End colors via color pickers. If the light supports RGB, the blueprint uses it; otherwise it falls back to Kelvin or brightness-only.

  • Clear UI descriptions
    Every input has a concise description so users immediately know what each option does (time source, duration, conditions, quiet hours, etc.).

  • Quality-of-life features

    • Quiet hours window to suppress early triggers
    • Check entity and Workday-only conditions
    • “Turning off cancels” to stop the ramp instantly
    • Step count control for smoother or lighter updates
    • Works seamlessly with Light Groups for multi-lamp wake-ups
  • Template & timing robustness
    Safer templating, explicit race-condition checks right before start, and consistent step timing.

How this differs from the original

  • Uses color_temp_kelvin instead of color_temp (mired).
  • Adds Start/End Kelvin sliders and optional RGB start/end color pickers.
  • Adds quiet hours, workday only, check entity, abort on off, and pre/post actions with descriptive UI.
  • Exposes a step count for smoothness control.

Migration notes (from Sbyx’s blueprint)

  • This is not a drop-in replacement; input names and options differ to provide clearer controls.
  • Import this as a new blueprint and create a new automation from it. You can keep Sbyx’s version side-by-side.
  • If you previously saw deprecation warnings about color_temp, those are resolved here thanks to color_temp_kelvin.

Wake-up Sunrise (Kelvin, smooth & smart)

blueprint:
  name: Wake-up Sunrise (Kelvin, smooth & smart)
  description: >
    Moderner Wecker mit Sonnenaufgangseffekt. Standard: warmes Weiß (≈2200 K) → tageslicht-weiß (≈6500 K),
    automatisch an die Gerätegrenzen angepasst. Optional statt Kelvin eine RGB-Farbkurve von Start- zu Endfarbe.
    Zukunftssicher dank color_temp_kelvin. Für mehrere Lampen bitte eine Light-Group wählen.
  domain: automation

  input:
    light_entity:
      name: Ziel-Licht
      description: >
        Licht oder Light-Group, die aufgeweckt werden soll.
      selector:
        entity:
          domain: light

    # --- Alarmquelle ---
    timestamp_sensor:
      name: Zeitstempel-Entität (optional)
      description: >
        Sensor mit device_class=timestamp (z. B. Handy-Wecker). Falls 'none', wird die manuelle Zeit verwendet.
      default: none
      selector:
        entity:
          device_class: timestamp

    manual_time:
      name: Manuelle Weckzeit
      description: >
        Täglicher Weckzeitpunkt, wenn kein Timestamp-Sensor gesetzt ist (Format HH:MM:SS).
      default: "07:00:00"
      selector:
        time: {}

    # --- Ablauf & Helligkeit ---
    sunrise_duration_min:
      name: Dauer des Sonnenaufgangs (Minuten)
      description: >
        Gesamtdauer der Rampe für Helligkeit und Farbe.
      default: 25
      selector:
        number:
          min: 5
          max: 120
          step: 5
          unit_of_measurement: min
          mode: slider

    start_brightness_pct:
      name: Starthelligkeit (%)
      description: >
        Anfangshelligkeit zu Beginn der Rampe. 1–10 % empfohlen (manche Lampen ignorieren 1 %).
      default: 3
      selector:
        number:
          min: 1
          max: 100
          step: 1
          mode: slider

    end_brightness_pct:
      name: Endhelligkeit (%)
      description: >
        Zielhelligkeit am Ende der Rampe.
      default: 100
      selector:
        number:
          min: 5
          max: 100
          step: 1
          mode: slider

    # --- Kelvin-Rampe (Standard) ---
    start_kelvin:
      name: Start-Kelvin (warm)
      description: >
        Farbtemperatur zu Beginn der Rampe (z. B. 2200 K = warm). Wird automatisch an die Gerätegrenzen angepasst.
      default: 2200
      selector:
        number:
          min: 1500
          max: 9000
          step: 50
          unit_of_measurement: K
          mode: slider

    end_kelvin:
      name: End-Kelvin (kühler / tageslicht)
      description: >
        Farbtemperatur am Ende der Rampe (z. B. 6500 K = tageslichtweiß). Wird automatisch an die Gerätegrenzen angepasst.
      default: 6500
      selector:
        number:
          min: 1500
          max: 9000
          step: 50
          unit_of_measurement: K
          mode: slider

    # --- Alternative: RGB-Farbkurve ---
    enable_color_ramp:
      name: Farbverlauf (RGB) statt Kelvin
      description: >
        Aktivieren, um statt Kelvin einen RGB-Farbverlauf von Start- zu Endfarbe zu fahren.
      default: false
      selector:
        boolean: {}

    start_color_rgb:
      name: Startfarbe (RGB)
      description: >
        Startfarbe für den RGB-Verlauf (nur relevant, wenn Farbverlauf aktiviert).
      default:
        r: 255
        g: 120
        b: 20
      selector:
        color_rgb: {}

    end_color_rgb:
      name: Endfarbe (RGB)
      description: >
        Endfarbe für den RGB-Verlauf (nur relevant, wenn Farbverlauf aktiviert).
      default:
        r: 255
        g: 255
        b: 255
      selector:
        color_rgb: {}

    ramp_steps:
      name: Schrittanzahl (Glätte)
      description: >
        Anzahl der Zwischenschritte für Helligkeit/Farbe (mehr = glattere Rampe, dafür mehr Service-Aufrufe).
      default: 60
      selector:
        number:
          min: 10
          max: 240
          step: 10

    # --- Bedingungen (optional) ---
    check_entity:
      name: Check-Entität
      description: >
        Muss 'on' oder 'home' sein, damit der Wecker startet (z. B. Workday-, Device-Tracker- oder Person-Entity).
        Leerlassen (= none), wenn keine Bedingung gewünscht ist.
      default: none
      selector:
        entity: {}

    require_workday:
      name: Nur an Arbeitstagen
      description: >
        Ausführen nur, wenn der Workday-Sensor 'on' ist.
      default: false
      selector:
        boolean: {}

    workday_sensor:
      name: Workday-Sensor
      description: >
        Zu prüfender Workday-Sensor, wenn „Nur an Arbeitstagen“ aktiv ist.
      default: binary_sensor.workday
      selector:
        entity:
          domain: binary_sensor

    respect_quiet_hours:
      name: Ruhezeiten beachten
      description: >
        Verhindert den Start, wenn die aktuelle Zeit innerhalb des Ruhezeitfensters liegt.
      default: false
      selector:
        boolean: {}

    quiet_start:
      name: Ruhezeit Beginn
      description: >
        Beginn des Ruhezeitfensters (Format HH:MM:SS).
      default: "22:00:00"
      selector:
        time: {}

    quiet_end:
      name: Ruhezeit Ende
      description: >
        Ende des Ruhezeitfensters (Format HH:MM:SS).
      default: "06:00:00"
      selector:
        time: {}

    off_cancels:
      name: Ausschalten bricht ab
      description: >
        Wenn das Licht während der Rampe ausgeschaltet wird, wird der Vorgang abgebrochen.
      default: true
      selector:
        boolean: {}

    # --- Vor-/Nach-Aktionen ---
    pre_actions:
      name: Vor-Aktionen
      description: >
        Aktionen, die unmittelbar vor Start der Rampe ausgeführt werden (z. B. Heizung anheben).
      default: []
      selector:
        action: {}

    post_actions:
      name: Nach-Aktionen
      description: >
        Aktionen am Ende der Rampe (z. B. Musik starten).
      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

  # Gerätegrenzen für Kelvin (bevorzugt nativ, sonst aus Mired ableiten)
  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 %}

  # Benutzerwerte (Kelvin) → an Gerätegrenzen clampen und Reihenfolge sichern (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-Zeit (Sekunden), mindestens 1 s
  tick: >-
    {% set t = (seconds | float) / (steps | float) %}
    {{ [ t, 1 ] | max | int }}

  # RGB-Start/Ende (+ Komponenten)
  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) }}"

  # Fähigkeiten des Geräts
  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 }}

  # Bedingungen
  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) Auf gültige Alarmbasis warten (Timestamp vorhanden ODER manuelle Zeit)
  - wait_template: >-
      {{ sensor_ts == 'none' or as_timestamp(states(sensor_ts), None) != None }}

  # 2) Startfenster (0 < Alarm - jetzt <= Dauer) + Nebenbedingungen erfüllt
  - 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) Vor-Aktionen
  - choose: []
    default: !input pre_actions

  # 4) Bedingungen kurz vor Start erneut prüfen
  - condition: template
    value_template: "{{ cond_check_entity_ok and cond_workday_ok and cond_quiet_ok }}"

  # 5) Initiales Einschalten
  - choose:
      # 5a) RGB – falls aktiviert und unterstützt
      - 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 – Standard (falls unterstützt)
      - 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) Laufende Rampe
  - 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) Endzustand sicher setzen
  - 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) Nach-Aktionen
  - choose: []
    default: !input post_actions

mode: single
max_exceeded: silent
1 Like

Almost perfect with the huge updates and upgrades but…
Ich verstehe die deutsche Sprache nicht
Translation in English would have been very nice. Cheers :smile:

My Bad!

Import blueprint to My Home Assistant

https://gist.githubusercontent.com/BoBBer446/e94b479d06713f25ca5d6003dd48c132/raw