Last active
December 21, 2020 16:30
-
-
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
This file contains hidden or 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
#!/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