Skip to content

Temperature Linear

The Temperature Linear module provides a simple three-zone mapping from temperature to fan speed. Define your temperature thresholds and fan speed percentages, and the controller handles the rest — no complex tuning required.

Choose Linear when you want straightforward temperature-based fan control without the complexity of PID tuning or multi-point curves. It works well for most home and office setups where you just need fans to ramp up as temperature rises.

For more precise temperature targeting, consider Temperature PID. For finer control over the speed curve shape, see Temperature Curve. The modules overview has a comparison table.

VariableDefaultDescription
friendly_name"Fancontroller"Device name prefix for all entities
t_off"25.0"Temperature below which fans turn off (C)
t1"30.0"Lower temperature setpoint (C)
t2"50.0"Upper temperature setpoint (C)
fanpercent1"30.0"Fan speed at t1 (%)
fanpercent2"100.0"Fan speed at t2 and above (%)
temperature_sensor_id"fancontroller_temperature"Id of the sensor supplying the control temperature

The module divides temperature into four zones:

ZoneTemperature RangeFan SpeedBehavior
OffBelow t_off0%Fans completely off
Constant lowt_off to t1fanpercent1Fans run at a fixed low speed
Rampt1 to t2fanpercent1 to fanpercent2Linear ramp between the two speeds
Constant highAbove t2fanpercent2Fans run at maximum configured speed

With the defaults (t_off=25, t1=30, t2=50, fanpercent1=30%, fanpercent2=100%):

  • Below 25 C: fans off
  • 25-30 C: fans at 30%
  • 30-50 C: fans ramp linearly from 30% to 100%
  • Above 50 C: fans at 100%
EntityRangeStepUnitDefaultDescription
Linear Off Temperature20 — 500.5C25.0Temperature below which fans stop
Linear T120 — 500.5C30.0Lower temperature setpoint
Linear T220 — 500.5C50.0Upper temperature setpoint
Linear Fan Percent 10 — 1001%30Fan speed at T1
Linear Fan Percent 20 — 1001%100Fan speed at T2 and above
EntityUnitDescription
Linear Output%Current calculated fan speed (updated every 10s)
EntityDefaultDescription
Auto Control Fan 1ONEnable linear control for fan 1
Auto Control Fan 2ONEnable linear control for fan 2
Auto Control Fan 3ONEnable linear control for fan 3
Auto Control Fan 4ONEnable linear control for fan 4

This block goes in your own configuration, not in the module. See why the assignment lives in the consuming config for the reason. version: 3 is what makes entity grouping work at all; without it, entity grouping has no effect.

web_server:
version: 3
sorting_groups:
- id: grp_temp_linear
name: "Linear Temperature Control"
sorting_weight: 100
number:
- id: !extend linear_t_off
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 10
- id: !extend linear_t1
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 20
- id: !extend linear_t2
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 30
- id: !extend linear_fanpercent1
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 40
- id: !extend linear_fanpercent2
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 50
sensor:
- id: !extend linear_output_value
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 60
switch:
- id: !extend auto_control_fan1
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 70
- id: !extend auto_control_fan2
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 80
- id: !extend auto_control_fan3
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 90
- id: !extend auto_control_fan4
web_server:
sorting_group_id: grp_temp_linear
sorting_weight: 100

This is the same block examples/standalone.yaml ships, entity for entity and weight for weight. Copy it verbatim into a config that already declares web_server: with version: 3.

packages:
temperature_linear:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files:
- path: modules/temperature_linear.yaml
vars:
friendly_name: "My Fan Controller"
packages:
temperature_linear:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files:
- path: modules/temperature_linear.yaml
vars:
friendly_name: "Desktop Cooler"
t_off: "28.0"
t1: "32.0"
t2: "45.0"
fanpercent1: "20.0"
fanpercent2: "80.0"

This profile keeps fans off until 28 C, starts at a quiet 20%, and caps at 80% for reduced noise.

  • Adjust t_off based on ambient temperature — if your room is normally 24 C, setting t_off to 25 C means fans only spin when something is actively generating heat
  • Set fanpercent1 to your fans’ minimum reliable speed — most fans need at least 20-30% PWM to start spinning consistently. Check the troubleshooting guide if fans stutter at low speeds.
  • Use the HA number entities to fine-tune — all five parameters can be adjusted live from Home Assistant without reflashing
  • Watch the Linear Output sensor — it shows the current calculated fan speed percentage, helpful for verifying your thresholds behave as expected

By default the module reads fancontroller_temperature, the onboard HDC1080 that every hardware package declares. Set temperature_sensor_id to the id of any other temperature sensor in your configuration to control from that instead:

packages:
# modules/bme680.yaml is not standalone - it needs the shared I2C bus (bus_a)
# that your hardware package declares. Use your own board revision here.
hardware:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files: [hardware-rev-3.1.yaml]
bme680:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files: [modules/bme680.yaml]
temperature_linear:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files:
- path: modules/temperature_linear.yaml
vars:
temperature_sensor_id: "bme680_temperature"

Remember that t_off, t1, and t2 are absolute temperatures. An external sensor measuring room or intake air reads lower than the board does, so thresholds tuned for the onboard sensor will usually need lowering.

Omit the variable and it falls back to fancontroller_temperature, so existing configurations keep working unchanged. A misspelled id fails the build rather than quietly falling back to the onboard sensor.

The Qwiic modules build their sensor ids from an id-prefix substitution, so BME680 gives you bme680_temperature, SCD41 gives scd41_temperature, and DS2484 gives whatever ids you assign your own dallas_temp sensors.

If you also run the SSD1306 OLED module, note that it displays the onboard sensor regardless of this setting, so the screen and the fan control can show different temperatures.

The module names the sensor it is reading in its boot log, so you can confirm from a log dump that the override took effect.