1 from datetime import datetime
7 import urllib.request, urllib.error, urllib.parse
10 class weather_renderer(renderer.debuggable_abstaining_renderer):
11 """A renderer to fetch forecast from wunderground."""
16 super(weather_renderer, self).__init__(name_to_timeout_dict, False)
17 self.file_prefix = file_prefix
19 def debug_prefix(self):
20 return "weather(%s)" % (self.file_prefix)
22 def periodic_render(self, key):
23 return self.fetch_weather()
25 def describe_time(self, index):
35 def describe_wind(self, mph):
47 def describe_magnitude(self, mm):
55 def describe_precip(self, rain, snow):
56 if rain == 0 and snow == 0:
57 return "no precipitation"
58 magnitude = rain + snow
59 if rain > 0 and snow > 0:
60 return "a %s mix of rain and snow" % self.describe_magnitude(magnitude)
62 return "%s rain" % self.describe_magnitude(magnitude)
64 return "%s snow" % self.describe_magnitude(magnitude)
66 def fix_caps(self, s):
69 for x in s.split("."):
71 r += x.capitalize() + ". "
72 r = r.replace(". .", ".")
75 def pick_icon(self, conditions, rain, snow):
76 # rain snow clouds sun
80 # mostlycloudy.gif F F 6+ X
81 # partlycloudy.gif F F 4+ 4-
83 # partlysunny.gif F F X 5+
84 # mostlysunny.gif F F X 6+
87 # flurries.gif F T X X (<1")
88 # snow.gif F T X X (else)
96 count = min(len(conditions), len(rain), len(snow))
97 for x in range(0, count):
98 seen_rain = rain[x] > 0;
99 seen_snow = snow[x] > 0;
100 total_snow += snow[x]
101 txt = conditions[x].lower()
104 if ("clear" in txt or "sun" in txt):
107 if (seen_rain and seen_snow):
108 if (total_snow < 10):
113 if (total_snow < 10):
114 return "flurries.gif"
119 if (cloud_count >= 6):
120 return "mostlycloudy.gif"
121 elif (cloud_count >= 4):
122 return "partlycloudy.gif"
123 if (clear_count >= 7):
125 elif (clear_count >= 6):
126 return "mostlysunny.gif"
127 elif (clear_count >= 4):
128 return "partlysunny.gif"
131 def describe_weather(self,
133 wind, conditions, rain, snow):
136 # -onight------ -morning----- -afternoon-- -evening----
137 # 12a-3a 3a-6a 6a-9a 9a-12p 12p-3p 3p-6p 6p-9p 9p-12p
138 # Wind: [12.1 3.06 3.47 4.12 3.69 3.31 2.73 2.1]
139 # Conditions: [Clouds Clouds Clouds Clouds Clouds Clouds Clear Clear]
140 # Rain: [0.4 0.2 0 0 0 0 0 0]
141 # Snow: [0 0 0 0 0 0 0 0]
144 count = min(len(wind), len(conditions), len(rain), len(snow))
151 for x in range(0, count):
152 time = self.describe_time(x)
162 if (txt != lcondition):
163 if txt != "Snow" and txt != "Rain":
168 txt = self.describe_wind(wind[x])
170 if (len(current) > 0):
172 current += txt + " winds"
176 txt = self.describe_precip(rain[x], snow[x])
178 if (len(current) > 0):
189 if (random.randint(0, 3) == 0):
190 if (time != "overnight"):
191 descr += current + " in the " + time + ". "
192 descr += current + " overnight. "
194 if (time != "overnight"):
196 descr += time + ", " + current + ". "
198 current = current.replace("cloudy", "clouds")
199 descr += current + " developing. "
201 if (ltime == "overnight" or ltime == "morning"):
202 descr += "Conditions continuing the rest of the day. "
203 descr = descr.replace("with breezy winds", "and breezy")
204 descr = descr.replace("Clear developing", "Skies clearing")
205 descr = self.fix_caps(descr)
208 def fetch_weather(self):
209 if self.file_prefix == "stevens":
210 text_location = "Stevens Pass, WA"
211 param = "lat=47.74&lon=-121.08"
212 elif self.file_prefix == "telma":
213 text_location = "Telma, WA"
214 param = "lat=47.84&lon=-120.81"
216 text_location = "Bellevue, WA"
219 www = urllib.request.urlopen('http://api.openweathermap.org/data/2.5/forecast?%s&APPID=%s&units=imperial' % (
220 param, secrets.openweather_key))
221 response = www.read()
223 parsed_json = json.loads(response)
225 # https://openweathermap.org/forecast5
231 # "main":{"temp":261.45,"temp_min":259.086,"temp_max":261.45,"pressure":1023.48,"sea_level":1045.39,"grnd_level":1023.48,"humidity":79,"temp_kf":2.37},
233 # {"id":800,"main":"Clear","description":"clear sky","icon":"02n"}
235 # "clouds":{"all":8},
236 # "wind":{"speed":4.77,"deg":232.505},
239 # "dt_txt":"2017-01-30 18:00:00"
241 # {"dt":1485810000,....
242 f = file_writer.file_writer('weather-%s_3_10800.html' % self.file_prefix)
244 <h1>Weather at %s:</h1>
247 <table width=99%% cellspacing=10 border=0>
248 <tr>""" % text_location)
249 count = parsed_json['cnt']
258 for x in range(0, count):
259 data = parsed_json['list'][x]
260 dt = data['dt_txt'] # 2019-10-07 18:00:00
261 date = dt.split(" ")[0]
262 time = dt.split(" ")[1]
264 conditions[date] = []
271 for x in range(0, count):
272 data = parsed_json['list'][x]
273 dt = data['dt_txt'] # 2019-10-07 18:00:00
274 date = dt.split(" ")[0]
275 time = dt.split(" ")[1]
279 temp = data["main"]["temp"]
280 if (highs[date] < temp):
282 if (temp < lows[date]):
284 wind[date].append(data["wind"]["speed"])
285 conditions[date].append(data["weather"][0]["main"])
286 if "rain" in data and "3h" in data["rain"]:
287 rain[date].append(data["rain"]["3h"])
290 if "snow" in data and "3h" in data["snow"]:
291 snow[date].append(data["snow"]["3h"])
295 # {u'clouds': {u'all': 0},
296 # u'sys': {u'pod': u'd'},
297 # u'dt_txt': u'2019-10-09 21:00:00',
299 # {u'main': u'Clear',
302 # u'description': u'clear sky'}
308 # u'grnd_level': 1018.95,
309 # u'temp_max': 54.74,
310 # u'sea_level': 1026.46,
312 # u'pressure': 1026.46,
315 # u'wind': {u'speed': 6.31, u'deg': 10.09}}
318 #for x in xrange(0, 5):
319 # fcast = parsed_json['forecast']['txt_forecast']['forecastday'][x]
320 # text = fcast['fcttext']
321 # text = re.subn(r' ([0-9]+)F', r' \1°F', text)[0]
322 # f.write('<td style="vertical-align:top;font-size:75%%"><P STYLE="padding:8px;">%s</P></td>' % text)
323 #f.write('</tr></table>')
327 #f.write("<table border=0 cellspacing=10>\n")
329 for date in sorted(highs.keys()):
330 today = datetime.fromtimestamp(ts[date])
331 formatted_date = today.strftime('%a %e %b')
332 if (formatted_date in days_seen):
334 days_seen[formatted_date] = True
335 num_days = len(list(days_seen.keys()))
338 for date in sorted(highs.keys()):
345 today = datetime.fromtimestamp(ts[date])
346 formatted_date = today.strftime('%a %e %b')
347 if (formatted_date in days_seen):
349 days_seen[formatted_date] = True
350 f.write('<td width=%d%% style="vertical-align:top;">\n' % (100 / num_days))
351 f.write('<table border=0>\n')
354 f.write(' <tr><td colspan=3 height=50><b><center><font size=6>' + formatted_date + '</font></center></b></td></tr>\n')
357 f.write(' <tr><td colspan=3 height=100><center><img src="/icons/weather/%s" height=125></center></td></tr>\n' %
358 self.pick_icon(conditions[date], rain[date], snow[date]))
362 if (lows[date] <= 32.5):
364 f.write(' <tr><td width=33%% align=left><font color="%s"><b>%d°F </b></font></td>\n' % (
365 color, int(lows[date])))
370 f.write(' <td width=33%%><center><b><font style="background-color:#dfdfff; color:#003355">%3.1f"</font></b></center></td>\n' % precip)
372 f.write(' <td width=33%> </td>\n')
376 if (highs[date] >= 80):
378 f.write(' <td align=right><font color="%s"><b> %d°F</b></font></td></tr>\n' % (
379 color, int(highs[date])))
382 f.write('<tr><td colspan=3 style="vertical-align:top;font-size:75%%">%s</td></tr>\n' %
383 self.describe_weather(highs[date], lows[date], wind[date], conditions[date], rain[date], snow[date]))
384 f.write('</table>\n</td>\n')
385 f.write("</tr></table></center>")
388 #x = weather_renderer({"Stevens": 1000},
390 #x.periodic_render("Stevens")