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 / Humidifiers / Sirens | homeassistant.turn_off |
| Covers (Gates / Blinds) | cover.close_cover |
| Valves | valve.close_valve |
| Vacuums | vacuum.return_to_base |
Template entities, Groups, and Helpers are also fully supported.
- 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.
This new version of Time Off has several breaking changes. It is recommended you remove Time Off from all your devices before you upgrade to this version. If you do not, you may find disabled/ghost entities on your device page which you will have to manually delete.
Note
If you used automations or scripts in conjunction with the previous version of Time Off, please review this documentation for breaking changes.
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. - Survives restarts — if HA restarts while the timer is running, the countdown resumes automatically. If the timer expired during the shutdown, the device will be turned off (or the expired event fired) immediately on restart.
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 and fire atime_off_timer_expiredevent.
- 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. Since automations can not 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 Time Off config entry ID |
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:
alias: Porch Light - Turned Off Notification
description: "Notify when the porch light timer expires"
mode: single
triggers:
- trigger: device
domain: time_off
device_id: light.porch_light
type: timer_expired
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: device
domain: time_off
device_id: light.porch_light
type: timer_expired
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 before 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, Time Off waits up to 30 seconds for all managed entities to report their state before resuming countdowns. If a timer expired during the shutdown window, the appropriate action (turn off or fire event) is taken immediately on startup. -
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.


