Ventilation Controller is a Home Assistant custom integration for rooms with a motorized window, door, damper, or any other cover entity that can be positioned from 0 to 100%.
It works well for Drivent window actuators, but it is not tied to Drivent. Any Home Assistant cover entity with 0-100% position control can be used.
The idea is simple: the integration opens the window only when it is useful.
It watches the room temperature, optional outdoor temperature, target temperature, optional AC state, optional CO₂ level, and optional exhaust fan. If outdoor air can actually cool the room, the controller can open the window. If the outdoor air is not useful, the temperature PID keeps the window closed. If the AC is running, it can close the window so you do not cool the street. If CO₂ gets high, the controller can prioritize air-quality ventilation and open the window even when temperature ventilation is disabled or blocked by a low outdoor cooling delta. If an exhaust fan is selected, it can run during active CO₂ ventilation or as an airflow booster.
In plain English: this is smart ventilation for Home Assistant. The window opens when it helps, stays closed when it does not, and an optional exhaust fan can help move air when the open window alone is not enough. The integration still exposes enough diagnostics to understand why it did what it did.
- What It Can Do
- How It Works
- Temperature Ventilation
- CO₂ Ventilation Assist
- Exhaust Fan Assist
- PID Behavior
- Controller Status
- Settings And Entities
- Installation
- Notes
- Authors
- Support
- Control a motorized window, door, damper, or vent through a Home Assistant
coverentity - Compatible with Drivent actuators and other 0-100% positionable covers
- Use indoor temperature and a target temperature to calculate the window position
- Use optional outdoor temperature to decide whether cooling with outside air makes sense
- Support temperature ventilation modes:
disabled,force, andauto - Avoid opening the window when outdoor air is not useful
- Use a temperature deadband so the window does not twitch around the target
- Protect against AC conflicts by closing the window when a selected climate entity is cooling
- Use optional priority CO₂ ventilation for air quality
- Let CO₂ open the window to its configured ventilation position independently from temperature ventilation mode and outdoor cooling delta
- Use an optional exhaust fan, switch, or group as an airflow booster, including forced fan requests during active CO₂ ventilation
- Show status sensors for temperature control, CO₂ ventilation, and fan assist
- Expose tuning values as Home Assistant entities
The integration controls one cover entity. The cover must support percentage positioning.
For temperature control, it calculates a PID output between your configured minimum and maximum window position. In auto mode, it first checks whether the room is warmer than outdoors by enough degrees. If not, the PID is blocked because opening the window would not help.
For CO₂ control, the integration can apply a temporary minimum window position. This is an air-quality priority path: it does not depend on Temperature ventilation mode and does not require a sufficient outdoor cooling delta. Example:
- PID wants
10% - CO₂ ventilation wants at least
30% - final window position becomes
max(10, 30) = 30%
If PID already wants 100%, CO₂ does not override anything. It simply reports that CO₂ ventilation is active while the window is already open more than CO₂ requires. If temperature ventilation is disabled or blocked by a low outdoor cooling delta, CO₂ can still open the window to its ventilation position.
An optional exhaust fan can be selected as a fan, switch, or group entity. It does not replace the window PID. For temperature ventilation, it helps airflow after the window is already open enough and natural ventilation is not producing enough effect. For CO₂ ventilation, it can be forced on while CO₂ ventilation is active.
The controller is disabled.
- PID does not run
- window is moved to the minimum position
- controller status is
disabled
If CO₂ ventilation is enabled and CO₂ is above the threshold, the CO₂ path can still open the window to its ventilation position.
PID runs using indoor temperature only.
- outdoor temperature is not required
- cooling delta is not used to block PID
- controller status is
coolingwhile PID is regulating
PID runs only when outside air can cool the room.
- outdoor temperature sensor is required
Cooling delta = current_temp - outdoor_temp- PID is allowed when
Cooling delta >= Cooling delta threshold - PID is blocked when
Cooling delta <= threshold - hysteresis - between those values, the previous allowed/blocked state is kept
If the outdoor sensor is unavailable in auto mode, PID is blocked and the window is moved to the minimum position.
CO₂ support is optional. If no CO₂ sensor is selected, CO₂ entities and logic are not created.
When a CO₂ sensor is selected, the integration can expose:
CO₂sensorCO₂ statusCO₂ ventilationmode:disabledorauto- CO₂ threshold and hysteresis
- CO₂ ventilation position
- CO₂ no-effect detection
- cold outdoor-air guard settings
CO₂ contributes a minimum window position and can run even when the temperature PID is disabled or blocked by a low outdoor cooling delta. If the window is already manually open farther than the CO₂ position, CO₂ does not close it back down.
CO₂ ventilation is blocked when:
- AC conflict protection is active and the AC is cooling
- the room is already near or below the target temperature
Exhaust fan support is optional. If no fan, switch, or group entity is selected, fan entities and logic are not created.
The fan mode has two states:
disabled— the integration never touches the fanauto— the integration can turn the fan on or off when airflow needs help
In auto, the fan can turn on for temperature only when:
- the window is already open at or above
Fan minimum window position - the room is above target plus the temperature deadband
- outdoor air is cooler by at least the configured cooling delta threshold
- AC conflict protection is not blocking ventilation
- the room temperature has not dropped enough during
Fan no cooling timeout
For CO₂, the fan can turn on when CO₂ ventilation is active. In that case CO₂ takes priority over the normal fan minimum-window-position check.
Manual fan control has priority in auto: if you turn the fan entity on or off manually, the integration holds that manual state for Fan manual override timeout before auto control resumes.
The same physical fan, switch, or group can be selected in several Ventilation Controller entries. Fan requests are shared by entity ID: the selected entity is turned on when at least one room requests airflow boost, and it is turned off only when no room requests it anymore. If the entity is selected in only one entry, behavior is the same as a single local fan.
The PID calculation follows the same style as node-red-contrib-pid:
PID proportional bandis the temperature range that maps output from 0% to 100%PID integral timeis in seconds; larger values make integral correction slowerPID derivative timeis in seconds;0disables derivative action- for cooling, the Node-RED output is inverted, so a room temperature above target opens the window more
The main controller status explains the temperature/window decision:
disabled— temperature ventilation is disabledcooling— PID is active and regulating the windowdeadband— room temperature is inside the deadbandauto_blocked_by_delta— outdoor air is not useful enoughac_active_window_closed— AC protection closed the windowoutdoor_sensor_unavailable— outdoor sensor is unavailable in auto modetemp_sensor_unavailable— indoor temperature sensor is unavailablecover_unavailable— controlled cover entity is unavailableidle— no cooling action is neededintegral_locked— PID output is at a limit and integral accumulation is temporarily heldco2_ventilating— CO₂ opened or is holding the window at its ventilation positionco2_no_effect— CO₂ ventilation did not reduce CO₂ enough within the configured timeouterror— unexpected controller update error
The CO₂ status separately explains the CO₂ side:
disabledidleco2_highco2_ventilatingco2_blocked_by_acco2_blocked_by_temperatureco2_no_effect
The fan status explains the exhaust fan side:
disabledidleauto_waitingtemperature_boostco2_boostmanual_onmanual_offcooldownblocked_by_acblocked_by_windowmax_runtime
Main controls:
Temperature ventilation:disabled,force,autoCO₂ ventilation:disabled,auto, shown only when a CO₂ sensor is selectedFan mode:disabled,auto, shown only when a fan, switch, or group entity is selectedAC conflict protection, shown only when an AC climate entity is selectedTarget temperatureRoomOutdoor temperatureWindow positionController status
Configuration and tuning:
Ventilation PID profilePID proportional bandPID integral timePID derivative timeTemperature deadbandOutdoor cooling delta thresholdOutdoor cooling delta hysteresisWindow movement thresholdSystem update interval- CO₂ threshold, hysteresis, ventilation position, timeout, minimum drop, and cold-air guards
- fan minimum window position, no-cooling timeout, minimum temperature drop, max runtime, cooldown, and manual override timeout
Diagnostic sensors:
Cooling deltaPID outputCO₂ positionFan status
- Open HACS.
- Add this repository as a custom integration repository.
- Install Ventilation Controller.
- Restart Home Assistant.
- Go to Settings -> Devices & services -> Add integration.
- Select the room sensor, optional outdoor sensor, optional AC entity, optional CO₂ sensor, optional fan/switch/group entity, and target cover.
Copy custom_components/ventilation_controller to /config/custom_components/ventilation_controller and restart Home Assistant.
- The controlled cover must support 0-100% positioning.
forcemode can work without an outdoor sensor.automode requires an outdoor sensor.- AC protection never turns the AC on or off; it only reacts to the selected climate entity state.
- CO₂ is an air-quality priority path: it can open the window to its configured position even when temperature ventilation is disabled or blocked.
- Fan assist never changes the PID output or window target; it only turns the selected fan/switch/group on or off.
- The same fan/switch/group may be reused by several rooms; requests are coordinated so one room does not turn the fan off while another room still needs it.
Created by @samsonovss with help from Shade, an OpenClaw personal AI agent.
If this integration is useful to you, you can support its development:
- PayPal: samsonov@hotmail.com
- Telegram: https://t.me/samsonovss
- Bitcoin (BTC):
bc1q3cza0kasutzes4hfddxuclmd9ghn5v7zw2nr5c
Please send Bitcoin (BTC) only to the address above. Do not send USDT, ETH, BNB, TRX, or other tokens to this address.
Thank you for supporting the integration.
