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.
Relative humidity on a dashboard is a poor venting coach. Outdoor air at 80% RH can still be drier than a warm bathroom; 55% outside on a cold morning can add moisture if you throw the sash open. This pack compares absolute humidity (grams of water per cubic metre) indoors and outdoors, then sends one clear push: open, keep airing, or close.
Entity ids are blueprint inputs. Do not publish a live room inventory.
Pairs with the ESP32-C6 climate sensor for the indoor numbers, and with window-open climate pause so a heat pump does not fight the sash you just opened.
What problem it solves
You want a “should I open this window” nudge that uses indoor versus outdoor moisture, not a fixed indoor RH threshold, and you want the opposite nudge when leaving the window open has stopped helping.
How it works
- Indoor climate — an ESPHome node (ESP32-C6 + AHT20 or DHT22) publishes temperature and relative humidity. A second outdoor node is better than a city weather station; a HA
weatherentity is the no-extra-hardware fallback. - Absolute humidity — two template sensors convert each T + RH pair with the Magnus formula. Relative humidity is not compared. The useful number is indoor AH minus outdoor AH (positive = indoor air holds more water; opening should dry the room).
- Recommendation — a template select / text helper:
openwhen the delta is above the open margin and indoor RH is above a comfort floor (so you do not nag a dry room);keepwhile a contact is already open and the delta is still worth it;closewhen the delta drops below the close margin, outdoor air is wetter, or an outdoor temperature floor is breached (pack default: do not recommend opening below about 5 °C). - Notify on change — the blueprint fires only when the recommendation changes, not every 30-second sensor tick. Title and message are inputs. Optional: skip
openif nobody is home (person or zone input). - Contact-aware close — if you wired a reed,
closeis suppressed while the contact is already shut.openis suppressed while it is already open. Without a contact, you get the raw recommendation.
This is a decision helper, not a mould inspection and not a mechanical-ventilation controller.
Hardware notes
- Indoor node. Same Super Mini + AHT20 pattern as the climate-sensor pack (I²C on GPIO0 / GPIO1). DHT22 on a single GPIO works if that is what you have; AHT20 is the more stable reading.
- Outdoor. Prefer a second ESPHome climate in a shaded, ventilated enclosure. A
weatherentity (Met.no, OpenWeather, AccuWeather) is fine for a first pass; city humidity lags a garden and has no room-by-room temperature. - Contact. Any binary sensor that is
onwhen the opening is open. Optional. The pack still works as a “should I?” notify without it. - Placement. Keep the indoor sensor out of the shower steam plume and off a sunny sill. You want room air, not the pane.
Wi-Fi credentials stay in secrets.yaml. Never paste SSIDs, PSKs, or API keys into a published YAML.
Snippet
Indoor node (substitutions, not a live house). Trimmed from the pack YAML:
substitutions:
friendly_name: Bathroom climate
sda_pin: GPIO0
scl_pin: GPIO1
sensor:
- platform: aht10
variant: AHT20
i2c_id: bus_a
temperature:
name: Temperature
id: indoor_t
humidity:
name: Humidity
id: indoor_rh
Absolute humidity templates (blueprint inputs replace entity ids):
template:
- sensor:
- name: Indoor absolute humidity
unit_of_measurement: g/m³
state: >
{% set t = states(indoor_temp) | float(none) %}
{% set rh = states(indoor_humid) | float(none) %}
{% if t is none or rh is none %}
unknown
{% else %}
{% set es = 6.112 * e ** ((17.67 * t) / (t + 243.5)) %}
{{ ((es * rh * 2.1674) / (273.15 + t)) | round(2) }}
{% endif %}
- name: Outdoor absolute humidity
unit_of_measurement: g/m³
state: >
{% set t = state_attr(weather_entity, 'temperature') | float(none) %}
{% set rh = state_attr(weather_entity, 'humidity') | float(none) %}
{% if t is none or rh is none %}
unknown
{% else %}
{% set es = 6.112 * e ** ((17.67 * t) / (t + 243.5)) %}
{{ ((es * rh * 2.1674) / (273.15 + t)) | round(2) }}
{% endif %}
Recommendation notify (change only):
trigger:
- platform: state
entity_id: !input vent_recommendation
action:
- condition: template
value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
- service: !input notify_service
data:
title: "Vent guide"
message: >
{{ trigger.to_state.state }} · indoor
{{ states(indoor_ah) }} g/m³ vs outdoor
{{ states(outdoor_ah) }} g/m³
Setup outline
- Flash the indoor ESPHome YAML (or reuse an existing climate node). Confirm T and RH in Home Assistant.
- Pick the outdoor source: a second node, or a
weatherentity that actually has ahumidityattribute (Developer Tools → the weather entity). - Add the two AH templates and the recommendation template. Create the optional contact input.
- Import the blueprint. Fill indoor T/RH, outdoor weather or outdoor sensors, notify service, open/close margins, outdoor temperature floor.
- Test: bathroom after a shower with drier outdoor air →
open; open the sash and wait as indoor AH falls →keepthenclose; cold wet outdoor air → noopen.
Troubleshooting
- Always says open. You compared relative humidity, or the outdoor entity has no
humidityattribute (templates stayunknownand the default branch is wrong). Check AH numbers first; they should be a few g/m³, not 40–90. - Always says close. Open margin is too high, or the outdoor weather station is a city average that is drier than your garden. A local outdoor node fixes most of this.
- Spam. You notified on every sensor write instead of recommendation changes. Use the pack’s state-change trigger.
- Outdoor AH is unknown. That weather integration does not publish humidity. Switch provider, or add the outdoor ESPHome node.
- DHT22 jumps ±8%. That sensor is noisy; raise the open/close hysteresis (pack default a few tenths of a g/m³) or swap to AHT20.
Credits
Based on the community project FreshAirIQ – Intelligent Ventilation for Home Assistant and the Magnus absolute-humidity relationship used in that work. This write-up is an original overview of a two-sensor AH-delta plus notify pack, not a paste of the integration.