Air-q device any good

Unfortunately they didn’t choose a good name for googling, tons of results with nothing to do with this device, which I found out on a recent release note of home assistant

I have already several sensors, like Netatmo, a DIY MQ131 on Raspberry PI for the Ozone, some $30 tuya stuff I already trashed and sensors inside air purifiers (Dyson PH04, Levoit core 400s).

The Dyson sensors gives me Formaldehyde, VOC, PM2.5, PM10 and NO2. The read values seems more or less plausible, Regarding my Ozone sensor I have no idea, I just did some tests with outdoor air quality station 20km away.

I know I cannot have professional grade sensors for few bucks but I would like to have a decent reading that I can use to compare, and eventually calibrate, other sensors.

My idea was to buy the science version with additional formaldehyde sensor.

Does anyone have some experience with this Air-Q?

Thanks
sugo

Well, just ordered that device, I will review it as soon as I played around it a bit.

Also interested to know if the Air-Q Science edition is required for Home Assistant access, otherwise Pro version looks really good.

my understanding so far is that all the versions works locally with home assistant, science option is needed for more advanced stuff, my guess is you can get raw values instead of just standard API values

I will let you know

Here a first review

TL;DR
Worth the money, but do not excpect a professional quality, this is impossible with less than $2k per sensor.
Sensors are good, more effort could be done to compensate for humidity and temperature, but this job can be done using HA, you will need to dedicate some time anyway.

Small intro:
Professional sensors are REALLY expensive, this device is for amatorial use, still they choose quality sensors in the low cost (less that $100) range. Some gas are really hard to measure, and low cost sensors often have cross sensitivity on other gas, like NO2 sensor is sensitive to O3 and to H2S. Still we can have something good enough paying attention on compensation and calibration. This was impossible just a few years ago.
All the sensors comes factory calibrated, this is good, what is not good is calibration is done ar specific pressure/temperature and humidity. The devices software has autocalibration feature which is terrible, I tried more than a week with confidence, but then I disabled that feature, I read raw values (when possible, some sensors have built in compensations) and then do the math in HA.
I choosed the science version of the device, which in my understanding additionally have mqtt option, not needed for HA, since official integration use HTTP POST queries. I suggest you the Pro version.
The forum looks like only german speaking and the community is quite small, but this will change soon I guess.
The standard airQ integration gather data every 10s, I disabled autopolling and enabled automation every 1min to gather data, it’s more than enough.

I initially did testing indoor, but values were really bad, because of my HVAC system, and when airing the house.
I placed the device outdoor, it’s not water proof but temperature range permit that.

I will share some graph soon, I am still playing around a bit, and I will have to correct the formulas when put the device indoor again. At least I have something to start now.

Easy part first:

Temperature: sensor is TI HDC1080, good enough, considering that the device itself does heat, it has been well compensated.

Relative Humidity: TI HDC1080, very good, first time I see accurate value in <30% range, this is really important since we will use this value for several calibrations/compensations.

Pressure: hard to tell, value is like 3 hPa higher than my Netatmo sensor, which is in line with nearest official meteo station, it can be easily calibrated anyway.

CO2: CM1107, from Cubic, very good one; NDIR Dual-channel Infrared provide more than enough accurate value. Still I added some percentage of humidity compensation. Look in line with Netatmo, but it’s better, more reactive and does not need calibration in fresh air for set of 400ppm, still this function can be enabled but I don’t see a good reason to use it, in fact it’s disabled by default.

  - sensor:
    - name: "AirQ Compensated Calibrated CO2"
      unit_of_measurement: "ppm"
      state: "{{ states('sensor.airq_co2') | float - 5 - 0.9 * states('sensor.airq_humidity') | float }}"

CO: sensor is TGS5141, looks good, plausible values; vendor provides information on how do compensate for temperature.

  - sensor:
    - name: "AirQ Compensated Calibrated CO"
      unit_of_measurement: "mg/m³"
      state: "{{ states('sensor.airq_co') | float - 0.1 * (states('sensor.airq_temperature') | float - 20) / 20 }}"

O2: used sensor is LuminOx LOX-02, manufactured by SST Sensing; it’s quite expensive for the low cost range, it also embed a pressure, temperature and humidity sensor; the manufacter claims to compensate in hardware for pressure and temperature, no mention about humidity compensation, strange enough since a sensor is there; despite those claims the compensation is not good at all, but we can do a good job compensating this value in HA.

  - sensor:
    - name: "AirQ Compensated Calibrated O2"
      unit_of_measurement: "%"
      state: "{{ 0.9987 * states('sensor.airq_oxygen') | float + ( 1021 - states('sensor.airq_pressure') | float ) * 0.001 + 0.029 * (20 - states('sensor.airq_temperature') | float) + states('sensor.airq_absolute_humidity') | float / 55 }}"

Particulate Matter: not easy to judge, values looks plausible, I cannot compare them to official station (less than 10 km away) because I live in a small old village where all the houses are attached toghether, 10 meters high and with tight streets; a lot of houses have wood/pellet stoves, which recent studies say they generate more particulate than fuel engines, and in fact I can smell it. I can see some relation between PM and NO2; since wood stoves generate NO this is a confirmation that measurement are plausible, because following reaction happens: NO + O3 => NO2 + O2. But I have no idea whether the values are good. Compared with sensor inside my Dyson air purifier and other low cost sensors I have, values are like 2x higher. Sensor is the PMSA003 by Plantower, it use Laser Scattering technology, measuring number of paricles larger than 0.3, 0.5, 1, 2.5, 5 and 10 micron in 1 liter, then it calculates PM1, PM2.5 and PM10 in ug/m3. As you may know PM2.5 includes PM1, and PM10 includes PM2.5, making PM1 <= PM2.5 <= PM10. The majority of my measured values are in PM1 and PM2.5, PM10 (excluding the PM2.5 part) look considerably low compared to the ratio I observe on all the official stations; this could be explained by the fact that wood stoves emit more fine particles in respect to traffic from fuel combustion. I will have to find a way to calibrate this.

https://www.nature.com/articles/s12276-020-0405-1

VOC: sensor is SGP30, it uses Multi-Pixel MOX technology, I can only say values are plausible, compared to my other sensors. Not easy to say, sometimes values are 1:1 and sometimes are 1:10, but VOC includes so many compounds that a 1:10 value is still not bad. What I can say is I can clearly see VOC dropping to 0 when I air the room and then slowly grow up, and have peaks when my wife do cooking.
https://sensirion.com/products/catalog/SGP30/

Now the hard part:

NO2/O3/H2S sensors comes from the same manufacter, those sensors are good for measuring immediate healthy risky concentration of gas, but since they range like 0-5 ppm, which is 5000 ppb, and we are looking for max 5 or 10 ppb error, then stuff becomes hard. Still, we can do something. I am confident I found good enough formulas for compansation, this has been done reading some papers, comparing the values with my other sensors, with official professional publicy available sensors, and with empirical attempt. Anyway I cannot be sure measured values are actually good, it’s just my opinion, they looks plausible.

First of all: averaging to reduce the noise:

sensor: 
  - platform: statistics
    name: "Average 1h H2S"
    entity_id: sensor.airq_compensated_calibrated_h2s
    state_characteristic: mean
    max_age:
      minutes: 60
    sampling_size: 60  
    
  - platform: statistics
    name: "Average 1h NO2"
    entity_id: sensor.airq_compensated_calibrated_no2
    state_characteristic: mean
    max_age:
      minutes: 60
    sampling_size: 60  
    
  - platform: statistics
    name: "Average 1h O3"
    entity_id: sensor.airq_compensated_calibrated_o3
    state_characteristic: mean
    max_age:
      minutes: 60
    sampling_size: 60 

NO2: 3SP_NO2_5F-P, it’s claimed not having cross sensitivity, especially for Ozone, for which there’s a filter. Well I found at least a 10 or 15% of cross sensitivity, and the filter is still new, it will be exhausted soon.

  - trigger:
      - platform: time_pattern
        minutes: "/1"
        seconds: "1"   
    sensor:
    - name: "AirQ Compensated Calibrated NO2"
      unit_of_measurement: "µg/m³"
      state: "{{ 0.7 * states('sensor.airq_no2') | float - 0.15 * states('sensor.average_1h_o3') | float(0) - 0.85 * states('sensor.airq_temperature') | float + 0.05 * states('sensor.airq_humidity') | float  }}"

H2S: 3SP_H2S_50-P

  - trigger:
      - platform: time_pattern
        minutes: "/1"
        seconds: "5"   
    sensor:
    - name: "AirQ Compensated Calibrated H2S"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.airq_h2s') | float - 0.04 * (20 - states('sensor.airq_temperature') | float) + 0.18 * states('sensor.average_1h_o3') | float(0) + 0.2 * states('sensor.average_1h_no2') | float(0) - 10 }}"

O3: 3SP_O3_20-P

  - trigger:
      - platform: time_pattern
        minutes: "/1"
        seconds: "10"   
    sensor:
    - name: "AirQ Compensated Calibrated O3"
      unit_of_measurement: "µg/m³"
      state: "{{states('sensor.airq_ozone') | float / 1.1 - 1 * states('sensor.average_1h_no2') | float(0)  + 0.5 * states('sensor.average_1h_h2s') | float(0) }}"
2 Likes

Graph of CO2 over 12h

O2 compensation in pressure, temperature and AH (discontinuity are HA restarts done while trying calibrating other sensors)

I finally placed the device inside again.

Here the reaction on this abrupt change to O2.

First of all, the amount of oxygen (O2) in the atmosphere, assuming there is no water vapor in the atmosphere, is 0.2095 kPa O2 per kPa air or 20.95%. The atmospheric concentration of O2 has remained constant for several hundred years at 20.95%. The percentage is the same at sea level or on Mount Everest.
This is true both outdoor and indoor, except if you live in a perfectly sealed house, in which case you will die for the high CO2 level far before a low O2 level occours.
The only thing that can lower considerably the O2 level is, for example, your house is on fire or some gas leakage is happening, it can be methane, nitrogen or others.
That’s why we want to report relative O2 percentage in dry air, we need a solid value to evaluate a possible health risk.
I would say anything below 20.85% is to be considered suspect.

Significant points in the graph:
A) Device placed indoor
B) HVAC started and messed up the readings
C) Stopped HVAC

I would say that, even if a bit adjustment could be done, we have now a solid O2 value to use, independent from pressure, temperature and humidity.

Just put the device far from any HVAC source

1 Like

The 2x value of PM, in respect to my other sensors, could be partially explained by the fact that no humidity compansation is in place. With 95% RH and especially when there is fog the impact is huge. But even a 70% RH has impact, more on PM1 than PM10.

I read thie excellent paper and applied something similar, just less complex

This was also an interesting reading

This the caonf in HA:

  - sensor:
    - name: "Average 1h PM1 PM2_5"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.average_1h_pm2_5') | float - states('sensor.average_1h_pm1') | float }}"

  - sensor:
    - name: "Average 1h PM2_5 PM10"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.average_1h_pm10') | float - states('sensor.average_1h_pm2_5') | float }}"
     
  - sensor:
    - name: "AirQ Compensated Calibrated PM1"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.average_1h_pm1') | float / ( 1 + 0.4 / ( 1 / ( states('sensor.airq_humidity') | float / 100 ) - 0.97 )) }}"
      
  - sensor:
    - name: "AirQ Compensated Calibrated PM1 PM2_5"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.average_1h_pm1_pm2_5') | float / ( 1 + 0.3 / ( 1 / ( states('sensor.airq_humidity') | float / 100 ) - 0.97 )) }}"

  - sensor:
    - name: "AirQ Compensated Calibrated PM2_5 PM10"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.average_1h_pm2_5_pm10') | float / ( 1 + 0.1 / ( 1 / ( states('sensor.airq_humidity') | float / 100 ) - 0.97 )) }}"
           
  - sensor:
    - name: "AirQ Compensated Calibrated PM2_5"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.airq_compensated_calibrated_pm1') | float + states('sensor.airq_compensated_calibrated_pm1_pm2_5') | float }}"

  - sensor:
    - name: "AirQ Compensated Calibrated PM10"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.airq_compensated_calibrated_pm2_5') | float + states('sensor.airq_compensated_calibrated_pm2_5_pm10') | float }}"

2 Likes

Thank you very much for the effort,.much appreciated. I own the Air-q sci since several months, but still having some work to do for reliable values. Since the HA integration appeared, it got much easier.

Finally some results in the area of NO2 / O3, this was hard stuff to achieve.

Quick recap:

  • our O3 sensor has 1:1 cross sensitivity to NO2 and 1:2 negative cross sensitivity to H2S
  • NO2 and O3 are usually correlated, see quick explaination below

When the sun rise, and with maximum effect at noon, UV light hit the VOC and NOx molecules, this creates Ozone as a chemical reaction. I am not an expert on this topic, but there’s plenty of analisys you can google, basically (copy pasted with no shame):

It is well established that the inter-conversion of O3, NO
and NO2 under atmospheric conditions is generally
dominated by the following reactions:
NO2 + hv = NO + O (1)
O + O2 = O3 + M (2)
O3 + NO = NO2 + O2 (3)
M (usually N2 or O2) represents a molecule that absorbs
the excess vibrational energy and thereby stabilizes the O3
molecule formed. hv represents the energy of a photon
(with a wavelength of < 424 nm) and O is an active
monoatomic oxygen molecule. These equations form a
cycle with no net chemistry i.e. the overall effect of
reaction (2) is the reverse of reaction (1). These reactions
therefore represent a closed system in which the NOx (NO
and NO2) components and the OX (O3 and NO2)
components relate separately. During daytime hours, NO,
NO2 and O3 are typically equilibrated on a timescale of a
few minutes. This is known as a photostationary state.

We trust whatever is written above :slight_smile:

What I noticed is O3 + NO2 (what basically out O3 sensor gives us) is more or less constant, that makes sense, as for the explaination above.

Our NO2 sensors has a lot less cross sensitivities, what I noticed is it has a huge relation with temperature, to be more specific, the relation is not with the absolute value of T, instead it does reacts to how fast the temperature changes, graphically speaking to the slope, what in math is called a derivative.

Even a really small change in temperature does fuck up our data, see here an example:

See here another example

It is clear that that data is pure bullshit, but the issue is because we are reading really low values compared to what the sensor is meant to do (see the datasheet some post above), we can still try to get something useful with a compensation in temperature and with some sort of Exponential smoothing - Wikipedia and averaging.

See what happens when I put the device outside, with a drastic climate change:

Here is how NO2 corrections are handled:

And Ozone:

Here you have a comparison of the achieved result with an official station 10km away

And here the math:

sensor:

  - platform: derivative
    source: sensor.airq_temperature
    name: "Derivata T AirQ"
    time_window: "00:01:00"
    unit_time: h

  - platform: statistics
    name: "Sum Derivative T AirQ 15"
    entity_id: sensor.derivata_t_airq
    state_characteristic: sum
    max_age:
      minutes: 15
    sampling_size: 15 
  - platform: statistics
    name: "Sum Derivative T AirQ 30"
    entity_id: sensor.derivata_t_airq
    state_characteristic: sum
    max_age:
      minutes: 30
    sampling_size: 30 
  - platform: statistics
    name: "Sum Derivative T AirQ 1h"
    entity_id: sensor.derivata_t_airq
    state_characteristic: sum
    max_age:
      minutes: 60
    sampling_size: 60 
  - platform: statistics
    name: "Sum Derivative T AirQ 2h"
    entity_id: sensor.derivata_t_airq
    state_characteristic: sum
    max_age:
      minutes: 120
    sampling_size: 120 

  - platform: filter
    name: "filtered airq no2 avg"
    entity_id: sensor.airq_compensated_no2
    filters:
      - filter: lowpass
        time_constant: 45
      - filter: time_simple_moving_average
        window_size: "00:10"

  - platform: filter
    name: "filtered airq o3 avg"
    entity_id: sensor.airq_ozone
    filters:
      - filter: lowpass
        time_constant: 30
      - filter: time_simple_moving_average
        window_size: "00:15"

  - platform: statistics
    name: "distance 95 no2"
    entity_id: sensor.airq_no2
    state_characteristic: distance_95_percent_of_values
    max_age:
      minutes: 10080
    sampling_size: 10080   
  - platform: statistics
    name: "min multiplied no2"
    entity_id: sensor.airq_compensated_multiplied_no2
    state_characteristic: value_min
    max_age:
      minutes: 10080
    sampling_size: 10080  

  - platform: statistics
    name: "min o3"
    entity_id: sensor.filtered_airq_o3_avg
    state_characteristic: value_min
    max_age:
      minutes: 10080
    sampling_size: 10080 
  - platform: statistics
    name: "max o3"
    entity_id: sensor.filtered_airq_o3_avg
    state_characteristic: value_max
    max_age:
      minutes: 10080
    sampling_size: 10080 



template:

  - trigger:
      - platform: time_pattern
        minutes: "/1"
        seconds: "1"   
    sensor:
    - name: "AirQ Compensated NO2"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.airq_no2') | float(0) - 0.1 * states('sensor.filtered_airq_o3_avg') | float(0) + 0.06 * states('sensor.sum_derivative_t_airq_15') | float(0) + 0.12 * states('sensor.sum_derivative_t_airq_30') | float(0) + 0.03 * states('sensor.sum_derivative_t_airq_1h') | float(0) + 0.02 * states('sensor.sum_derivative_t_airq_2h') | float(0) }}"

  - sensor:
    - name: "AirQ NO2 multiplier"
      state: "{{ ( states('sensor.max_o3') | float - states('sensor.min_o3') | float ) / states('sensor.distance_95_no2') | float }}"

  - sensor:
    - name: "AirQ Compensated multiplied NO2"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.airq_no2_multiplier') | float * states('sensor.filtered_airq_no2_avg') | float }}"

  - sensor:
    - name: "AirQ Compensated Calibrated NO2"
      unit_of_measurement: "µg/m³"
      state: "{{ states('sensor.airq_compensated_multiplied_no2') | float - states('sensor.min_multiplied_no2') | float  }}"


  - trigger:
      - platform: time_pattern
        minutes: "/1"
        seconds: "5"   
    sensor:
    - name: "AirQ Compensated Calibrated H2S"
      unit_of_measurement: "µg/m³"
      state: "{{ max( 0.6 * max( states('sensor.airq_h2s') | float(0), 0.1) - 0.04 * (20 - states('sensor.airq_temperature') | float) + 0.18 * 0.6 * states('sensor.average_1h_o3') | float(0) + 0.2 * 0.6 * states('sensor.average_1h_no2') | float(0) - 12, 0.1) }}"

  - trigger:
      - platform: time_pattern
        minutes: "/1"
        seconds: "10"   
    sensor:
    - name: "AirQ Compensated Calibrated O3"
      unit_of_measurement: "µg/m³"
      state: "{{ max( max( states('sensor.filtered_airq_o3_avg') | float(0), 0.1) - max( states('sensor.airq_compensated_calibrated_no2') | float(0), 0.1 )  + 0.5 * max( states('sensor.airq_compensated_calibrated_h2s') | float(0), 0.1) - max( states('sensor.min_03') | float(0), 0.1 ), 0.1) }}"


2 Likes

I honestly wasn’t expecting the results to be so good, here a 24h run

2 Likes

VOC are also highly influenced by absolute humidity, and the electronic depends as usual on temperature, which in my case is floating due to HVAC system.

I asked on airq forum and awaiting an answer:

From
https://sensirion.com/media/documents/984E0DD5/61644B8B/Sensirion_Gas_Sensors_Datasheet_SGP30.pdf

“The SGP30 features an on-chip humidity compensation for the air quality signals (CO2eq and TVOC) and sensor raw signals
(H2 signal and Ethanol signal). To use the on-chip humidity compensation an absolute humidity value from an external humidity
sensor like the SHTxx is required. Using the “sgp30_set_absolute_humidity” command, a new humidity value can be written to
the SGP30 by sending 2 data bytes (MSB first) and 1 CRC byte.”

I have the strong feeling this is not applied, can you please confirm?

I found some useful information here:
https://freepaper.me/downloads/abstract/10.1016/j.snb.2021.129601

and as usual I simplified it a LOT

sensor:
  - platform: statistics
    name: "min T"
    entity_id: sensor.airq_temperature
    state_characteristic: value_min
    max_age:
      minutes: 1440
    sampling_size: 1440 

template:
  - sensor:
    - name: "AirQ Compensated VOC"
      unique_id: 'airq-0001-voc-0001'
      unit_of_measurement: "ppb"
      state: "{{ states('sensor.airq_voc') | float * ( 2 / states('sensor.airq_absolute_humidity') | float ) ** 0.8 - 10 * ( states('sensor.airq_temperature') | float - states('sensor.min_t') | float ) }}"

2 Likes

Hi @sugo,

How has your experience with the Air-Q been for the past six months? Would you recommend it over other HA compatible air-quality measuring devices. I’ve just tried the Eve Room and Netatmo station, however those were quite underwhelming. Also they don’t support the all the measurements I’m looking for (in particular PM values).

I’m considering the Air-Q Basic, as I’d want something that at the very least has decent measurements for VOC, PM1/2.5/10, and relative humidity. Other sensors are a bonus but not as crucial. I like that it’s also expandable in case I discover a need for a sensor in the future which it doesn’t include out of the box.

I also have a Netatmo with several modules (rain, wind, and 4 internal modules), it’s good but as you said it only have few sensors.
Yes, I still recommend the airq, while not perfect, there is not a better device in the market, in my opinion.
PM1/2.5/10, and relative humidity are really good sensors, even with no correction they are reliable
VOC is also a good sensor but you have to understand that “VOC” is really generic, in that area there are bad substances like formaldehyde but also harmless stuff. It’s nice to have the VOC measurement but take that value for what it is. For example: when I cook, electric induction so no gas or other burning stuff, and around the house there is a nice smell of “whatever I am cooking”, VOC values goes to a peak of like 5000 and more PPB, but I honestly doubt that the air is unhealthy, when I cook something that burn a bit or making the bread in the oven, makes PM values to raise a lot, and this is really a good indication, I also have an air purifier that provides PM2.5 value and automatically increase the power in such circumstances. So yes, go for it :slight_smile:

1 Like

Having read your analysis of what consumer-level stuff is available, I am inclined to think that 90% of them will give you random numbers as readings and the remaining 10% require some serious levels of “interpretation” to be of any use.
I will come back in a decade and hope that the technology has matured :smiley:

Thanks @sugo—I went ahead and got the Basic! I have been running it for a couple of days and it seems good so far.

My only issue with it so far was that the temperature sensor was off about 1 C compared to three other thermometers I have (all of which are within 0.1 C of each other). So I assume the air-Q was off and calibrated it based on what the others said.

@tkhome : I am glad it’s good for you.

About temperature, just two things to consider:

  • check you have the two sensors at the same height in the room, and possibly close together, I noticed just like 50 cm of height difference can change a lot the measure, for the different density of the air, it tends to form stratification
  • airq is quite light and does not have a lot of thermal inertia, so it change fast to the actual temperature, others like netatmo tend to take time to change, since more mass and more inertial energy inside it.

Cheers