|
@@ -0,0 +1,214 @@
|
|
1
|
+#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+# ----------------------------------------------------------------------------
|
|
4
|
+# "THE BEER-WARE LICENSE" (Revision 42):
|
|
5
|
+# <xythobuz@xythobuz.de> wrote this file. As long as you retain this notice
|
|
6
|
+# you can do whatever you want with this stuff. If we meet some day, and you
|
|
7
|
+# think this stuff is worth it, you can buy me a beer in return. Thomas Buck
|
|
8
|
+# ----------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+import util
|
|
11
|
+import time
|
|
12
|
+from datetime import datetime, timezone, timedelta
|
|
13
|
+
|
|
14
|
+from wetterdienst import Settings
|
|
15
|
+from wetterdienst.provider.dwd.mosmix import (
|
|
16
|
+ DwdForecastDate,
|
|
17
|
+ DwdMosmixRequest,
|
|
18
|
+ DwdMosmixType,
|
|
19
|
+)
|
|
20
|
+import polars
|
|
21
|
+
|
|
22
|
+class WeatherScreen:
|
|
23
|
+ def __init__(self, g, i, latlon, timestep = 5.0, refresh = (60.0 * 60.0)):
|
|
24
|
+ self.gui = g
|
|
25
|
+ self.input = i
|
|
26
|
+ self.latlon = latlon
|
|
27
|
+ self.timestep = timestep
|
|
28
|
+
|
|
29
|
+ DrawText = util.getTextDrawer()
|
|
30
|
+ self.t_head = DrawText(self.gui, (255, 255, 255), (0, 0, 0))
|
|
31
|
+ self.t_sub = DrawText(self.gui, (0, 255, 255), (0, 0, 0))
|
|
32
|
+ self.t_val = DrawText(self.gui, (255, 0, 255), (0, 0, 0))
|
|
33
|
+ self.t_aux = DrawText(self.gui, (255, 255, 0), (0, 0, 0))
|
|
34
|
+
|
|
35
|
+ self.params = [
|
|
36
|
+ "weather_significant",
|
|
37
|
+ "temperature_air_mean_200",
|
|
38
|
+ "cloud_cover_effective",
|
|
39
|
+ ]
|
|
40
|
+ self.num_state = len(self.params) + 1
|
|
41
|
+
|
|
42
|
+ self.find_station()
|
|
43
|
+ self.restart()
|
|
44
|
+
|
|
45
|
+ def restart(self):
|
|
46
|
+ self.state = 0
|
|
47
|
+ self.old_keys = {
|
|
48
|
+ "left": False,
|
|
49
|
+ "right": False,
|
|
50
|
+ "up": False,
|
|
51
|
+ "down": False,
|
|
52
|
+ "a": False,
|
|
53
|
+ "b": False,
|
|
54
|
+ "x": False,
|
|
55
|
+ "y": False,
|
|
56
|
+ "l": False,
|
|
57
|
+ "r": False,
|
|
58
|
+ "start": False,
|
|
59
|
+ "select": False,
|
|
60
|
+ }
|
|
61
|
+ self.last = time.time()
|
|
62
|
+
|
|
63
|
+ def find_station(self):
|
|
64
|
+ settings = Settings(ts_shape=True, ts_humanize=True)
|
|
65
|
+ request = DwdMosmixRequest(
|
|
66
|
+ parameter=self.params,
|
|
67
|
+ start_issue=DwdForecastDate.LATEST,
|
|
68
|
+ mosmix_type=DwdMosmixType.LARGE,
|
|
69
|
+ settings=settings,
|
|
70
|
+ )
|
|
71
|
+ stations = request.filter_by_distance(latlon=self.latlon, distance=30)
|
|
72
|
+ self.station = stations.df[0]
|
|
73
|
+
|
|
74
|
+ print("Found station '{}' ({}) in {:.2f} km distance".format(
|
|
75
|
+ self.station["name"][0],
|
|
76
|
+ self.station["station_id"][0],
|
|
77
|
+ self.station["distance"][0]
|
|
78
|
+ ))
|
|
79
|
+
|
|
80
|
+ self.forecast = request.filter_by_station_id(station_id=[
|
|
81
|
+ self.station["station_id"][0],
|
|
82
|
+ ])
|
|
83
|
+ self.parse_forecast()
|
|
84
|
+
|
|
85
|
+ def get_forecast(self):
|
|
86
|
+ settings = Settings(ts_shape=True, ts_humanize=True)
|
|
87
|
+ request = DwdMosmixRequest(
|
|
88
|
+ parameter=self.params,
|
|
89
|
+ start_issue=DwdForecastDate.LATEST,
|
|
90
|
+ mosmix_type=DwdMosmixType.LARGE,
|
|
91
|
+ settings=settings,
|
|
92
|
+ )
|
|
93
|
+ self.forecast = request.filter_by_station_id(station_id=[self.station["station_id"][0]])
|
|
94
|
+ self.parse_forecast()
|
|
95
|
+
|
|
96
|
+ def parse_forecast(self):
|
|
97
|
+ self.last_forecast = time.time()
|
|
98
|
+
|
|
99
|
+ response = next(self.forecast.values.query())
|
|
100
|
+ response_subset = response.df.select(["date", "parameter", "value"])
|
|
101
|
+
|
|
102
|
+ # only datapoints from -1h to +12h from now
|
|
103
|
+ start_time = datetime.now(timezone.utc) - timedelta(hours=1)
|
|
104
|
+ end_time = datetime.now(timezone.utc) + timedelta(hours=12)
|
|
105
|
+ self.data = response_subset.filter((polars.col("date") >= start_time) & (polars.col("date") <= end_time))
|
|
106
|
+
|
|
107
|
+ #print(self.data)
|
|
108
|
+ #for p in self.params:
|
|
109
|
+ # print(self.data.filter(polars.col("parameter") == p)[0])
|
|
110
|
+ #for i in range(0, len(self.data)):
|
|
111
|
+ # print(self.data[i])
|
|
112
|
+
|
|
113
|
+ def buttons(self):
|
|
114
|
+ keys = self.input.get()
|
|
115
|
+
|
|
116
|
+ if keys["up"] and (not self.old_keys["up"]) and (not self.old_keys["select"]):
|
|
117
|
+ self.state = (self.state + 1) % self.num_state
|
|
118
|
+ self.last = time.time()
|
|
119
|
+ elif keys["down"] and (not self.old_keys["select"]):
|
|
120
|
+ self.state = (self.state - 1) % self.num_state
|
|
121
|
+ self.last = time.time()
|
|
122
|
+
|
|
123
|
+ self.old_keys = keys.copy()
|
|
124
|
+
|
|
125
|
+ def finished(self):
|
|
126
|
+ return False # TODO
|
|
127
|
+
|
|
128
|
+ def draw_station_info(self):
|
|
129
|
+ # heading
|
|
130
|
+ self.t_head.setText("Weather:", "lemon")
|
|
131
|
+ self.t_head.draw(3, -self.gui.height / 2 + 7)
|
|
132
|
+
|
|
133
|
+ # station info (2 lines)
|
|
134
|
+ self.t_sub.setText(self.station["name"][0], "tom-thumb")
|
|
135
|
+ self.t_sub.draw(0, -self.gui.height / 2 + 5 + 6 * 2)
|
|
136
|
+ self.t_sub.draw(self.gui.width, -self.gui.height / 2 + 5 + 6 * 3)
|
|
137
|
+
|
|
138
|
+ # distance
|
|
139
|
+ self.t_val.setText("Distance:", "tom-thumb")
|
|
140
|
+ self.t_val.draw(0, -self.gui.height / 2 + 5 + 6 * 5)
|
|
141
|
+ self.t_val.setText("{:.2f} km".format(self.station["distance"][0]), "tom-thumb")
|
|
142
|
+ self.t_val.draw(0, -self.gui.height / 2 + 5 + 6 * 6)
|
|
143
|
+
|
|
144
|
+ # lat lon
|
|
145
|
+ self.t_aux.setText("Lat: {:.2f}".format(self.station["latitude"][0]), "tom-thumb")
|
|
146
|
+ self.t_aux.draw(0, -self.gui.height / 2 + 5 + 6 * 8)
|
|
147
|
+ self.t_aux.setText("Lon: {:.2f}".format(self.station["longitude"][0]), "tom-thumb")
|
|
148
|
+ self.t_aux.draw(0, -self.gui.height / 2 + 5 + 6 * 9)
|
|
149
|
+
|
|
150
|
+ def draw_weather(self):
|
|
151
|
+ # heading
|
|
152
|
+ self.t_head.setText("Weather:", "lemon")
|
|
153
|
+ self.t_head.draw(3, -self.gui.height / 2 + 7)
|
|
154
|
+
|
|
155
|
+ val = self.data.filter(polars.col("parameter") == self.params[0])[0]["value"][0]
|
|
156
|
+ self.t_val.setText("{:.0f}".format(val), "tom-thumb")
|
|
157
|
+ self.t_val.draw(0, -self.gui.height / 2 + 5 + 6 * 5)
|
|
158
|
+
|
|
159
|
+ def draw_temperature(self):
|
|
160
|
+ # heading
|
|
161
|
+ self.t_head.setText("Temps:", "lemon")
|
|
162
|
+ self.t_head.draw(3, -self.gui.height / 2 + 7)
|
|
163
|
+
|
|
164
|
+ # current temperature
|
|
165
|
+ self.t_sub.setText("Current:", "tom-thumb")
|
|
166
|
+ self.t_sub.draw(0, -self.gui.height / 2 + 5 + 6 * 2)
|
|
167
|
+ val = self.data.filter(polars.col("parameter") == self.params[1])[0]["value"][0]
|
|
168
|
+ val = val - 273.15 # kelvin to celsius
|
|
169
|
+ self.t_val.setText("{:.1f} °C".format(val), "tom-thumb")
|
|
170
|
+ self.t_val.draw(0, -self.gui.height / 2 + 5 + 6 * 3)
|
|
171
|
+
|
|
172
|
+ def draw_cloud_cover(self):
|
|
173
|
+ # heading
|
|
174
|
+ self.t_head.setText("Clouds:", "lemon")
|
|
175
|
+ self.t_head.draw(3, -self.gui.height / 2 + 7)
|
|
176
|
+
|
|
177
|
+ val = self.data.filter(polars.col("parameter") == self.params[2])[0]["value"][0]
|
|
178
|
+ self.t_val.setText("{:.1f} %".format(val), "tom-thumb")
|
|
179
|
+ self.t_val.draw(0, -self.gui.height / 2 + 5 + 6 * 5)
|
|
180
|
+
|
|
181
|
+ def draw(self):
|
|
182
|
+ # handle button input
|
|
183
|
+ if self.input != None:
|
|
184
|
+ self.buttons()
|
|
185
|
+
|
|
186
|
+ # draw screen contents
|
|
187
|
+ if self.state == 0:
|
|
188
|
+ self.draw_station_info()
|
|
189
|
+ elif self.state == 1:
|
|
190
|
+ self.draw_weather()
|
|
191
|
+ elif self.state == 2:
|
|
192
|
+ self.draw_temperature()
|
|
193
|
+ elif self.state == 3:
|
|
194
|
+ self.draw_cloud_cover()
|
|
195
|
+
|
|
196
|
+ # advance to next screen after time has passed
|
|
197
|
+ if (time.time() - self.last) >= self.timestep:
|
|
198
|
+ self.state = (self.state + 1) % self.num_state
|
|
199
|
+ self.last = time.time()
|
|
200
|
+
|
|
201
|
+ # draw progress bar on bottom most row
|
|
202
|
+ elapsed = (time.time() - self.last)
|
|
203
|
+ ratio = elapsed / self.timestep
|
|
204
|
+ for i in range(0, int(ratio * self.gui.width)):
|
|
205
|
+ self.gui.set_pixel(i, self.gui.height - 1, (255, 0, 0))
|
|
206
|
+
|
|
207
|
+if __name__ == "__main__":
|
|
208
|
+ from config import Config
|
|
209
|
+
|
|
210
|
+ i = util.getInput()
|
|
211
|
+ t = util.getTarget(i)
|
|
212
|
+
|
|
213
|
+ s = WeatherScreen(t, i, Config.weather_latlon, 2.0, 60.0 * 10.0)
|
|
214
|
+ util.loop(t, s.draw)
|