Update:
- 08/14/2016 Able to send HTML formatted warning Email with current radar map
- 08/13/2014 Fixed the API overuse
I have rewritten the weather tracker. The script now requests the weather forecast data in the json format, instead of prior xml. Using json format makes my code much easier to read, because the decoded data from json is dictionary data type used in python. Besides, it reduces my api calls significantly the the app refreshes, now every forecast report only needs 6 api calls from wunderground.
I also added the function to determine the geolocation based on the public IP. Public IP is obtained by calling the api from ipify.org, and this IP address is sent to freegeopip.com to obtain the zipcode.
The script now is interpreting the UV index not just as a number, but as a suggestion how to understand the UV. Same thing is applied to the Heat Index.
Weather forecast is now able to look ahead for the next 7 hours, if the chance of precipitation is above 50%, an email will be sent to the recipients as a weather alert.
Here is what it looks like now,
from urllib.request import urlopen import json key = 'ENTER YOUR API' class weatherUnderground(): #=================================================================== def getPublicIP(self): try: response = urlopen('http://api.ipify.org/?format=json').read().decode('utf-8') except: return None else: jsonIP = json.loads(response) PublicIP = jsonIP['ip'] return PublicIP #=================================================================== def getGeoLocation(self, IP): if IP == None: return '45401' else: try: url = 'http://freegeoip.net/json/' + IP response = urlopen(url).read().decode('utf-8') except: return '453000' else: jsonGeo = json.loads(response) return jsonGeo['zip_code'] #=================================================================== def getWeatherInfo(self, arg_zip): zipcode = arg_zip info = [] url='http://api.wunderground.com/api/' + key + '/conditions/q/' + zipcode + '.json' try: weather_html = urlopen(url).read().decode('utf-8') except: return None else: weather_obj = json.loads(weather_html) city = weather_obj['current_observation']['display_location']['city'] lati = weather_obj['current_observation']['display_location']['latitude'] long = weather_obj['current_observation']['display_location']['longitude'] elev = weather_obj['current_observation']['display_location']['elevation'] weather = weather_obj['current_observation']['weather'] temp_F = weather_obj['current_observation']['temp_f'] feelslike_temp_F = weather_obj['current_observation']['feelslike_f'] humidity = weather_obj['current_observation']['relative_humidity'] UV = weather_obj['current_observation']['UV'] HI = weather_obj['current_observation']['heat_index_f'] info = [city, lati, long, elev, weather, temp_F, feelslike_temp_F, humidity, UV, HI] return info #=================================================================== def _init_(self): pass #=================================================================== def checkUV(self, arg_uv): info = '' suggestion = '' try: uv = int(arg_uv) except: return None else: if uv <= 2: info = 'Minimal' suggestion = 'Sun burn in 60 minutes. Apply SPF 15 sunscreen.' elif uv > 2 and uv <=4: info = 'Low' suggestion = 'Sun burn in 45 minutes. Apply SPF 15 sunscreen.' elif uv > 4 and uv <= 6: info = 'Moderate' suggestion = 'Sun burn in 30 minutes. Apply SPF 15 sunscreen.' elif uv > 6 and uv <= 9: info = 'High' suggestion = 'Sun burn in 15-25 minutes. Apply SPF 15 to 30 sunscreen.' elif uv > 9: info = 'Very high' suggestion = 'Sun burn in 10 minutes. Apply SPF 30 sunscreen.' else: info = 'UV index out of range' suggestion = 'No suggestions' return {'info': info, 'suggestion': suggestion} #=================================================================== def checkHI(self, arg_HI): info = '' suggestion = '' try: HI = int(arg_HI) except: return None else: if HI in range(80, 91): info = 'Caution' suggestion = 'Fatigue is possible with prolonged exposure and activity. Continuing activity could result in heat cramps.' elif HI in range(91, 105): info = 'Extreme Caution' suggestion = 'Heat cramps and heat exhaustion are possible. Continuing activity could result in heat stroke.' elif HI in range(105, 130): info = 'Danger' suggestion = 'Heat cramps and heat exhaustion are likely, heat stroke is probable with continued activity.' elif HI > 130: info = 'Extreme Danger' suggestion = 'heat stroke is imminent..' else: info = 'HI index out of range' suggestion = 'No suggestions' return {'info': info, 'suggestion': suggestion} #=================================================================== def hourlyForecast(self, arg_zip): zipcode = arg_zip url = 'http://api.wunderground.com/api/' + key + '/hourly/q/' + zipcode +'.json' pop = [] weather = [] hour = [] try: forecast_html = urlopen(url).read().decode('utf-8') except: return None else: forecast_obj = json.loads(forecast_html) for i in range(0, 7): pop.append(forecast_obj['hourly_forecast'][i]['pop']) weather.append(forecast_obj['hourly_forecast'][i]['wx']) hour.append(forecast_obj['hourly_forecast'][i]['FCTTIME']['civil']) return [pop, weather, hour] #=================================================================== def sendMsg(self): ip = self.getPublicIP() zipCode = self.getGeoLocation(ip) current_weather = self.getWeatherInfo(zipCode) if current_weather != None: UV_suggestion = self.checkUV(current_weather[8]) HI_suggestion = self.checkHI(current_weather[9]) rain = self.willRain(zipCode) badWeather = 0 heatWeather = 0 if UV_suggestion != None: UVMsg = ('UV level: {}\nSuggestion: {}').format(UV_suggestion['info'], UV_suggestion['suggestion']) else: UVMsg = 'Error in fetching UV data.' if HI_suggestion != None: HIMsg = ('Heat Index: {}\nNote: {}').format(HI_suggestion['info'], HI_suggestion['suggestion']) else: HIMsg = 'Error in fetching HI data.' try: HI = int(current_weather[9]) except: heatWeather = 0 else: if HI > 100: heatWeather = 1 else: heatWeather = 0 currentWeather = ('Current Weather in {}\n\n{}, {} F, feels like {} F\n\n{}\n\n{}').format(current_weather[0],current_weather[4],current_weather[5],current_weather[6], UVMsg, HIMsg) if rain != None: futureWeather = 'Looking ahead: Expect {} at {}.\nChance of Rain: {}%.\n\n'.format(rain[1], rain[2], rain[0]) badWeather = 1 else: futureWeather = 'Looking ahead: No precipitation in 7 hours.' badWeather = 0 finalMsg = currentWeather + '\n\n' + futureWeather forecastTable = '{:15s} {:15s} {:20s}\n'.format('Time', 'Weather','Chance of Precipitation') hourly_Forecast = self.hourlyForecast(zipCode) if hourly_Forecast != None: for i in range(0, 3): tempHolder = '{:15s} {:15s} {:20s}\n'.format(hourly_Forecast[2][i], hourly_Forecast[1][i],hourly_Forecast[0][i]+'%') forecastTable = forecastTable + tempHolder #+ '\n' else: forecastTable = 'Error in fetching forecast' return [finalMsg, badWeather, forecastTable, heatWeather] else: return ['Error',0,'Error','0'] #=================================================================== def willRain(self, zipCode): hourly_Forecast = self.hourlyForecast(zipCode) #badWeather = ['snow','rain','rain showers','snow showers','thunderstorm','chance of rain'] #isBadPop = 0 #isBadWeather = 0 location = 19 if hourly_Forecast != None: for i in range(0, 7): if int(hourly_Forecast[0][i]) > 60: location = i break else: return None if location != 19: return [hourly_Forecast[0][location], hourly_Forecast[1][location], hourly_Forecast[2][location]] else: return None