Skip to content

Instantly share code, notes, and snippets.

@chew-z
Created April 30, 2017 03:44
Show Gist options
  • Save chew-z/19fefd5791a7e84405ba91ff814d8ef0 to your computer and use it in GitHub Desktop.
Save chew-z/19fefd5791a7e84405ba91ff814d8ef0 to your computer and use it in GitHub Desktop.
Yet another weather forecast in your shell
#!/usr/bin/env python3
'''
Yet another weather forecast for your terminal.
usage: python3 oweather.py [-h] [-c CITY] [-w] [-v]
Get weather from OpenWeather.
optional arguments:
-h, --help show this help message and exit
-c CITY, --city CITY Forecast location (default: Iboih,ID)
-w, --weekly Weekly forecast (default: False)
-v, --verbose Some additional useless info like Beaufort scale etc.
(default: False)
Get API Key for yourself at https://openweathermap.org/api
Make and alias for your shell:
alias weather='python3 /path/to/oweather.py'
Try --weekly and --verbose commandline switches
'''
import sys
import time
import argparse
import logging
import requests
import json
import math
from tabulate import tabulate
weather_uri = "http://api.openweathermap.org/data/2.5/"
openweather_api_key = 'GET YOUR OWN API KEY'
def get_emoji():
platform = sys.platform
if platform.startswith('darwin'):
return {
'sun': '🌞 ' + '\x1b[0;33;44m',
'snow': '❄️ ' + '\x1b[1;37;47m',
'rain': '☔ ' + '\x1b[0;34;40m',
'cloud': '⛅ ' + '\x1b[0;34;47m',
'default': '🌏 ' + '\x1b[0;36;40m',
}
def icon(conditions):
emoji = get_emoji()
cond = conditions.lower()
if 'cloud' in cond:
return emoji.get('cloud')
if 'snow' in cond or 'sleet' in cond or 'hail' in cond:
return emoji.get('snow')
if 'rain' in cond or 'storm' in cond:
return emoji.get('rain')
if 'clear' in cond or 'sun' in cond:
return emoji.get('sun')
return emoji.get('default')
def degrees_to_cardinal(d):
'''
note: this is approximate...
'''
dirs = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
"S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"]
if d is None:
return None
# ix = int((d + 11.25)/22.5)
ix = int((d + 11.25) / 22.5 - 0.02)
return dirs[ix % 16]
def wind_kn(ms):
"Convert wind from metres per second to knots"
if ms is None:
return None
return ms * 3.6 / 1.852
def wind_bft(ms):
"Convert wind from metres per second to Beaufort scale"
_bft_threshold = (0.3, 1.5, 3.4, 5.4, 7.9, 10.7, 13.8, 17.1, 20.7, 24.4, 28.4, 32.6)
if ms is None:
return None
for bft in range(len(_bft_threshold)):
if ms < _bft_threshold[bft]:
return bft
return len(_bft_threshold)
def beaufort(b, label):
_beaufort_sea = ('Sea like a mirror',
'Ripples with the appearance of scales are formed, but without foam crests',
'Small wavelets, still short, but more pronounced. Crests have a glassy appearance and do '
'not break',
'Large wavelets. Crests begin to break. Foam of glassy appearance. Perhaps scattered white '
'horses',
'Small waves, becoming larger; fairly frequent white horses',
'Moderate waves, taking a more pronounced long form; many white horses are formed.'
'Chance of some spray',
'Large waves begin to form; the white foam crests are more extensive everywhere. '
'Probably some spray',
'Sea heaps up and white foam from breaking waves begins to be blown in streaks along the '
'direction of the wind',
'Moderately high waves of greater length; edges of crests begin to break into spindrift. '
'The foam is blown in well-marked streaks along the direction of the wind',
'High waves. Dense streaks of foam along the direction of the wind. Crests of waves begin '
'to topple, tumble and roll over. Spray may affect visibility',
'Very high waves with long over-hanging crests. The resulting foam, in great patches, '
'is blown in dense white streaks along the direction of the wind. On the whole the surface '
'of the sea takes on a white appearance. The tumbling of the sea becomes heavy and '
'shock-like. Visibility affected',
'Exceptionally high waves (small and medium-size ships might disappear behind the waves). '
'The sea is completely covered with long white patches of foam flying along the direction '
'of the wind. Everywhere the edges of the wave crests are blown into froth. Visibility '
'affected',
'The air is filled with foam and spray. Sea completely white with driving spray; '
'visibility very seriously affected')
_beaufort_land = ('Calm. Smoke rises vertically',
'Wind motion visible in smoke',
'Wind felt on exposed skin. Leaves rustle',
'Leaves and smaller twigs in constant motion',
'Dust and loose paper raised. Small branches begin to move',
'Branches of a moderate size move. Small trees begin to sway',
'Large branches in motion. Whistling heard in overhead wires. Umbrella use becomes '
'difficult. Empty plastic garbage cans tip over',
'Whole trees in motion. Effort needed to walk against the wind. Swaying of skyscrapers '
'may be felt, especially by people on upper floors',
'Twigs broken from trees. Cars veer on road',
'Larger branches break off trees, and some small trees blow over. Construction/temporary '
'signs and barricades blow over. Damage to circus tents and canopies',
'Trees are broken off or uprooted, saplings bent and deformed, poorly attached asphalt '
'shingles and shingles in poor condition peel off roofs',
'Widespread vegetation damage. More damage to most roofing surfaces, asphalt tiles that '
'have curled up and/or fractured due to age may break away completely',
'Considerable and widespread damage to vegetation, a few windows broken, structural damage '
'to mobile homes and poorly constructed sheds and barns. Debris may be hurled about')
_beaufort_label = ('Calm', 'Light Air', 'Light Breeze', 'Gentle Breeze', 'Moderate Breeze',
'Fresh Breeze', 'Strong Breeze', 'Near Gale', 'Gale', 'Severe Gale', 'Storm',
'Violent Storm', 'Hurricane')
if label == 'sea':
return _beaufort_sea[b]
elif label == 'land':
return _beaufort_land[b]
else:
return _beaufort_label[b]
def dew_point(temp, hum):
"""Compute dew point, using formula from
http://en.wikipedia.org/wiki/Dew_point.
"""
if temp is None or hum is None:
return None
a = 17.27
b = 237.7
gamma = ((a * temp) / (b + temp)) + math.log(float(hum) / 100.0)
return (b * gamma) / (a - gamma) - 273.15
def getArgs(argv=None):
'''
Command line arguments...
'''
parser = argparse.ArgumentParser(description='Get weather from OpenWeather.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-c', '--city',
default='Iboih,ID',
help='Forecast location')
parser.add_argument('-w', '--weekly', default=False,
action="store_true",
help='Weekly forecast')
parser.add_argument('-v', '--verbose', default=False,
action="store_true",
help='Some additional useless info like Beaufort scale etc.')
return parser.parse_args(argv)
def dailyForecast(city):
'''
print weather as it is now..
'''
request_uri = weather_uri + 'weather?q=' + city + '&appid=' + openweather_api_key
if verbose:
print (request_uri)
response = requests.get(request_uri)
open_weather_json = json.loads(response.text)
print("==============================================================")
print('\x1b[6;30;42m' + 'City:', open_weather_json['name'], open_weather_json['sys']['country'],
'[',
open_weather_json['coord']['lat'], " / ", open_weather_json['coord']['lon'],
']' + '\x1b[0m')
print('[ Sunrise:', time.strftime('%H:%M',
time.localtime(open_weather_json['sys']['sunrise'])),
'/ Sunset:', time.strftime('%H:%M',
time.localtime(open_weather_json['sys']['sunset'])), ']')
print('')
weather = str(open_weather_json['weather'][0]['description'])
print('Weather:', icon(weather), weather, '\x1b[0m')
print(' Temp:', '{:02.1f}'.format(open_weather_json['main']['temp'] - 273.15) +
u"\u00B0" + 'C')
if verbose:
print(" High:", '{:02.1f}'.format(open_weather_json['main']['temp_max'] - 273.15), "C",
" Low:", '{:02.1f}'.format(open_weather_json['main']['temp_min'] - 273.15), "C")
print(' Humidity [%]:', open_weather_json['main']['humidity'])
print(' Pressure [hPa]:', '{:02.1f}'.format(open_weather_json['main']['pressure']))
if 'deg' in open_weather_json['wind']:
deg = int(open_weather_json['wind']['deg'])
else:
deg = None
speed = float(open_weather_json['wind']['speed'])
print(' Wind [m/s]:', '{:03.1f}'.format(speed), degrees_to_cardinal(deg))
if verbose:
print(' Wind [land]: ' + str(wind_bft(speed)) + 'B',
beaufort(int(wind_bft(speed)), 'land'))
print(' Wind [sea]: ' + str(wind_bft(speed)) + 'B', beaufort(int(wind_bft(speed)), 'sea'))
print(' Wind: ' + '{:03.1f}'.format(wind_kn(speed)), 'knots from',
'{:03.0f}'.format(open_weather_json['wind']['deg']) + u"\u00B0" + '/',
beaufort(int(wind_bft(speed)), 'label'))
print(' Clouds [%]: ', open_weather_json['clouds']['all'])
print(' Dew point:', '{:02.1f}'.format(dew_point(open_weather_json['main']['temp'],
open_weather_json['main']['humidity'])) + u"\u00B0" + 'C')
print(" Time:", time.strftime('%H:%M (%z)', time.localtime(open_weather_json['dt'])))
print("==============================================================")
def weeklyForecast(city):
'''
print weekly forecast
'''
request_uri = weather_uri + 'forecast?q=' + city + '&appid=' + openweather_api_key
# print (request_uri)
response = requests.get(request_uri)
open_weather_json = json.loads(response.text)
print("==============================================================")
print('\x1b[6;30;42m' + 'City:', open_weather_json['city']['name'],
open_weather_json['city']['country'],
' [',
open_weather_json['city']['coord']['lat'], ' / ', open_weather_json['city']['coord']['lon'],
' ]' + '\x1b[0m')
print("")
print('\x1b[6;30;42m' + 'Weekly forecast:' + '\x1b[0m')
table = []
for item in open_weather_json['list']:
weather = str(item['weather'][0]['description'])
table.append([
time.strftime('%a %H:%M', time.localtime(item['dt'])),
'{:02.1f}'.format(item['main']['temp'] - 273.15),
'{:02.1f}'.format(item['main']['pressure']),
item['main']['humidity'],
'{:03.0f}'.format(item['wind']['speed']) + ' ' +
degrees_to_cardinal(int(item['wind']['deg'])),
# item['weather'][0]['description']])
icon(weather) + weather + '\x1b[0m'])
headers = ['Date', 'Temp', 'Pressure', 'Humidity', 'Wind', 'Description']
print(tabulate(table, headers, tablefmt="simple"))
print("==============================================================")
if __name__ == '__main__':
FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
logging.basicConfig(filename='/tmp/weather.log', level=logging.DEBUG,
format=FORMAT, datefmt='%a, %d %b %Y %H:%M:%S',)
logging.info('--- oweather.py logging started ---.')
args = getArgs()
city = args.city
weekly = args.weekly
verbose = args.verbose
if weekly:
weeklyForecast(city)
else:
dailyForecast(city)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment