Sunday, January 6, 2019

Monitoring air quality with Purple Air

PA-II: Dual Laser Air Sensor

Started getting fed up with idiot neighbors burning of brush piles around dusk that fill the valley with smoke so thick it gets hard to breath outside. It was becoming an almost daily occurrence so I started looking for a way to monitor the air. Meaning first I need to know is it building before the house gets enough smoke in it I start hacking up a lung and need to sit on a HEPA filter. Second I wanted charts to show the scoffers that had not experienced how bad it was just how bad it is. After all I could just be sensitive to the smoke. What I came up with after a fair bit of searching was these PurpleAir sensors. Note there are options out there like IQAir that appear to be indoor/outdoor sensors but really are just indoor sensors that also display info pulled from the nearest air quality site which could be a long way away. 

I'm impressed enough with this outdoor sensor I'm thinking of replacing my Awair V1 indoor sensors with their indoor version PA-I-Indoor.

One thing that confused me and you should be aware of was if you pay with paypal it makes this your main email. It then asks for an email to use for alerts and stuff. Fine as it goes but then when you try and log into the site you find it wants a gmail account to use. I sent emails to support, the first of which bounced because they do not accept emails from home addresses. The second, sent from my gmail account, they said it was sorted at which point I realized there really seems no reason to log into the PurpleAir site. There appears to be nothing you gain access to.

Install is very easy but I should note the 17 foot power cable is to the USB charger which does not appear to be outdoor rated. The USB cable it comes with is about 6 feet long but could be replaced with a longer one if need be.

There are actually to maps data is displayed on. The one linked from the page menus

There is also a hidden map that I think is an older version from comments I've seen in forums.

You can also download data for any of the sensors.

You can also pull JSON data from the servers via
https://www.purpleair.com/json?show=21483
where 21483 is the ID of your sensor

Lastly there is a web server on the sensor itself you can view

or pull JSON formatted data from 
http://10.10.1.204/json
where 10.10.1.204 id the IP address of the sensor.

The data you get 

Here are examples of the JSON you get
Pulling form the server looks like this 
[
{
"mapVersion": "0.71",
"baseVersion": "6",
"mapVersionString": "",
"results": [
{
"ID": 21483,
"ParentID": null,
"Label": "RMRR",
"DEVICE_LOCATIONTYPE": "outside",
"THINGSPEAK_PRIMARY_ID": "649780",
"THINGSPEAK_PRIMARY_ID_READ_KEY": "S7WM9LD098LFRR93",
"THINGSPEAK_SECONDARY_ID": "649781",
"THINGSPEAK_SECONDARY_ID_READ_KEY": "45URJ7DPOE361Z7U",
"Lat": 30.572352,
"Lon": -97.948247,
"PM2_5Value": "0.92",
"LastSeen": 1545077164,
"State": null,
"Type": "PMS5003+PMS5003+BME280",
"Hidden": "false",
"Flag": null,
"DEVICE_BRIGHTNESS": "15",
"DEVICE_HARDWAREDISCOVERED": "2.0+BME280+PMSX003A+PMSX003B",
"DEVICE_FIRMWAREVERSION": "2.50i",
"Version": "2.50i",
"LastUpdateCheck": 1545075564,
"Uptime": "1610",
"RSSI": "-42",
"isOwner": 0,
"A_H": null,
"temp_f": "71",
"humidity": "25",
"pressure": "990.04",
"AGE": 1,
"Stats": "{\"v\":0.92,\"v1\":1.02,\"v2\":1.12,\"v3\":1.36,\"v4\":5.55,\"v5\":5.57,\"v6\":1.28,\"pm\":0.92,\"lastModified\":1545077164572,\"timeSinceModified\":79934}"
},
{
"ID": 21484,
"ParentID": 21483,
"Label": "RMRR B",
"DEVICE_LOCATIONTYPE": null,
"THINGSPEAK_PRIMARY_ID": "649782",
"THINGSPEAK_PRIMARY_ID_READ_KEY": "8MM2J153E5W6DUYM",
"THINGSPEAK_SECONDARY_ID": "649783",
"THINGSPEAK_SECONDARY_ID_READ_KEY": "16JV1IU1V9U1PQ93",
"Lat": 30.572352,
"Lon": -97.948247,
"PM2_5Value": "0.61",
"LastSeen": 1545077194,
"State": null,
"Type": null,
"Hidden": "false",
"Flag": null,
"DEVICE_BRIGHTNESS": null,
"DEVICE_HARDWAREDISCOVERED": "2.0+BME280+PMSX003A+PMSX003B",
"DEVICE_FIRMWAREVERSION": null,
"Version": "2.50i",
"LastUpdateCheck": null,
"Uptime": "1640",
"RSSI": "-42",
"isOwner": 0,
"A_H": null,
"temp_f": "71",
"humidity": "25",
"pressure": "990.07",
"AGE": 0,
"Stats": "{\"v\":0.61,\"v1\":0.93,\"v2\":0.99,\"v3\":1.27,\"v4\":5.85,\"v5\":5.8,\"v6\":1.32,\"pm\":0.61,\"lastModified\":1545077194599,\"timeSinceModified\":79932}"
}
]
},
{
"mapVersion": "0.71",
"baseVersion": "6",
"mapVersionString": "",
"results": [
{
"ID": 21483,
"ParentID": null,
"Label": "RMRR",
"DEVICE_LOCATIONTYPE": "outside",
"THINGSPEAK_PRIMARY_ID": "649780",
"THINGSPEAK_PRIMARY_ID_READ_KEY": "S7WM9LD098LFRR93",
"THINGSPEAK_SECONDARY_ID": "649781",
"THINGSPEAK_SECONDARY_ID_READ_KEY": "45URJ7DPOE361Z7U",
"Lat": 30.572352,
"Lon": -97.948247,
"PM2_5Value": "0.00",
"LastSeen": 1545344129,
"State": null,
"Type": "PMS5003+PMS5003+BME280",
"Hidden": "false",
"Flag": null,
"DEVICE_BRIGHTNESS": "15",
"DEVICE_HARDWAREDISCOVERED": "2.0+BME280+PMSX003A+PMSX003B",
"DEVICE_FIRMWAREVERSION": "2.50i",
"Version": "2.50i",
"LastUpdateCheck": 1545341654,
"Uptime": "2490",
"RSSI": "-43",
"isOwner": 0,
"A_H": null,
"temp_f": "63",
"humidity": "23",
"pressure": "985.20",
"AGE": 0,
"Stats": "{\"v\":0.0,\"v1\":0.0,\"v2\":0.01,\"v3\":0.03,\"v4\":3.68,\"v5\":8.67,\"v6\":3.55,\"pm\":0.0,\"lastModified\":1545344129495,\"timeSinceModified\":80034}"
},
{
"ID": 21484,
"ParentID": 21483,
"Label": "RMRR B",
"DEVICE_LOCATIONTYPE": null,
"THINGSPEAK_PRIMARY_ID": "649782",
"THINGSPEAK_PRIMARY_ID_READ_KEY": "8MM2J153E5W6DUYM",
"THINGSPEAK_SECONDARY_ID": "649783",
"THINGSPEAK_SECONDARY_ID_READ_KEY": "16JV1IU1V9U1PQ93",
"Lat": 30.572352,
"Lon": -97.948247,
"PM2_5Value": "0.00",
"LastSeen": 1545344159,
"State": null,
"Type": null,
"Hidden": "false",
"Flag": null,
"DEVICE_BRIGHTNESS": null,
"DEVICE_HARDWAREDISCOVERED": "2.0+BME280+PMSX003A+PMSX003B",
"DEVICE_FIRMWAREVERSION": null,
"Version": "2.50i",
"LastUpdateCheck": null,
"Uptime": "2520",
"RSSI": "-42",
"isOwner": 0,
"A_H": null,
"temp_f": "63",
"humidity": "23",
"pressure": "985.07",
"AGE": 0,
"Stats": "{\"v\":0.0,\"v1\":0.0,\"v2\":0.0,\"v3\":0.01,\"v4\":3.79,\"v5\":8.79,\"v6\":3.58,\"pm\":0.0,\"lastModified\":1545344159548,\"timeSinceModified\":80121}"
}
]
}
]

Pulling locally form the sensor direct looks like this
{
"SensorId": "80:7d:3a:56:37:be",
"DateTime": "2018/12/17T19:22:47z",
"Geo": "AirMonitor_37be",
"Mem": 26064,
"Id": 8,
"Adc": 0.01,
"lat": 30.572351,
"lon": -97.948250,
"accuracy": 0,
"elevation": 0.00,
"version": "2.50i",
"uptime": 260,
"rssi": -45,
"hardwareversion": "2.0",
"hardwarediscovered": "2.0+BME280+PMSX003A+PMSX003B",
"current_temp_f": 73,
"current_humidity": 26,
"current_dewpoint_f": 36.09,
"pressure": 990.73,
"pm1_0_atm_b": 0.23,
"pm2_5_atm_b": 0.60,
"pm10_0_atm_b": 2.00,
"pm1_0_cf_1_b": 0.23,
"pm2_5_cf_1_b": 0.60,
"pm10_0_cf_1_b": 2.00,
"p_0_3_um_b": 270.83,
"p_0_5_um_b": 71.53,
"p_1_0_um_b": 8.91,
"p_2_5_um_b": 3.40,
"p_5_0_um_b": 1.91,
"p_10_0_um_b": 1.40,
"pm1_0_atm": 1.00,
"pm2_5_atm": 1.31,
"pm10_0_atm": 1.31,
"pm1_0_cf_1": 1.00,
"pm2_5_cf_1": 1.31,
"pm10_0_cf_1": 1.31,
"p_0_3_um": 332.80,
"p_0_5_um": 88.82,
"p_1_0_um": 10.27,
"p_2_5_um": 0.71,
"p_5_0_um": 0.30,
"p_10_0_um": 0.00,
"responseCode": "201",
"responseCode_date": 1545074540,
"key1_responseCode": "200",
"key1_responseCode_date": 1545074567,
"key1_count": 1159,
"key2_responseCode": "200",
"key2_responseCode_date": 1545074497,
"key2_count": 1061,
"key1_responseCode_b": "200",
"key1_responseCode_date_b": 1545074517,
"key1_count_b": 1139,
"key2_responseCode_b": "200",
"key2_responseCode_date_b": 1545074527,
"key2_count_b": 1040
}

An example of how I linked to Homeseer

I'm using a MyMonitor checker to load that JSON data into Homeseer virtuals.
It calls the MyMonitor.vb script to create the virtuals for you.