Adaptive Fan Speed Control
Automatically controls fan speed based on one or more sensors — temperature,
humidity, or air quality — and/or a thermostat's deviation from setpoint.
Each sensor is evaluated independently and the fan always serves the most
urgent condition, no averaging or dilution.
Note: I don't have an AQ sensor (if someone can test and report?) but in theory everything should work. I faked one but without an actual test of each type hard to say for sure.
Credits
This blueprint is a fork of lennon101's excellent Adaptive fan speed control based on temperature and speed range, which was itself based on MickW69's Multi-speed Fan Control Based on Temperature.
All credit for the original concept and groundwork goes to them. I myself used lennon101's version for a long while until I needed extra features. This fork is the result of that and extends the original with multi-sensor support, humidity and air quality modes, thermostat mode, partial sensor degradation, notification throttling, and a handful of quality-of-life improvements around testing and configuration.
Usage
- Import the Blueprint
- Create an Automation from the imported Blueprint
- Add your sensors — one or more temperature, humidity, or air quality
sensors, and/or a single-setpoint thermostat - Fill in the threshold sliders for whichever sensor types you're using —
the rest are ignored automatically
Features
- Multiple sensors — configure as many sensors and/or thermostats as you
like; whichever computes the highest fan speed wins. The fan only turns off
once every configured sensor agrees it's satisfied - Auto-detected sensor modes — Temperature, Humidity, Air Quality, and
Thermostat modes are detected automatically from each entity'sdevice_class;
each mode has its own dedicated threshold sliders - Humidity direction — High Mode ramps up as humidity rises (e.g. a
bathroom exhaust fan); Low Mode ramps up as humidity falls (e.g. circulating
dry air) - Air quality cooldown bypass — any active air quality sensor skips the
cooldown delay entirely so it can respond on every check; air quality events
are more time-sensitive than comfort-based ones - Thermostat support — deviation from setpoint drives fan speed instead of
absolute value; direction flips automatically with HVAC mode (Cool vs. Heat) - Partial sensor degradation — if one sensor goes offline, the automation
keeps running off whichever sensors are still healthy rather than freezing
entirely; it only freezes if every configured sensor is down simultaneously - Unavailable sensor notifications — optional companion app notification
when a sensor goes down, listing which ones; throttled to once per outage
plus a one-time "recovered" notification with an optional tracking helper - Configurable update frequency — polls every 5, 10, 15, 30, or 60 seconds,
independent of how often your sensor actually reports updates - Cooldown delay — configurable delay between speed adjustments to prevent
jittery, frequent changes; can be disabled entirely via a toggle for easier
testing - Maximum percentage change — caps how much the fan speed can change per
adjustment for smoother ramps - Auto turn-on — optionally turns the fan back on automatically once a
sensor crosses back above its off threshold - Blocker entity — disable the automation entirely via a helper (e.g. sleep
mode, away mode) - Custom trigger override — add your own trigger(s) to supplement the
polling interval, e.g. react on every native sensor state change
How sensor modes work
Mode is auto-detected per entity from its device_class attribute — you don't
configure this separately. All mode inputs are always visible (blueprints
can't conditionally hide inputs), but only the ones matching your sensor types
are actually used.
| Sensor type | Mode triggered | Threshold inputs used |
|---|---|---|
climate.* entity |
Thermostat Mode | Deviation Amount, Deviation Off |
device_class: humidity |
Humidity Mode | Min/Max/Off Humidity + Direction |
device_class: aqi / carbon_dioxide / pm25 / etc. |
Air Quality Mode | Min/Max/Off Air Quality |
| Anything else (or no device_class) | Temperature Mode (fallback) | Min/Max/Off Temperature |
Note: Temperature Mode is the fallback for anything unrecognized — including
sensors with nodevice_classset at all. If you're using a custom ESPHome
sensor for humidity or air quality, check in Developer Tools → States that its
device_classattribute is set correctly, otherwise it will silently fall into
Temperature Mode and use the wrong sliders.
Multiple sensors — max-wins combination
When more than one sensor is configured, each computes its own target fan speed
independently. The highest result wins — the fan always serves the most urgent
condition, never an average of all of them. A sensor that's satisfied (below its
own off threshold) contributes 0 to the comparison, so it can never suppress a
more urgent signal from a different sensor.
Example: AQ sensor wants 75% (CO2 climbing), thermostat wants 0% (room is
comfortable) → fan runs at 75%. The thermostat's 0 loses the comparison; it
doesn't dilute or cap the AQ result.
The reverse also holds: if temperature is critical and AQ is mild, temperature
still wins. No sensor type has hardcoded priority — whichever is most demanding
at that moment drives the fan.
Configuration
| Input | Description |
|---|---|
| Sensors or Thermostat(s) | One or more sensor.* or climate.* entities — add as many as you need |
| Fan | The fan entity to control |
| Min / Max Fan Speed | Speed range the fan operates in |
| Min / Max / Off Temperature | Temperature Mode thresholds (absolute degrees) |
| Min / Max / Off Humidity | Humidity Mode thresholds (%) |
| Humidity Direction | High (more fan as humidity rises) or Low (more fan as humidity falls) |
| Min / Max / Off Air Quality | Air Quality Mode thresholds (ppm, µg/m³, index, etc.) |
| Deviation Amount | Thermostat Mode — deviation from setpoint that maps to max fan speed |
| Deviation Off | Thermostat Mode — deviation below which the fan turns off |
| Enable auto fan on | Whether the fan turns back on automatically when a sensor crosses above its off threshold |
| Update Check Frequency | How often to re-evaluate sensors (5–60 seconds) |
| Change frequency delay | Cooldown between speed adjustments (30–1200 seconds) |
| Enable Change Frequency Delay | Toggle the cooldown on/off — turn off while testing |
| Minimum percentage change | Smallest speed change worth making |
| Maximum percentage change | Largest speed change allowed per adjustment |
| Blocking entity (optional) | If this entity is "on", the automation won't run (I use a "quite time" helper and an automation to reset after time limit) |
| Custom Trigger Override (optional) | Add your own trigger(s) to supplement the polling interval (untested but in theory should work) |
| Notify Device (optional) | Companion app device to notify if a sensor goes unavailable |
| Notify State Helper (optional) | An input_boolean you create — throttles unavailable notifications to once per outage and enables a recovery notification |
Tips, gotchas, and known limitations
- Air quality defaults are intentionally aggressive — the Off/Min/Max Air
Quality defaults use a tighter range than temperature so the fan ramps to full
speed faster. Tune these to your actual sensor's scale (CO2 ppm, AQI index,
µg/m³, etc. all use different ranges) - Disable the cooldown while testing — especially useful when manually
nudging a thermostat setpoint to watch the fan respond; with the cooldown
enabled the automation sleeps through several check cycles at once, which makes
speed changes appear to skip ahead rather than step through the ramp - Thermostat mode: single-setpoint only — dual-setpoint thermostats (Heat/Cool
or Auto mode with separate high/low targets) aren't supported; the missing
temperatureattribute is treated like an unavailable sensor - All mode threshold inputs are always visible in the configuration form —
blueprints can't conditionally hide inputs based on which sensor types you've
added, so the unused ones just get ignored - Same-type sensors share thresholds — two humidity sensors in the list share
one set of humidity sliders, not independently configurable ones - Mode detection requires
device_classto be set on the entity — worth
checking in Developer Tools → States before assuming it's working
Changelog
- 2026-06-24: Initial release
