3 from dataclasses import dataclass
8 from typing import List
17 logger = logging.getLogger(__name__)
19 cfg = config.add_commandline_args(
20 f'Cached Weather Data List ({__file__})',
21 'Arguments controlling cached weather data',
24 '--weather_data_cachefile',
26 default=f'{os.environ["HOME"]}/cache/.weather_summary_cache',
28 help='File in which to cache weather data'
31 '--weather_data_stalest_acceptable',
32 type=argparse_utils.valid_duration,
33 default=datetime.timedelta(seconds=7200), # 2 hours
35 help='Maximum acceptable age of cached data. If zero, forces a refetch'
41 date: datetime.date # The date
42 high: float # The predicted high in F
43 low: float # The predicted low in F
44 precipitation_inches: float # Number of inches of precipitation / day
45 conditions: List[str] # Conditions per ~3h window
46 most_common_condition: str # The most common condition
47 icon: str # An icon to represent it
50 @persistent.persistent_autoloaded_singleton()
51 class CachedWeatherData(persistent.Persistent):
54 if weather_data is not None:
55 self.weather_data = weather_data
57 self.weather_data = {}
75 now = datetime.datetime.now()
81 param = "id=5786882" # Bellevue, WA
82 key = "c0b160c49743622f62a9cd3cda0270b3"
83 www = urllib.request.urlopen(
84 f'http://api.openweathermap.org/data/2.5/weather?zip=98005,us&APPID={key}&units=imperial'
88 parsed_json = json.loads(response)
89 logger.debug(parsed_json)
90 dt = datetime.datetime.fromtimestamp(parsed_json["dt"]).date()
92 condition = parsed_json["weather"][0]["main"]
93 icon = icon_by_condition.get(condition, '?')
95 if 'rain' in parsed_json:
96 if '3h' in parsed_json['rain']:
97 p += float(parsed_json['rain']['3h'])
98 elif '1h' in parsed_json['rain']:
99 p += float(parsed_json['rain']['1h'])
100 if 'snow' in parsed_json:
101 if '3h' in parsed_json['snow']:
102 p += float(parsed_json['snow']['3h'])
103 elif '1h' in parsed_json['snow']:
104 p += float(parsed_json['snow']['1h'])
105 if dt == now.date() and now.hour > 18 and condition == 'Clear':
107 self.weather_data[dt] = WeatherData(
109 high = float(parsed_json["main"]["temp_max"]),
110 low = float(parsed_json["main"]["temp_min"]),
111 precipitation_inches = p / 25.4,
112 conditions = [condition],
113 most_common_condition = condition,
117 www = urllib.request.urlopen(
118 f"http://api.openweathermap.org/data/2.5/forecast?{param}&APPID={key}&units=imperial"
120 response = www.read()
122 parsed_json = json.loads(response)
123 logger.debug(parsed_json)
124 count = parsed_json["cnt"]
125 for x in range(count):
126 data = parsed_json["list"][x]
127 dt = datetime.datetime.strptime(data['dt_txt'], '%Y-%m-%d %H:%M:%S')
135 data["main"]["temp"],
136 data['main']['temp_min'],
137 data['main']['temp_max'],
139 if highs[dt] is None or temp > highs[dt]:
141 if lows[dt] is None or temp < lows[dt]:
143 cond = data["weather"][0]["main"]
145 if 'rain' in parsed_json:
146 if '3h' in parsed_json['rain']:
147 precip[dt] += float(parsed_json['rain']['3h'])
148 elif '1h' in parsed_json['rain']:
149 precip[dt] += float(parsed_json['rain']['1h'])
150 if 'snow' in parsed_json:
151 if '3h' in parsed_json['snow']:
152 precip[dt] += float(parsed_json['snow']['3h'])
153 elif '1h' in parsed_json['snow']:
154 precip[dt] += float(parsed_json['snow']['1h'])
155 conditions[dt].append(cond)
157 today = datetime_utils.now_pacific().date()
158 for dt in sorted(dates):
160 high = highs.get(dt, None)
163 self.weather_data[today].high < high
165 self.weather_data[today].high = high
167 most_common_condition = list_utils.most_common(conditions[dt])
168 icon = icon_by_condition.get(most_common_condition, '?')
169 if dt == now.date() and now.hour > 18 and condition == 'Clear':
171 self.weather_data[dt] = WeatherData(
175 precipitation_inches = precip[dt] / 25.4,
176 conditions = conditions[dt],
177 most_common_condition = most_common_condition,
183 if persistent.was_file_written_within_n_seconds(
184 config.config['weather_data_cachefile'],
185 config.config['weather_data_stalest_acceptable'].total_seconds(),
188 with open(config.config['weather_data_cachefile'], 'rb') as rf:
189 weather_data = pickle.load(rf)
190 return cls(weather_data)
195 with open(config.config['weather_data_cachefile'], 'wb') as wf:
199 pickle.HIGHEST_PROTOCOL,