#!/usr/bin/env python3
-from datetime import datetime
+import logging
import json
-from typing import Dict, List
import urllib.request
import urllib.error
import urllib.parse
+from datetime import datetime
+from collections import defaultdict
+from typing import Dict, List
import file_writer
import renderer
import kiosk_secrets as secrets
+logger = logging.getLogger(__file__)
+
class weather_renderer(renderer.abstaining_renderer):
"""A renderer to fetch forecast from wunderground."""
super().__init__(name_to_timeout_dict)
self.file_prefix = file_prefix
- def periodic_render(self, key: str) -> bool:
- return self.fetch_weather()
-
- def pick_icon(
- self, conditions: List[str], rain: List[float], snow: List[float]
- ) -> str:
+ @staticmethod
+ def pick_icon(conditions: List[str], rain: List[float], snow: List[float]) -> str:
# rain snow clouds sun
# fog.gif
# hazy.gif
return "partlysunny.gif"
return "clear.gif"
+ def periodic_render(self, key: str) -> bool:
+ return self.fetch_weather()
+
def fetch_weather(self) -> bool:
if self.file_prefix == "stevens":
text_location = "Stevens Pass, WA"
else:
text_location = "Bellevue, WA"
param = "id=5786882"
-
- www = urllib.request.urlopen(
- "http://api.openweathermap.org/data/2.5/forecast?%s&APPID=%s&units=imperial"
- % (param, secrets.openweather_key)
- )
+ secret = secrets.openweather_key
+ url = f"http://api.openweathermap.org/data/2.5/forecast?{param}&APPID={secret}&units=imperial"
+ logger.info(f"GETting {url}")
+ www = urllib.request.urlopen(url)
response = www.read()
www.close()
+ if www.getcode() != 200:
+ logger.error("Bad response: {response}")
+ raise Exception(response)
parsed_json = json.loads(response)
+ logger.info("URL read ok")
# https://openweathermap.org/forecast5
# {"cod":"200",
# {"dt":1485810000,....
with file_writer.file_writer(f"weather-{self.file_prefix}_3_10800.html") as f:
- f.write(f"""
+ f.write(
+ f"""
<h1>Upcoming weather at {text_location}:</h1>
<hr>
-<script src="/kiosk/Chart.js"></script>""")
- f.write("""
+<script src="/kiosk/Chart.js"></script>"""
+ )
+ f.write(
+ """
<script>
function makePrecipChart(name, xValues, yValues) {
const config = {
display: false,
beginAtZero: true,
min: 0.0,
- max: 10.0,
+ max: 12.0,
grid: {
display: false,
},
}
</script>
<center>
-""")
+"""
+ )
count = parsed_json["cnt"]
ts = {}
highs = {}
lows = {}
- wind: Dict[str, List[float]] = {}
- conditions: Dict[str, List[str]] = {}
- rain: Dict[str, List[float]] = {}
- snow: Dict[str, List[float]] = {}
- precip: Dict[str, List[float]] = {}
-
- for x in range(0, count):
- data = parsed_json["list"][x]
- dt = data["dt_txt"] # 2019-10-07 18:00:00
- (date, time) = dt.split(' ')
- wind[date] = []
- conditions[date] = []
- highs[date] = None
- lows[date] = None
- rain[date] = []
- snow[date] = []
- precip[date] = []
- ts[date] = 0
+ wind: Dict[str, List[float]] = defaultdict(list)
+ conditions: Dict[str, List[str]] = defaultdict(list)
+ rain: Dict[str, List[float]] = defaultdict(list)
+ snow: Dict[str, List[float]] = defaultdict(list)
+ precip: Dict[str, List[float]] = defaultdict(list)
for x in range(0, count):
data = parsed_json["list"][x]
dt = data["dt_txt"] # 2019-10-07 18:00:00
- (date, time) = dt.split(' ')
+ (date, time) = dt.split(" ")
_ = data["dt"]
- if _ > ts[date]:
+ if _ not in ts or _ > ts[date]:
ts[date] = _
temp = data["main"]["temp"]
- if highs[date] is None or highs[date] < temp:
+
+ # High and low temp
+ if date not in highs or highs[date] < temp:
highs[date] = temp
- if lows[date] is None or temp < lows[date]:
+ if date not in lows or lows[date] > temp:
lows[date] = temp
+
+ # Windspeed and conditions
wind[date].append(data["wind"]["speed"])
conditions[date].append(data["weather"][0]["main"])
+
+ # 3h precipitation (rain / snow)
if "rain" in data and "3h" in data["rain"]:
rain[date].append(data["rain"]["3h"])
else:
if remaining >= 3:
width = "33%"
else:
- width = f'{100/remaining}%'
+ width = f"{100/remaining}%"
- precip[date] = []
- aggregate_precip = 0.0
+ aggregate_daily_precip = 0.0
for r, s in zip(rain[date], snow[date]):
- aggregate = r + s
- aggregate_precip += aggregate
- precip[date].append(aggregate)
+ hourly_aggregate = r + s
+ aggregate_daily_precip += hourly_aggregate
+ precip[date].append(hourly_aggregate)
+ logger.debug(
+ f"Aggregate precip on {date} was {aggregate_daily_precip} cm"
+ )
if first_day:
while len(precip[date]) < 8:
precip[date].insert(0, 0)
- first_day = False
+ first_day = False
day = datetime.fromtimestamp(ts[date])
formatted_date = day.strftime("%a %e %b")
)
# Date
- f.write(f'''
+ f.write(
+ f"""
<tr>
<td colspan=3 height=50>
<center>
<font size=7><b>{formatted_date}</b></font>
</center>
</td>
-</tr>''')
+</tr>"""
+ )
# Conditions icon
- icon = self.pick_icon(conditions[date], rain[date], snow[date])
- f.write(f'''
+ icon = weather_renderer.pick_icon(
+ conditions[date], rain[date], snow[date]
+ )
+ f.write(
+ f"""
<tr>
<td colspan=3 height=100>
<center>
<img src="/kiosk/images/weather/{icon}" height=145>
</center>
</td>
-</tr>''')
+</tr>"""
+ )
# Low temp -- left
color = "#000099"
if lows[date] <= 32.5:
color = "#009999"
- f.write(f'''
+ f.write(
+ f"""
<tr>
<td width=33% align=left>
<font color="{color}" size=6>
<b>{int(lows[date])}°F </b>
</font>
</td>
-''')
+"""
+ )
# Total aggregate_precip in inches
- aggregate_precip *= 0.0393701
- if aggregate_precip > 0.025:
- f.write(f'''
+ aggregate_daily_precip /= 2.54
+ if aggregate_daily_precip > 0.025:
+ f.write(
+ f"""
<td width=33%>
<center>
<font style="background-color:#dfdfff; color:#003355" size=6>
- <b>{aggregate_precip:3.1f}”</b>
+ <b>{aggregate_daily_precip:3.1f}”</b>
</font>
</center>
</td>
-''')
+"""
+ )
else:
f.write(" <td width=33%> </td>\n")
color = "#800000"
if highs[date] >= 80:
color = "#AA0000"
- f.write(f'''
+ f.write(
+ f"""
<td align=right>
<font color="{color}" size=6>
<b> {int(highs[date])}°F</b>
</font>
</td>
-</tr>''')
+</tr>"""
+ )
# Precip graph
- f.write(f'''
+ f.write(
+ f"""
<tr>
<td colspan=3 style="vertical-align:top;">
<canvas id="myChart{n}" style="width:100%;max-width:400px;height:180px;"></canvas>
</td>
</tr>
<script>
-var yValues{n} = ''')
+var yValues{n} = """
+ )
f.write(precip[date].__repr__())
- f.write(f''';
+ f.write(
+ f""";
var xValues{n} = [ 3, 6, 9, 12, 15, 18, 21, 24 ];
makePrecipChart("myChart{n}", xValues{n}, yValues{n});
</script>
</table>
</div>
-''')
+"""
+ )
f.write("</div></center>")
return True
-#x = weather_renderer({"Stevens": 1000}, "stevens")
-#x.periodic_render("Stevens")
+# x = weather_renderer({"Stevens": 1000}, "stevens")
+# x.periodic_render("Stevens")