Yahoo! made a change to their weather API authentication rules on 15/03/2016 (See
) which broke many applications using their weather service including my Kodi installations. I have changed the KODI Yahoo! Weather add-on (default.py) to use YQL over SSL to fetch the weather instead of the RSS feed API which is used in version 3.0.9. The code is actually much neater using JSON instead of XML. This fixed my issue.
Here is the working default.py code. It would be nice if ronie could update the addon in the KODI repository if the code is deemed to work. In the meantime, you can backup your current default.py and replace it with this code (be careful with Python indenting). Hope it's nice weather where you live
Code:
import os, sys, socket, urllib2
from xml.dom import minidom
import xbmc, xbmcgui, xbmcaddon
if sys.version_info < (2, 7):
import simplejson
else:
import json as simplejson
ADDON = xbmcaddon.Addon()
ADDONNAME = ADDON.getAddonInfo('name')
ADDONID = ADDON.getAddonInfo('id')
ADDONVERSION = ADDON.getAddonInfo('version')
CWD = ADDON.getAddonInfo('path').decode("utf-8")
RESOURCE = xbmc.translatePath( os.path.join( CWD, 'resources', 'lib' ).encode("utf-8") ).decode("utf-8")
sys.path.append(RESOURCE)
from utilities import *
YQL_URL = 'https://query.yahooapis.com/v1/public/yql?q=%s&format=json'
LOC_QUERY = 'select * from geo.places where text="%s"'
FORECAST_QUERY = 'select * from weather.forecast where woeid=%s and u="c"'
WEATHER_ICON = xbmc.translatePath('special://temp/weather/%s.png').decode("utf-8")
WEATHER_WINDOW = xbmcgui.Window(12600)
socket.setdefaulttimeout(10)
def log(txt):
if isinstance (txt,str):
txt = txt.decode("utf-8")
message = u'%s: %s' % (ADDONID, txt)
xbmc.log(msg=message.encode("utf-8"), level=xbmc.LOGDEBUG)
def set_property(name, value):
WEATHER_WINDOW.setProperty(name, value)
def refresh_locations():
locations = 0
for count in range(1, 6):
loc_name = ADDON.getSetting('Location%s' % count)
if loc_name != '':
locations += 1
set_property('Location%s' % count, loc_name)
set_property('Locations', str(locations))
log('available locations: %s' % str(locations))
def location(loc):
items = []
locs = []
locids = []
log('searching for location: %s' % loc)
query = find_location(loc)
log('location data: %s' % query)
data = parse_data(query)
if data != '' and data.has_key('query') and data['query'].has_key('results') and data['query']['results'].has_key('place'):
if isinstance (data['query']['results']['place'],list):
for item in data['query']['results']['place']:
listitem = item['name'] + ' (' + (item['admin1']['content'] + ' - ' if item['admin1'] is not None else '') + item['country']['code'] + ')'
location = item['name'] + ' (' + item['country']['code'] + ')'
locationid = item['woeid']
items.append(listitem)
locs.append(location)
locids.append(locationid)
else:
listitem = data['query']['results']['place']['name'] + ' (' + data['query']['results']['place']['admin1']['content'] + ' - ' + data['query']['results']['place']['country']['code'] + ')'
location = data['query']['results']['place']['name'] + ' (' + data['query']['results']['place']['country']['code'] + ')'
locationid = data['query']['results']['place']['woeid']
items.append(listitem)
locs.append(location)
locids.append(locationid)
return items, locs, locids
def find_location(loc):
query = urllib2.quote(LOC_QUERY % loc)
url = YQL_URL % query
try:
req = urllib2.urlopen(url)
response = req.read()
req.close()
except:
response = ''
return response
def parse_data(reply):
try:
data = simplejson.loads(reply)
except:
log('failed to parse weather data')
data = ''
return data
def forecast(loc,locid):
log('weather location: %s' % locid)
retry = 0
while (retry < 6) and (not MONITOR.abortRequested()):
query = get_weather(locid)
if query != '':
retry = 6
else:
retry += 1
xbmc.sleep(10000)
log('weather download failed')
log('forecast data: %s' % query)
if query != '':
properties(query,loc)
else:
clear()
def get_weather(locid):
query = urllib2.quote(FORECAST_QUERY % locid)
url = YQL_URL % query
try:
req = urllib2.urlopen(url)
response = req.read()
req.close()
except:
response = ''
return response
def clear():
set_property('Current.Condition' , 'N/A')
set_property('Current.Temperature' , '0')
set_property('Current.Wind' , '0')
set_property('Current.WindDirection' , 'N/A')
set_property('Current.Humidity' , '0')
set_property('Current.FeelsLike' , '0')
set_property('Current.UVIndex' , '0')
set_property('Current.DewPoint' , '0')
set_property('Current.OutlookIcon' , 'na.png')
set_property('Current.FanartCode' , 'na')
for count in range (0, 5):
set_property('Day%i.Title' % count, 'N/A')
set_property('Day%i.HighTemp' % count, '0')
set_property('Day%i.LowTemp' % count, '0')
set_property('Day%i.Outlook' % count, 'N/A')
set_property('Day%i.OutlookIcon' % count, 'na.png')
set_property('Day%i.FanartCode' % count, 'na')
def properties(data,loc):
json = parse_data(data)
wind = json['query']['results']['channel']['wind']
atmosphere = json['query']['results']['channel']['atmosphere']
astronomy = json['query']['results']['channel']['astronomy']
condition = json['query']['results']['channel']['item']['condition']
forecast = json['query']['results']['channel']['item']['forecast']
set_property('Current.Location' , loc)
set_property('Current.Condition' , condition['text'].replace('/', ' / '))
set_property('Current.Temperature' , condition['temp'])
set_property('Current.Wind' , wind['speed'])
if (wind['direction'] != ''):
set_property('Current.WindDirection' , winddir(int(wind['direction'])))
else:
set_property('Current.WindDirection' , '')
set_property('Current.WindChill' , wind['chill'])
set_property('Current.Humidity' , atmosphere['humidity'])
set_property('Current.Visibility' , atmosphere['visibility'])
set_property('Current.Pressure' , atmosphere['pressure'])
if (wind['speed'] != ''):
set_property('Current.FeelsLike' , feelslike(int(condition['temp']), int(round(float(wind['speed']) + 0.5))))
else:
set_property('Current.FeelsLike' , '')
if (condition['temp'] != '') and (atmosphere['humidity'] != ''):
set_property('Current.DewPoint' , dewpoint(int(condition['temp']), int(atmosphere['humidity'])))
else:
set_property('Current.DewPoint' , '')
set_property('Current.UVIndex' , '')
set_property('Current.OutlookIcon' , '%s.png' % condition['code']) # Kodi translates it to Current.ConditionIcon
set_property('Current.FanartCode' , condition['code'])
set_property('Today.Sunrise' , astronomy['sunrise'])
set_property('Today.Sunset' , astronomy['sunset'])
for count, item in enumerate(forecast):
set_property('Day%i.Title' % count, DAYS[item['day']])
set_property('Day%i.HighTemp' % count, item['high'])
set_property('Day%i.LowTemp' % count, item['low'])
set_property('Day%i.Outlook' % count, item['text'].replace('/', ' / '))
set_property('Day%i.OutlookIcon' % count, '%s.png' % item['code'])
set_property('Day%i.FanartCode' % count, item['code'])
class MyMonitor(xbmc.Monitor):
def __init__(self, *args, **kwargs):
xbmc.Monitor.__init__(self)
log('version %s started: %s' % (ADDONVERSION, sys.argv))
MONITOR = MyMonitor()
set_property('Forecast.IsFetched' , '')
set_property('Current.IsFetched' , '')
set_property('Today.IsFetched' , '')
set_property('Daily.IsFetched' , '')
set_property('Weekend.IsFetched' , '')
set_property('36Hour.IsFetched' , '')
set_property('Hourly.IsFetched' , '')
set_property('Alerts.IsFetched' , '')
set_property('Map.IsFetched' , '')
set_property('WeatherProvider' , ADDONNAME)
set_property('WeatherProviderLogo', xbmc.translatePath(os.path.join(CWD, 'resources', 'banner.png')))
if sys.argv[1].startswith('Location'):
keyboard = xbmc.Keyboard('', xbmc.getLocalizedString(14024), False)
keyboard.doModal()
if (keyboard.isConfirmed() and keyboard.getText() != ''):
text = keyboard.getText()
items, locs, locids = location(text)
dialog = xbmcgui.Dialog()
if locs != []:
selected = dialog.select(xbmc.getLocalizedString(396), items)
if selected != -1:
ADDON.setSetting(sys.argv[1], locs[selected])
ADDON.setSetting(sys.argv[1] + 'id', locids[selected])
log('selected location: %s' % locs[selected])
else:
log('no locations found')
dialog.ok(ADDONNAME, xbmc.getLocalizedString(284))
else:
location = ADDON.getSetting('Location%s' % sys.argv[1])
locationid = ADDON.getSetting('Location%sid' % sys.argv[1])
if (locationid == '') and (sys.argv[1] != '1'):
location = ADDON.getSetting('Location1')
locationid = ADDON.getSetting('Location1id')
log('trying location 1 instead')
if not locationid == '':
forecast(location, locationid)
else:
log('empty location id')
clear()
refresh_locations()
log('finished')