PurpleAir Air Quality Sensor

Yep,
Listen to what I mean, not what I say.

I liked your graph and tweaked it for my liking.
I’m debating between these two:


I think I like the sudden color change vice the gradual since it will display orange if it is on the high end of the green band or low of the yellow; and since the ‘orange’ band is actually above the yellow…it makes it slightly confusing.

Sudden Change

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - entities:
          - entity: sensor.purpleair_description
            name: Air Quality is
            icon: blank
        show_header_toggle: false
        type: entities
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_aqi
    unit: AQI
    name: AQI from Purple (24h)
    icon: 'mdi:chemical-weapon'
    show:
      fill: true
      legend: false
      labels: false
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds:
      - value: 0
        color: '#00e500'
      - value: 50
        color: '#00e500'
      - value: 50.5
        color: '#fffe0a'
      - value: 100
        color: '#fffe0a'
      - value: 100.5
        color: '#fe7f03'
      - value: 150
        color: '#fe7f03'
      - value: 150.5
        color: '#ff0200'
      - value: 200
        color: '#ff0200'
      - value: 200.5
        color: '#98004c'
      - value: 300.5
        color: '#7f0024'

Gradual Change

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - entities:
          - entity: sensor.purpleair_description
            name: Air Quality is
            icon: blank
        show_header_toggle: false
        type: entities
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_aqi
    unit: AQI
    name: AQI from Purple (24h)
    icon: 'mdi:chemical-weapon'
    show:
      fill: true
      legend: false
      labels: false
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds:
      - value: 0
        color: '#00e500'
      - value: 50.5
        color: '#fffe0a'
      - value: 100.5
        color: '#fe7f03'
      - value: 150.5
        color: '#ff0200'
      - value: 200.5
        color: '#98004c'
      - value: 300.5
        color: '#7f0024'

I am still looking for a good alternative to what I have for the Air Quality Description. If anyone knows a card that I can change color for the text based on value, I would be thankful. I did not see a way to change the ‘title’ dynamically or via a variable.

3 Likes

I’ve been exploring this lovelace custom element for a different use, but is very powerful. I believe I saw some folks using this and another custom element for color change of lovelace element. If the color of an element is specified as an attribute, using this custom element you should be able to change it based on any HA sensor attribute. Working with this custom element is complex, don’t get frustrated.

Here is an example I am doing to dynamically modify the standard glance card to change three attributes of each element based on sensor attributes:

  1. change card name
  2. change card icon
  3. change card tap action
     - type: 'custom:config-template-card'
        variables:
          - states['sensor.pulsepoint_pasa'].state
        entities:
          - sensor.pulsepoint_pasa
        card:
          type: glance
          title: "Pasadena Fire Department"
          entities:
            - entity: sensor.pulsepoint_pasa
              name: "${ states['sensor.pulsepoint_pasa'].attributes.time_local + ' ' + states['sensor.pulsepoint_pasa'].attributes.address }"
              icon: "${states['sensor.pulsepoint_pasa'].attributes.close_to_key_location ? 'mdi:home-alert-outline' : 'mdi:eye-outline'}"
              tap_action:
                action: url
                url_path: "${ 'https://maps.google.com/maps?&z=10&mrt=yp&t=k&layer=t&q=' + states['sensor.pulsepoint_pasa'].attributes.latitude + '+' + states['sensor.pulsepoint_pasa'].attributes.longitude }"

My most reset attempt:

type: vertical-stack
cards:
  - type: entity
    entity: sensor.purpleair_description
    name: Air Quality is
    icon: blank
    show_header_toggle: false
  - type: 'custom:canvas-gauge-card'
    card_height: 300
    entity: sensor.purpleair_aqi
    name: ''
    gauge:
      type: radial-gauge
      title: AQI
      width: 300
      height: 300
      minValue: 0
      maxValue: 500
      startAngle: 40
      ticksAngle: 280
      valueBox: true
      majorTicks:
        - '0'
        - '50'
        - '100'
        - '150'
        - '200'
        - '250'
        - '300'
        - '350'
        - '400'
        - '450'
        - '500'
      minorTicks: 10
      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: 0
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_aqi
    unit: AQI
    name: AQI from Purple (24h)
    icon: 'mdi:chemical-weapon'
    show:
      fill: true
      legend: false
      labels: false
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds:
      - value: 0
        color: '#00e500'
      - value: 50
        color: '#00e500'
      - value: 50.5
        color: '#fffe0a'
      - value: 100
        color: '#fffe0a'
      - value: 100.5
        color: '#fe7f03'
      - value: 150
        color: '#fe7f03'
      - value: 150.5
        color: '#ff0200'
      - value: 200
        color: '#ff0200'
      - value: 200.5
        color: '#98004c'
      - value: 300.5
        color: '#7f0024'

I have tried styles and I just can’t get anything to change color of the “Air Quality is …” part. Based on what I can find it might be because I am using the Vertical Stack Card. I have also attempted to insert the state of the sensor into the title of the graph with no luck.

1 Like

Thoughts on this one?
I think this is my final.
Icons change based on air quality.
Font color inverts from black to white as levels get higher.
Colors between the three sections are more closely matched now.

type: vertical-stack
cards:
  - type: 'custom:button-text-card'
    title: |
      [[[ return states["sensor.purpleair_description"].state ]]]
    subtitle: Air Quaility
    icon_size: 55
    icon_color: |
      [[[
        if(states["sensor.purpleair_aqi"].state >= 300){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 200){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 150){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 100){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 50){
          return 'black';
        } else{
          return 'black';
        }
      ]]]
    icon: |
      [[[
        if(states["sensor.purpleair_aqi"].state >= 300){
          return 'mdi:emoticon-dead';
        } else if(states["sensor.purpleair_aqi"].state >= 200){
          return 'mdi:emoticon-cry';
        } else if(states["sensor.purpleair_aqi"].state >= 150){
          return 'mdi:emoticon-sad';
        } else if(states["sensor.purpleair_aqi"].state >= 100){
          return 'mdi:emoticon-confused';
        } else if(states["sensor.purpleair_aqi"].state >= 50){
          return 'mdi:emoticon-neutral';
        } else{
          return 'mdi:emoticon-excited';
        }
      ]]]
    font_color: |
      [[[
        if(states["sensor.purpleair_aqi"].state >= 300){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 200){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 150){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 100){
          return 'white';
        } else if(states["sensor.purpleair_aqi"].state >= 50){
          return 'black';
        } else{
          return 'black';
        }
      ]]]
    large: true
    background_color: |
      [[[
        if(states["sensor.purpleair_aqi"].state >= 300){
          return '#731425';
        } else if(states["sensor.purpleair_aqi"].state >= 200){
          return '#8C1A4B';
        } else if(states["sensor.purpleair_aqi"].state >= 150){
          return '#EA3324';
        } else if(states["sensor.purpleair_aqi"].state >= 100){
          return '#EF8533';
        } else if(states["sensor.purpleair_aqi"].state >= 50){
          return '#FFFF55';
        } else{
          return '#68FF43';
        }
      ]]]
  - type: 'custom:canvas-gauge-card'
    card_height: 300
    entity: sensor.purpleair_aqi
    name: ''
    gauge:
      type: radial-gauge
      title: AQI
      width: 300
      height: 300
      minValue: 0
      maxValue: 500
      startAngle: 40
      ticksAngle: 280
      valueBox: true
      majorTicks:
        - '0'
        - '50'
        - '100'
        - '150'
        - '200'
        - '250'
        - '300'
        - '350'
        - '400'
        - '450'
        - '500'
      minorTicks: 10
      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: 0
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_aqi
    unit: AQI
    name: ' '
    icon: blank
    show:
      fill: true
      legend: false
      labels: false
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds:
      - value: 0
        color: '#68FF43'
      - value: 50
        color: '#68FF43'
      - value: 50.5
        color: '#FFFF55'
      - value: 100
        color: '#FFFF55'
      - value: 100.5
        color: '#EF8533'
      - value: 150
        color: '#EF8533'
      - value: 150.5
        color: '#EA3324'
      - value: 200
        color: '#EA3324'
      - value: 200.5
        color: '#8C1A4B'
      - value: 300
        color: '#8C1A4B'
      - value: 300.5
        color: '#731425'
2 Likes

https://community.home-assistant.io/t/lovelace-button-text-card/210687/8?u=glennha

Examples of each air quality.

3 Likes

Sheesh, we’re all doing the same things. Life in CA I guess? So I have a pms5300 on an esphome and it works great. I get the raw values In the log but am struggling with how to get to the AQI conversion. My esphome has a sensor as follows:

  - platform: pmsx003
    type: PMSX003
    pm_1_0:
      filters:
        #- throttle: 60s
        - delta: 2.0
        - sliding_window_moving_average:
            window_size: 45
      name: sensornode_airquality_pm_1_0
      #name: "Particulate Matter <1.0µm Concentration"
    pm_2_5:
      filters:
        #- throttle: 60s
        - delta: 2.0
        - sliding_window_moving_average:
            window_size: 45
        name: sensornode_airquality_pm_2_5

sensornode_airquality_pm_2_5 Contains the 2.5pm value, Now, how do I pump that result into an AQI conversion calculator? I’m so Close but so confused! A template? In my sensor.yaml file? I see your reference to the esphome mods for the HM3301 but I don’t know how to use that.

UPDATE: I figured it out after sleeping on it. Create a new sensor based on a Template:
sensornode_airquality_pm_2_5 is defined in esphome.

Boy, is yaml friggin fussy. WTH is there no line #'s for errors in the template “developer” tab??? you’d think that would be painfully obvious to anyone who has ever tried to debug more than 3 lines of template code!!!

#sensor.yaml:
#
#-----------------------------
  - platform: template
    sensors:
      inside_aqi:
        friendly_name: 'Inside AQI Calc'
        value_template: >-
          {% macro calcAQI(Cp, Ih, Il, BPh, BPl) -%}
          {{ (((Ih - Il)/(BPh - BPl)) * (Cp - BPl) + Il)|round }}
          {%- endmacro %}
          {% if (states('sensor.sensornode_airquality_pm_2_5')|float) > 1000 %}
           invalid
          {% elif (states('sensor.sensornode_airquality_pm_2_5')|float) > 350.5 %}
            {{ calcAQI((states('sensor.sensornode_airquality_pm_2_5')|float), 500.0, 401.0, 500.0, 350.5) }}
          {% elif (states('sensor.sensornode_airquality_pm_2_5')|float) > 250.5 %}
            {{ calcAQI((states('sensor.sensornode_airquality_pm_2_5')|float), 400.0, 301.0, 350.4, 250.5) }}
          {% elif (states('sensor.sensornode_airquality_pm_2_5')|float) > 150.5 %}
            {{ calcAQI((states('sensor.sensornode_airquality_pm_2_5')|float), 300.0, 201.0, 250.4, 150.5) }}
          {% elif (states('sensor.sensornode_airquality_pm_2_5')|float) > 55.5 %}
            {{ calcAQI((states('sensor.sensornode_airquality_pm_2_5')|float), 200.0, 151.0, 150.4, 55.5) }}
          {% elif (states('sensor.sensornode_airquality_pm_2_5')|float) > 35.5 %}
            {{ calcAQI((states('sensor.sensornode_airquality_pm_2_5')|float), 150.0, 101.0, 55.4, 35.5) }}
          {% elif (states('sensor.sensornode_airquality_pm_2_5')|float) > 12.1 %}
            {{ calcAQI((states('sensor.sensornode_airquality_pm_2_5')|float), 100.0, 51.0, 35.4, 12.1) }}
          {% elif (states('sensor.sensornode_airquality_pm_2_5')|float) >= 0.0 %}
            {{ calcAQI((states('sensor.sensornode_airquality_pm_2_5')|float), 50.0, 0.0, 12.0, 0.0) }}
          {% else %}
            invalid
          {% endif %}
        entity_id: sensor.inside_aqi

this defines a new sensor called sensor.inside_aqi that you can use in lovelace
image

type: vertical-stack
cards:
  - type: entities
    entities:
      - sensor.sensornode_airquality_humidity
      - sensor.sensornode_airquality_pressure
      - sensor.sensornode_airquality_temp
      - sensor.sensornode_airquality_pm_2_5
      - sensor.inside_aqi
title: Inside weather
1 Like

This is my first rest sensor, so perhaps this is unrelated, but I’m receiving empty responses from PurpleAir:

2020-11-08 12:21:57 ERROR (MainThread) [homeassistant.components.rest.data] Error fetching data: https://www.purpleair.com/json?show=55831 failed with 
2020-11-08 12:21:57 DEBUG (MainThread) [homeassistant.components.rest.sensor] Data fetched from resource: None
2020-11-08 12:21:57 WARNING (MainThread) [homeassistant.components.rest.sensor] Empty reply found when expecting JSON data

I’ve tried the URLs in the various examples in this thread to similar effect, as well as HTTP instead of HTTPS. All the URLs output the expected results when used with curl.

Is there some setup here I’m missing?

- platform: rest
  name: purpleair
  resource: https://www.purpleair.com/json?show=55831
  scan_interval: 600
  device_class: timestamp
  value_template: >
    {{ state_attr('sensor.purpleair', 'results')[0]['LastSeen'] }}
  json_attributes:
    - results

I finally received my outdoor sensor, thanks for posting your rest sensor. Coping yours saved me a plethora of time. I did make some modifications but the bulk is a copy of yours.

I added the description template and removed memory info. I’m debating making an average of the A & B but probably won’t.

I am in the process of DewPoint displays that mimic the AQI displays (graphs).
I just need to add the following to my config when I get the chance:

- platform: template
  sensors:
    purpleair_dewpoint_description:
      friendly_name: 'PurpleAir DewPoint Description'
      value_template: >
        {% if (states('sensor.purpleair_dewpoint')|float) >= 75.5 %}
          Miserable
        {% elif (states('sensor.purpleair_dewpoint')|float) >= 70.5 %}
          Oppressive
        {% elif (states('sensor.purpleair_dewpoint')|float) >= 65.5 %}
          Uncomfortable
        {% elif (states('sensor.purpleair_dewpoint')|float) >= 60.5 %}
          Getting Sticky
        {% elif (states('sensor.purpleair_dewpoint')|float) >= 55.5 %}
          Comfortable
        {% elif (states('sensor.purpleair_dewpoint')|float) >= 0.0 %}
          Pleasant
        {% else %}
          undefined
        {% endif %}
      entity_id: sensor.purpleair

I’m still looking at which icons to associate with each category but it will probably be close to what I use for Air Quality. (I am open to suggestions).

The following is mostly done, only the top ‘Comfort Level’ is incomplete awaiting the above yaml.


I will post the finish layout yaml when complete.

Comfort Level Indicator:

https://community.home-assistant.io/t/dewpoint-comfort-level-display/253344

I may be taking this to far…
I have graphs for every particle level, more than I can fit on one screen at a time.
The color change levels come from the PurpleAir Map.

type: vertical-stack
cards:
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_p_0_3_um_a
    unit: PM 0.3
    name: Ultrafine Particles 0.3 (A) (24hr)
    icon: 'mdi:grain'
    hour24: true
    show:
      fill: true
      legend: false
      labels: false
      state: true
      icon: true
      extrema: true
      average: true
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds_transition: hard
    color_thresholds:
      - value: 0
        color: '#68FF43'
      - value: 1000
        color: '#FFFF55'
      - value: 3000
        color: '#EF8533'
      - value: 10000
        color: '#EA3324'
      - value: 20000
        color: '#8C1A4B'
      - value: 30000
        color: '#731425'
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_p_0_5_um_a
    unit: PM 0.5
    name: Ultrafine Particles 0.5 (A)(24hr)
    icon: 'mdi:grain'
    hour24: true
    show:
      fill: true
      legend: false
      labels: false
      state: true
      icon: true
      extrema: true
      average: true
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds_transition: hard
    color_thresholds:
      - value: 0
        color: '#68FF43'
      - value: 1000
        color: '#FFFF55'
      - value: 2000
        color: '#EF8533'
      - value: 4000
        color: '#EA3324'
      - value: 8000
        color: '#8C1A4B'
      - value: 16000
        color: '#731425'
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_p_1_0_um_a
    unit: PM 1
    name: Ultrafine Particles 1 (A) (24hr)
    icon: 'mdi:grain'
    hour24: true
    show:
      fill: true
      legend: false
      labels: false
      state: true
      icon: true
      extrema: true
      average: true
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds_transition: hard
    color_thresholds:
      - value: 0
        color: '#68FF43'
      - value: 100
        color: '#FFFF55'
      - value: 200
        color: '#EF8533'
      - value: 400
        color: '#EA3324'
      - value: 600
        color: '#8C1A4B'
      - value: 800
        color: '#731425'
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_p_2_5_um_a
    unit: PM 2.5
    name: Fine Particles 2.5 (A) (24hr)
    icon: 'mdi:grain'
    hour24: true
    show:
      fill: true
      legend: false
      labels: false
      state: true
      icon: true
      extrema: true
      average: true
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds_transition: hard
    color_thresholds:
      - value: 0
        color: '#68FF43'
      - value: 8
        color: '#FFFF55'
      - value: 16
        color: '#EF8533'
      - value: 24
        color: '#EA3324'
      - value: 40
        color: '#8C1A4B'
      - value: 60
        color: '#731425'
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_p_5_0_um_a
    unit: PM 5
    name: Coarse Particles 5 (A) (24hr)
    icon: 'mdi:grain'
    hour24: true
    show:
      fill: true
      legend: false
      labels: false
      state: true
      icon: true
      extrema: true
      average: true
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds_transition: hard
    color_thresholds:
      - value: 0
        color: '#68FF43'
      - value: 4
        color: '#FFFF55'
      - value: 8
        color: '#EF8533'
      - value: 12
        color: '#EA3324'
      - value: 20
        color: '#8C1A4B'
      - value: 30
        color: '#731425'
  - type: 'custom:mini-graph-card'
    entities:
      - sensor.purpleair_p_10_0_um_a
    unit: PM 10
    name: Coarse Particles 10 (A) (24hr)
    icon: 'mdi:grain'
    hour24: true
    show:
      fill: true
      legend: false
      labels: false
      state: true
      icon: true
      extrema: true
      average: true
      name: true
      points: false
      name_adaptive_color: true
      icon_adaptive_color: true
      show_legend: false
    font_size: 75
    line_width: 3
    points_per_hour: 4
    hours_to_show: 24
    color_thresholds_transition: hard
    color_thresholds:
      - value: 0
        color: '#68FF43'
      - value: 2
        color: '#FFFF55'
      - value: 4
        color: '#EF8533'
      - value: 6
        color: '#EA3324'
      - value: 10
        color: '#8C1A4B'
      - value: 15
        color: '#731425'

would this PurpleAir detect cigarettes being smoked within 10feet?

The sensor sees a PM2.5 spike when we use the stove (with vent exhaust hood running) which is 25’ away from it. Here is my data from last night:

As you can see, my spouse cooked a frittata at about 5:30pm. No idea what the slight bump is at 7:30pm.

So – yeah, assuming the wind wasn’t blowing the smoke away, I’d think a cigarette would be easy to see in the data. What would be more challenging is to say definitively “this is a cigarette!” as opposed to the other zillion sources of PM2.5 spikes.

1 Like

Always modifying my views. I have some tweaking to do, and can post the background image once I get it a little more matching on the AQI. I also am not completely happy with the AQI clip-art at the bottom.

Hi all,

I have a Coway airmega and the custom integration provides a number in μg/m3. I have been looking around for a conversion to AQI, but its not really straightforward:
https://forum.airnowtech.org/t/the-aqi-equation/169
https://www.airnow.gov/aqi/aqi-calculator-concentration/

I’m trying to combine multiple sources of AQI (Airnow and my Xiaomi AQI sensor) into one graph and would be great to get my Coway to have the same unit of measurement. Anyone have an idea how to create the calculation template?

Need more info. It is probably safe to assume this is for 2.5 particle size but it could be 10.
There are different AQI scales also. Unfortunately the same acronym has at least four widely used and different scales.

Edit: post 83 above sets it up for you
https://community.home-assistant.io/t/purpleair-air-quality-sensor/146588/83

Oooh I like those gauges. Are these a mod? Can you share your setup for them?

Gauges are the same as posted above (somewhere up there).
I just ‘recently’ added the backgrounds, and still playing with them.
Give me a little bit and I will just upload my backgrounds and the logos I used to github for easier updating.

1 Like

thanks, I looked more into what is exposed in the integration and I guess it does have “air quality” and PM2.5. However, Im confused with all the units:

Airnow AQI:
image

Airnow PM2.5:
image

Airmega 400S:
image

Xiaomi AQI monitor:
image

I currently have the Airnow AQI, Airmega Air Quality Index, and Xiaomi in one graph but its not really making sense? The Airmega shows ug/m3 as the units for AQI. In the PM2.5 graph, Airnow has ug/m3 as units. I think I may the Airmega sensors swapped in the graphs?

https://github.com/GlennGoddard/CanvasGaugeBackgrounds

Still a work in progress.
I have each gauge in a directory.
I have the front-end yaml code, background, and any logo used.

I have shifted from embedding the text in the background to using state-label to allow anyone to change the text to the language of their choice.

I don’t have every thing uploaded yet, since I don’t have all the images on me at this time.

You will just have to modify the yaml to fit whatever sensors you have.