Home Assistant

Daily device scheduler

Clock-time on and off for a switch, light, or fan, with per-day toggles and an optional restore-safe timer that tracks the remaining time until the scheduled off — including after a manual override or a Home Assistant restart.

Difficulty · BeginnerSetup · 20–40 minutesHA · current stable (blueprint)
Get the pack · from $19Have us set it up

Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.

A bathroom fan that should run 07:00–08:30 on weekdays is a clock problem, not an “on means 30 minutes” problem. This workflow turns a switch, light, or fan on at a time you pick, off at a later time, and only on the days you tick. An optional timer helper tracks the remaining minutes until the scheduled off so a dashboard ring stays honest after a wall-switch override or a Home Assistant restart.

Entity ids are blueprint inputs. Do not publish a live plug name.

This is not relay auto-off timer. That pack starts a duration when you turn the device on. This pack owns the daily window; the timer is a display and a recovery aid, not the schedule itself.

What problem it solves

You want “this load is on between two clock times, on these days,” with a visible countdown, without the timer restarting from a full duration when someone flips the switch at 08:10, and without forgetting the off after a reboot.

How it works

  1. Clock triggers — two time triggers (on_time, off_time). Each run checks today’s weekday against seven boolean inputs (Mon–Sun, default all on). Uncheck a day and that day’s on/off do nothing. Uncheck every day and the schedule is paused (same as disabling the automation).
  2. Scheduled on — if today is enabled, homeassistant.turn_on the device. If a timer helper is configured, timer.start with duration = seconds from now until off_time (not a fixed 30 minutes).
  3. Scheduled off — homeassistant.turn_off. timer.cancel if the helper is still active.
  4. Manual / mid-window on — a state trigger on the device going on outside the clock trigger. If we are still inside today’s window (after on, before off, day enabled), start the timer with the remaining time until off_time. A 07:00–08:30 fan flipped on at 08:10 gets twenty minutes, not ninety.
  5. Manual off — device goes off → timer.cancel. The clock off later is a no-op if the device is already off.
  6. Restart — a homeassistant start trigger. If the device is on, today is enabled, and now is still inside the window, sync the timer to the remaining seconds. The timer helper must have Restore enabled so a mid-countdown reboot still has a remaining time; the start trigger covers the case where the device stayed on but the timer did not.
  7. Timer idle — if the optional timer expires (active → idle, not a cancel), turn the device off. That is the safety net for a missed clock-off after a restart.

Overnight windows (on 22:00, off 06:00) wrap midnight the same way quiet-hours packs do: “after on or before off” instead of a naive after/before pair.

Do not also put an ESPHome off_delay or a second daily time automation on the same entity. Two owners fight.

Hardware notes

  • Any HA switch, light, or fan. A smart plug, a GPIO relay already adopted, a ceiling fan entity. The pack does not care about the transport.
  • Timer helper. Optional. Create it with Restore on (Settings → Devices & services → Helpers, or restore: true in YAML). Skip it if you only want the clock and do not need a dashboard ring.
  • No extra hub. If the entity already toggles in HA, this is a blueprint and (optionally) one helper.

Snippet

The pack ships a blueprint. Inputs replace real entity ids:

trigger:
  - platform: time
    at: !input on_time
    id: clock_on
  - platform: time
    at: !input off_time
    id: clock_off
  - platform: state
    entity_id: !input target_device
    to: "on"
    id: device_on
  - platform: state
    entity_id: !input target_device
    to: "off"
    id: device_off
  - platform: homeassistant
    event: start
    id: ha_start
  - platform: state
    entity_id: !input off_timer
    from: active
    to: idle
    id: timer_expired

action:
  - variables:
      day_ok: >
        {{ {0: monday, 1: tuesday, 2: wednesday, 3: thursday,
            4: friday, 5: saturday, 6: sunday}[now().weekday()] }}
      remain_s: >
        {{ [((off_clock - now().strftime('%H:%M:%S')) | as_timedelta).total_seconds(),
            0] | max | int }}
  - choose:
      - conditions:
          - condition: trigger
            id: clock_on
          - condition: template
            value_template: "{{ day_ok }}"
        sequence:
          - service: homeassistant.turn_on
            target:
              entity_id: !input target_device
          - service: timer.start
            target:
              entity_id: !input off_timer
            data:
              duration:
                seconds: "{{ remain_s }}"
      - conditions:
          - condition: trigger
            id: clock_off
        sequence:
          - service: homeassistant.turn_off
            target:
              entity_id: !input target_device
          - service: timer.cancel
            target:
              entity_id: !input off_timer
      # mid-window manual on, restart sync, timer-expired off: full blueprint in the pack

Timer helper (restore is the reboot-safe part):

timer:
  daily_schedule:
    name: Daily schedule remaining
    duration: "01:30:00"
    restore: true

Setup outline

  1. Confirm the switch / light / fan toggles from HA. Optionally create a timer helper with Restore on.
  2. Import the blueprint. Fill the device, on time, off time, weekday ticks, and the optional timer.
  3. Wait for the next on-time (or set times a few minutes ahead). Confirm: device on, timer showing time-until-off — not a canned duration.
  4. Test the edges: flip on mid-window → remaining, not full length; flip off early → timer cancelled; restart HA while in-window and on → timer resynced and still turns off at the clock; uncheck today → no clock on.

Troubleshooting

  • Never turns on. Today is unchecked, the automation is disabled, or you pointed at a binary_sensor instead of a switch. Weekday 0 is Monday in the pack.
  • Timer always starts at the full window. The mid-window path used !input default_duration instead of remaining-until-off. Use the pack’s remaining calculation.
  • Forgot to turn off after a reboot. Restore is off on the timer and the start trigger is missing. Enable both; a delay: in an automation will not come back.
  • Manual off leaves the timer running. The device-off trigger is missing, or you used timer.finish (that looks like an expiry and will bounce an off). Use timer.cancel.
  • Overnight window never matches. after / before without wrap. Use the pack’s wrapping condition.
  • Two offs, or a chatter. Another daily time automation or an ESPHome off_delay also owns this entity. Pick one owner.

Credits

Based on the community blueprint Daily Time-Based Device Scheduler with Optional Timer. This write-up is an original overview of the weekday-filtered clock window with remaining-time timer sync.

Changelog

v1.0 · Oct 18, 2026: First publish.