3 from datetime import datetime
5 from typing import Dict, List
12 import kiosk_secrets as secrets
15 class weather_renderer(renderer.abstaining_renderer):
16 """A renderer to fetch forecast from wunderground."""
18 def __init__(self, name_to_timeout_dict: Dict[str, int], file_prefix: str) -> None:
19 super().__init__(name_to_timeout_dict)
20 self.file_prefix = file_prefix
22 def periodic_render(self, key: str) -> bool:
23 return self.fetch_weather()
26 self, conditions: List[str], rain: List[float], snow: List[float]
28 # rain snow clouds sun
32 # mostlycloudy.gif F F 6+ X
33 # partlycloudy.gif F F 4+ 4-
35 # partlysunny.gif F F X 5+
36 # mostlysunny.gif F F X 6+
39 # flurries.gif F T X X (<1")
40 # snow.gif F T X X (else)
48 count = min(len(conditions), len(rain), len(snow))
49 for x in range(0, count):
50 seen_rain = rain[x] > 0
51 seen_snow = snow[x] > 0
53 txt = conditions[x].lower()
56 if "clear" in txt or "sun" in txt:
59 if seen_rain and seen_snow:
72 return "mostlycloudy.gif"
73 elif cloud_count >= 4:
74 return "partlycloudy.gif"
77 elif clear_count >= 6:
78 return "mostlysunny.gif"
79 elif clear_count >= 4:
80 return "partlysunny.gif"
83 def fetch_weather(self) -> bool:
84 if self.file_prefix == "stevens":
85 text_location = "Stevens Pass, WA"
86 param = "lat=47.74&lon=-121.08"
87 elif self.file_prefix == "telma":
88 text_location = "Telma, WA"
89 param = "lat=47.84&lon=-120.81"
91 text_location = "Bellevue, WA"
94 www = urllib.request.urlopen(
95 "http://api.openweathermap.org/data/2.5/forecast?%s&APPID=%s&units=imperial"
96 % (param, secrets.openweather_key)
100 parsed_json = json.loads(response)
102 # https://openweathermap.org/forecast5
108 # "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},
110 # {"id":800,"main":"Clear","description":"clear sky","icon":"02n"}
112 # "clouds":{"all":8},
113 # "wind":{"speed":4.77,"deg":232.505},
116 # "dt_txt":"2017-01-30 18:00:00"
118 # {"dt":1485810000,....
120 with file_writer.file_writer(f"weather-{self.file_prefix}_3_10800.html") as f:
122 <h1>Upcoming weather at {text_location}:</h1>
124 <script src="/kiosk/Chart.js"></script>""")
127 function makePrecipChart(name, xValues, yValues) {
136 backgroundColor: "rgba(0,0,255,0.1)",
137 borderColor: "rgba(0,0,255,0.9)",
166 return new Chart(name, config);
171 count = parsed_json["cnt"]
176 wind: Dict[str, List[float]] = {}
177 conditions: Dict[str, List[str]] = {}
178 rain: Dict[str, List[float]] = {}
179 snow: Dict[str, List[float]] = {}
180 precip: Dict[str, List[float]] = {}
182 for x in range(0, count):
183 data = parsed_json["list"][x]
184 dt = data["dt_txt"] # 2019-10-07 18:00:00
185 (date, time) = dt.split(' ')
187 conditions[date] = []
195 for x in range(0, count):
196 data = parsed_json["list"][x]
197 dt = data["dt_txt"] # 2019-10-07 18:00:00
198 (date, time) = dt.split(' ')
202 temp = data["main"]["temp"]
203 if highs[date] is None or highs[date] < temp:
205 if lows[date] is None or temp < lows[date]:
207 wind[date].append(data["wind"]["speed"])
208 conditions[date].append(data["weather"][0]["main"])
209 if "rain" in data and "3h" in data["rain"]:
210 rain[date].append(data["rain"]["3h"])
213 if "snow" in data and "3h" in data["snow"]:
214 snow[date].append(data["snow"]["3h"])
218 # {u'clouds': {u'all': 0},
219 # u'sys': {u'pod': u'd'},
220 # u'dt_txt': u'2019-10-09 21:00:00',
222 # {u'main': u'Clear',
225 # u'description': u'clear sky'}
231 # u'grnd_level': 1018.95,
232 # u'temp_max': 54.74,
233 # u'sea_level': 1026.46,
235 # u'pressure': 1026.46,
238 # u'wind': {u'speed': 6.31, u'deg': 10.09}}
241 for date in sorted(highs.keys()):
242 day = datetime.fromtimestamp(ts[date])
243 formatted_date = day.strftime("%a %e %b")
244 if formatted_date in days_seen:
246 days_seen.add(formatted_date)
247 total = len(days_seen)
251 for n, date in enumerate(sorted(highs.keys())):
255 f.write('<div STYLE="overflow:hidden; width:100%; height:500px">')
256 remaining = total - n
260 width = f'{100/remaining}%'
263 aggregate_precip = 0.0
264 for r, s in zip(rain[date], snow[date]):
266 aggregate_precip += aggregate
267 precip[date].append(aggregate)
269 while len(precip[date]) < 8:
270 precip[date].insert(0, 0)
273 day = datetime.fromtimestamp(ts[date])
274 formatted_date = day.strftime("%a %e %b")
275 if formatted_date in days_seen:
277 days_seen.add(formatted_date)
279 f'<div style="width:{width}; height:500px; float:left"><table>\n'
285 <td colspan=3 height=50>
287 <font size=7><b>{formatted_date}</b></font>
293 icon = self.pick_icon(conditions[date], rain[date], snow[date])
296 <td colspan=3 height=100>
298 <img src="/kiosk/images/weather/{icon}" height=145>
305 if lows[date] <= 32.5:
309 <td width=33% align=left>
310 <font color="{color}" size=6>
311 <b>{int(lows[date])}°F </b>
316 # Total aggregate_precip in inches
317 aggregate_precip *= 0.0393701
318 if aggregate_precip > 0.025:
322 <font style="background-color:#dfdfff; color:#003355" size=6>
323 <b>{aggregate_precip:3.1f}”</b>
329 f.write(" <td width=33%> </td>\n")
331 # High temp + precip chart
333 if highs[date] >= 80:
337 <font color="{color}" size=6>
338 <b> {int(highs[date])}°F</b>
346 <td colspan=3 style="vertical-align:top;">
347 <canvas id="myChart{n}" style="width:100%;max-width:400px;height:180px;"></canvas>
351 var yValues{n} = ''')
352 f.write(precip[date].__repr__())
354 var xValues{n} = [ 3, 6, 9, 12, 15, 18, 21, 24 ];
355 makePrecipChart("myChart{n}", xValues{n}, yValues{n});
360 f.write("</div></center>")
364 x = weather_renderer({"Stevens": 1000}, "stevens")
365 x.periodic_render("Stevens")