skalnik / aqi-wtf

😷 WTF is the AQI near me right now?!
https://aqi.wtf
MIT License
44 stars 12 forks source link

aqi-wtf now broken due to decommission of /json endpoint #81

Closed tacomilkshake closed 2 years ago

tacomilkshake commented 2 years ago

https://community.purpleair.com/t/discontinuation-of-the-json-and-data-json-urls/713

Looks like this will need to use an authenticated API key now, and the authenticated API endpoints.

tacomilkshake commented 2 years ago

Given I previously forked your project for my own uses and have diverged a bit since then, a pull request isn't suitable. However, here is my newly updated version of your AQI calculation script that uses the appropriate fields from Purple's new authenticated API:

// From https://www.epa.gov/sites/default/files/2021-05/documents/toolsresourceswebinar_purpleairsmoke_210519b.pdf final slide
function epaAQIFromPMandHumidity(pm, humidity) {
  if (pm < 50) {
    return aqiFromPM(0.52 * pm - 0.086 * humidity + 5.75)
  } else if (pm < 229) {
    return aqiFromPM(0.786 * pm - 0.086 * humidity + 5.75)
  } else {
    return aqiFromPM(0.69 * pm + 8.84 * Math.pow(10, -4) * Math.pow(pm, 2) + 2.97)
  }
}

function aqiFromPM(pm) {
  if (isNaN(pm)) return '-'
  if (pm == undefined) return '-'
  if (pm < 0) return 0
  if (pm > 1000) return '-'

  if (pm > 350.5) {
    return calcAQI(pm, 500, 401, 500, 350.5)
  } else if (pm > 250.5) {
    return calcAQI(pm, 400, 301, 350.4, 250.5)
  } else if (pm > 150.5) {
    return calcAQI(pm, 300, 201, 250.4, 150.5)
  } else if (pm > 55.5) {
    return calcAQI(pm, 200, 151, 150.4, 55.5)
  } else if (pm > 35.5) {
    return calcAQI(pm, 150, 101, 55.4, 35.5)
  } else if (pm > 12.1) {
    return calcAQI(pm, 100, 51, 35.4, 12.1)
  } else if (pm >= 0) {
    return calcAQI(pm, 50, 0, 12, 0)
  } else {
    return undefined
  }
}

function calcAQI(Cp, Ih, Il, BPh, BPl) {
  // The AQI equation https://forum.airnowtech.org/t/the-aqi-equation/169
  var a = Ih - Il
  var b = BPh - BPl
  var c = Cp - BPl
  return Math.round((a / b) * c + Il)
}

function getAQI(reading) {
  let humidity = reading.sensor.humidity
  const pm25 = reading.sensor['pm2.5_cf_1']
  const aqi = epaAQIFromPMandHumidity(pm25, humidity)

  return aqi
}

export function getAQIClass(aqi) {
  if (aqi >= 401) {
    return 'very-hazardous'
  } else if (aqi >= 301) {
    return 'hazardous'
  } else if (aqi >= 201) {
    return 'very-unhealthy'
  } else if (aqi >= 151) {
    return 'unhealthy'
  } else if (aqi >= 101) {
    return 'unhealthy-for-sensitive-groups'
  } else if (aqi >= 51) {
    return 'moderate'
  } else if (aqi >= 0) {
    return 'good'
  } else {
    return undefined
  }
}

export function getAQIClassFromReading(reading) {
  const aqi = getAQI(reading)
  return getAQIClass(aqi)
}

export function getAQIFromReading(reading) {
  const aqi = getAQI(reading)
  return aqi
}
skalnik commented 2 years ago

Thanks for this! I'll get this fixed up. Super appreciate the report + code 💖

tacomilkshake commented 2 years ago

No problem! Really helpful website you built...

By the way, the new API is vastly improved, faster, etc. -- I think they also plan to add some average and calculated metrics to the response over time.

skalnik commented 2 years ago

For anyone following along, I've slowly started poking at this and am awaiting an API key.

skalnik commented 2 years ago

Closed with https://github.com/skalnik/aqi-wtf/pull/82

Thanks again for the report @jamiesteven!