(Phone) Alarm Time Sunrise Simulator

This automation gradually brightens your lights before your phone alarm goes off, simulating a natural sunrise to ease you awake. It uses your phone’s alarm sensor as the trigger, so the lights automatically adapt to whenever you set your alarm. No need to manually adjust the automation if your wake time changes daily. The brightness ramps up smoothly using an ease-in curve, reaching full brightness one minute before the alarm sounds.

What you can configure:

  • Which lights to control
  • How many minutes before the alarm the ramp should start (5–120 minutes)
  • Maximum brightness level (1–100%)
  • Color temperature of the lights (warm to cool, 2000–6500 K)
  • Time-of-day window (only activate for alarms within your morning hours)
  • Optional binary sensor gate (e.g., only run when you’re home or a sleep tracker confirms you’re in bed)

If you want to use your phones alarm, you need to activate the next alarm sensor in your companion app. For that go to: Settings (in the App) → Companion-App → Manage Sensors → Aktivate the “next alarm” sensor

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

blueprint:
  name: "Wake Up Light Routine (Phone Alarm)"
  description: >
    Gradually increases the brightness of selected lights before your phone alarm goes off,
    simulating a sunrise. Brightness ramps up slowly at first then accelerates — matching
    a natural sunrise curve. Full brightness is reached 1 minute before the alarm.
    Only activates within a configurable time-of-day window.
    Optionally gated by a binary sensor (e.g. sleep tracker, presence, or manual toggle).
  domain: automation
  input:

    alarm_sensor:
      name: Phone Alarm Sensor
      description: >
        Sensor from your phone companion app (Android/iOS) providing the next alarm time.
        Expected format: 2026-03-13T06:30:00+00:00
      selector:
        entity:
          domain: sensor

    lead_time:
      name: Wake-Up Lead Time (minutes)
      description: How many minutes before the alarm the lights should start ramping up.
      default: 30
      selector:
        number:
          min: 5
          max: 120
          step: 5
          unit_of_measurement: min
          mode: slider

    lights:
      name: Lights
      description: One or more lights to use for the wake-up routine.
      selector:
        target:
          entity:
            domain: light

    max_brightness:
      name: Maximum Brightness (%)
      description: The brightness the lights should reach 1 minute before the alarm sounds.
      default: 100
      selector:
        number:
          min: 1
          max: 100
          step: 1
          unit_of_measurement: "%"
          mode: slider

    use_color_temp:
      name: Set Color Temperature?
      description: >
        Enable this to also control the color temperature of the lights.
        Disable for bulbs that do not support color temperature.
      default: true
      selector:
        boolean:

    color_temperature:
      name: Color Temperature (Kelvin)
      description: >
        Only used when "Set Color Temperature" is enabled above.
        Lower = warmer/amber (2000 K), higher = cool daylight (6500 K).
        3000 K is a comfortable warm white.
      default: 3000
      selector:
        number:
          min: 2000
          max: 6500
          step: 100
          unit_of_measurement: K
          mode: slider

    window_start:
      name: Active Window — Earliest Alarm Time
      description: >
        Routine only activates when the alarm falls at or after this time of day.
        Prevents triggering for late-night alarms.
      default: "05:00:00"
      selector:
        time:

    window_end:
      name: Active Window — Latest Alarm Time
      description: >
        Routine only activates when the alarm falls at or before this time of day.
      default: "09:00:00"
      selector:
        time:

    enable_sensor:
      name: Enable Binary Sensor (optional)
      description: >
        Optional. When set, the routine only runs while this binary sensor is ON.
        Leave empty to always allow the routine.
      default:
      selector:
        entity:
          domain: binary_sensor

# ─────────────────────────────────────────────────────────────────────────────
# How it works:
#   • The trigger fires every minute via time_pattern.
#   • Conditions check that we are currently inside a valid ramp window.
#   • The ramp runs from (alarm - lead_time) to (alarm - 1 min), so full
#     brightness is reached 1 minute before the alarm sounds.
#   • The action recalculates the eased brightness for this exact moment
#     and calls light.turn_on with a 59-second transition so adjacent
#     ticks blend smoothly into each other.
#   • mode: restart ensures a new tick always replaces the previous one.
# ─────────────────────────────────────────────────────────────────────────────

trigger:
  - platform: time_pattern
    minutes: "/1"

# Map all blueprint inputs to plain variables usable inside Jinja2 templates
variables:
  var_alarm_sensor: !input alarm_sensor
  var_lead_time: !input lead_time
  var_max_brightness: !input max_brightness
  var_use_color_temp: !input use_color_temp
  var_color_temperature: !input color_temperature
  var_window_start: !input window_start
  var_window_end: !input window_end
  var_enable_sensor: !input enable_sensor

condition:
  # 1. Alarm sensor must return a usable (non-error) value
  - condition: template
    value_template: >
      {{ states(var_alarm_sensor) not in ['unknown', 'unavailable', 'none', 'None', ''] }}

  # 2. Alarm time must fall within the configured time-of-day window
  - condition: template
    value_template: >
      {% set alarm_dt = states(var_alarm_sensor) | as_datetime | as_local %}
      {% set hms = alarm_dt.strftime('%H:%M:%S') %}
      {{ hms >= var_window_start and hms <= var_window_end }}

  # 3. Right now must be at or after the ramp start (alarm minus lead time)
  - condition: template
    value_template: >
      {% set alarm_ts = states(var_alarm_sensor) | as_datetime | as_local | as_timestamp %}
      {% set start_ts = alarm_ts - (var_lead_time | int) * 60 %}
      {{ now() | as_timestamp >= start_ts }}

  # 4. Right now must still be before the alarm fires
  - condition: template
    value_template: >
      {% set alarm_ts = states(var_alarm_sensor) | as_datetime | as_local | as_timestamp %}
      {{ now() | as_timestamp < alarm_ts }}

  # 5. Optional binary sensor gate — passes automatically when not configured
  - condition: template
    value_template: >
      {% if var_enable_sensor == none or var_enable_sensor == '' %}
        true
      {% else %}
        {{ is_state(var_enable_sensor, 'on') }}
      {% endif %}

action:
  - variables:
      _alarm_ts: >
        {{ states(var_alarm_sensor) | as_datetime | as_local | as_timestamp }}
      _lead_secs: "{{ (var_lead_time | int) * 60 }}"
      _start_ts: "{{ _alarm_ts - _lead_secs }}"
      _now_ts: "{{ now() | as_timestamp }}"

      # The ramp ends 60 seconds BEFORE the alarm, so full brightness is
      # reached 1 minute early. We divide by (lead_secs - 60) instead of lead_secs.
      _ramp_secs: "{{ _lead_secs - 60 }}"

      # Linear progress: 0.0 at ramp start → 1.0 one minute before alarm
      _linear: >
        {% set p = (_now_ts - _start_ts) / _ramp_secs %}
        {% if p < 0 %}{{ 0.0 }}
        {% elif p > 1 %}{{ 1.0 }}
        {% else %}{{ p }}
        {% endif %}

      # Quadratic ease-in: slow start, accelerates toward full brightness
      _eased: "{{ _linear * _linear }}"

      # Final brightness clamped to [1, max_brightness]
      _brightness: >
        {% set b = (_eased * (var_max_brightness | int)) | round(0) | int %}
        {% if b < 1 %}{{ 1 }}
        {% elif b > (var_max_brightness | int) %}{{ var_max_brightness | int }}
        {% else %}{{ b }}
        {% endif %}

  # Branch: with color temperature
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ var_use_color_temp == true }}"
        sequence:
          - action: light.turn_on
            target: !input lights
            data:
              brightness_pct: "{{ _brightness }}"
              color_temp_kelvin: "{{ var_color_temperature | int }}"
              transition: 59
    # Default: brightness only (for bulbs without color temperature support)
    default:
      - action: light.turn_on
        target: !input lights
        data:
          brightness_pct: "{{ _brightness }}"
          transition: 59

mode: restart