Skip to content

Instantly share code, notes, and snippets.

@jasonsnell
Last active December 18, 2020 18:07
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jasonsnell/7fdb03d3d7dd2b87ed88454b581786d8 to your computer and use it in GitHub Desktop.
Save jasonsnell/7fdb03d3d7dd2b87ed88454b581786d8 to your computer and use it in GitHub Desktop.
Air quality from PurpleAir sensor but with EPA adjustments
#!/usr/bin/env php
<?php
error_reporting( 0 );
// This version requires SwiftBar, rather than BitBar. swiftbar.app
// <bitbar.title>EPA Air Quality</bitbar.title>
// <bitbar.version>v2.0</bitbar.version>
// <bitbar.author>Jason Snell</bitbar.author>
// <bitbar.author.github>jasonsnell</bitbar.author.github>
// <bitbar.desc>Displays PurpleAir data from a nearby station.</bitbar.desc>
// <swiftbar.hideRunInTerminal>true</swiftbar.hideRunInTerminal>
// <swiftbar.hideDisablePlugin>true</swiftbar.hideDisablePlugin>
// <swiftbar.hideSwiftBar>true</swiftbar.hideSwiftBar>
// This version uses the "US EPA" adjustment for wood smoke
// since my version is really meant to be when there are fires.
// Info here: <https://cfpub.epa.gov/si/si_public_file_download.cfm?p_download_id=540979&Lab=CEMM>
// jsnell@intertext.com
// Put your nearest PurpleAir sensor ID here
$show = '4896';
$url = ( 'https://www.purpleair.com/json?show=' . $show );
// Get the sensor data via JSON
$json = @file_get_contents( $url );
$array = json_decode( $json, true );
for ( $i = 0; $i < sizeof( $array['results'] ); $i++ ) {
$array['results'][ $i ]['Stats'] = json_decode( $array['results'][ $i ]['Stats'], true );
}
$pm25a = $array['results'][0]['pm2_5_cf_1'];
$pm25b = $array['results'][1]['pm2_5_cf_1'];
$humidity = $array['results'][0]['humidity'];
$location = $array['results'][0]['Label'];
$lat = $array['results'][1]['Lat'];
$lon = $array['results'][1]['Lon'];
$sensor1 = $array['results'][0]['Stats']['v2'];
$sensor1l = $array['results'][0]['Stats']['v'];
$asensor = $array['results'][0]['Stats']['v1'];
$bsensor = $array['results'][1]['Stats']['v1'];
$epaPM = ( (0.52 * ( ($pm25a + $pm25b) / 2) - (.085 * $humidity) + 5.71) );
$AQI = aqiFromPM( $epaPM );
$aAQI = aqiFromPM( $asensor );
$bAQI = aqiFromPM( $bsensor );
settype( $AQI, 'integer' );
settype( $aAQI, 'integer' );
settype( $bAQI, 'integer' );
echo getAQISymbol($AQI) . ' ' . $AQI . getAQItrend( $sensor1, $sensor1l ) . '
---
' . getAQIDescription( $AQI ) . ' (' . $AQI . ')|color=' . getAQIcolor( $AQI ) . '
' . $location . '|href=https://www.purpleair.com/map?opt=1/i/mAQI/a10/cC5&select=' . $show . '#11/' . $lat . '/' . $lon ;
// Function to get AQI number from PPM reading
function aqiFromPM( $pm ) {
if ( is_nan( $pm ) ) {
return '-';
}
if ( isset( $pm ) == false ) {
return 'Error: No value';
}
if ( $pm < 0.0 ) {
return $pm;
}
if ( $pm > 1000.0 ) {
return '-';
}
if ( $pm > 350.5 ) {
return calcAQI( $pm, 500.0, 401.0, 500.0, 350.5 );
} elseif ( $pm > 250.5 ) {
return calcAQI( $pm, 400.0, 301.0, 350.4, 250.5 );
} elseif ( $pm > 150.5 ) {
return calcAQI( $pm, 300.0, 201.0, 250.4, 150.5 );
} elseif ( $pm > 55.5 ) {
return calcAQI( $pm, 200.0, 151.0, 150.4, 55.5 );
} elseif ( $pm > 35.5 ) {
return calcAQI( $pm, 150.0, 101.0, 55.4, 35.5 );
} elseif ( $pm > 12.1 ) {
return calcAQI( $pm, 100.0, 51.0, 35.4, 12.1 );
} elseif ( $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 ) {
$a = ( $Ih - $Il );
$b = ( $BPh - $BPl );
$c = ( $Cp - $BPl );
return round( ( $a / $b ) * $c + $Il );
}
// Function that gets the AQI's description
function getAQIDescription( $aqinum ) {
if ( $aqinum >= 401 ) {
return 'Hazardous';
} elseif ( $aqinum >= 301 ) {
return 'Hazardous';
} elseif ( $aqinum >= 201 ) {
return 'Very Unhealthy';
} elseif ( $aqinum >= 151 ) {
return 'Unhealthy';
} elseif ( $aqinum >= 101 ) {
return 'Unhealthy SG';
} elseif ( $aqinum >= 51 ) {
return 'Moderate';
} elseif ( $aqinum >= 0 ) {
return 'Good';
} else {
return 'Unknown';
}
}
// Function that gets the AQI's SF Symbol
function getAQISymbol( $aqinum ) {
if ( $aqinum >= 201 ) {
return ':smoke.fill: ';
} elseif ( $aqinum >= 151 ) {
return ':aqi.high: ';
} elseif ( $aqinum >= 101 ) {
return ':aqi.medium: ';
} elseif ( $aqinum >= 51 ) {
return ':aqi.medium: ';
} elseif ( $aqinum >= 0 ) {
return ':aqi.low: ';
} else {
return 'Unknown';
}
}
// Function that gets the AQI's color code
function getAQIColor( $aqinum ) {
if ( $aqinum >= 301 ) {
return '#7e0023';
} elseif ( $aqinum >= 201 ) {
return '#8f3f97';
} elseif ( $aqinum >= 151 ) {
return '#ff0000';
} elseif ( $aqinum >= 101 ) {
return '#cf7332,Orange';
} elseif ( $aqinum >= 51 ) {
return '#333333,#ffff00';
} elseif ( $aqinum >= 0 ) {
return '#0c990c,#00e400';
} else {
return '#000000,#ffffff';
}
}
// Function that checks for the AQI trend
function getAQItrend( $average, $live ) {
if ( ( $average - $live ) > 6 ) {
return ' :arrowtriangle.down.fill:';
} elseif ( ( $average - $live ) < -6 ) {
return ' :arrowtriangle.up.fill:';
} else {
return '';
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment