PurpleAir Air Quality Sensor

Are you sure? Maybe I’m drowning in PDFs, but I think the one I had in the YAML and the one I linked above are both the same (ID 349513 from the URL link) and are titled 4/2020. In either case, I cleaned up the one in the YAML to remove the extra junk in the URL from the comment.

I didn’t really notice a difference last year. But my house is in a hilly/mountainous area where it is hard to compare to anything nearby and wide area values aren’t usable.

If you have a dew point value calculation, I’m all ears!

My mistake, PurpleAir has DewPoint built in. I use the calculation on my indoor sensors, but here you go:

- platform: template
  sensors:
    bathroom_master_dewpoint:
      friendly_name: "Bathroom Master Dewpoint"
      value_template: >-
        {% set h = states('sensor.bathmaster_multisensor_humidity_air') | float(default=0) %}
        {% set t = states('sensor.bathmaster_multisensor_temperature_air') | float(default=0) %}
        {{((t-32)*5/9-(100-h)/5)*9/5+32}}
      unit_of_measurement: "F"

Here is my mostly complete PA in HA:

- platform: rest
  name: 'PurpleAir'
  resource: http://10.74.1.198/json?live=false
  value_template: '{{ value_json.SensorId }}'
  scan_interval: 120
  json_attributes:
    #- Mem
    #- memfrag
    - uptime
    - rssi
    #- Adc
    - current_temp_f
    - current_humidity
    - current_dewpoint_f
    - pressure
    - p_0_3_um
    - p_0_3_um_b
    - p_0_5_um
    - p_0_5_um_b
    - p_1_0_um
    - p_1_0_um_b
    - pm1_0_atm
    - pm1_0_atm_b
    - p_2_5_um
    - p_2_5_um_b
    - pm2_5_atm
    - pm2_5_atm_b
    - p_5_0_um
    - p_5_0_um_b
    - p_10_0_um
    - p_10_0_um_b
    - pm10_0_atm
    - pm10_0_atm_b
    - pm2.5_aqi
    - pm2.5_aqi_b
- platform: template
  sensors:
   # purpleair_memory:
   #   friendly_name: "PurpleAir Memory"
   #   value_template: '{{ states.sensor.purpleair.attributes["Mem"] }}'
   # purpleair_adc:
   #   friendly_name: "PurpleAir ADC"
   #   value_template: '{{ states.sensor.purpleair.attributes["Adc"] }}'
    purpleair_uptime:
      friendly_name: "PA Uptime"
      value_template: '{{ states.sensor.purpleair.attributes["uptime"] }}'
      unit_of_measurement: "seconds"
   # purpleair_memory_fragmentation:
   #   friendly_name: "PurpleAir Memory Fragmentation"
   #   value_template: '{{ states.sensor.purpleair.attributes["memfrag"] }}'
    purpleair_wifi_rssi:
      friendly_name: "PA Wifi RSSI"
      value_template: '{{ states.sensor.purpleair.attributes["rssi"] }}'
      device_class: signal_strength
    purpleair_temp:
      friendly_name: "PA Temperature"
      value_template: '{{ states.sensor.purpleair.attributes["current_temp_f"] }}'
      unit_of_measurement: "F"
      device_class: temperature
    purpleair_humidity:
      friendly_name: "PA Humidity"
      value_template: '{{ states.sensor.purpleair.attributes["current_humidity"] }}'
      unit_of_measurement: "%"
      device_class: humidity
    purpleair_dewpoint:
      friendly_name: "PA Dewpoint"
      value_template: '{{ states.sensor.purpleair.attributes["current_dewpoint_f"] }}'
      unit_of_measurement: "F"
      device_class: temperature
    purpleair_pressure:
      friendly_name: "PA Pressure"
      value_template: '{{ states.sensor.purpleair.attributes["pressure"] }}'
      unit_of_measurement: "mbar"
      device_class: pressure
    purpleair_aqi_a:
      friendly_name: "PA AirQuality A"
      value_template: '{{ states.sensor.purpleair.attributes["pm2.5_aqi"] }}'
      unit_of_measurement: "AQI"
    purpleair_aqi_b:
      friendly_name: "PA AirQuality B"
      value_template: '{{ states.sensor.purpleair.attributes["pm2.5_aqi_b"] }}'
      unit_of_measurement: "AQI"
    purpleair_p_0_3_um_a:
      friendly_name: "PA .3um Partical Count A"
      value_template: '{{ states.sensor.purpleair.attributes["p_0_3_um"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_0_3_um_b:
      friendly_name: "PurpleAir .3um Partical Count B"
      value_template: '{{ states.sensor.purpleair.attributes["p_0_3_um_b"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_0_5_um_a:
      friendly_name: "PA .5um Partical Count A"
      value_template: '{{ states.sensor.purpleair.attributes["p_0_5_um"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_0_5_um_b:
      friendly_name: "PA .5um Partical Count B"
      value_template: '{{ states.sensor.purpleair.attributes["p_0_5_um_b"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_1_0_um_a:
      friendly_name: "PA 1.0um Partical Count A"
      value_template: '{{ states.sensor.purpleair.attributes["p_1_0_um"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_1_0_um_b:
      friendly_name: "PA 1.0um Partical Count B"
      value_template: '{{ states.sensor.purpleair.attributes["p_1_0_um_b"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_2_5_um_a:
      friendly_name: "PA 2.5um Partical Count A"
      value_template: '{{ states.sensor.purpleair.attributes["p_2_5_um"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_2_5_um_b:
      friendly_name: "PA 2.5um Partical Count B"
      value_template: '{{ states.sensor.purpleair.attributes["p_2_5_um_b"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_5_0_um_a:
      friendly_name: "PA 5.0um Partical Count A"
      value_template: '{{ states.sensor.purpleair.attributes["p_5_0_um"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_5_0_um_b:
      friendly_name: "PA 5.0um Partical Count B"
      value_template: '{{ states.sensor.purpleair.attributes["p_5_0_um_b"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_10_0_um_a:
      friendly_name: "PA 10.0um Partical Count A"
      value_template: '{{ states.sensor.purpleair.attributes["p_10_0_um"] }}'
      unit_of_measurement: "um/dl"
    purpleair_p_10_0_um_b:
      friendly_name: "PA 10.0um Partical Count B"
      value_template: '{{ states.sensor.purpleair.attributes["p_10_0_um_b"] }}'
      unit_of_measurement: "um/dl"
    purpleair_pm10_atm_a:
      friendly_name: "PA 10.0um Mass A"
      value_template: '{{ states.sensor.purpleair.attributes["pm10_0_atm"] }}'
      unit_of_measurement: "ug/m3"
    purpleair_pm10_atm_b:
      friendly_name: "PA 10.0um Mass B"
      value_template: '{{ states.sensor.purpleair.attributes["pm10_0_atm_b"] }}'
      unit_of_measurement: "ug/m3"
    purpleair_pm1_atm_a:
      friendly_name: "PA 1.0um Mass A"
      value_template: '{{ states.sensor.purpleair.attributes["pm1_0_atm"] }}'
      unit_of_measurement: "ug/m3"
    purpleair_pm1_atm_b:
      friendly_name: "PA 1.0um Mass B"
      value_template: '{{ states.sensor.purpleair.attributes["pm1_0_atm_b"] }}'
      unit_of_measurement: "ug/m3"
    purpleair_pm2_5_atm_a:
      friendly_name: "PA 2.5um Mass A"
      value_template: '{{ states.sensor.purpleair.attributes["pm2_5_atm"] }}'
      unit_of_measurement: "ug/m3"
    purpleair_pm2_5_atm_b:
      friendly_name: "PA 2.5um Mass B"
      value_template: '{{ states.sensor.purpleair.attributes["pm2_5_atm_b"] }}'
      unit_of_measurement: "ug/m3"
    purpleair_description:
      friendly_name: 'PA AQI Description'
      unique_id: purpleair_description
      value_template: >
        {% set aqi = states('sensor.purpleair_aqi_a')|float(default=0) %}
        {% if aqi >= 401.0 %}
          Very Hazardous
        {% elif aqi >= 301.0 %}
          Hazardous
        {% elif aqi >= 201.0 %}
          Very Unhealthy
        {% elif aqi >= 151.0 %}
          Unhealthy
        {% elif aqi >= 101.0 %}
          Unhealthy for Sensitive Groups
        {% elif aqi >= 51.0 %}
          Moderate
        {% elif aqi >= 0.0 %}
          Good
        {% else %}
          {{states('sensor.purpleair_description')}}
        {% endif %}
      entity_id: sensor.purpleair
      #availability_template: '{{ not is_state("sensor.purpleair", "unavailable") }}'

1 Like

Wow, I never realized that the sensor had dew point and pressure.

But I went down a bit of a rabbit hole. I had a hunch that the built-in dew point was based on the raw values without the suggested corrections. I did some math and confirmed that to be the case. I also realized that the simplified formula was way off for the currently dry air I’m experiencing. So, I implemented a more complete formula. I don’t know if this is right, but it seems to produce a reasonable value. :man_shrugging:

Updates in GitHub.

There has been a lot of additions to the device class selections since I set mine up. None of the air quality related items where there before.

This may give you some ideas:
https://github.com/GlennGoddard/CanvasGaugeBackgrounds

I would also add all the sensors to your yaml, even if you don’t use them, just to help keep track of them. I’m referring to wifi signal, memory fragmentation, etc…; I have them and just comment out the ones I don’t see a need for.

I have a Weather Station and I find that that, at least for my location, the dewpoint from PurpleAir and my simplified formula work fairly well.
I also like the description of air quality, which is at the bottom of my yaml, this makes Text-To-Speech (TTS) and phone notifications more useful. If lights are on in a room then the speaker will anouce any change in air quality from one band to another. Everything is self hosted, my speakers are just RPi with a USB speaker running KODI. I use MaryTTS for the speech and this setup has been working great for a few years now.

I pulled some of my calculations out of my PurpleAir setup once I got my Tempest Weather Station but you might find some useful calculations here:
https://github.com/briis/hass-weatherflow2mqtt

I’m attempting to locally import multiple PurpleAir’s into HA, but I seem to be unable to figure out why this code is not working. The code works fine if I try to import just the humidity from one sensor, but if I try adding a second one like shown below, it fails to work.

  - platform: rest
    name: "PurpleAir"
    resource: http://10.140.150.160/json?live=true
    value_template: "91:yb:at:12:42:ef"
    json_attributes:
      - current_humidity

  - platform: template
    sensors:
      purpleair_humidity:
        friendly_name: "PurpleAir Indoor Humidity"
        value_template: '{{ states.sensor.purpleair.attributes["current_humidity"] }}'
        unit_of_measurement: "%"

  - platform: rest
    name: "PurpleAir"
    resource: http://10.140.150.161/json?live=true
    value_template: "48:3g:da:88:1c:46"
    json_attributes:
      - current_humidity

  - platform: template
    sensors:
      purpleair_humidity:
        friendly_name: "PurpleAir Outdoor Humidity"
        value_template: '{{ states.sensor.purpleair.attributes["current_humidity"] }}'
        unit_of_measurement: "%"

Does anyone have any ideas of how I can import two PurpleAir’s into HA?

Use the example 3 posts up and just change the name and the IP address to differentiate.

I’m not sure about your value_template and you have reuse the same name a few times.

I ultimately ended up using Home Assistant’s NodeRed integration to get both sensors working. I spent many countless hours setting this NodeRed configuration up so I figured I would post it here to save someone the time in the future if they plan to use something like this.

Here is a complete list for the outdoor PurpleAir, the indoor PurpleAir (which I recieved today) had slightly different ‘sensors’.

Uncomment if desired. I have yet to figure out all the items.

### PurpleAir Local Outside
- platform: rest
  name: 'PurpleAir'
  resource: http://10.74.1.198/json?live=false
  value_template: '{{ value_json.SensorId }}'
  scan_interval: 120
  json_attributes:
    #- SensorId
    #- DateTime
    #- Geo
    #- Mem
    #- memfrag
    #- memfb
    #- memcs
    #- Id
    #- lat
    #- log
    #- Adc
    #- loggingrate
    #- place
    #- version
    - uptime
    - rssi
    #- period
    #- httpsuccess
    #- httpsends
    #- hardwareversion
    #- hardwarediscovered
    - current_temp_f
    - current_humidity
    - current_dewpoint_f
    - pressure
    - p25aqic_b
    - pm2.5_aqi_b
    - pm1_0_cf_1_b
    - p_0_3_um_b
    - pm2_5_cf_1_b
    - p_0_5_um_b
    - pm10_0_cf_1_b
    - p_1_0_um_b
    - pm1_0_atm_b
    - p_2_5_um_b
    - pm2_5_atm_b
    - p_5_0_um_b
    - pm10_0_atm_b
    - p_10_0_um_b
    - p25aqic
    - pm2.5_aqi
    - pm1_0_cf_1
    - p_0_3_um
    - pm2_5_cf_1
    - p_0_5_um
    - pm10_0_cf_1
    - p_1_0_um
    - pm1_0_atm
    - p_2_5_um
    - pm2_5_atm
    - p_5_0_um
    - pm10_0_atm
    - p_10_0_um
    #- pa_latency
    #- response
    #- response_date
    #- latency
    #- key1_response
    #- key1_response_date
    #- key1_count
    #- ts_latency
    #- key2_response
    #- key2_response_date
    #- key2_count
    #- ts_s_latency
    #- key1_response_b
    #- key1_response_date_b
    #- key1_count_b
    #- ts_latency_b
    #- key2_response_b
    #- key2_response_date_b
    #- key2_count_b
    #- ts_s_latency_b
    #- wlstate
    #- status_0
    #- status_1
    #- status_2
    #- status_3
    #- status_4
    #- status_5
    #- status_6
    #- status_7
    #- status_8
    #- status_9
    #- ssid

Below are the Indoor PA options:

    #- SensorId
    #- DateTime
    #- Geo
    #- Mem
    #- memfrag
    #- memfb
    #- memcs
    #- Id
    #- lat
    #- lon
    #- Adc
    #- loggingrate
    #- place
    #- version
    - uptime
    - rssi
    #- period
    #- httpsuccess
    #- httpsends
    #- hardwareversion
    #- hardwarediscovered
    - current_temp_f
    - current_humidity
    - current_dewpoint_f
    - pressure
    #- p25aqic
    - pm2.5_aqi
    - pm1_0_cf_1
    - p_0_3_um
    - pm2_5_cf_1
    - p_0_5_um
    - pm10_0_cf_1
    - p_1_0_um
    - pm1_0_atm
    - p_2_5_um
    - pm2_5_atm
    - p_5_0_um
    - pm10_0_atm
    - p_10_0_um
    #- pa_latency
    #- key1_response
    #- key1_response_date
    #- key1_count
    #- ts_latency
    #- key2_response
    #- key2_response_date
    #- key2_count
    #- ts_s_latency
    #- key1_response_b
    #- key1_response_date_b
    #- key1_count_b
    #- ts_latency_b
    #- wlstate
    #- status_0
    #- status_1
    #- status_2
    #- status_3
    #- status_4
    #- status_5
    #- status_7
    #- status_8
    #- status_9
    #- ssid
2 Likes

To anyone interested in purchasing an indoor and an outdoor sensor, I would highly recommend purchasing two outdoor sensors instead of an indoor sensor and an outdoor sensor. The indoor sensor is not nearly as accurate as the outdoor sensor.

I’ll be returning my indoor sensor and replacing it with an outdoor sensor. So I’ll have 2 outdoor sensors, but one will be used indoors and one will be used outdoors.

Source relating to the inferiority of the indoor sensor: http://www.aqmd.gov/docs/default-source/aq-spec/summary/purple-air-pa-i-indoor---summary-report.pdf?sfvrsn=20

1 Like

Sure, post this the day after I get my indoor.

1 Like

No worries GlennHA, I did the same thing. I personally contacted PurpleAir and they were more than willing to exchange the indoor sensor for the outdoor sensor. It came out to about an extra $60 that I had to pay them for the price difference between the two sensors which I believe is completely worth it.

I’ve finally got the PurpleAir Outdoor sensor incorporated into HA in a very nice manner that looks decent.

The PM2.5 colors are pulled directly from PurpleAir’s JSON file as well so it updates the color-codes perfectly.

If anyone is interested in using this, here is the HA Lovelave code (requires my NodeRed code to pull in the data as I linked above):

type: vertical-stack
title: PurpleAir Outdoor Sensor's
cards:
  - type: horizontal-stack
    cards:
      - entity: sensor.purpleair_outdoor_aqi_2_5_120s_moving_average_a
        color: black;
        scale: 35px
        title: PM2.5 (A)
        type: custom:bignumber-card
        noneCardClass: custom:bignumber-card
        style: |
          ha-card {
            background: {{ states('sensor.purpleair_outdoor_pm_2_5_aqi_rgb_color_a') }}

                         }  
      - entity: sensor.purpleair_outdoor_aqi_2_5_120s_moving_average_b
        color: black;
        scale: 35px
        title: PM2.5 (B)
        type: custom:bignumber-card
        noneCardClass: custom:bignumber-card
        style: |
          ha-card {
            background: {{ states('sensor.purpleair_outdoor_pm_2_5_aqi_rgb_color_b') }}

                         }  
  - type: horizontal-stack
    cards:
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: PM1.0 (µg/m3)
          - entity: sensor.purpleair_outdoor_1_0um_mass_a
            title: 'Sensor: A'
            scale: 20px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_1_0um_mass_b
            title: 'Sensor: B'
            scale: 20px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: PM2.5 (µg/m3)
          - entity: sensor.purpleair_outdoor_2_5um_mass_a
            title: 'Sensor: A'
            scale: 20px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_2_5um_mass_b
            title: 'Sensor: B'
            scale: 20px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: PM10 (µg/m3)
          - entity: sensor.purpleair_outdoor_10_0um_mass_a
            title: 'Sensor: A'
            scale: 20px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_10_0um_mass_b
            title: 'Sensor: B'
            scale: 20px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
  - type: custom:stack-in-card
    cards:
      - type: custom:button-card
        name: Partical Counter (particles/deciliter)
  - type: horizontal-stack
    cards:
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: '>=0.3µm'
          - entity: sensor.purpleair_outdoor_3um_partical_count_a
            title: 'Sensor: A'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_3um_partical_count_b
            title: 'Sensor: B'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: '>=0.5µm'
          - entity: sensor.purpleair_outdoor_5um_partical_count_a
            title: 'Sensor: A'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_5um_partical_count_b
            title: 'Sensor: B'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: '>=1.0µm'
          - entity: sensor.purpleair_outdoor_1_0um_partical_count_a
            title: 'Sensor: A'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_1_0um_partical_count_b
            title: 'Sensor: B'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: '>=2.5µm'
          - entity: sensor.purpleair_outdoor_2_5um_partical_count_a
            title: 'Sensor: A'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_2_5um_partical_count_b
            title: 'Sensor: B'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: '>=5.0µm'
          - entity: sensor.purpleair_outdoor_5_0um_partical_count_a
            title: 'Sensor: A'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_5_0um_partical_count_b
            title: 'Sensor: B'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
      - type: custom:stack-in-card
        cards:
          - type: custom:button-card
            name: '>=10µm'
          - entity: sensor.purpleair_outdoor_10_0um_partical_count_a
            title: 'Sensor: A'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card
          - entity: sensor.purpleair_outdoor_10_0um_partical_count_b
            title: 'Sensor: B'
            scale: 12px
            type: custom:bignumber-card
            hideunit: true
            noneCardClass: custom:bignumber-card

1 Like

I wonder if there is a way to remove some majorTicks but still have the colors fitting to the ticks (and as I assume the pointer pointing to the correct values)?
My idea is to strech the lower intervals and compress the bigger intervals because nobody cares if it is 360 or 390.
grafik

type: custom:mod-card
card_mod:
  class: top-level-graph
card:
  type: custom:canvas-gauge-card
  entity: sensor.pm2_5
  gauge:
    type: radial-gauge
    title: Feinstaub 2.5
    width: 230
    height: 230
    minValue: 0
    maxValue: 500
    startAngle: 40
    ticksAngle: 280
    valueBox: true
    majorTicks:
      - '0'
      - '50'
      - '100'
      - '150'
      - '200'
      - '300'
      - '500'
    minorTicks: 5
    strokeTicks: true
    highlights:
      - from: 0
        to: 50
        color: rgba(104, 225, 67, .75)
      - from: 50
        to: 100
        color: rgba(255, 255, 85, .75)
      - from: 100
        to: 150
        color: rgba(239, 133, 51, .75)
      - from: 150
        to: 200
        color: rgba(234, 51, 36, .75)
      - from: 200
        to: 300
        color: rgba(140, 26, 75, .75)
      - from: 300
        to: 500
        color: rgba(115, 20, 37, .75)
    borders: 'no'
    needleType: arrow
    needleWidth: 4
    needleCircleSize: 7
    needleCircleOuter: true
    needleCircleInner: false
    animationDuration: 1500
    animationRule: linear
    valueBoxBorderRadius: 10
    colorValueBoxRect: '#222'
    colorValueBoxRectEnd: '#333'
    valueDec: 0
    valueInt: null

You can change the major ticks and change the maxValue to anything you want.

Did so.
But why are the colors not fitting to the numbers or vice versa?
And the pointer seems to be wrong, too.
In the picture above the 50 is in the yellow area although it should be green.

      - from: 0
        to: 50
        color: rgba(104, 225, 67, .75)
``

The major ticks must be equally apart, so you can’t skip numbers. It would appear you can in the documentation, but in practice I’ve not ever been able to get it to work without equidistant numbers.

Alright…please go easy on me for asking this question, but I’ve read through most of the thread and I’m still a little confused. Do I need to purchase a sensor to gain access to the API for purpleair? Or is it possible to obtain the information needed to create a Air quality card without a sensor? I checked their map and see several sensors right in my area, is there a way to tap into those? Sorry…I’m just confused! lol

I have no Purple devices. I read from three Purple devices triangulated around my locations every 15 minutes and average these to give me a sense of AQI in area.
Good hunting!

1 Like

thanks for the quick response! Do I need an API key to grab the data?

For the way I went, no api key needed. I wrote a small python program that I run in docker, based on the post listed below. My code just loops thru the list of purple sensors I have selected every 15 minutes, extracts the data from each sensor, then creates a MQTT message for each sensor. I then consume these in Home Assistant as MQTT sensors. Runs very solid for months continuously, however some of the Purple sensors do seem to go off line for periods. I have found the data useful. Good hunting!

1 Like