AQI From PMS7003 pm2.5 sensor

EDIT 06/2024: The US AQI thresholds have been updated and you should use the updated script, below

I live in Northern Californa, and for this fire season I recently got a couple PMS7003 particulate sensor from China, along with a couple of ESP32 dev boards. This sensor has a laser diode that can measure the particulate concentration at pm2.5 (particles > 2.5µm) and pm10 (particles > 10µm), and ESPHome has a config that knows how to read the data and send it to HA via the ESPHome integration.

However, as far as I can tell HA doesn’t have any sort of filter to convert the concentration readings to AQI, which is a farily simple but non-linear calculation. So here’s the template I whipped up, based on The AQI Equation (2015 - Obsolete on May 6th, 2024) - Air Quality and AQI Info - AirNow Discussion Forum

templates:
- sensor:
    - name: "Family Room AQI (pm2.5)"
      state: >-
        {% macro aqi(val, val_l, val_h, aqi_l, aqi_h) -%}
          {{(((aqi_h-aqi_l)/(val_h - val_l) * (val - val_l)) + aqi_l)|round(0)}}
        {%- endmacro %}
        {% set v = states('sensor.family_room_pm2_5')|round(1) %}
        {% if v <= 12.0 %}
           {{aqi(v, 0, 12.0, 0, 50)}}
        {% elif 12.0 < v <= 35.4 %}
           {{aqi(v, 12.1, 35.4, 51, 100)}}
        {% elif 35.4 < v <= 55.4 %}
           {{aqi(v, 35.5, 55.4, 101, 150)}}
        {% elif 55.4 < v <= 150.5 %}
           {{aqi(v, 55.5, 150.4, 151, 200)}}
        {% elif 150.4 < v <= 250.4 %}
           {{aqi(v, 150.4, 250.4, 201, 300)}}
        {% elif 250.5 < v <= 500.4 %}
           {{aqi(v, 250.5, 500.4, 301, 500)}}
        {% else %}
           Holy shit it's bad.  Get out!
        {% endif %}
      unit_of_measurement: AQI
      device_class: aqi

Relatedly, here’s a fragment of the ESPHome config I’m using for the sensor. It takes readings every second, which is way too much data. This chops that down to every minute, I could really probably get away with every 5 or 10 minutes and be just fine:

sensor:
  - platform: pmsx003
    type: PMSX003
    pm_2_5:
      name: "pm2.5"
      unit_of_measurement: "µg/m³"
      icon: "mdi:face-mask-outline"
      state_class: "measurement"
      filters:
        - sliding_window_moving_average:
            window_size: 60
            send_every: 60
    pm_10_0:
      name: "pm10"
      unit_of_measurement: "µg/m³"
      icon: "mdi:face-mask-outline"
      state_class: "measurement"
      filters:
        - sliding_window_moving_average:
            window_size: 60
            send_every: 60
10 Likes

Thank you SO much for this :slight_smile:

As of May 2024 the US AQI uses new calculations, here’s an updated template

templates:
- sensor:
    - name: "Family Room AQI (pm2.5)"
      state: >-
        {% macro aqi(val, val_l, val_h, aqi_l, aqi_h) -%}
          {{(((aqi_h-aqi_l)/(val_h - val_l) * (val - val_l)) + aqi_l)|round(0)}}
        {%- endmacro %}
        {% set v = states('sensor.family_room_pm2_5')|round(1) %}
        {% if v <= 9.0 %}
           {{aqi(v, 0, 9.0, 0, 50)}}
        {% elif 9.0 < v <= 35.4 %}
           {{aqi(v, 9.1, 35.4, 51, 100)}}
        {% elif 35.4 < v <= 55.4 %}
           {{aqi(v, 35.5, 55.4, 101, 150)}}
        {% elif 55.4 < v <= 125.4 %}
           {{aqi(v, 55.5, 125.4, 151, 200)}}
        {% elif 125.4 < v <= 225.4 %}
           {{aqi(v, 125.5, 225.4, 201, 300)}}
        {% elif 250.5 < v <= 500.4 %}
           {{aqi(v, 250.5, 500.4, 301, 500)}}
        {% else %}
           Holy shit it's bad.  Get out!
        {% endif %}
      unit_of_measurement: AQI
      device_class: aqi
5 Likes

Thank you for this! It was exactly what I was looking for!

Just to save someone else some frustration or for those new to HA, a simple “how to” on using this:

prior to anything, get your pm2.5 sensor working!!! If that’s not working you cannot proceed

find your pm2.5 sensor, hit the gear then look for the entity ID. mine is “sensor.living_room_particulate_matter_2_5mm_concentration”. record yours somewhere

edit configuration.yaml and add the chunk of code from the Jul 2024 post (AQI From PMS7003 pm2.5 sensor - #3 by emoses)

edit the 1st line and remove the extra s (it should be template, not templates)

edit the 3rd line to whatever you want to call it

edit the 8th line to be YOUR pm2.5 sensor you recorded above. so for my example, my line 8 looks like this:
{% set v = states(‘sensor.living_room_particulate_matter_2_5mm_concentration’)|round(1) %}

save the file and tell home assistant to restart

for me, I added the new sensor to the card my other living room equipment is in. You should be able to search for it via the name you gave it on line 3


hope this helps some folks. took me a bit to figure out where and how all this actually got implemented into HA