Home AssistantESPHome

Humidity climate control

Temperature-corrected humidity turns a fan, dehumidifier, or humidifier on and off with separate thresholds, a max-run watchdog, and a cooldown after you touch the switch yourself.

Difficulty · IntermediateSetup · 45–90 minutesHA · current stable (blueprint)ESPHome · current stable
Get the pack · from $19Have us set it up

Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.

A raw humidity number lies across rooms. Cold air at 60% relative humidity can hold less water than warm air at 45%. This pack corrects a reference sensor to the temperature of the room you are controlling, then turns a fan, dehumidifier, or humidifier on above one threshold and off below a lower one. If you switch the device yourself, automation backs off for a cooldown. A max-run timer force-stops a device that would otherwise run until a sensor fails.

Entity ids are blueprint inputs. Do not publish a live room inventory.

This is device control. The humidity window-vent guide only tells you whether opening a window will dry the air. Use that for the sash, and this pack for a plug-in fan or humidifier. The indoor numbers can come from the ESP32-C6 climate sensor.

What problem it solves

You want a bathroom fan or a humidifier to follow moisture, not a single relative-humidity trip point that chatters, and you want a manual on or off to stick until you are done.

How it works

  1. Primary sensor — temperature and relative humidity in the room the device serves. An ESPHome AHT20 or DHT22 is enough. AHT20 is the steadier reading.
  2. Temperature correction — optional reference sensors (another room, or outdoors). The pack converts the reference relative humidity into the relative humidity that same air would show at the primary room’s temperature, using the Magnus–Tetens saturation curve. A cold hallway no longer looks “wetter” just because its percentage is higher. Without a reference, the blueprint falls back to the primary relative humidity.
  3. Hysteresis — devices turn on when the corrected value rises through the on threshold and stay on until it falls through a lower off threshold. One threshold chatters.
  4. One group — every selected fan, switch, or helper shares the same on/off decision and the same lockout. Touch any one of them and the whole group waits.
  5. Manual cooldown — a state change whose context has a user id starts the override timer and suspends automation until it ends. The cooldown is separate from the safety rest.
  6. Max run and rest — if a device stays on longer than the cap, the pack turns it off and holds a rest period so a stuck sensor cannot run a humidifier all day. After the rest, each device is checked again on its own.

Consumer humidity sensors disagree by a few percent. Keep the gap between the on and off thresholds wider than that, or the group will hunt.

Hardware notes

  • Node. ESP32-C6 Super Mini with an AHT20 on I²C, or a DHT22 on one GPIO. Same pattern as the climate-sensor pack. One node can be the primary; a second node is the reference if you want the correction.
  • Load. A smart plug or a relay on the fan, dehumidifier, or humidifier. The blueprint targets switches and fans. It does not dim a light and it does not set a thermostat temperature.
  • Placement. Primary sensor in the room air, not in the exhaust stream and not on a sunlit sill. A reference outdoor sensor belongs in shade.

Wi-Fi credentials stay in secrets.yaml.

Snippet

Corrected reference humidity, as a percentage at the primary room’s temperature. Inputs replace the entity ids in the blueprint.

template:
  - sensor:
      - name: Reference humidity at room temperature
        unit_of_measurement: "%"
        state: >
          {% set t_ref = states(ref_temp) | float(none) %}
          {% set rh_ref = states(ref_humid) | float(none) %}
          {% set t_room = states(room_temp) | float(none) %}
          {% if t_ref is none or rh_ref is none or t_room is none %}
            unknown
          {% else %}
            {% set es_ref = 6.112 * e ** ((17.67 * t_ref) / (t_ref + 243.5)) %}
            {% set es_room = 6.112 * e ** ((17.67 * t_room) / (t_room + 243.5)) %}
            {{ ((es_ref * rh_ref) / es_room) | round(1) }}
          {% endif %}

On, off, and a manual cooldown. The pack uses a blueprint; this is the shape:

trigger:
  - platform: state
    entity_id: !input humidity_sensor
  - platform: state
    entity_id: !input devices
action:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.to_state.context.user_id is not none }}"
        sequence:
          - action: timer.start
            target:
              entity_id: !input override_timer
            data:
              duration: !input override_for
      - conditions:
          - condition: state
            entity_id: !input override_timer
            state: "idle"
          - condition: numeric_state
            entity_id: !input corrected_humidity
            above: !input on_above
        sequence:
          - action: homeassistant.turn_on
            target: !input devices

Max-run, rest, and the falling-threshold off path are in the full blueprint.

Setup outline

  1. Flash or reuse a climate node. Confirm temperature and humidity in Developer Tools.
  2. Optional: add the corrected-humidity template and a reference node. If you skip it, point the blueprint at the primary humidity sensor.
  3. Create two timer helpers: manual override and safety rest. Create the device list (one fan is enough).
  4. Import the blueprint. Set on and off thresholds, max run, rest, and the override duration.
  5. Test: raise humidity (or temporarily lower the on threshold) and confirm the device turns on; lower it and confirm the off threshold; toggle the device in the dashboard and confirm automation waits out the cooldown.

Troubleshooting

  • It fights you. The change had no user id, so it did not count as manual. Some wall switches and some integrations report a physical press without one. Use the dashboard or a helper boolean as the override, which the pack treats as manual.
  • The fan never starts. The on threshold is above what the sensor actually reaches, or the corrected template is unknown because a reference entity is missing. Check the template state before the blueprint.
  • Rapid clicking. On and off thresholds are too close for a noisy DHT22. Widen the gap, or move the primary sensor to an AHT20.
  • Runs for hours. Max run is unset or longer than you think, or the sensor is in the steam and never falls. Cap the run and move the sensor.
  • Turns back on during the rest. The rest timer did not survive a Home Assistant restart. The pack re-evaluates when the timer is missing; after a restart, expect one fresh decision rather than the remainder of the old rest.

Credits

Based on the community blueprint Smart Humidity Climate Control (multi-device with manual override) and the matching project Absolute Humidity Control. This write-up is an original overview of temperature-corrected on/off control with a cooldown and a max-run rest, not a paste of that blueprint.

Changelog

v1.0 · Oct 22, 2026: First publish.