Eiko Wagenknecht
Software Developer, Freelancer & Founder

Controlling Ventilation System Based on CO2 Values with openHAB

Eiko Wagenknecht

In this article, I’ll show you how to automatically control a ventilation system with openHAB based on the CO2 level in a room. I’m using the Maico Push/Pull 45 ventilation system and a Netatmo CO2 sensor, but the principles can be easily applied to other systems.

Table of Contents

Version

At the time of publishing this article, openHAB 3.0.1 was current. With newer versions, some details might have changed. Feel free to leave a comment if you know any adjustments needed for newer versions - I’ll update this article accordingly.

Required Hardware

The approach described works with other ventilation systems that can be connected to openHAB, or with other CO2 sensors.

Prerequisites

The prerequisite is that both the ventilation system and the CO2 sensor are already integrated into openHAB. In my case, I connected the ventilation system via the Homematic CCU3, simply because I’ve been using Homematic longer than openHAB. Setting up this connection is relatively complex and described in a separate article. The Homematic hub is linked to openHAB via the Homematic Binding. The Netatmo sensors are directly integrated into openHAB using the Netatmo Binding.

Items

For controlling the system, I have the following items available:

Ventilation system:

Group    e_EG_Buero_Lueftung                     "Lüftungsanlage"                (r_EG_Buero)                ["Fan"]
Number    EG_Buero_Lueftung_Stufe_Ist             "Aktuelle Stufe [%d]"           (e_EG_Buero_Lueftung)       ["Measurement"]     { channel="homematic:GATEWAY-EXTRAS-CCU3:ccu3:GWE00000000:1#Lueftung-BueroSchlafzimmer-Ist" }
Number    EG_Buero_Lueftung_Stufe_Soll            "Eingestellte Stufe [%d]"       (e_EG_Buero_Lueftung)       ["Control"]         { channel="homematic:GATEWAY-EXTRAS-CCU3:ccu3:GWE00000000:1#Lueftung-BueroSchlafzimmer-Soll" }
Dimmer    EG_Buero_Lueftung_Stufe_Soll_Prozent    "Eingestellte Stufe [%d %%]"                                                    { ga="Fan" [ name="Büro Lüfter", speeds="0=aus:null,20=eins,40=default:zwei,60=drei,80=vier,100=maximal:fuenf", lang="de", ordered=true ] }
Switch    EG_Buero_Lueftung_Automatik             "Automatikmodus (CO2)"          (e_EG_Buero_Lueftung)       ["Switch"]

Netatmo:

Group                    e_EG_Buero_Netatmo                                  "Netatmo"                                  (r_EG_Buero)                                                    ["Sensor"]
Number:Dimensionless    EG_Buero_Netatmo_CO2                                "CO2 Gehalt [%.0f ppm]"                        (e_EG_Buero_Netatmo, u_Persist_Change)                          ["Measurement", "CO2"]          { channel="netatmo:NAModule4:api:*****:Co2" }
Number:Temperature      EG_Buero_Netatmo_Temperatur                         "Temperatur [%.1f °C]"                     (e_EG_Buero_Netatmo, u_Persist_Change)                          ["Measurement", "Temperature"]  { channel="netatmo:NAModule4:api:*****:Temperature" }
Number:Dimensionless    EG_Buero_Netatmo_Luftfeuchtigkeit                   "Luftfeuchtigkeit [%.0f rF]"                  (e_EG_Buero_Netatmo, u_Persist_Change)                          ["Measurement", "Humidity"]     { channel="netatmo:NAModule4:api:*****:Humidity" }

For actual control, only EG_Buero_Lueftung_Stufe_Soll, EG_Buero_Lueftung_Automatik, and EG_Buero_Netatmo_CO2 are needed. The other items are included just for completeness.

Rules

After some brainstorming, I developed the following control logic:

In openHAB, I implemented this with the following rules:

Activating/Deactivating Automatic Mode

rule "Lüftung - deaktivieren - Büro/Schlafzimmer"

when
  Item EG_Buero_Fenster_Offen changed or
  Item System_Variable_Schlafmodus changed

then
  logDebug("co2", "Executing rule 'Ventilation (de)activate Office/Bedroom'.")
  if (EG_Buero_Fenster_Offen.state == CLOSED && System_Variable_Schlafmodus.state == OFF) {
    logInfo("co2", "Ventilation system Office/Bedroom is being switched to automatic mode.")
    EG_Buero_Lueftung_Automatik.sendCommand(ON)
  } else {
    logInfo("co2", "Ventilation system Office/Bedroom is being switched off.")
    EG_Buero_Lueftung_Automatik.sendCommand(OFF)
    EG_Buero_Lueftung_Stufe_Soll.sendCommand(0)
  }
end

Control Based on Current CO2 Value

rule "Lüftung - An CO2 Wert anpassen - Büro/Schlafzimmer"

when
  Time cron "0 0/5 * * * ?"  // every 5 minutes

then
  logDebug("co2", "Executing rule 'CO2 Office/Bedroom'.")
  if (EG_Kueche_Lueftung_Automatik.state != ON || EG_Wohnen_Lueftung_Automatik.state != ON) {
    logDebug("co2", "Automatic mode for Kitchen/Living room deactivated. No action.")
    return
  }

  val minLevel = 1
  val maxLevel = 4
  val setPointLow = 700
  val setPointHigh = 900
  val currentValue = (EG_Buero_Netatmo_CO2.state as Number).intValue
  val currentLevel = (EG_Buero_Lueftung_Stufe_Soll.state as Number).intValue

  logDebug("co2", "Current CO2 level:" + currentValue)
  logDebug("co2", "Target value (low):" + setPointLow)
  logDebug("co2", "Target value (high):" + setPointHigh)
  if (currentValue < setPointLow) {
    if (currentLevel > minLevel) {
      logDebug("co2", "CO2 value below target, reducing ventilation level.")
      EG_Buero_Lueftung_Stufe_Soll.sendCommand(currentLevel - 1)
    } else {
      logDebug("co2", "CO2 value below target, but level already at minimum. No action.")
    }
  } else if (currentValue > setPointHigh) {
    if (currentLevel < maxLevel) {
      logDebug("co2", "CO2 value above target, increasing ventilation level.")
      EG_Buero_Lueftung_Stufe_Soll.sendCommand(currentLevel + 1)
    } else {
      logDebug("co2", "CO2 value above target, but level already at maximum. No action.")
    }
  }
end

Conclusion

I look forward to your comments on whether this article has been helpful. Feel free to write if you have questions or if something remains unclear. I’m also curious about what other automation projects you’ve been able to implement based on this!

No Comments? No Problem.

This blog doesn't support comments, but your thoughts and questions are always welcome. Reach out through the contact details in the footer below.

Support Me

If you found this page helpful and want to say thanks, I would be very grateful if you could use this link for your next purchase on amazon.com. I get a small commission, and it costs you nothing extra. If you'd like to support me in another way, you can find more options here.