PurpleAir Air Quality Sensor

@catchdave Thanks so much for this! I am seeing one issue I was hoping I could get your input on. After I added this integration, everything worked great. But it appears that I haven’t received any new data since adding the integration. When I click on one of the entities it states that the last data it received was ~45 minutes ago.

It looks like this should update every minute, right? Any idea why I’m not seeing that?

@rdlaner
It does make an API call every minute. I assume what you are seeing is the “last changed” time, not last accessed/checked time.

If you hover the time that says “45 minutes ago”, the tooltip will say “Last changed 45 mins ago”. You can always manally visit the device to verify the data matches.

1 Like

Great work! I just added my home sensor using your integration. What’s the best way to visualize the data in a dashboard?

1 Like

You can use something like Grafana and InfluxDB. The integration also supports long-term statistics, so you can use Lovelace cards that show statistics.

I pulled it into a normal card to display the PM2.5, thanks! Is there any way to get the EPA AQI value also? Could the new integration calculate this? Or does anyone have a template sensor that works?

As a rule, device integrations can’t create sensors based on data that the device itself doesn’t provide—so, the integration won’t provide AQI directly.

That said, happy to share what I’m doing. I recently read this article, which advocates using raw values instead of AQI. A few quotes of interest:

[…] the more I start to ignore AQI and just pay attention to the direct measure -micrograms. Micrograms don’t have ever-changing conversion formulas, and they don’t depend on your government’s scale.

But wait, AQI is great because 100 is roughly “bad,” so it’s easy to understand. If we use micrograms, how do we know what’s good and what’s bad? I use the WHO guidelines:

Annual limit = 10 micrograms

24-hour limit = 25 micrograms

The WHO bases its limits on studies of the health effects of pollution.

I now follow the same strategy of looking at raw values to determine air quality. The challenge is that the EPA determines “healthiness level” by looking at values over time, like 24 hours (source). So, I first create statistics sensors that calculate the mean of both PM2.5 and PM10.0 over 24 hours:

sensor:
  - platform: statistics
    name: "Average Outdoor PM2.5 (24h)"
    entity_id: sensor.sensor_pm2_5_mass_concentration
    state_characteristic: mean
    max_age:
      hours: 24

  - platform: statistics
    name: "Average Outdoor PM10.0 (24h)"
    entity_id: sensor.sensor_pm10_0_mass_concentration
    state_characteristic: mean
    max_age:
      hours: 24

Then, I have this template sensor:

sensor:
    - name: Local Outdoor Air Quality
      state: >
        {% set pm2_5_avg = states("sensor.average_outdoor_pm2_5_24h") | int %}
        {% if 0 <= pm2_5_avg <= 12.0 %}
          {% set pm2_5_rating = 0 %}
        {% elif 12.0 < pm2_5_avg <= 35.4 %}
          {% set pm2_5_rating = 1 %}
        {% elif 35.4 < pm2_5_avg <= 55.4 %}
          {% set pm2_5_rating = 2 %}
        {% elif 55.4 < pm2_5_avg <= 150.4 %}
          {% set pm2_5_rating = 3 %}
        {% elif 150.4 < pm2_5_avg <= 250.4 %}
          {% set pm2_5_rating = 4 %}
        {% else %}
          {% set pm2_5_rating = 5 %}
        {% endif %}

        {% set pm10_0_avg = states("sensor.average_outdoor_pm10_0_24h") | int %}
        {% if 0 <= pm10_0_avg <= 54.0 %}
          {% set pm10_0_rating = 0 %}
        {% elif 54.0 < pm10_0_avg <= 154.0 %}
          {% set pm10_0_rating = 1 %}
        {% elif 154.0 < pm10_0_avg <= 254.0 %}
          {% set pm10_0_rating = 2 %}
        {% elif 254.0 < pm10_0_avg <= 354.0 %}
          {% set pm10_0_rating = 3 %}
        {% elif 354.0 < pm10_0_avg <= 424.0 %}
          {% set pm10_0_rating = 4 %}
        {% else %}
          {% set pm10_0_rating = 5 %}
        {% endif %}

        {% set rating = [pm2_5_rating, pm10_0_rating] | max %}
        {% if rating == 0 %}
          Good
        {% elif rating == 1 %}
          Moderate
        {% elif rating == 2 %}
          Unhealthy for sensitive groups
        {% elif rating == 3 %}
          Unhealthy
        {% elif rating == 4 %}
          Very unhealthy
        {% else %}
          Hazardous
        {% endif %}
      unique_id: local_outdoor_air_quality

This is a more conservative approach, where the “worse” of the two sensors determines the overall value.

CleanShot 2023-01-05 at 09.25.52

4 Likes

Thank you for this! I just tweaked it for the single indoor sensor I’m pulling in from my kitchen. With this logic, it’s currently reading as “Moderate”. Interesting that the PurpleAir itself is showing a green indicator light.

Here’s what the PurpleAir site shows for my sensor right now:

That “4” value seems to match with “VG1 PM2.5 AQI” as stored in HA and I think comes from the integration.

Does this appear to be doing the right thing?

That must be a value that their web or mobile app is calculating; the API docs don’t list anything having to do with AQI.

Can you tell me where “VG1 PM2.5 AQI as stored in HA” comes from? That isn’t an entity I’m creating (to my knowledge).

Sorry, I think that’s an old value that I was using from a previous template. However, I’m having trouble finding out where it’s coming from. (I grepped my sensor.yaml, and it’s not there anymore, and even restarted HA.) I’ll need to investigate more. Thanks for your help!

1 Like

So I needed to use Recorder.purge_entities to get rid of that old template sensor. Now I’m back to keeping an eye on my kitchen’s air quality via the new calculated air quality value. Thanks again.

1 Like

Is there a way to add a private sensor for PurpleAir to get my indoor air quality? It would be nice to be able to add a sensor by its identifier.

Yes, I have both the indoor and outdoor info above (Post 128). I have both and use both locally.

@bachya
Is there an easy way to remove the sensor from the Homeassistant map?

Thanks - George

Not yet—I have an open PR to add the ability, but it’s awaiting further review: Add ability to configure map icons for PurpleAir by bachya · Pull Request #86124 · home-assistant/core · GitHub

Thank you!

1 Like

This unofficial integration is able to pull the AQI value from PurpleAir with no issues. What are they doing which the official integration cannot?

It’s not a matter of capability; it’s a core standard for official integrations.

I have some odd behavior with my PurpleAir monitor: Every few calls, the rest call needs a really long time to respond. Is anyone else seeing this or is it just my sensor?

I only poll mine locally every 5 mins and I don’t notice any delays.
Are you polling the local rest API or the web API?
What is you frequency of updates?

Hopefully someone is monitoring the old tread:
Has anyone checked out the new PurpleAir sensors that now detect VOC?