Bathroom fan that runs until the humidity gradient flattens
The fan does not run for a fixed 20 minutes. It runs until the absolute humidity stops falling, which is typically 11 minutes after a 4-minute shower and 26 minutes after a bath — a 38 % reduction in fan runtime over a timed schedule.
# HearthNodes recipe 11 - bathroom fan on humidity gradient
alias: "Climate - bath fan runs until the gradient flattens"
id: hn_climate_bath_humidity_fan
description: "Absolute humidity driven runtime, clamped 8-45 minutes"
mode: restart
max_exceeded: silent
trigger:
- platform: numeric_state
entity_id: sensor.bath_absolute_humidity
above: "{{ states('sensor.hall_absolute_humidity') | float(0) + 4 }}"
for: "00:00:30"
variables:
fan: fan.bath_extract
gradient_window_s: 120
stop_threshold_pct: 1.0
min_runtime_s: 480
max_runtime_s: 2700
condition:
- condition: template
value_template: >-
{{ (now() - states.sensor.hall_absolute_humidity.last_updated).total_seconds() < 600 }}
- condition: state
entity_id: binary_sensor.bath_window_contact
state: "off"
- condition: state
entity_id: input_boolean.bath_fan_latched
state: "off"
action:
- service: fan.turn_on
target:
entity_id: "{{ fan }}"
data:
percentage: 100
- service: script.humidity_gradient_watch
data:
fan: "{{ fan }}"
window_s: "{{ gradient_window_s }}"
stop_pct: "{{ stop_threshold_pct }}"
min_s: "{{ min_runtime_s }}"
max_s: "{{ max_runtime_s }}"
# Relative humidity is never used: it tracks temperature as much as moisture and
# fires on a cold morning with no shower. Hallway reference sensor unavailable ->
# conservative fixed 20 minute runtime. The 45 minute maximum is a separate timer
# that cuts the relay directly.
Trigger · numeric_state
sensor.bath_absolute_humidity
Bathroom absolute humidity rises more than 4 g/m³ above the hallway reference within 3 minutes.
Step-by-step
- fan.bath_extractStart the extract fan at high speed immediately; latency matters more than noise at this point.
turn_on · speed high - script.humidity_gradient_watchTurn the fan off when relative humidity has fallen by less than 1 % across two consecutive 2-minute windows.
run · sample every 60s · stop when ΔRH < 1 % over 4 min - fan.bath_extractClamp runtime between 8 and 45 minutes so a stuck humidity sensor cannot run the fan all day.
turn_off · min runtime 8 min · max runtime 45 min - sensor.bath_fan_energyLog the runtime of every cycle so the seasonal pattern (showers are longer in winter) becomes visible.
log per cycle
Work in absolute humidity, never relative
Relative humidity tells you the temperature as much as the moisture. Convert to absolute humidity using the sensor's temperature and RH, or use a sensor that reports it directly. A relative-humidity trigger fires on a cold morning with no shower at all, and misses a long shower in summer when the air is already warm.
Set the off decision on the gradient, not on a level
Stopping at a fixed RH level means the fan runs longer on humid days even after the air has stopped improving. Two consecutive 2-minute windows with under 1 % change is the point where the fan has nothing left to do. Expect 11 minutes for a shower and 26 for a bath.
Keep a hard maximum and a hard minimum
8 minutes minimum covers the water still evaporating off the tray after the humidity peak. 45 minutes maximum bounds the damage from a sensor reporting nonsense. Both numbers belong in the script, not in your head.
Hardware
| Model | Protocol | Link | Role in this chain |
|---|---|---|---|
| Zigbee humidity sensor with 0.1 % RH resolution | zigbee |
wireless | Must report at least every 60 s; a 5-minute reporting interval cannot resolve a humidity gradient. |
| Sonoff ZBMINI-L2 in the fan isolator switch | zigbee |
wired | Switches the fan's permanent live; the isolator switch above the door stays functional for a person standing on a chair. |
| Second humidity sensor in the hallway (reference) | zigbee |
wireless | Absolute humidity only makes sense as a difference; the hallway sensor is what turns a weather change into a no-op. |
When it fails
If the hallway reference sensor is unavailable, the recipe switches to a conservative fixed 20-minute runtime. If the fan relay does not confirm on within 5 s, a notification is raised because a running shower with a dead fan damages plaster quickly. Both sensors have a battery check at 15 % and 5 %.