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.



Sunday, December 30, 2018

Wemo local control from Homeseer

Wemo Mini Smart Plug, WiFi Enabled, Works with Alexa, Google Assistant & Apple HomeKit

3Camel price chart

Got a couple of these for half price on Good Friday looking for another cheap option to reset cams and such. I also wanted something I could control locally unlike the Etekcity and Sonoff options which Wemo is supposed to do.

What did not work

First I tried the Eric Fetty Wemo Software Plug-in for HS3 This appears to integrate well from past reviews but for some reason seems to have stopped working for some like me. I could never get it to discover the plugs.  Trying to debug lead me to the github project palominolabs/belkin-wemo-switch then cybergarage/cybergarage-upnp. After some playing around with these I found it only discovered the Wemo plugs intermittently. I looked a few others. Most of them wanted to set up some sort of bridge server.

Getting a working interface

Then I came across agilemation/Belkin-WeMo-Command-Line-Tools. The only "downside" seems to be it uses linux shell scripts byt installing cygwin will get around that. When you install cygwin be sure to get the curl command. Of course you could reverse engineer this to make the HTTP calls but calling from Homeseer is simple enough to make the effort seem like a waste. So here is what I did:
Install cygwin (including curl)
Install npm via Node.js
Make a folder for the projects. I put mine in C:\npm
cd c:\npm
Run the command
npm install belkin-wemo-command-line-tools
The new commands are in C:\npm\node_modules\belkin-wemo-command-line-tools
in C:\npm I created a bat command containing

cd C:\npm\node_modules\belkin-wemo-command-line-tools
C:\cygwin\bin\sh wemo -h %1 -a %2

Now you have a simple command with the syntax of wemo ip_of_switch ON_or_OFF

Linking to Homeseer

First you need to create a virtual similar to those outlined in this post.
Then you just need to setup a couple events to trigger on the state change of that virtual similar to these.
In this case 10.10.1.141 is the IP address of my first Wemo switch.

Note you could also create scripts to get state, signal strength and name info and even set the name of the plug. Though at that point reverse engineering the shell script into VB might be worth the effort.