-
-
Save Critter/75abec5470b97bfc8098573030afd0c8 to your computer and use it in GitHub Desktop.
Purple AQI Widget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const API_URL = "https://www.purpleair.com/json?show="; | |
// const CACHE_FILE = "aqi_data.json" | |
// Find a nearby PurpleAir sensor ID via https://fire.airnow.gov/ | |
// Click a sensor near your location: the ID is the trailing integers | |
// https://www.purpleair.com/json has all sensors by location & ID. | |
let SENSOR_ID = args.widgetParameter || "4896" | |
async function getSensorData(url, id) { | |
let req = new Request(`${url}${id}`) | |
let json = await req.loadJSON() | |
return { | |
"val": json.results[0].PM2_5Value, | |
"ts": json.results[0].LastSeen, | |
"loc": json.results[0].Label | |
} | |
} | |
// Calculates the AQI level based on | |
// https://cfpub.epa.gov/airnow/index.cfm?action=aqibasics.aqi#unh | |
function calculateLevel(aqi) { | |
let res = { | |
level: "OK", | |
color: Color.white() | |
} | |
let level = parseInt(aqi, 10) || 0 | |
if (level > 300) { | |
res.level = "Hazardous" | |
res.color = new Color('7e0023') | |
return res | |
} else if (level > 200) { | |
res.level = "Very Unhealthy" | |
res.color = new Color('8f3f97') | |
return res | |
} else if (level > 151) { | |
res.level = "Unhealthy" | |
res.color = Color.red() | |
return res | |
} else if (level > 101) { | |
res.level = "Unhealthy (S.G.)" | |
res.color = new Color('ff7e00') | |
return res | |
} else if (level > 51) { | |
res.level = "Moderate" | |
res.color = new Color('ffff00') | |
return res | |
} else if (level > 0) { | |
res.level = "Good" | |
res.color = new Color('00e400') | |
return res | |
} | |
return res | |
} | |
//Function to get AQI number from PPM reading | |
function aqiFromPM (pm) { | |
if (pm > 350.5) { | |
return calcAQI(pm, 500.0, 401.0, 500.0, 350.5) | |
} else if (pm > 250.5) { | |
return calcAQI(pm, 400.0, 301.0, 350.4, 250.5) | |
} else if (pm > 150.5) { | |
return calcAQI(pm, 300.0, 201.0, 250.4, 150.5) | |
} else if (pm > 55.5) { | |
return calcAQI(pm, 200.0, 151.0, 150.4, 55.5) | |
} else if (pm > 35.5) { | |
return calcAQI(pm, 150.0, 101.0, 55.4, 35.5) | |
} else if (pm > 12.1) { | |
return calcAQI(pm, 100.0, 51.0, 35.4, 12.1) | |
} else if (pm >= 0.0) { | |
return calcAQI(pm, 50.0, 0.0, 12.0, 0.0) | |
} else { | |
return "-" | |
} | |
} | |
//Function that actually calculates the AQI number | |
function calcAQI(Cp, Ih, Il, BPh, BPl) { | |
let a = (Ih - Il); | |
let b = (BPh - BPl); | |
let c = (Cp - BPl); | |
return Math.round((a/b) * c + Il); | |
} | |
async function run() { | |
let wg = new ListWidget() | |
try { | |
console.log(`Using sensor ID: ${SENSOR_ID}`) | |
let data = await getSensorData(API_URL, SENSOR_ID) | |
console.log(data) | |
let header = wg.addText("Air Quality") | |
header.textSize = 15 | |
header.textColor = Color.black() | |
let aqi = aqiFromPM(data.val) | |
let level = calculateLevel(aqi) | |
let aqitext = aqi.toString() | |
console.log(aqi) | |
console.log(level.level) | |
wg.backgroundColor = level.color | |
let content = wg.addText(aqitext) | |
content.textSize = 50 | |
content.textColor = Color.black() | |
let wordLevel = wg.addText(level.level) | |
wordLevel.textSize = 15 | |
wordLevel.textColor = Color.black() | |
let id = wg.addText(data.loc) | |
id.textSize = 10 | |
id.textColor = Color.black() | |
let updatedAt = new Date(data.ts*1000).toLocaleTimeString("en-US", { timeZone: "PST" }) | |
let ts = wg.addText(`Updated ${updatedAt}`) | |
ts.textSize = 10 | |
ts.textColor = Color.black() | |
} catch (e) { | |
console.log(e) | |
let err = wg.addText(`error: ${e}`) | |
err.textSize = 10 | |
err.textColor = Color.red() | |
err.textOpacity = 30 | |
} | |
Script.setWidget(wg) | |
Script.complete() | |
} | |
await run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment