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.
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:
- change card name
- change card icon
- 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.
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'
https://community.home-assistant.io/t/lovelace-button-text-card/210687/8?u=glennha
Examples of each air quality.
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
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
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.
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.
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:
Airnow PM2.5:
Airmega 400S:
Xiaomi AQI monitor:
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.