A Powerful Auto-Off Timer for Home Assistant
Time Off is a lightweight, local-push integration that solves the "I forgot to turn it off" problem once and for all. It adds a set of timer controls and sensors directly into the control panel of your selected devices, so you can manage countdowns without writing a single line of YAML.
In standard Home Assistant setups, creating a simple auto-off timer usually requires:
- Creating a Timer Helper
- Writing an Automation to start the timer when the device turns on
- Writing a Second Automation to turn the device off when the timer finishes
This leads to a cluttered Helpers list and disconnected logic - a timer named timer.fan_1 living in a separate menu from the fan itself, with no obvious link between them.
Time Off changes the workflow:
- Tethered Logic: The timer, controls, and sensors all live inside the device itself.
- No More Ghost Helpers: Everything is created and named automatically based on the device it's controlling.
- One-Step Setup: No YAML or multi-step automations required. Just add the integration, pick your device, and set a duration.
Time Off is device-aware. It automatically detects the type of device you are controlling and sends the correct command when the timer expires:
| Device Type | Action on Expiry |
|---|---|
| Lights / Fans / Switches / Media Players / Climate / Humidifiers / Sirens / Input Booleans | homeassistant.turn_off |
| Covers (Gates / Blinds) | cover.close_cover |
| Valves | valve.close_valve |
| Vacuums | vacuum.return_to_base |
Template entities and Helpers are also fully supported, including helper groups that appear as a light, switch, fan, or other supported domain.
- Download the
time_offzip archive from this repository and unpack it. - Copy the
time_offfolder into yourcustom_components/directory. - Restart Home Assistant.
- Go to Settings -> Devices & Services -> Add Integration and search for Time Off.
- Open HACS.
- Click the three dots (top right) -> Custom repositories.
- Paste
https://github.com/sfox38/time_offand select Integration as the category. - Click Download, then restart Home Assistant.
Once installed, Time Off appears in your Integrations panel.
- Click
Add Service(or the + icon) to add a new device. - Select your device from the dropdown and click Submit.
- You will be taken to the device's control page where the Time Off entities will appear.
Templates, Groups, and Helpers: Since these device types usually lack a dedicated Device page, Time Off automatically creates one containing only the Time Off entities.
Note
By default, adding Time Off to a device will not affect its behaviour. You must set Time Off to a value greater than 0 to activate the timer.
Time Off adds the following entities to your device:
number.[device]_time_off
The default timer duration. This is the value that Off After is set to each time the device turns on.
- Unit: Minutes (supports 0.1 increments for 6-second precision).
- Set to
0to disable the timer entirely. - The configured value survives restarts.
Running countdowns also survive restarts - see Advanced Notes for how recovery works.
switch.[device]_trigger_only
Controls how Time Off behaves when the timer expires.
| State | Behaviour |
|---|---|
| Off (default) | Time Off turns the device off automatically. |
| On | Time Off fires the event only - the device is left alone. |
In both cases the time_off_timer_expired event is fired. In Trigger Only mode the timer loops continuously, firing the event on each cycle, until the device is turned off or time_off.stop_timer is called.
binary_sensor.[device]_timer_active
Indicates whether a countdown is currently in progress.
- Visible in the Sensor section of the device card.
- State:
onwhile counting down,offwhen idle. - Attributes:
expiry- the exact timestamp when the timer will finish.remaining- a human-readable countdown string (e.g.2m 30s).
sensor.[device]_off_after
A sensor showing the active countdown duration currently in use. This value is taken from the Time Off entity each time your device is turned on.
- Visible in the Diagnostic section of the device card.
- Can be changed via the
time_off.set_off_afterservice (see Services) without affecting theTime Offdefault.- Your device must already be
onin order for any change to take effect. - The timer will immediately restart with the new duration when this value is changed.
- Setting this value to
0will immediately stop the timer. Thetime_off_timer_expiredevent is not fired - this is a clean cancellation, not an expiry.
- Your device must already be
- When your device is turned
offthis value will reset to0.
Manually starts or restarts the timer for a managed device. It uses the value from Time Off for the timer duration.
Important
The entity_id must be the managed device (e.g. fan.bathroom_fan, light.porch_light) - not the Time Off entity.
action: time_off.start_timer
data:
entity_id: fan.bathroom_fanStops the active timer immediately. Does not turn the device off and does not fire the time_off_timer_expired event.
Important
The entity_id must be the managed device (e.g. fan.bathroom_fan, light.porch_light) - not the Time Off entity.
action: time_off.stop_timer
data:
entity_id: fan.bathroom_fanImmediately restarts the timer with a specific duration without changing the Time Off default.
Use this to start a countdown with a duration that differs from the Time Off default. Automations cannot change the value of Off After directly - set_off_after is the sole mechanism. start_timer, by contrast, always starts the countdown from the Time Off default, so it cannot be used to inject a custom duration.
This distinction is what makes the Trigger Only looping pattern so powerful: Time Off holds the initial, default, duration (e.g. 20 minutes), while set_off_after injects a shorter recheck duration (e.g. 5 minutes) for subsequent cycles - without permanently altering the 20-minute default. Thus, when the device is turned on again, the timer duration is taken from Time Off - 20 minutes.
If minutes is 0, the timer stops immediately rather than restarting.
Important
The entity_id must be the managed device (e.g. fan.bathroom_fan, light.porch_light) - not the Off After sensor or other Time Off entity.
action: time_off.set_off_after
data:
entity_id: fan.bathroom_fan
minutes: 30Fired when the timer reaches zero. In Trigger Only mode this fires on every loop cycle.
Payload:
| Field | Description |
|---|---|
entity_id |
The managed device entity ID (e.g. fan.bathroom_fan) |
device_id |
The Home Assistant device registry ID of the managed device (used internally by the UI device trigger to filter per device) |
Note
You only need entity_id to filter or act on an event. It is unlikely you will need to use device_id directly. In general, automations written against entity IDs are more readable, portable, and easier to debug than those written against device IDs.
trigger: event
event_type: time_off_timer_expired
event_data:
entity_id: fan.bathroom_fanSet Time Off to 4 minutes. The light turns off automatically after 4 minutes every time it's switched on. No coding required.
If you want to also trigger a notification when the light turns off, create a new automation and select the Time Off device trigger from the UI (Device -> [Your Device] -> Time Off: When timer expires), or use an event trigger in YAML:
alias: Porch Light - Turned Off Notification
description: "Notify when the porch light timer expires"
mode: single
triggers:
- trigger: event
event_type: time_off_timer_expired
event_data:
entity_id: light.porch_light
actions:
- action: notify.mobile_app
data:
message: "Porch light has been turned off automatically."Set Time Off to 2 minutes. As soon as the gate opens, the timer starts. After 2 minutes it closes automatically. No coding required.
If you also want a warning notification before the gate closes:
alias: Front Gate - Pre-Close Warning
description: "Notify 30 seconds before the gate closes"
mode: single
triggers:
- trigger: state
entity_id: binary_sensor.gate_timer_active
to: "on"
actions:
- delay: "00:01:30"
- action: notify.mobile_app
data:
message: "Front gate closing in 30 seconds."Set Time Off to 20 minutes and enable Time Off Trigger Only. Use the time_off_timer_expired event to trigger a humidity check - if humidity is still high, let the fan keep running; otherwise turn it off.
The porch light turns on for 20 minutes (Time Off = 20, Trigger Only = On). When the first timer expires, switch to 5-minute rechecks scanning two mmWave sensors. The light stays on as long as presence is detected, then turns off cleanly once the area is clear.
alias: Porch Light - Presence-Aware Shutoff
description: >
After the initial 20-minute period, recheck every 5 minutes for presence.
Turn off the light only when both mmWave sensors report clear.
mode: single
triggers:
- trigger: event
event_type: time_off_timer_expired
event_data:
entity_id: light.porch_light
actions:
- if:
- condition: or
conditions:
- condition: state
entity_id: binary_sensor.porch_mmwave_1
state: "on"
- condition: state
entity_id: binary_sensor.porch_mmwave_2
state: "on"
then:
# Presence detected - set Off After to 5 minutes for the next cycle.
# Trigger Only mode restarts the timer automatically, so no explicit timer restart is needed.
- action: time_off.set_off_after
data:
entity_id: light.porch_light
minutes: 5
else:
# No presence - turn off the light. The timer stops itself automatically.
- action: light.turn_off
target:
entity_id: light.porch_lightNote
Set Time Off to 20 and Time Off Trigger Only to On on the device card. On the first expiry the automation sets Off After to 5 minutes - Trigger Only mode immediately restarts the timer and picks up the new value for the next cycle. This continues every 5 minutes until no presence is detected and the light is turned off.
-
Trigger Only loop: In
Trigger Onlymode the timer fires thetime_off_timer_expiredevent and immediately restarts. This continues until the device is manually turned off ortime_off.stop_timeris called. Each cycle reads the currentOff Aftervalue, so you can change the duration mid-loop viatime_off.set_off_after. -
Restart recovery: Timer state is persisted to
.storage/time_off.timers. On restart, if any timers were active at shutdown, Time Off waits a fixed 30 seconds for slow-loading integrations (Zigbee, Z-Wave) to report device state before acting. Once the wait completes: timers still in the future resume their countdowns; timers that expired during the shutdown window act immediately (turn off or fire event). InTrigger Onlymode, a timer that expired during shutdown fires the event and resumes looping if the device is still on. If the device was turned off while HA was down, the stale expiry is cleared soTimer Activedoes not remain on with no countdown running. -
Manual stop is clean: Calling
time_off.stop_timercancels the countdown, clears the persisted expiry, and firestime_off_cache_updated- but does not turn the device off and does not firetime_off_timer_expired. -
Multiple devices: Each device gets its own independent instance. Timers run in parallel with no shared state between devices.
-
Template entities: Fully supported. Template fans, lights, and switches work identically to physical devices.


