Update weather.com weather backend to reflect API changes (#783)
This commit is contained in:
parent
e0234c1223
commit
dad5eb0c5c
@ -67,52 +67,26 @@ class WeathercomHTMLParser(HTMLParser):
|
||||
# Look for feed information embedded as a javascript variable
|
||||
begin = content.find('window.__data')
|
||||
if begin != -1:
|
||||
weather_data = None
|
||||
self.logger.debug('Located window.__data')
|
||||
# Look for end of JSON dict and end of javascript statement
|
||||
end = content.find('};', begin)
|
||||
if end == -1:
|
||||
self.logger.debug('Failed to locate end of javascript statement')
|
||||
else:
|
||||
# Strip the "window.__data=" from the beginning
|
||||
json_data = self.load_json(
|
||||
content[begin:end + 1].split('=', 1)[1].lstrip()
|
||||
# Strip the "window.__data=" from the beginning and load json
|
||||
json_data = self.load_json(
|
||||
content[begin:].split('=', 1)[1].lstrip()
|
||||
)
|
||||
if json_data is not None:
|
||||
try:
|
||||
weather_data = json_data['dal']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if weather_data is None:
|
||||
self.logger.debug(
|
||||
'Failed to locate weather data in the '
|
||||
'following data: %s', json_data
|
||||
)
|
||||
if json_data is not None:
|
||||
def _find_weather_data(data):
|
||||
'''
|
||||
Helper designed to minimize impact of potential
|
||||
structural changes to this data.
|
||||
'''
|
||||
if isinstance(data, dict):
|
||||
if 'Observation' in data and 'DailyForecast' in data:
|
||||
return data
|
||||
else:
|
||||
for key in data:
|
||||
ret = _find_weather_data(data[key])
|
||||
if ret is not None:
|
||||
return ret
|
||||
return None
|
||||
|
||||
weather_data = _find_weather_data(json_data)
|
||||
if weather_data is None:
|
||||
self.logger.debug(
|
||||
'Failed to locate weather data in the '
|
||||
'following data structure: %s', json_data
|
||||
)
|
||||
else:
|
||||
self.weather_data = weather_data
|
||||
return
|
||||
|
||||
for line in content.splitlines():
|
||||
line = line.strip().rstrip(';')
|
||||
if line.startswith('var adaptorParams'):
|
||||
# Strip off the "var adaptorParams = " from the beginning,
|
||||
# and the javascript semicolon from the end. This will give
|
||||
# us JSON that we can load.
|
||||
weather_data = self.load_json(line.split('=', 1)[1].lstrip())
|
||||
if weather_data is not None:
|
||||
self.weather_data = weather_data
|
||||
return
|
||||
else:
|
||||
self.weather_data = weather_data
|
||||
return
|
||||
|
||||
|
||||
class Weathercom(WeatherBackend):
|
||||
@ -216,15 +190,15 @@ class Weathercom(WeatherBackend):
|
||||
return
|
||||
|
||||
try:
|
||||
observed = self.parser.weather_data['Observation']
|
||||
observed = self.parser.weather_data['getSunV3CurrentObservationsUrlConfig']
|
||||
# Observation data stored under a sub-key containing the
|
||||
# lat/long coordinates. For example:
|
||||
# lat/long coordinates, locale info, etc. For example:
|
||||
#
|
||||
# geocode:41.77,-88.35:language:en-US:units:e
|
||||
#
|
||||
# Since this is the only key under "Observation", we can just
|
||||
# use next(iter(observed)) to get it.
|
||||
observed = observed[next(iter(observed))]['data']['vt1observation']
|
||||
observed = observed[next(iter(observed))]['data']
|
||||
except KeyError:
|
||||
self.logger.error(
|
||||
'Failed to retrieve current conditions from API response. '
|
||||
@ -234,11 +208,10 @@ class Weathercom(WeatherBackend):
|
||||
return
|
||||
|
||||
try:
|
||||
forecast = self.parser.weather_data['DailyForecast']
|
||||
forecast = self.parser.weather_data['getSunV3DailyForecastUrlConfig']
|
||||
# Same as above, use next(iter(forecast)) to drill down to the
|
||||
# correct nested dict level.
|
||||
forecast = forecast[next(iter(forecast))]
|
||||
forecast = forecast['data']['vt1dailyForecast'][0]
|
||||
forecast = forecast[next(iter(forecast))]['data']
|
||||
except (IndexError, KeyError):
|
||||
self.logger.error(
|
||||
'Failed to retrieve forecast data from API response. '
|
||||
@ -248,11 +221,11 @@ class Weathercom(WeatherBackend):
|
||||
return
|
||||
|
||||
try:
|
||||
self.city_name = self.parser.weather_data['Location']
|
||||
location = self.parser.weather_data['getSunV3LocationPointUrlConfig']
|
||||
# Again, same technique as above used to get down to the
|
||||
# correct nested dict level.
|
||||
self.city_name = self.city_name[next(iter(self.city_name))]
|
||||
self.city_name = self.city_name['data']['location']['displayName']
|
||||
location = location[next(iter(location))]
|
||||
self.city_name = location['data']['location']['displayName']
|
||||
except KeyError:
|
||||
self.logger.warning(
|
||||
'Failed to get city name from API response, falling back '
|
||||
@ -260,19 +233,15 @@ class Weathercom(WeatherBackend):
|
||||
)
|
||||
self.city_name = self.location_code
|
||||
|
||||
# Cut off the timezone from the end of the string (it's after the last
|
||||
# space, hence the use of rpartition). International timezones (or ones
|
||||
# outside the system locale) don't seem to be handled well by
|
||||
# datetime.datetime.strptime().
|
||||
try:
|
||||
observation_time_str = str(observed.get('observationTime', ''))
|
||||
observation_time_str = str(observed.get('validTimeLocal', ''))
|
||||
observation_time = datetime.strptime(observation_time_str,
|
||||
'%Y-%d-%yT%H:%M:%S%z')
|
||||
except (ValueError, AttributeError):
|
||||
observation_time = datetime.fromtimestamp(0)
|
||||
|
||||
try:
|
||||
pressure_trend_str = observed.get('barometerTrend', '').lower()
|
||||
pressure_trend_str = observed.get('pressureTendencyTrend', '').lower()
|
||||
except AttributeError:
|
||||
pressure_trend_str = ''
|
||||
|
||||
@ -283,18 +252,14 @@ class Weathercom(WeatherBackend):
|
||||
else:
|
||||
pressure_trend = ''
|
||||
|
||||
self.logger.critical('forecast = %s', forecast)
|
||||
try:
|
||||
high_temp = forecast.get('day', {}).get('temperature', '')
|
||||
high_temp = forecast.get('temperatureMax', [])[0] or ''
|
||||
except (AttributeError, IndexError):
|
||||
high_temp = ''
|
||||
else:
|
||||
if high_temp is None:
|
||||
# In the mid-afternoon, the high temp disappears from the
|
||||
# forecast, so just set high_temp to an empty string.
|
||||
high_temp = ''
|
||||
|
||||
try:
|
||||
low_temp = forecast.get('night', {}).get('temperature', '')
|
||||
low_temp = forecast.get('temperatureMin', [])[0]
|
||||
except (AttributeError, IndexError):
|
||||
low_temp = ''
|
||||
|
||||
@ -310,25 +275,25 @@ class Weathercom(WeatherBackend):
|
||||
visibility_unit = 'km'
|
||||
|
||||
self.data['city'] = self.city_name
|
||||
self.data['condition'] = str(observed.get('phrase', ''))
|
||||
self.data['condition'] = str(observed.get('wxPhraseMedium', ''))
|
||||
self.data['observation_time'] = observation_time
|
||||
self.data['current_temp'] = str(observed.get('temperature', ''))
|
||||
self.data['low_temp'] = str(low_temp)
|
||||
self.data['high_temp'] = str(high_temp)
|
||||
self.data['temp_unit'] = temp_unit
|
||||
self.data['feelslike'] = str(observed.get('feelsLike', ''))
|
||||
self.data['dewpoint'] = str(observed.get('dewPoint', ''))
|
||||
self.data['feelslike'] = str(observed.get('temperatureFeelsLike', ''))
|
||||
self.data['dewpoint'] = str(observed.get('temperatureDewPoint', ''))
|
||||
self.data['wind_speed'] = str(observed.get('windSpeed', ''))
|
||||
self.data['wind_unit'] = wind_unit
|
||||
self.data['wind_direction'] = str(observed.get('windDirCompass', ''))
|
||||
self.data['wind_direction'] = str(observed.get('windDirectionCardinal', ''))
|
||||
# Gust can be None, using "or" to ensure empty string in this case
|
||||
self.data['wind_gust'] = str(observed.get('gust', '') or '')
|
||||
self.data['pressure'] = str(observed.get('altimeter', ''))
|
||||
self.data['wind_gust'] = str(observed.get('windGust', '') or '')
|
||||
self.data['pressure'] = str(observed.get('pressureAltimeter', ''))
|
||||
self.data['pressure_unit'] = pressure_unit
|
||||
self.data['pressure_trend'] = pressure_trend
|
||||
self.data['visibility'] = str(observed.get('visibility', ''))
|
||||
self.data['visibility_unit'] = visibility_unit
|
||||
self.data['humidity'] = str(observed.get('humidity', ''))
|
||||
self.data['humidity'] = str(observed.get('relativeHumidity', ''))
|
||||
self.data['uv_index'] = str(observed.get('uvIndex', ''))
|
||||
except Exception:
|
||||
# Don't let an uncaught exception kill the update thread
|
||||
|
Loading…
Reference in New Issue
Block a user