NuWeather: switch to OneCall API for OpenWeatherMap

This API is more complete and includes things like daily forecasts.
This commit is contained in:
James Lu 2020-08-03 14:13:11 -07:00
parent 47b7b422e4
commit 1eb83ea9df

View File

@ -1,6 +1,6 @@
### ###
# Copyright (c) 2011-2014, Valentin Lorentz # Copyright (c) 2011-2014, Valentin Lorentz
# Copyright (c) 2018-2019, James Lu <james@overdrivenetworks.com> # Copyright (c) 2018-2020, James Lu <james@overdrivenetworks.com>
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@ -180,6 +180,8 @@ class NuWeather(callbacks.Plugin):
@staticmethod @staticmethod
def _format_uv(uv): def _format_uv(uv):
if uv is None:
return _('N/A')
# From https://en.wikipedia.org/wiki/Ultraviolet_index#Index_usage 2018-12-30 # From https://en.wikipedia.org/wiki/Ultraviolet_index#Index_usage 2018-12-30
uv = float(uv) uv = float(uv)
if uv <= 2.9: if uv <= 2.9:
@ -503,71 +505,56 @@ class NuWeather(callbacks.Plugin):
if not latlon: if not latlon:
raise callbacks.Error("Unknown location %s." % location) raise callbacks.Error("Unknown location %s." % location)
lat, lon, __, ___, geocode_backend = latlon lat, lon, display_name, _geocode_id, geocode_backend = latlon
url = 'https://api.openweathermap.org/data/2.5/weather?' + utils.web.urlencode({ url = 'https://api.openweathermap.org/data/2.5/onecall?' + utils.web.urlencode({
'appid': apikey, 'appid': apikey,
'lat': lat, 'lat': lat,
'lon': lon, 'lon': lon,
'units': 'imperial', 'units': 'imperial',
}) })
self.log.debug('NuWeather: using url %s (current data)', url) self.log.debug('NuWeather: using url %s', url)
f = utils.web.getUrl(url, headers=HEADERS).decode('utf-8') f = utils.web.getUrl(url, headers=HEADERS).decode('utf-8')
data = json.loads(f, strict=False) data = json.loads(f, strict=False)
currentdata = data['current']
# XXX: are the units for this consistent across APIs?
if currentdata.get('snow'):
precip = self._format_precip(mm=currentdata['snow']['1h'] * 10)
elif currentdata.get('rain'):
precip = self._format_precip(mm=currentdata['rain']['1h'])
else:
precip = 'N/A'
output = { output = {
'location': '%s, %s' % (data['name'], data['sys']['country']), 'location': display_name,
'poweredby': 'OpenWeatherMap+' + geocode_backend, 'poweredby': 'OpenWeatherMap+' + geocode_backend,
'url': 'https://openweathermap.org/weathermap?' + utils.web.urlencode({ 'url': 'https://openweathermap.org/weathermap?' + utils.web.urlencode({
'lat': lat, 'lat': lat,
'lon': lon, 'lon': lon,
'zoom': 12 'zoom': 12
}), }),
# Unfortunately not all of the fields we use are available
'current': { 'current': {
'condition': data['weather'][0]['description'], 'condition': currentdata['weather'][0]['description'],
'temperature': self._format_temp(f=data['main']['temp']), 'temperature': self._format_temp(f=currentdata['temp']),
'feels_like': 'N/A', 'feels_like': self._format_temp(f=currentdata['feels_like']),
'humidity': self._format_percentage(data['main']['humidity']), 'humidity': self._format_percentage(currentdata['humidity']),
'precip': 'N/A', 'precip': precip,
'wind': self._format_distance(mi=data['wind']['speed'], speed=True), 'wind': self._format_distance(mi=currentdata['wind_speed'], speed=True),
'wind_gust': 'N/A', 'wind_dir': self._wind_direction(currentdata['wind_deg']),
'wind_dir': self._wind_direction(data['wind']['deg']), 'wind_gust': self._format_distance(mi=currentdata.get('wind_gust'), speed=True),
'uv': 'N/A', # Requires a separate API call 'uv': self._format_uv(currentdata.get('uvi')),
'visibility': self._format_distance(km=data['visibility']/1000), 'visibility': self._format_distance(km=currentdata['visibility']/1000),
} }
} }
tzoffset = data['timezone']
# Get extended forecast (a separate API call)
url = 'https://api.openweathermap.org/data/2.5/forecast?' + utils.web.urlencode({
'appid': apikey,
'lat': lat,
'lon': lon,
'units': 'imperial',
})
self.log.debug('NuWeather: using url %s (extended forecast)', url)
f = utils.web.getUrl(url, headers=HEADERS).decode('utf-8')
data = json.loads(f, strict=False)
def _owm_make_dayname(utcts):
# OWM gives timestamps in UTC, but the /weather endpoint provides a timezone
# offset in seconds. Use this data with pendulum to generate a human readable time.
ts = utcts + tzoffset
if pendulum:
dt = pendulum.from_timestamp(ts)
return dt.format('dddd hA') # Return strings like "Sunday 8PM"
return ts # fallback
# OWM's 5 day forecast gives data by 3 hour intervals. The actual daily forecast
# requires a subscription.
output['forecast'] = [ output['forecast'] = [
{'dayname': _owm_make_dayname(forecast['dt']), {'dayname': self._get_dayname(forecast['dt'], idx, tz=data['timezone']),
'max': self._format_temp(f=forecast['main']['temp_max']), 'max': self._format_temp(f=forecast['temp']['max']),
'min': self._format_temp(f=forecast['main']['temp_min']), 'min': self._format_temp(f=forecast['temp']['min']),
'summary': forecast['weather'][0]['description']} 'summary': forecast['weather'][0]['description']}
for forecast in data['list'][1::2] # grab data every 6 hours, excluding first pocket for idx, forecast in enumerate(data['daily'])
] ]
return output return output