Thanks for the excellent configuration, @colohan. I made some small tweaks for my own use that others might find useful. Namely, I made the value of the data holder the last time the sensor was seen, moved the AQI value to a template sensor, added in the 8 degree temperature adjustment as suggested, put a couple variables in the template to reduce repetition in the conditionals so they’re easier to read, and added unique_id
s and device_class
es to the entities, and added a binary_sensor
for connectivity and use in availability_template values.
# https://community.home-assistant.io/t/purpleair-air-quality-sensor/146588
# Reasonable sensors pulled from https://www.purpleair.com/map: 37021, 20801, 20707.
- platform: rest
name: 'PurpleAir'
# Substitute in the URL of the sensor you care about. To find the URL, go
# to purpleair.com/map, find your sensor, click on it, click on "Get This
# Widget" then click on "JSON".
resource: https://www.purpleair.com/json?key=PMWGYB07N4CDBH23&show=37021
# Only query once a minute to avoid rate limits:
scan_interval: 60
# Set the sensor value to the last update time of the device.
device_class: timestamp
value_template: >
{{ state_attr('sensor.purpleair', 'results')[0]['LastSeen'] }}
# The value of the sensor can't be longer than 255 characters, but the
# attributes can. Store away all the data for use by the templates below.
json_attributes:
- results
- platform: template
sensors:
purpleair_aqi:
friendly_name: 'PurpleAir AQI'
unique_id: purpleair_aqi
# Set this sensor to be the AQI value.
#
# Code translated from JavaScript found at:
# https://docs.google.com/document/d/15ijz94dXJ-YAZLi9iZ_RaBwrZ4KtYeCy08goGBwnbCU/edit#
value_template: >
{% macro calcAQI(Cp, Ih, Il, BPh, BPl) -%}
{{ (((Ih - Il)/(BPh - BPl)) * (Cp - BPl) + Il)|round }}
{%- endmacro %}
{% set pm25 = state_attr('sensor.purpleair', 'results')[0]['PM2_5Value']|float %}
{% if pm25 > 1000 %}
invalid
{% elif pm25 > 350.5 %}
{{ calcAQI(pm25, 500.0, 401.0, 500.0, 350.5) }}
{% elif pm25 > 250.5 %}
{{ calcAQI(pm25, 400.0, 301.0, 350.4, 250.5) }}
{% elif pm25 > 150.5 %}
{{ calcAQI(pm25, 300.0, 201.0, 250.4, 150.5) }}
{% elif pm25 > 55.5 %}
{{ calcAQI(pm25, 200.0, 151.0, 150.4, 55.5) }}
{% elif pm25 > 35.5 %}
{{ calcAQI(pm25, 150.0, 101.0, 55.4, 35.5) }}
{% elif pm25 > 12.1 %}
{{ calcAQI(pm25, 100.0, 51.0, 35.4, 12.1) }}
{% elif pm25 >= 0.0 %}
{{ calcAQI(pm25, 50.0, 0.0, 12.0, 0.0) }}
{% else %}
invalid
{% endif %}
unit_of_measurement: "AQI"
entity_id: sensor.purpleair
availability_template: "{{ sensor.purpleair_available }}"
purpleair_description:
friendly_name: 'PurpleAir AQI Description'
unique_id: purpleair_description
value_template: >
{% set aqi = states('sensor.purpleair_aqi')|float %}
{% 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 %}
undefined
{% endif %}
entity_id: sensor.purpleair
availability_template: "{{ sensor.purpleair_available }}"
purpleair_pm25:
friendly_name: 'PurpleAir PM 2.5'
unique_id: purpleair_pm25
value_template: "{{ state_attr('sensor.purpleair', 'results')[0]['PM2_5Value'] }}"
unit_of_measurement: "μg/m3"
entity_id: sensor.purpleair
availability_template: "{{ sensor.purpleair_available }}"
purpleair_temp:
friendly_name: 'PurpleAir Temperature'
unique_id: purpleair_temp
# Why "- 8"?
# https://www2.purpleair.com/community/faq#!hc-primary-and-secondary-data-header
value_template: "{{ state_attr('sensor.purpleair', 'results')[0]['temp_f'] | float - 8 }}"
unit_of_measurement: "°F"
device_class: temperature
entity_id: sensor.purpleair
availability_template: "{{ sensor.purpleair_available }}"
purpleair_humidity:
friendly_name: 'PurpleAir Humidity'
unique_id: purpleair_humidity
value_template: "{{ state_attr('sensor.purpleair', 'results')[0]['humidity'] }}"
unit_of_measurement: "%"
device_class: humidity
entity_id: sensor.purpleair
availability_template: "{{ sensor.purpleair_available }}"
purpleair_pressure:
friendly_name: 'PurpleAir Pressure'
unique_id: purpleair_pressure
value_template: "{{ state_attr('sensor.purpleair', 'results')[0]['pressure'] }}"
unit_of_measurement: "mb"
device_class: pressure
entity_id: sensor.purpleair
availability_template: "{{ sensor.purpleair_available }}"
binary_sensor:
- platform: template
sensors:
# Add a connectivity sensor for purpleair. Why isn't the above rest sensor
# a binary_sensor? Turns out binary_sensor rest devices can't have a
# json_attributes section.
purpleair_available:
friendly_name: 'PurpleAir Available'
unique_id: purpleair_available
value_template: >
{% set stats = state_attr('sensor.purpleair', 'results')[0]['Stats']|from_json %}
{% set minutes = stats['timeSinceModified']|int / 1000 /60 %}
{{ minutes < 5 }}
entity_id: sensor.purpleair
device_class: connectivity