Run a water/oil diffuser on a smart outlet with two independent modes that can run at the same time:
- Duty cycle: every X minutes, turn ON for Y minutes
- Presence: when a presence/motion sensor turns on, keep ON for Z minutes
Optional TTS announcement (auto-uses the outlet’s Area), plus a safety cutoff.
What it does
- Duty cycle — every X minutes, power the outlet ON for Y minutes.
- Presence — when your
binary_sensor(motion/occupancy) turns on, power ON for Z minutes. - TTS (optional) — says “Freshening up .” where Area comes from the outlet’s Area.
- Safety cutoff — if the outlet is ON longer than Max runtime, force OFF.
- Non-interference — each mode turns OFF only if it turned ON, so they don’t fight.
- Fast response — automation runs in parallel, so presence isn’t blocked by duty-cycle timers.
Setup (quick)
- Import the blueprint via the button above.
- Create an automation from the blueprint.
- Pick Diffuser outlet (your smart plug) and Presence / motion sensor (optional, any
binary_sensorwith occupancy/motion). - Toggle modes:
- Duty cycle enabled → set Cycle interval (min) and On-time per cycle.
- Presence mode enabled → set Presence on-time.
- (Optional) TTS: choose TTS engine (
tts.*) and one or more media_player speakers.
- Optional voice, cache, pre-roll (ms).
- (Optional) Safety cutoff: set max run time.
- Save. Tip: assign the outlet to an Area (Settings → Areas) for a natural room name in TTS.
Inputs (explained)
Core
- Diffuser outlet — smart plug powering the diffuser.
- Presence / motion sensor —
binary_sensor(device_class occupancy or motion). Optional.
Mode toggles
- Duty cycle enabled — run a cycle every X minutes.
- Presence mode enabled — when sensor turns on, run for Z minutes.
Duty cycle
- Cycle interval (minutes) — how often to start a cycle.
- On-time per cycle — how long to keep the outlet ON each cycle.
Tip: For no overlap, keep interval > on-time.
Presence
- Presence on-time — how long to keep the outlet ON after detection.
TTS (optional)
- TTS announcement before turning ON — master toggle.
- TTS engine — pick your
tts.*entity (e.g.,tts.piper). - Speakers for TTS — one or more
media_playerentities. - Cache / Voice / Pre-roll (ms) — provider-specific options.
Message is auto-generated: “Freshening up .”
Safety
- Safety cutoff — max ON runtime — hard stop to prevent runaway.
How it works (under the hood)
- Parallel mode ensures presence triggers aren’t blocked by duty-cycle delays.
- Minute tick: duty cycle checks every minute and runs when
(minutes_since_epoch % interval) == 0for predictable starts (00, X, 2X…). - Non-interfering OFF: each branch tracks if the outlet was already ON and only turns it OFF if it turned it ON.
- TTS uses
tts.speakwith your selected engine & speakers; the room is taken fromarea_name(outlet_switch).
Troubleshooting
- Presence doesn’t fire → verify your sensor is a
binary_sensorand goes toonon detection (Developer Tools → States). - TTS silent → pick a TTS engine and at least one media_player; some engines don’t use
options.voice—leave Voice blank if unsure. - Outlet won’t turn OFF → confirm On-time per cycle / Presence on-time aren’t
0:00:00. Safety cutoff will still force OFF. - Timing feels odd → duty cycles start on interval boundaries; to avoid back-to-back runs, set interval > on-time.
This is intended to control waterless oil diffusers primarily but works with water oil diffusers as well but you’d rely on the duty cycle option rather than presence sensor option. You can purchase a cheap smart plug on amazon for around 16 bucks sometimes less. The waterless air diffuser i use is the Scenta XL Smart Scent Air Machine, highly recommend as its the only one i know of that’s reliable with cheap spare nozzle (part that makes people toss their machines) readily available on amazon! I have been using this automation for a long time went through nearly 3 gallons of diffuser oil and wanted to share it via a blueprint. If you have edge-case sensors (e.g., not on/off), drop your entity_id and its exact states and I’ll add a compatible trigger recipe. Enjoy the good smells! ![]()

