I want to share one of my favorite automations built on Aqara temperature/humidity sensors: dew point based ventilation advice.
The problem: In summer, many people open basement or bathroom windows on hot days to “dry out” the room. But warm outdoor air carries a lot of moisture — when it hits cool walls, it condenses. You’re actually making the room wetter, which leads to mold.
The physics: Relative humidity alone is misleading, because it depends on temperature. The dew point tells you the absolute moisture content of the air. Simple rule: only ventilate when the outdoor dew point is lower than the indoor dew point. Then, and only then, airing out actually removes moisture.
My setup:
Aqara temperature/humidity sensors in the rooms I monitor (basement, bathroom) + one outdoor reference on the terrace
Aqara Hub M3, everything integrated into Home Assistant
Home Assistant calculates the dew point from temperature + relative humidity for each sensor (template sensor with the Magnus formula)
An automation compares indoor vs. outdoor dew point and notifies me when it’s a good time to ventilate — and warns me when opening the window would make things worse
On my dashboard I use color coding: green = ventilate now, red = keep windows closed
The Aqara T/H sensors are perfect for this — cheap enough to put one in every critical room, and accurate enough for dew point math.
Happy to share the template sensor code if anyone is interested. How do you manage humidity in your homes?
StarterTips — submitting this as my entry for the Smart Home Tips activity. @AqaraBot / forum team: I can’t edit tags yet with my trust level — could you kindly add the StarterTips tag to this topic? Thanks!
As promised in the original post — here is the template sensor code for the dew point calculation. It goes into configuration.yaml under template: → sensor:. One block per indoor room, plus one outdoor reference (mine sits on the terrace):
- name: "Dew Point Basement"
unique_id: dew_point_basement
device_class: temperature
state_class: measurement
unit_of_measurement: "°C"
icon: mdi:water-thermometer
availability: >
{{ states('sensor.basement_temperature') not in ['unknown', 'unavailable', 'none']
and states('sensor.basement_humidity') not in ['unknown', 'unavailable', 'none'] }}
state: >
{% set t = states('sensor.basement_temperature') | float(0) %}
{% set rh = states('sensor.basement_humidity') | float(50) %}
{% set a = log(rh / 100) + (17.62 * t) / (243.12 + t) %}
{{ (243.12 * a / (17.62 - a)) | round(1) }}
- name: "Dew Point Outdoor"
unique_id: dew_point_outdoor
device_class: temperature
state_class: measurement
unit_of_measurement: "°C"
icon: mdi:water-thermometer-outline
availability: >
{{ states('sensor.terrace_temperature') not in ['unknown', 'unavailable', 'none']
and states('sensor.terrace_humidity') not in ['unknown', 'unavailable', 'none'] }}
state: >
{% set t = states('sensor.terrace_temperature') | float(0) %}
{% set rh = states('sensor.terrace_humidity') | float(50) %}
{% set a = log(rh / 100) + (17.62 * t) / (243.12 + t) %}
{{ (243.12 * a / (17.62 - a)) | round(1) }}
How it works: the two {% set %} lines read temperature and relative humidity from your Aqara sensors, then the Magnus formula converts them into the dew point. Swap the entity IDs for your own sensor names and duplicate the block for every room you want to monitor.
Three details that proved themselves in daily use:
The availability block matters. Without it, a sensor going offline makes the template happily calculate with 0 °C — and your dashboard shows a phantom dew point.
device_class + state_class make the dew points behave like real temperature sensors: proper long-term statistics, history graphs, and unit handling for free.
For the actual recommendation, compare indoor vs. outdoor: ventilating helps when the outdoor dew point is at least ~2–3 °C below the indoor one — a small margin avoids constant flip-flopping around zero difference.
If anyone builds this and runs into issues, post your template here and I’ll take a look.
In my Home Assistant setup I use Aqara temperature and humidity sensors as room references, and I also compare them with separate room sensors. Before relying on a narrow dew point margin, I would place the sensors next to each other for a while and check for a consistent offset. That helps separate a real moisture difference from placement effects such as an exterior wall, sunlight, or airflow.
I would also treat the recommendation as a state instead of reacting to every single update. Require the outdoor dew point to remain below the indoor value by the chosen margin for a few minutes, then use a slightly different threshold before switching back. That reduces repeated notifications from small sensor changes. A door or window contact can suppress the prompt once ventilation is already in progress.
Excellent points, both of them. The side-by-side calibration is something I’d recommend to anyone copying this setup - my Aqara T/H sensors happened to track within a few tenths of a degree, but you only know that after checking, and with a 2–3 °C margin a 1 °C offset would absolutely matter.
Update -implemented, here’s the full recipe. Since @technotron007’s suggestions made a lot of sense, the setup now works exactly that way: the recommendation is a state with hysteresis, and the notification only fires on state changes. For anyone who wants to copy it:
Step 1 -Template binary sensor (goes into configuration.yaml under template:, one block per room; requires the dew point sensors from my earlier post):
- binary_sensor:
# Thresholds: ON at >= 3.0 °C gap (indoor minus outdoor), OFF below 1.5 °C
- name: "Ventilation useful Basement"
unique_id: ventilation_useful_basement
icon: mdi:window-open-variant
delay_on: "00:10:00" # gap must hold for 10 min before turning ON
delay_off: "00:05:00" # gap must be gone for 5 min before turning OFF
availability: >
{{ states('sensor.dew_point_basement') not in ['unknown', 'unavailable', 'none']
and states('sensor.dew_point_outdoor') not in ['unknown', 'unavailable', 'none'] }}
state: >
{% set diff = states('sensor.dew_point_basement') | float(0) - states('sensor.dew_point_outdoor') | float(0) %}
{% if diff >= 3.0 %}
true
{% elif diff < 1.5 %}
false
{% else %}
{{ is_state('binary_sensor.ventilation_useful_basement', 'on') }}
{% endif %}
attributes:
gap: >
{{ (states('sensor.dew_point_basement') | float(0) - states('sensor.dew_point_outdoor') | float(0)) | round(1) }}
The else branch is the hysteresis: between 1.5 and 3.0 °C the sensor simply keeps its previous state, so it can’t flap around a single threshold. delay_on/delay_off filter short spikes on top of that.
Step 2 -Notification automation (Automations → new → Edit in YAML):
alias: Ventilation advice basement
triggers:
- trigger: state
entity_id: binary_sensor.ventilation_useful_basement
from: "off"
to: "on"
- trigger: state
entity_id: binary_sensor.ventilation_useful_basement
from: "on"
to: "off"
actions:
- variables:
useful: "{{ trigger.to_state.state == 'on' }}"
# Replace with your real window contact; a non-existent entity counts as "not open"
window: binary_sensor.window_basement
message: >-
{% if useful %}
Ventilating the basement is worth it now: dew point indoors {{ states('sensor.dew_point_basement') }} °C, outdoors {{ states('sensor.dew_point_outdoor') }} °C.
{% else %}
Ventilating the basement no longer helps -close the window.
{% endif %}
# Suppress the prompt if the window is already open
- condition: template
value_template: "{{ not (useful and is_state(window, 'on')) }}"
# Push: always
- action: notify.mobile_app_your_phone
data:
title: "{{ '🪟 Ventilate: basement' if useful else '✅ Close window: basement' }}"
message: "{{ message }}"
# Voice announcement: only during the day
- if:
- condition: time
after: "08:00:00"
before: "21:00:00"
then:
- action: notify.alexa_media_everywhere
data:
message: "{{ message }}"
data:
type: announce
mode: restart
Testing without waiting for the weather: Developer Tools → States → pick the binary sensor → set state to on → the notification must arrive immediately; set it back to off for the “close the window” message. The override only lasts until the next real sensor update.
Two notes: the 3.0 / 1.5 °C values are a starting point for a cool basement -adjust to your building. And the side-by-side sensor calibration @technotron007 mentioned is still step zero: a 1 °C offset between sensors would eat a third of that margin.