-
Notifications
You must be signed in to change notification settings - Fork 1
/
AirQuality-AirNow.groovy
140 lines (114 loc) · 5.41 KB
/
AirQuality-AirNow.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
* Import URL: https://raw.githubusercontent.com/HubitatCommunity/AirQuality-AirNow/master/AirQuality-AirNow.groovy"
*
* Copyright 2021 C Steele
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*
*
* JAS - v1.0.4 Cleaned up some logging items 11/10/23
* JAS - v1.0.3 split out category and color 08/08/23
* v1.0.2 PR from cmbruns
* According to Hubitat docs the airQualityIndex attribute is supposed range from 0 to 500, meaning it should be the
* full PM10-equivalent value, not the 6-category meaning previously used and matches Ecowitt air quality sensor range.
* v1.0.1 renamed "PM2.5" attribute to not use a dot (.)
* csteele v1.0.0 created.
*/
static String version() { return '1.0.4' }
import groovy.transform.Field
metadata {
definition (name: "Air Quality from AirNow", namespace: "csteele", author: "CSteele") {
capability "AirQuality"
capability "Sensor"
attribute 'O3', 'number'
attribute 'PM2_5', 'number'
attribute 'PM10', 'number'
attribute 'airQualityIndex', 'number'
attribute 'airQualityColor', 'STRING'
attribute 'airQualityCategory', 'number'
command 'pollAirNow'
}
preferences {
input "apiKey", "text", title: "<b>Type AirNow.org API Key Here</b>", required: true, defaultValue: null
input "pollEvery", "enum", title: "<b>Publish AQI how frequently?</b>", required:false, defaultValue: 1, options:[1:"1 hour",2:"2 hours",8:"8 hours",16:"16 hours"]
input "basedOn", "enum", title: "<b>Publish AQI Number based on?</b>", required:false, defaultValue: 1, options:[1:"O3 ozone",2:"PM2.5 particle",3:"PM10 partice", 4: "Worst AQI"]
input "debugOutput", "bool", title: "Enable debug logging", required:false, defaultValue: true
input "txtEnable", "bool", title: "Enable descriptionText logging", required:false, defaultValue: true
}
}
void pollAirNow() {
if ( apiKey == null ) {
return
}
Map params = [
uri: 'https://www.airnowapi.org/aq/observation/latLong/current/?format=application/json&latitude=' + (String)location.latitude + '&longitude=' + (String)location.longitude + '&distance=25&API_KEY=' + (String)apiKey,
timeout: 20 ]
asynchttpGet('pollHandler', params)
}
void pollHandler(resp, data) {
if (resp.getStatus() == 200 || resp.getStatus() == 207) {
aqi = parseJson(resp.data)
def isBasis = aqiBasis[basedOn as Integer]
def maxAQI = -1
def maxAQICat = -1
aqi.each { obs ->
if ((obs.AQI >= 0) && (obs.AQI <= 2000)) { // sanity check the value - AirNow api glitch removal
if (obs.AQI > maxAQI) {
maxAQI = obs.AQI
maxAQICat = obs.Category.Number
}
def descriptionText = "${device.displayName} ${obs.ParameterName} is ${obs.AQI}"
def attrNam = obs.ParameterName.replace('.', '_')
if (debugOutput) log.info "${descriptionText}"
sendEvent(name: attrNam, value: obs.AQI, descriptionText: descriptionText)
if (isBasis == obs.ParameterName) {
descriptionText = "${device.displayName} airQualityIndex is ${obs.AQI}"
if (txtEnable) log.info "AQI Values updated. Enable Debug to monitor changes realtime."
if (debugOutput) log.debug "${descriptionText}"
sendEvent(name: "airQualityIndex", value: obs.AQI, descriptionText: descriptionText)
descriptionText = "${device.displayName} airQualityCategory is ${aqiCategory[obs.Category.Number]}"
if (debugOutput) log.debug "${descriptionText}"
sendEvent(name: "airQualityCategory", value: aqiCategory[obs.Category.Number], descriptionText: descriptionText)
descriptionText = "${device.displayName} airQualityColor is ${aqiColor[obs.Category.Number]}"
if (debugOutput) log.info "${descriptionText}"
sendEvent(name: "airQualityColor", value: aqiColor[obs.Category.Number], descriptionText: descriptionText)
}
}
}
if (isBasis == "maxAQI") {
descriptionText = "${device.displayName} airQualityIndex is $maxAQI"
if (txtEnable) log.info "AQI Values updated. Enable Debug to monitor changes realtime."
if (debugOutput) log.debug "${descriptionText}"
sendEvent(name: "airQualityIndex", value: maxAQI, descriptionText: descriptionText)
descriptionText = "${device.displayName} airQualityColor is ${aqiColor[maxAQICat]}"
if (debugOutput) log.debug "${descriptionText}"
sendEvent(name: "airQualityColor", value: aqiColor[maxAQICat], descriptionText: descriptionText)
}
}
}
void updated() {
unschedule()
if (apiKey) schedule("3 7 0/${pollEvery} ? * * *", pollAirNow)
if (debugOutput) runIn(1800,logsOff)
}
void uninstalled() {
unschedule()
}
def logsOff(){
log.warn "debug logging disabled..."
device.updateSetting("debugOutput",[value:"false",type:"bool"])
}
@Field static aqiColor = [1: "Green", 2: "Yellow", 3: "Orange", 4: "Red", 5: "Purple", 6: "Maroon"]
@Field static aqiCategory = [1: "Good", 2: "Moderate", 3: "Unhealthy for Sensitive Groups", 4: "Unhealthy", 5: "Very Unhealthy", 6: "Hazardous"]
@Field static aqiBasis = [1: "O3", 2: "PM2.5", 3: "PM10", 4: "maxAQI"]