python-google-weather-api 0.0.9__py3-none-any.whl → 0.0.11__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ from .api import GoogleWeatherApi
4
4
  from .exceptions import (
5
5
  GoogleWeatherApiAuthError,
6
6
  GoogleWeatherApiConnectionError,
7
+ GoogleWeatherApiConnectionTimeoutError,
7
8
  GoogleWeatherApiError,
8
9
  GoogleWeatherApiResponseError,
9
10
  )
@@ -50,6 +51,7 @@ __all__ = [
50
51
  "GoogleWeatherApi",
51
52
  "GoogleWeatherApiAuthError",
52
53
  "GoogleWeatherApiConnectionError",
54
+ "GoogleWeatherApiConnectionTimeoutError",
53
55
  "GoogleWeatherApiError",
54
56
  "GoogleWeatherApiResponseError",
55
57
  "HourlyForecastResponse",
google_weather_api/api.py CHANGED
@@ -4,16 +4,19 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  from http import HTTPStatus
7
- from typing import Any
7
+ from typing import Any, TypeVar
8
8
 
9
9
  import aiohttp
10
+ from mashumaro.exceptions import InvalidFieldValue, MissingField
10
11
 
11
12
  from .exceptions import (
12
13
  GoogleWeatherApiAuthError,
13
14
  GoogleWeatherApiConnectionError,
15
+ GoogleWeatherApiConnectionTimeoutError,
14
16
  GoogleWeatherApiResponseError,
15
17
  )
16
18
  from .model import (
19
+ BaseModel,
17
20
  CurrentConditionsResponse,
18
21
  DailyForecastResponse,
19
22
  HourlyForecastResponse,
@@ -22,6 +25,17 @@ from .model import (
22
25
 
23
26
  _LOGGER = logging.getLogger(__name__)
24
27
 
28
+ _ResponseT = TypeVar("_ResponseT", bound=BaseModel)
29
+
30
+
31
+ def _parse_response(model: type[_ResponseT], data: dict[str, Any]) -> _ResponseT:
32
+ """Deserialize an API response dictionary, wrapping schema errors."""
33
+ try:
34
+ return model.from_dict(data)
35
+ except (InvalidFieldValue, MissingField) as err:
36
+ raise GoogleWeatherApiResponseError(str(err)) from err
37
+
38
+
25
39
  _BASE_URL = "https://weather.googleapis.com/v1"
26
40
  # See https://docs.cloud.google.com/docs/authentication/api-keys-use
27
41
  _API_KEY_HEADER = "X-Goog-Api-Key"
@@ -71,28 +85,33 @@ class GoogleWeatherApi:
71
85
  headers=headers,
72
86
  timeout=self.timeout,
73
87
  ) as resp:
74
- res: dict[str, Any] = await resp.json()
75
88
  _LOGGER.debug("Got %s for %s", resp.status, url)
76
- if resp.status != HTTPStatus.OK:
77
- error_data = res.get("error", {})
78
- error_msg = error_data.get("message", "Unknown API error")
79
-
80
- if resp.status in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN):
81
- raise GoogleWeatherApiAuthError(error_msg)
82
-
83
- # Google APIs often return 400 Bad Request for invalid API keys
84
- if resp.status == HTTPStatus.BAD_REQUEST:
85
- for detail in error_data.get("details", []):
86
- if detail.get("reason") == "API_KEY_INVALID":
87
- raise GoogleWeatherApiAuthError(error_msg)
88
-
89
- raise GoogleWeatherApiResponseError(error_msg)
90
- return res
89
+ status_code = resp.status
90
+ body = None if status_code == HTTPStatus.GATEWAY_TIMEOUT else await resp.json()
91
91
  except TimeoutError as err:
92
- raise GoogleWeatherApiConnectionError("Timeout") from err
92
+ raise GoogleWeatherApiConnectionTimeoutError("Timeout") from err
93
93
  except aiohttp.ClientError as err:
94
94
  raise GoogleWeatherApiConnectionError(err) from err
95
95
 
96
+ if status_code == HTTPStatus.GATEWAY_TIMEOUT:
97
+ raise GoogleWeatherApiConnectionTimeoutError("Timeout")
98
+ res: dict[str, Any] = body if isinstance(body, dict) else {}
99
+ if status_code != HTTPStatus.OK:
100
+ error_data = res.get("error", {})
101
+ error_msg = error_data.get("message", "Unknown API error")
102
+
103
+ if status_code in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN):
104
+ raise GoogleWeatherApiAuthError(error_msg)
105
+
106
+ # Google APIs often return 400 Bad Request for invalid API keys
107
+ if status_code == HTTPStatus.BAD_REQUEST:
108
+ for detail in error_data.get("details", []):
109
+ if detail.get("reason") == "API_KEY_INVALID":
110
+ raise GoogleWeatherApiAuthError(error_msg)
111
+
112
+ raise GoogleWeatherApiResponseError(error_msg)
113
+ return res
114
+
96
115
  async def _async_get_all_pages(
97
116
  self,
98
117
  endpoint: str,
@@ -134,7 +153,7 @@ class GoogleWeatherApi:
134
153
  "location.longitude": longitude,
135
154
  },
136
155
  )
137
- return CurrentConditionsResponse.from_dict(data)
156
+ return _parse_response(CurrentConditionsResponse, data)
138
157
 
139
158
  async def async_get_hourly_forecast(self, latitude: float, longitude: float, hours: int = 48) -> HourlyForecastResponse:
140
159
  """Fetch hourly weather forecast.
@@ -151,7 +170,7 @@ class GoogleWeatherApi:
151
170
  "forecastHours",
152
171
  hours,
153
172
  )
154
- return HourlyForecastResponse.from_dict(data)
173
+ return _parse_response(HourlyForecastResponse, data)
155
174
 
156
175
  async def async_get_daily_forecast(self, latitude: float, longitude: float, days: int = 10) -> DailyForecastResponse:
157
176
  """Fetch daily weather forecast.
@@ -168,7 +187,7 @@ class GoogleWeatherApi:
168
187
  "forecastDays",
169
188
  days,
170
189
  )
171
- return DailyForecastResponse.from_dict(data)
190
+ return _parse_response(DailyForecastResponse, data)
172
191
 
173
192
  async def async_get_minute_forecast(
174
193
  self, latitude: float, longitude: float, page_size: int = 360
@@ -191,4 +210,4 @@ class GoogleWeatherApi:
191
210
  page_size,
192
211
  include_language_code=False,
193
212
  )
194
- return MinuteForecastResponse.from_dict(data)
213
+ return _parse_response(MinuteForecastResponse, data)
@@ -9,6 +9,10 @@ class GoogleWeatherApiConnectionError(GoogleWeatherApiError):
9
9
  """Exception connecting to the Google Weather API."""
10
10
 
11
11
 
12
+ class GoogleWeatherApiConnectionTimeoutError(GoogleWeatherApiConnectionError, TimeoutError):
13
+ """Exception raised when a request to the Google Weather API times out."""
14
+
15
+
12
16
  class GoogleWeatherApiAuthError(GoogleWeatherApiError):
13
17
  """Exception raised for authentication errors in the Google Weather API."""
14
18
 
@@ -42,6 +42,7 @@ class BaseModel(DataClassJSONMixin):
42
42
 
43
43
  serialize_by_alias = True
44
44
  allow_deserialization_not_by_alias = True
45
+ omit_none = True
45
46
 
46
47
 
47
48
  @dataclass
@@ -92,14 +93,14 @@ class Temperature(BaseModel):
92
93
  class TemperatureUnit(ApiStrEnum):
93
94
  """Represents a unit used to measure temperatures."""
94
95
 
95
- TEMPERATURE_UNIT_UNSPECIFIED = "TEMPERATURE_UNIT_UNSPECIFIED"
96
+ UNIT_UNSPECIFIED = "UNIT_UNSPECIFIED"
96
97
  CELSIUS = "CELSIUS"
97
98
  FAHRENHEIT = "FAHRENHEIT"
98
99
 
99
- degrees: float
100
+ degrees: float | None = None
100
101
  """The temperature value (in degrees) in the specified unit."""
101
102
 
102
- unit: TemperatureUnit
103
+ unit: TemperatureUnit = TemperatureUnit.UNIT_UNSPECIFIED
103
104
  """The code for the unit used to measure the temperature value."""
104
105
 
105
106
 
@@ -114,10 +115,10 @@ class QuantitativePrecipitationForecast(BaseModel):
114
115
  MILLIMETERS = "MILLIMETERS"
115
116
  INCHES = "INCHES"
116
117
 
117
- quantity: float
118
+ quantity: float | None = None
118
119
  """The amount of precipitation, measured as liquid water equivalent."""
119
120
 
120
- unit: Unit
121
+ unit: Unit = Unit.UNIT_UNSPECIFIED
121
122
  """The code of the unit used to measure the amount of accumulated precipitation."""
122
123
 
123
124
 
@@ -128,7 +129,7 @@ class PrecipitationProbability(BaseModel):
128
129
  class PrecipitationType(ApiStrEnum):
129
130
  """Represents the type of precipitation at a given location."""
130
131
 
131
- PRECIPITATION_TYPE_UNSPECIFIED = "PRECIPITATION_TYPE_UNSPECIFIED"
132
+ TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED"
132
133
  NONE = "NONE"
133
134
  SNOW = "SNOW"
134
135
  RAIN = "RAIN"
@@ -137,6 +138,7 @@ class PrecipitationProbability(BaseModel):
137
138
  RAIN_AND_SNOW = "RAIN_AND_SNOW"
138
139
  SLEET = "SLEET"
139
140
  FREEZING_RAIN = "FREEZING_RAIN"
141
+ PRECIPITATION_TYPE_HAIL = "PRECIPITATION_TYPE_HAIL"
140
142
 
141
143
  type: PrecipitationType
142
144
  """A code that indicates the type of precipitation."""
@@ -166,14 +168,14 @@ class WindSpeed(BaseModel):
166
168
  class SpeedUnit(ApiStrEnum):
167
169
  """Represents the unit used to measure speed."""
168
170
 
169
- SPEED_UNIT_UNSPECIFIED = "SPEED_UNIT_UNSPECIFIED"
171
+ UNIT_UNSPECIFIED = "UNIT_UNSPECIFIED"
170
172
  KILOMETERS_PER_HOUR = "KILOMETERS_PER_HOUR"
171
173
  MILES_PER_HOUR = "MILES_PER_HOUR"
172
174
 
173
- value: float
175
+ value: float | None = None
174
176
  """The value of the wind speed."""
175
177
 
176
- unit: SpeedUnit
178
+ unit: SpeedUnit = SpeedUnit.UNIT_UNSPECIFIED
177
179
  """The code that represents the unit used to measure the wind speed."""
178
180
 
179
181
 
@@ -184,7 +186,7 @@ class WindDirection(BaseModel):
184
186
  class CardinalDirection(ApiStrEnum):
185
187
  """Represents a cardinal direction (including ordinal directions)."""
186
188
 
187
- CARDINAL_DIRECTION_UNSPECIFIED = "CARDINAL_DIRECTION_UNSPECIFIED"
189
+ CARDINAL_UNSPECIFIED = "CARDINAL_UNSPECIFIED"
188
190
  NORTH = "NORTH"
189
191
  NORTH_NORTHEAST = "NORTH_NORTHEAST"
190
192
  NORTHEAST = "NORTHEAST"
@@ -219,7 +221,7 @@ class Wind(BaseModel):
219
221
  speed: WindSpeed
220
222
  """The speed of the wind."""
221
223
 
222
- gust: WindSpeed
224
+ gust: WindSpeed | None = None
223
225
  """The wind gust (sudden increase in the wind speed)."""
224
226
 
225
227
 
@@ -234,10 +236,10 @@ class Visibility(BaseModel):
234
236
  KILOMETERS = "KILOMETERS"
235
237
  MILES = "MILES"
236
238
 
237
- distance: float
239
+ distance: float | None = None
238
240
  """The visibility distance in the specified unit."""
239
241
 
240
- unit: Unit
242
+ unit: Unit = Unit.UNIT_UNSPECIFIED
241
243
  """The code that represents the unit used to measure the distance."""
242
244
 
243
245
 
@@ -289,6 +291,120 @@ class WeatherCondition(BaseModel):
289
291
  LIGHT_THUNDERSTORM_RAIN = "LIGHT_THUNDERSTORM_RAIN"
290
292
  SCATTERED_THUNDERSTORMS = "SCATTERED_THUNDERSTORMS"
291
293
  HEAVY_THUNDERSTORM = "HEAVY_THUNDERSTORM"
294
+ CLEAR_AFTER_FOG_IN_THE_MORNING = "CLEAR_AFTER_FOG_IN_THE_MORNING"
295
+ CLEAR_BUT_LATER_TURNING_CLOUDY = "CLEAR_BUT_LATER_TURNING_CLOUDY"
296
+ CLEAR_BUT_LATER_TURNING_TO_RAIN_AND_SNOW_MAIN_CONSTITUENT_RAIN = (
297
+ "CLEAR_BUT_LATER_TURNING_TO_RAIN_AND_SNOW_MAIN_CONSTITUENT_RAIN"
298
+ )
299
+ CLEAR_BUT_LATER_TURNING_TO_SNOW_AND_RAIN_MAIN_CONSTITUENT_SNOW = (
300
+ "CLEAR_BUT_LATER_TURNING_TO_SNOW_AND_RAIN_MAIN_CONSTITUENT_SNOW"
301
+ )
302
+ CLEAR_BUT_THUNDERSTORMS_LATER = "CLEAR_BUT_THUNDERSTORMS_LATER"
303
+ CLEAR_IN_THE_MORNING_BUT_CLOUDY_IN_THE_EVENING = "CLEAR_IN_THE_MORNING_BUT_CLOUDY_IN_THE_EVENING"
304
+ CLEAR_IN_THE_MORNING_WITH_BRIEF_RAIN_IN_THE_EVENING = "CLEAR_IN_THE_MORNING_WITH_BRIEF_RAIN_IN_THE_EVENING"
305
+ CLEAR_MORNING_WITH_BRIEF_RAIN = "CLEAR_MORNING_WITH_BRIEF_RAIN"
306
+ CLEAR_WITH_BRIEF_RAIN = "CLEAR_WITH_BRIEF_RAIN"
307
+ CLEAR_WITH_BRIEF_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT = "CLEAR_WITH_BRIEF_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT"
308
+ CLEAR_WITH_BRIEF_RAIN_LATER = "CLEAR_WITH_BRIEF_RAIN_LATER"
309
+ CLEAR_WITH_BRIEF_SNOW = "CLEAR_WITH_BRIEF_SNOW"
310
+ CLEAR_WITH_BRIEF_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT = "CLEAR_WITH_BRIEF_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT"
311
+ CLEAR_WITH_BRIEF_SNOW_LATER = "CLEAR_WITH_BRIEF_SNOW_LATER"
312
+ CLEAR_WITH_PERIODIC_CLOUDS_LATER = "CLEAR_WITH_PERIODIC_CLOUDS_LATER"
313
+ CLEAR_WITH_PERIODIC_RAIN = "CLEAR_WITH_PERIODIC_RAIN"
314
+ CLEAR_WITH_PERIODIC_RAIN_ACCOMPANIED_BY_THUNDER = "CLEAR_WITH_PERIODIC_RAIN_ACCOMPANIED_BY_THUNDER"
315
+ CLEAR_WITH_PERIODIC_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT = (
316
+ "CLEAR_WITH_PERIODIC_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT"
317
+ )
318
+ CLEAR_WITH_PERIODIC_RAIN_LATER = "CLEAR_WITH_PERIODIC_RAIN_LATER"
319
+ CLEAR_WITH_PERIODIC_SNOW = "CLEAR_WITH_PERIODIC_SNOW"
320
+ CLEAR_WITH_PERIODIC_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT = (
321
+ "CLEAR_WITH_PERIODIC_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT"
322
+ )
323
+ CLEAR_WITH_PERIODIC_SNOW_LATER = "CLEAR_WITH_PERIODIC_SNOW_LATER"
324
+ CLEAR_WITH_RAIN_AFTER_MIDNIGHT = "CLEAR_WITH_RAIN_AFTER_MIDNIGHT"
325
+ CLEAR_WITH_RAIN_IN_THE_NIGHT = "CLEAR_WITH_RAIN_IN_THE_NIGHT"
326
+ CLEAR_WITH_RAIN_LATER = "CLEAR_WITH_RAIN_LATER"
327
+ CLEAR_WITH_SNOW_ALONG_THE_MOUNTAINS = "CLEAR_WITH_SNOW_ALONG_THE_MOUNTAINS"
328
+ CLEAR_WITH_SNOW_LATER = "CLEAR_WITH_SNOW_LATER"
329
+ CLEAR_WITH_THUNDERSTORMS_ALONG_THE_MOUNTAINS = "CLEAR_WITH_THUNDERSTORMS_ALONG_THE_MOUNTAINS"
330
+ CLEAR_WITH_THUNDERSTORMS_IN_THE_AFTERNOON = "CLEAR_WITH_THUNDERSTORMS_IN_THE_AFTERNOON"
331
+ CLOUDY_BUT_CLEAR_LATER = "CLOUDY_BUT_CLEAR_LATER"
332
+ CLOUDY_BUT_LATER_TURNING_TO_RAIN_AND_SNOW_MAIN_CONSTITUENT_RAIN = (
333
+ "CLOUDY_BUT_LATER_TURNING_TO_RAIN_AND_SNOW_MAIN_CONSTITUENT_RAIN"
334
+ )
335
+ CLOUDY_BUT_LATER_TURNING_TO_SNOW_AND_RAIN_MAIN_CONSTITUENT_SNOW = (
336
+ "CLOUDY_BUT_LATER_TURNING_TO_SNOW_AND_RAIN_MAIN_CONSTITUENT_SNOW"
337
+ )
338
+ CLOUDY_BUT_PERIODICALLY_CLEAR_LATER = "CLOUDY_BUT_PERIODICALLY_CLEAR_LATER"
339
+ CLOUDY_BUT_THUNDERSTORMS_LATER = "CLOUDY_BUT_THUNDERSTORMS_LATER"
340
+ CLOUDY_MORNING_WITH_BRIEF_RAIN = "CLOUDY_MORNING_WITH_BRIEF_RAIN"
341
+ CLOUDY_PERIODICALLY_CLEAR = "CLOUDY_PERIODICALLY_CLEAR"
342
+ CLOUDY_SNOWING_IN_THE_EVENING = "CLOUDY_SNOWING_IN_THE_EVENING"
343
+ CLOUDY_SNOWING_IN_THE_NIGHT = "CLOUDY_SNOWING_IN_THE_NIGHT"
344
+ CLOUDY_WITH_BRIEF_RAIN = "CLOUDY_WITH_BRIEF_RAIN"
345
+ CLOUDY_WITH_BRIEF_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT = "CLOUDY_WITH_BRIEF_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT"
346
+ CLOUDY_WITH_BRIEF_RAIN_LATER = "CLOUDY_WITH_BRIEF_RAIN_LATER"
347
+ CLOUDY_WITH_BRIEF_SNOW = "CLOUDY_WITH_BRIEF_SNOW"
348
+ CLOUDY_WITH_BRIEF_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT = "CLOUDY_WITH_BRIEF_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT"
349
+ CLOUDY_WITH_BRIEF_SNOW_LATER = "CLOUDY_WITH_BRIEF_SNOW_LATER"
350
+ CLOUDY_WITH_BRIEF_THUNDERSTORM = "CLOUDY_WITH_BRIEF_THUNDERSTORM"
351
+ CLOUDY_WITH_FOG_ON_THE_OCEAN_AND_AT_THE_SEASHORE = "CLOUDY_WITH_FOG_ON_THE_OCEAN_AND_AT_THE_SEASHORE"
352
+ CLOUDY_WITH_PERIODIC_RAIN = "CLOUDY_WITH_PERIODIC_RAIN"
353
+ CLOUDY_WITH_PERIODIC_RAIN_ACCOMPANIED_BY_THUNDER = "CLOUDY_WITH_PERIODIC_RAIN_ACCOMPANIED_BY_THUNDER"
354
+ CLOUDY_WITH_PERIODIC_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT = (
355
+ "CLOUDY_WITH_PERIODIC_RAIN_AND_SNOW_RAIN_AS_MAIN_CONSTITUENT"
356
+ )
357
+ CLOUDY_WITH_PERIODIC_RAIN_LATER = "CLOUDY_WITH_PERIODIC_RAIN_LATER"
358
+ CLOUDY_WITH_PERIODIC_SNOW = "CLOUDY_WITH_PERIODIC_SNOW"
359
+ CLOUDY_WITH_PERIODIC_SNOW_ACCOMPANIED_BY_THUNDER = "CLOUDY_WITH_PERIODIC_SNOW_ACCOMPANIED_BY_THUNDER"
360
+ CLOUDY_WITH_PERIODIC_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT = (
361
+ "CLOUDY_WITH_PERIODIC_SNOW_AND_RAIN_SNOW_AS_MAIN_CONSTITUENT"
362
+ )
363
+ CLOUDY_WITH_PERIODIC_SNOW_LATER = "CLOUDY_WITH_PERIODIC_SNOW_LATER"
364
+ CLOUDY_WITH_RAIN_AFTER_MIDNIGHT = "CLOUDY_WITH_RAIN_AFTER_MIDNIGHT"
365
+ CLOUDY_WITH_RAIN_FROM_ABOUT_NOON = "CLOUDY_WITH_RAIN_FROM_ABOUT_NOON"
366
+ CLOUDY_WITH_RAIN_FROM_EVENING = "CLOUDY_WITH_RAIN_FROM_EVENING"
367
+ CLOUDY_WITH_RAIN_LATER = "CLOUDY_WITH_RAIN_LATER"
368
+ CLOUDY_WITH_SNOW_FROM_AROUND_NOON = "CLOUDY_WITH_SNOW_FROM_AROUND_NOON"
369
+ CLOUDY_WITH_SNOW_LATER = "CLOUDY_WITH_SNOW_LATER"
370
+ FOG = "FOG"
371
+ RAINSNOW_MIX_LATER_BECOMING_CLEAR_RAIN_AS_MAIN = "RAINSNOW_MIX_LATER_BECOMING_CLEAR_RAIN_AS_MAIN"
372
+ RAINSNOW_MIX_RAIN_AS_MAIN = "RAINSNOW_MIX_RAIN_AS_MAIN"
373
+ RAINY_BUT_PERIODICALLY_CLEAR = "RAINY_BUT_PERIODICALLY_CLEAR"
374
+ RAIN_ACCOMPANIED_BY_LIGHTNING = "RAIN_ACCOMPANIED_BY_LIGHTNING"
375
+ RAIN_ACCOMPANIED_BY_WINDSTORM = "RAIN_ACCOMPANIED_BY_WINDSTORM"
376
+ RAIN_CLEAR_AT_NIGHT = "RAIN_CLEAR_AT_NIGHT"
377
+ RAIN_CLEAR_FROM_AROUND_EVENING = "RAIN_CLEAR_FROM_AROUND_EVENING"
378
+ RAIN_CLEAR_FROM_AROUND_NOON = "RAIN_CLEAR_FROM_AROUND_NOON"
379
+ RAIN_LATER_BECOMING_CLEAR = "RAIN_LATER_BECOMING_CLEAR"
380
+ RAIN_LATER_BECOMING_CLOUDY = "RAIN_LATER_BECOMING_CLOUDY"
381
+ RAIN_LATER_BECOMING_SNOW = "RAIN_LATER_BECOMING_SNOW"
382
+ RAIN_LATER_WITH_PERIODIC_SNOW = "RAIN_LATER_WITH_PERIODIC_SNOW"
383
+ RAIN_SNOW_AT_NIGHT = "RAIN_SNOW_AT_NIGHT"
384
+ RAIN_SNOW_IN_THE_EVENING = "RAIN_SNOW_IN_THE_EVENING"
385
+ RAIN_TEMPORARILY_BECOMING_SLEET = "RAIN_TEMPORARILY_BECOMING_SLEET"
386
+ RAIN_THAT_PERIODICALLY_STOPS = "RAIN_THAT_PERIODICALLY_STOPS"
387
+ RAIN_WITH_BRIEF_SNOW = "RAIN_WITH_BRIEF_SNOW"
388
+ RAIN_WITH_PERIODIC_SNOW = "RAIN_WITH_PERIODIC_SNOW"
389
+ SNOWRAIN_MIX_LATER_BECOMING_CLEAR_SNOW_AS_MAIN = "SNOWRAIN_MIX_LATER_BECOMING_CLEAR_SNOW_AS_MAIN"
390
+ SNOWRAIN_MIX_LATER_BECOMING_CLOUDY_SNOW_AS_MAIN = "SNOWRAIN_MIX_LATER_BECOMING_CLOUDY_SNOW_AS_MAIN"
391
+ SNOWRAIN_MIX_SNOW_AS_MAIN = "SNOWRAIN_MIX_SNOW_AS_MAIN"
392
+ SNOW_ACCOMPANIED_BY_LIGHTNING = "SNOW_ACCOMPANIED_BY_LIGHTNING"
393
+ SNOW_BECOMING_SLEET = "SNOW_BECOMING_SLEET"
394
+ SNOW_LATER_CLEAR = "SNOW_LATER_CLEAR"
395
+ SNOW_LATER_CLOUDY = "SNOW_LATER_CLOUDY"
396
+ SNOW_LATER_RAIN = "SNOW_LATER_RAIN"
397
+ SNOW_PERIODICALLY_CLEAR = "SNOW_PERIODICALLY_CLEAR"
398
+ SNOW_THAT_PERIODICALLY_STOPS = "SNOW_THAT_PERIODICALLY_STOPS"
399
+ SNOW_TURNING_TO_RAIN_AROUND_NOON = "SNOW_TURNING_TO_RAIN_AROUND_NOON"
400
+ SNOW_TURNING_TO_RAIN_IN_THE_EVENING = "SNOW_TURNING_TO_RAIN_IN_THE_EVENING"
401
+ SNOW_TURNING_TO_RAIN_IN_THE_NIGHT = "SNOW_TURNING_TO_RAIN_IN_THE_NIGHT"
402
+ SNOW_WITH_BRIEF_RAIN = "SNOW_WITH_BRIEF_RAIN"
403
+ SNOW_WITH_BRIEF_SLEET = "SNOW_WITH_BRIEF_SLEET"
404
+ SNOW_WITH_PERIODIC_RAIN = "SNOW_WITH_PERIODIC_RAIN"
405
+ VERY_HOT = "VERY_HOT"
406
+ SLEET = "SLEET"
407
+ HEAVY_RAIN_TO_RAINSTORM = "HEAVY_RAIN_TO_RAINSTORM"
292
408
 
293
409
  icon_base_uri: str = field(metadata={"alias": "iconBaseUri"})
294
410
  """The base URI for the icon not including the file type extension."""
@@ -311,10 +427,10 @@ class IceThickness(BaseModel):
311
427
  MILLIMETERS = "MILLIMETERS"
312
428
  INCHES = "INCHES"
313
429
 
314
- thickness: float
430
+ thickness: float | None = None
315
431
  """The ice thickness value."""
316
432
 
317
- unit: Unit
433
+ unit: Unit = Unit.UNIT_UNSPECIFIED
318
434
  """The code that represents the unit used to measure the ice thickness."""
319
435
 
320
436
 
@@ -334,6 +450,9 @@ class CurrentConditionsHistory(BaseModel):
334
450
  qpf: QuantitativePrecipitationForecast
335
451
  """The amount of precipitation (rain or snow) accumulated over the last 24 hours."""
336
452
 
453
+ snow_qpf: QuantitativePrecipitationForecast | None = field(default=None, metadata={"alias": "snowQpf"})
454
+ """The amount of snow accumulation over the last 24 hours, measured as liquid water equivalent."""
455
+
337
456
 
338
457
  @dataclass
339
458
  class CurrentConditionsResponse(BaseModel):
@@ -387,11 +506,11 @@ class CurrentConditionsResponse(BaseModel):
387
506
  uv_index: int = field(metadata={"alias": "uvIndex"})
388
507
  """The current ultraviolet (UV) index."""
389
508
 
390
- thunderstorm_probability: int = field(metadata={"alias": "thunderstormProbability"})
391
- """The current thunderstorm probability (0-100)."""
509
+ thunderstorm_probability: int | None = field(default=None, metadata={"alias": "thunderstormProbability"})
510
+ """The current thunderstorm probability (0-100). Omitted in some regions (e.g. Japan and South Korea)."""
392
511
 
393
- cloud_cover: int = field(metadata={"alias": "cloudCover"})
394
- """The current percentage of the sky covered by clouds (0-100)."""
512
+ cloud_cover: int | None = field(default=None, metadata={"alias": "cloudCover"})
513
+ """The current percentage of the sky covered by clouds (0-100). Omitted in some regions (e.g. Japan and South Korea)."""
395
514
 
396
515
 
397
516
  @dataclass
@@ -410,7 +529,7 @@ class Date(BaseModel):
410
529
 
411
530
  @dataclass
412
531
  class ForecastDayPart(BaseModel):
413
- """Represents a forecast record for a part of the day (daytime or nighttime)."""
532
+ """Represents a forecast record for a part of the day (daytime, nighttime, or full day)."""
414
533
 
415
534
  interval: Interval
416
535
  """The UTC date and time when this part of the day starts and ends."""
@@ -430,11 +549,11 @@ class ForecastDayPart(BaseModel):
430
549
  uv_index: int = field(metadata={"alias": "uvIndex"})
431
550
  """The maximum forecasted ultraviolet (UV) index."""
432
551
 
433
- thunderstorm_probability: int = field(metadata={"alias": "thunderstormProbability"})
434
- """The average thunderstorm probability."""
552
+ thunderstorm_probability: int | None = field(default=None, metadata={"alias": "thunderstormProbability"})
553
+ """The average thunderstorm probability. Omitted in some regions (e.g. Japan and South Korea)."""
435
554
 
436
- cloud_cover: int = field(metadata={"alias": "cloudCover"})
437
- """Average cloud cover percent."""
555
+ cloud_cover: int | None = field(default=None, metadata={"alias": "cloudCover"})
556
+ """Average cloud cover percent. Omitted in some regions (e.g. Japan and South Korea)."""
438
557
 
439
558
  ice_thickness: IceThickness | None = field(default=None, metadata={"alias": "iceThickness"})
440
559
  """The forecasted ice thickness."""
@@ -458,7 +577,7 @@ class MoonEvents(BaseModel):
458
577
  class MoonPhase(ApiStrEnum):
459
578
  """Marks the moon phase (a.k.a. lunar phase)."""
460
579
 
461
- MOON_PHASE_UNSPECIFIED = "MOON_PHASE_UNSPECIFIED"
580
+ PHASE_UNSPECIFIED = "PHASE_UNSPECIFIED"
462
581
  NEW_MOON = "NEW_MOON"
463
582
  WAXING_CRESCENT = "WAXING_CRESCENT"
464
583
  FIRST_QUARTER = "FIRST_QUARTER"
@@ -488,12 +607,6 @@ class ForecastDay(BaseModel):
488
607
  display_date: Date = field(metadata={"alias": "displayDate"})
489
608
  """The local date in the time zone of the location."""
490
609
 
491
- daytime_forecast: ForecastDayPart = field(metadata={"alias": "daytimeForecast"})
492
- """The forecasted weather conditions for the daytime part of the day."""
493
-
494
- nighttime_forecast: ForecastDayPart = field(metadata={"alias": "nighttimeForecast"})
495
- """The forecasted weather conditions for the nighttime part of the day."""
496
-
497
610
  max_temperature: Temperature = field(metadata={"alias": "maxTemperature"})
498
611
  """The maximum (high) temperature throughout the day."""
499
612
 
@@ -515,6 +628,15 @@ class ForecastDay(BaseModel):
515
628
  moon_events: MoonEvents = field(metadata={"alias": "moonEvents"})
516
629
  """The events related to the moon (e.g. moonrise, moonset)."""
517
630
 
631
+ daytime_forecast: ForecastDayPart | None = field(default=None, metadata={"alias": "daytimeForecast"})
632
+ """The forecasted weather conditions for the daytime part of the day."""
633
+
634
+ nighttime_forecast: ForecastDayPart | None = field(default=None, metadata={"alias": "nighttimeForecast"})
635
+ """The forecasted weather conditions for the nighttime part of the day."""
636
+
637
+ full_day_forecast: ForecastDayPart | None = field(default=None, metadata={"alias": "fullDayForecast"})
638
+ """The forecasted weather conditions for the full day (returned instead of daytime/nighttime in Japan and South Korea)."""
639
+
518
640
  ice_thickness: IceThickness | None = field(default=None, metadata={"alias": "iceThickness"})
519
641
  """The accumulated amount of ice throughout the entire day."""
520
642
 
@@ -523,12 +645,12 @@ class ForecastDay(BaseModel):
523
645
  class DailyForecastResponse(BaseModel):
524
646
  """Response model for the forecast.days.lookup method."""
525
647
 
526
- forecast_days: list[ForecastDay] = field(metadata={"alias": "forecastDays"})
527
- """The daily forecast records."""
528
-
529
648
  time_zone: TimeZone = field(metadata={"alias": "timeZone"})
530
649
  """The time zone at the requested location."""
531
650
 
651
+ forecast_days: list[ForecastDay] = field(default_factory=list, metadata={"alias": "forecastDays"})
652
+ """The daily forecast records."""
653
+
532
654
  next_page_token: str | None = field(default=None, metadata={"alias": "nextPageToken"})
533
655
  """The token to retrieve the next page."""
534
656
 
@@ -617,11 +739,11 @@ class ForecastHour(BaseModel):
617
739
  uv_index: int = field(metadata={"alias": "uvIndex"})
618
740
  """The forecasted ultraviolet (UV) index."""
619
741
 
620
- thunderstorm_probability: int = field(metadata={"alias": "thunderstormProbability"})
621
- """The forecasted thunderstorm probability (0-100)."""
742
+ thunderstorm_probability: int | None = field(default=None, metadata={"alias": "thunderstormProbability"})
743
+ """The forecasted thunderstorm probability (0-100). Omitted in some regions (e.g. Japan and South Korea)."""
622
744
 
623
- cloud_cover: int = field(metadata={"alias": "cloudCover"})
624
- """The forecasted percentage of the sky covered by clouds (0-100)."""
745
+ cloud_cover: int | None = field(default=None, metadata={"alias": "cloudCover"})
746
+ """The forecasted percentage of the sky covered by clouds (0-100). Omitted in some regions (e.g. Japan and South Korea)."""
625
747
 
626
748
  ice_thickness: IceThickness | None = field(default=None, metadata={"alias": "iceThickness"})
627
749
  """The forecasted ice thickness."""
@@ -631,12 +753,12 @@ class ForecastHour(BaseModel):
631
753
  class HourlyForecastResponse(BaseModel):
632
754
  """Response model for the forecast.hours.lookup method."""
633
755
 
634
- forecast_hours: list[ForecastHour] = field(metadata={"alias": "forecastHours"})
635
- """The hourly forecast records."""
636
-
637
756
  time_zone: TimeZone = field(metadata={"alias": "timeZone"})
638
757
  """The time zone at the requested location."""
639
758
 
759
+ forecast_hours: list[ForecastHour] = field(default_factory=list, metadata={"alias": "forecastHours"})
760
+ """The hourly forecast records."""
761
+
640
762
  next_page_token: str | None = field(default=None, metadata={"alias": "nextPageToken"})
641
763
  """The token to retrieve the next page."""
642
764
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-google-weather-api
3
- Version: 0.0.9
3
+ Version: 0.0.11
4
4
  Summary: A python client library for Google Weather API
5
5
  Author-email: tronikos <tronikos@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -0,0 +1,10 @@
1
+ google_weather_api/__init__.py,sha256=sOOI0Muwa4HERSM6W1OCtumAasjuV_7WkCFKEDm3T8c,1691
2
+ google_weather_api/api.py,sha256=m8P4RGegQPS--eDvotlF4LXK8ZglcjLJqydsv9MMiHY,8198
3
+ google_weather_api/exceptions.py,sha256=z4rKXfRZLhkFLt1MISki1-nTxV_5Uc14ppjW7qu0ZNQ,708
4
+ google_weather_api/model.py,sha256=6PKYNwBp6cjLQiCTgTxs_L75VjkrsJVY5sgj00tJiMA,33879
5
+ google_weather_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ python_google_weather_api-0.0.11.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
+ python_google_weather_api-0.0.11.dist-info/METADATA,sha256=p_PP-eNyR9kjornyCu3efxa-UN7aIM_Gyw4QQyyph_8,1413
8
+ python_google_weather_api-0.0.11.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
9
+ python_google_weather_api-0.0.11.dist-info/top_level.txt,sha256=ZkFbtKDHz3vUJhH6DGEPVLj3PPeNpR9-Wg0f4HqGD78,19
10
+ python_google_weather_api-0.0.11.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- google_weather_api/__init__.py,sha256=Su4PjNcbIJFYoFCEH4wwdtT-M2HSMtLNLvF-TWGHiNo,1601
2
- google_weather_api/api.py,sha256=4_Y8TN6Mtrf4JDGoBOVm6W0uedxe5sPxMVx-ktD2kCI,7478
3
- google_weather_api/exceptions.py,sha256=AHHzb-cbE0bgzp4PH2IC5s3jHAJHONbL9iw5b-_MvoU,534
4
- google_weather_api/model.py,sha256=2Dx6VGh5Uaotb3McZSNs0BT6iJTrr5spYEUUC-IlXiA,24858
5
- google_weather_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- python_google_weather_api-0.0.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
- python_google_weather_api-0.0.9.dist-info/METADATA,sha256=SvCCnXAfToJfdwSvgBJ1D41y3lR3IsJGxgBwl_WJY5E,1412
8
- python_google_weather_api-0.0.9.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
9
- python_google_weather_api-0.0.9.dist-info/top_level.txt,sha256=ZkFbtKDHz3vUJhH6DGEPVLj3PPeNpR9-Wg0f4HqGD78,19
10
- python_google_weather_api-0.0.9.dist-info/RECORD,,