I recently watched Jeff Gerling’s video on CO₂ levels, and how to monitor it. My home office is also in the basement, with no windows. So I paid attention to the air quality when setting up down here.

In addition to monitoring the CO₂ level, I’m also automatically ventilating the space when the air quality gets too bad.

Table of contents

CO₂ levels

Normal background concentration in outdoor ambient air
Concentrations typical of occupied indoor spaces with good air exchange
Complaints of drowsiness and poor air.
2,000-5,000 ppm
Headaches, sleepiness and stagnant, stale, stuffy air. Poor concentration, loss of attention, increased heart rate and slight nausea may also be present.

Source: What are safe levels of CO and CO2 in rooms?

The sensor

Netatmo air quality sensor

I’ve purchased a Netatmo air quality sensor — it seemed to be the cheapest, and easiest way of reliably monitoring temperature, humidity, and CO₂. It sends me a notification if any of the values go above or below the “safe” zone.

It’s also very easy to add to Home Assistant, more on that below.

The fan

Backward centrifugal extractor and controller

The fan is a backward centrifugal extractor (RK 125L), capable of moving 168 m³/hour (99 ft³/min). I have a speed controller for the fan, but I mostly run it at full speed.

Our basement is about 40 m³ (1412 ft³) — so at full speed, the air is replaced 4.2 times per hour, theoretically. Meaning that it takes about 15 minutes to replace the air — again; theoretically.

Home office fan exhaust

Home Assistant

I’m using Home Assistant to automate the fan — based on the measured CO₂ level.

Controlling the fan

TP-Link HS110 smart plug

To control the fan I’m using a TP-Link HS110 smart plug. It also measures the watts used by the fan, and I use that to determine the fan speed step set on the controller.

Defining the smart plug:

  discovery: false
    - host: 10.x.x.x

Figure out the fan speed step:

  - sensor:
    - name: "Fan speed"
      unit_of_measurement: 'step'
      state: >
        {%- if states.sensor.office_fan_current_consumption.state | int > 70 %}
        {%- elif states.sensor.office_fan_current_consumption.state | int > 50 %}
        {%- elif states.sensor.office_fan_current_consumption.state | int > 40 %}
        {%- elif states.sensor.office_fan_current_consumption.state | int > 30 %}
        {%- elif states.sensor.office_fan_current_consumption.state | int > 20 %}
        {%- elif states.sensor.office_fan_current_consumption.state | int < 20 %}
        {% else %}
        {%- endif %}        
      icon: >
        {%- if states.sensor.office_fan_current_consumption.state | int > 3 %}
        {% else %}
        {%- endif %}        
Ventilation group in Home Assistant

Reading the sensor

The Netatmo sensor is added as an integration, and to do that we need to define the API credentials:

  client_id: xxxxxxxxxxxxxxxxxxxxxxxx
  client_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

This gives us access to the sensor data:

Climate group in Home Assistant

Automating the fan

I have a script that runs the fan for 15 minutes, this allows me to “manually” replace the air down here:

    alias: Ventilate office
      - service: switch.turn_on
          entity_id: switch.office_fan
      - delay:
          minutes: 15
      - service: switch.turn_off
          entity_id: switch.office_fan

And the automation that starts the fan if the CO₂ level rises above 1200ppm:

- alias: Fan on auto
    platform: numeric_state
    entity_id: sensor.netatmo_office_co2
    above: 1200
    service: switch.turn_on
    entity_id: switch.office_fan

- alias: Fan off auto
    platform: numeric_state
    entity_id: sensor.netatmo_office_co2
    below: 1200
    service: switch.turn_off
    entity_id: switch.office_fan
Screenshot from the Netatmo Home Coach app

The CO₂ level quickly drops when it hits the 1200ppm threshold, ensuring that the air down here never gets too bad 🙂

Last commit 2024-04-05, with message: Tag cleanup.