Skip to content

Instantly share code, notes, and snippets.

@fschiettecatte
Last active December 21, 2020 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fschiettecatte/20e1ea696b06707cf4ec649cc17d7b78 to your computer and use it in GitHub Desktop.
Save fschiettecatte/20e1ea696b06707cf4ec649cc17d7b78 to your computer and use it in GitHub Desktop.
SwiftBar plugin to get and display the Air Quality Index (AQI) in the macOS menu bar
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#--------------------------------------------------------------------------
#
# Author: Francois Schiettecatte (FS Consulting LLC.)
# Creation Date: October 10, 2020
#
#--------------------------------------------------------------------------
#
# Description:
#
# BitBar plugin to get and display the Air Quality Index (AQI)
# in the menu bar:
#
# https://github.com/matryer/bitbar
#
# EPA Air Quality Index site:
#
# https://www.airnow.gov/
#
# You will need to create an AirNow Account to get access to the API:
#
# https://docs.airnowapi.org/
#
# https://docs.airnowapi.org/account/request/
#
#
#--------------------------------------------------------------------------
#
# Imported modules
#
import json
import sys
import urllib.request
# Use this monkey patch around SSL certificate errors:
# [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
#--------------------------------------------------------------------------
#
# Constants
#
# Location for the URL to the AQI website - set based on your location
CITY = 'SetMe'
STATE = 'SetMe'
ZIP_CODE = SetMe
COUNTRY = 'SetMe'
# EPA URL
EPA_URL = 'https://www.airnow.gov/?city={0}&state={1}&country={2}'.format(CITY, STATE, COUNTRY)
# EPA API Key - Obtain via an AirNow Account (see above)
EPA_API_KEY = 'SetMe'
# API URL
EPA_API_URL = 'https://www.airnowapi.org/aq/observation/zipCode/current/?format=application/json&zipCode={0}&distance=25&API_KEY={1}'.format(ZIP_CODE, EPA_API_KEY)
#--------------------------------------------------------------------------
#
# Method: getLevelLabelAndColor
#
# Purpose: Get the level label and level color from the from AQI
#
# Parameters: aqi the AQI
#
# Exceptions: ValueError Missing AQI
# ValueError Invalid AQI
#
# Return: a tuple of the level label and level color
#
def getLevelLabelAndColor(aqi):
# Check parameters
if aqi is None:
raise ValueError('Missing AQI')
if aqi > 300:
return ('Hazardous', '🟤',)
elif aqi > 200:
return ('Very Unhealthy', '🟣',)
elif aqi > 150:
return ('Unhealthy', '🔴',)
elif aqi > 100:
return ('Unhealthy (S.G.)', '🟠',)
elif aqi > 50:
return ('Moderate', '🟡',)
elif aqi > -20:
return ('Good', '🟢',)
raise ValueError('Invalid AQI, {0} <= -20'.format(aqi))
#--------------------------------------------------------------------------
#
# Method: checkAQI
#
# Purpose: check AQI
#
# Parameters:
#
# Exceptions:
#
# Return:
#
def checkAQI():
# Content list
contentList = None
# Get the content from the site
try:
# Open the URL
response = urllib.request.urlopen(EPA_API_URL)
# Read the content
content = response.read()
# Decode the content and parse the JSON string
contentList = json.loads(content.decode())
except Exception as exception:
print('AQI Unavailable | color=Red')
print('---')
print('Failed to get data from remote site, exception: {0}'.format(str(exception)))
return
# Ozone values
ozoneAQI = None
ozoneLevelLabel = None
ozoneLevelColor = None
# Particle values
particleAQI = None
particleLevelLabel = None
particleLevelColor = None
# Get the ozone and particle values
try:
# AQI dict
aqiDict = dict()
# Populate the AQI dict
for contentDict in contentList:
aqiDict[contentDict['ParameterName']] = contentDict['AQI']
# Ozone values
ozoneAQI = aqiDict['O3']
ozoneLevelLabel, ozoneLevelColor = getLevelLabelAndColor(ozoneAQI)
# Particle values
particleAQI = aqiDict['PM2.5']
particleLevelLabel, particleLevelColor = getLevelLabelAndColor(particleAQI)
except Exception as exception:
pass
# Print the menu bar message
if ozoneAQI is not None:
if particleAQI is not None and ozoneLevelLabel != particleLevelLabel:
if ozoneAQI > particleAQI:
print('{0} {1}/{2}'.format(ozoneLevelColor, ozoneLevelLabel, particleLevelLabel))
else:
print('{0} {1}/{2}'.format(particleLevelColor, ozoneLevelLabel, particleLevelLabel))
else:
print('{0} {1}'.format(ozoneLevelColor, ozoneLevelLabel))
print('---')
print('{0} Ozone: {1} ({2}) | href={3}'.format(ozoneLevelColor, ozoneLevelLabel, int(ozoneAQI), EPA_URL))
if particleAQI is not None:
print('{0} Particle Pollution: {1} ({2}) | href={3}'.format(particleLevelColor, particleLevelLabel, int(particleAQI), EPA_URL))
else:
print('AQI Unavailable | color=Red')
print('---')
print('EPA AQI value is missing')
#--------------------------------------------------------------------------
#
# Method: __main__
#
# Purpose: __main__
#
# Parameters:
#
# Exceptions:
#
# Returns:
#
if __name__ == '__main__':
# Check AQI
checkAQI()
# And exit
sys.exit(0)
#--------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment