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, a soldering iron, or a heater on a plug should not still be on three hours later because someone walked out. This workflow binds a switch (ESP-01S relay, a smart plug, a fan entity) to a Home Assistant timer helper: turn the device on and the timer starts for a duration you set; the pack also writes now + duration into an input_datetime so a dashboard can show the planned off clock. Turn the device off by hand and the timer is cancelled. When the timer finishes, the device turns off. The timer is created with restore, so a Home Assistant restart mid-countdown still has a remaining time and still turns the relay off.
Entity ids are blueprint inputs. Do not publish a live plug name.
What problem it solves
You want “on means it will go off” — a visible end time on a dashboard, no leftover timer after a manual off, and no forgotten load after HA reboots.
How it works
- Device on —
switch/light/fangoeson. If the timer isidle,timer.startwith the default duration (or a remaining duration you compute to a fixed clock time). Then setinput_datetime.planned_offtonow + remainingso a glance card can show a real timestamp, not just a ring. - Device off (manual) — if the timer is still
active,timer.cancel. Clear or leave the datetime (pack default: clear). Nothing else runs; you already turned it off. - Timer finishes — timer goes from
activetoidlebecause it expired (not because you cancelled).homeassistant.turn_offon the device. That is the auto-off. - Restart — create the timer helper with Restore enabled (Helpers UI, or
restore: truein YAML). HA puts the timer back inactivewith remaining time. When it later hits idle, step 3 still runs. An automationdelaywould have been forgotten; a restored timer is not. - Duration vs clock — pick one mode per instance. Duration: “always 30 minutes from on.” Clock: “off at 22:30,” duration computed as seconds until that time, wrapping past midnight. Both write the same datetime helper so the dashboard does not care which mode you chose.
Do not also put an off_delay on the ESPHome switch if the HA timer owns the off. Two timers fight.
Hardware notes
- Any HA switch. A smart plug, a fan, a GPIO relay already adopted. The pack does not care about the transport.
- ESP-01S relay. Common 5 V module: GPIO0 or GPIO2 driving the relay, flash via USB-TTL once, then OTA. Keep mains in a proper enclosure; this is a dry-contact pattern, not a lesson in mains wiring.
- Loads. Match the relay or plug to the load. A heater or iron wants a rated plug, not a hobby relay on a breadboard.
- No extra hub. If the entity already toggles in HA, this is a timer, a datetime helper, and a blueprint.
Snippet
The pack ships a blueprint. Inputs replace real entity ids:
trigger:
- platform: state
entity_id: !input relay_switch
to: "on"
id: started
- platform: state
entity_id: !input relay_switch
to: "off"
id: stopped
- platform: state
entity_id: !input off_timer
from: active
to: idle
id: expired
action:
- choose:
- conditions:
- condition: trigger
id: started
sequence:
- service: timer.start
target:
entity_id: !input off_timer
data:
duration: !input default_duration
- service: input_datetime.set_datetime
target:
entity_id: !input planned_off
data:
datetime: >
{{ (now() + timedelta(seconds=duration_s)).strftime('%Y-%m-%d %H:%M:%S') }}
- conditions:
- condition: trigger
id: stopped
sequence:
- service: timer.cancel
target:
entity_id: !input off_timer
- conditions:
- condition: trigger
id: expired
- condition: state
entity_id: !input relay_switch
state: "on"
sequence:
- service: homeassistant.turn_off
target:
entity_id: !input relay_switch
Timer helper (restore is the restart-safe part):
timer:
relay_auto_off:
name: Relay auto-off
duration: "00:30:00"
restore: true
Optional ESPHome relay (credentials stay in secrets.yaml):
switch:
- platform: gpio
pin: GPIO0
name: "Utility relay"
id: utility_relay
restore_mode: RESTORE_DEFAULT_OFF
Setup outline
- Confirm the switch entity toggles from HA. Create a timer helper with Restore on, and an
input_datetime(date and time). - Import the blueprint. Fill switch, timer, datetime, and either a duration or a clock-off time.
- Turn the switch on. Confirm: timer active, datetime is
now + duration, dashboard card shows that clock. - Test the three paths: wait it out → switch off; toggle off early → timer cancelled; turn on, restart HA, wait the remainder → switch still goes off.
Troubleshooting
- Timer never starts. You turned the switch on from the same automation that starts the timer in a loop, or the entity is a
binary_sensor, not a switch. Trigger must be the device you touch. - Manual off leaves the timer running. The off trigger is missing, or you used
timer.finish(that looks like an expiry and will bounce the off action). Usetimer.cancelon manual off. - Off time blank after reboot. The datetime helper was written, but you looked at a timer-only card. Put the
input_datetimeon the dashboard; the restored timer is the authority for remaining seconds. - Forgot to turn off after a restart. Restore is off on the helper (Helpers → the timer → Restore).
delay:in an automation will not come back; the pack’s timer will. - Relay chatters. Two automations own the same switch (ESPHome
off_delayplus this pack). Pick one off path.
Credits
Based on the community blueprints Sync Device and Timer and Sync Device and Scheduled Off-Time. This write-up is an original overview of the restore-safe timer plus planned-off datetime pattern.