python-google-weather-api 0.0.7__py3-none-any.whl → 0.0.8__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.
@@ -21,9 +21,11 @@ from .model import (
21
21
  IceThickness,
22
22
  Interval,
23
23
  LocalizedText,
24
+ MinuteForecastResponse,
24
25
  MoonEvents,
25
26
  Precipitation,
26
27
  PrecipitationProbability,
28
+ PrecipitationSegment,
27
29
  QuantitativePrecipitationForecast,
28
30
  SunEvents,
29
31
  Temperature,
@@ -54,9 +56,11 @@ __all__ = [
54
56
  "IceThickness",
55
57
  "Interval",
56
58
  "LocalizedText",
59
+ "MinuteForecastResponse",
57
60
  "MoonEvents",
58
61
  "Precipitation",
59
62
  "PrecipitationProbability",
63
+ "PrecipitationSegment",
60
64
  "QuantitativePrecipitationForecast",
61
65
  "SunEvents",
62
66
  "Temperature",
google_weather_api/api.py CHANGED
@@ -17,6 +17,7 @@ from .model import (
17
17
  CurrentConditionsResponse,
18
18
  DailyForecastResponse,
19
19
  HourlyForecastResponse,
20
+ MinuteForecastResponse,
20
21
  )
21
22
 
22
23
  _LOGGER = logging.getLogger(__name__)
@@ -45,7 +46,7 @@ class GoogleWeatherApi:
45
46
  self.referrer = referrer
46
47
  self.timeout = aiohttp.ClientTimeout(total=timeout)
47
48
 
48
- async def _async_get(self, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
49
+ async def _async_get(self, endpoint: str, params: dict[str, Any], *, include_language_code: bool = True) -> dict[str, Any]:
49
50
  """Perform a GET request."""
50
51
  url = f"{_BASE_URL}/{endpoint}"
51
52
  headers = {aiohttp.hdrs.USER_AGENT: _USER_AGENT}
@@ -53,9 +54,10 @@ class GoogleWeatherApi:
53
54
  headers[aiohttp.hdrs.REFERER] = self.referrer
54
55
  params = {
55
56
  **params,
56
- "language_code": self.language_code,
57
57
  "units_system": self.units_system,
58
58
  }
59
+ if include_language_code:
60
+ params["language_code"] = self.language_code
59
61
  # Log before adding the API key so it doesn't end up in debug logs.
60
62
  _LOGGER.debug("GET %s with params: %s", url, params)
61
63
  params["key"] = self.api_key
@@ -89,18 +91,27 @@ class GoogleWeatherApi:
89
91
  raise GoogleWeatherApiConnectionError(err) from err
90
92
 
91
93
  async def _async_get_all_pages(
92
- self, endpoint: str, params: dict[str, Any], records_key: str, limit: int
94
+ self,
95
+ endpoint: str,
96
+ params: dict[str, Any],
97
+ records_key: str,
98
+ limit: int,
99
+ *,
100
+ include_language_code: bool = True,
93
101
  ) -> dict[str, Any]:
94
102
  """Perform a GET request, following pagination until `limit` records are collected.
95
103
 
96
- The API caps the number of records it returns per page (24 for hourly forecasts),
97
- regardless of the requested page size, so a single request is not enough.
104
+ Some endpoints cap the number of records they return per page regardless of the
105
+ requested page size, for example 24 for hourly forecasts, so a single request is
106
+ not always enough.
98
107
  """
99
108
  params = {**params, "page_size": limit}
100
- data = await self._async_get(endpoint, params)
109
+ data = await self._async_get(endpoint, params, include_language_code=include_language_code)
101
110
  records: list[Any] = data.get(records_key, [])
102
111
  while len(records) < limit and (page_token := data.get("nextPageToken")):
103
- data = await self._async_get(endpoint, {**params, "page_token": page_token})
112
+ data = await self._async_get(
113
+ endpoint, {**params, "page_token": page_token}, include_language_code=include_language_code
114
+ )
104
115
  page_records: list[Any] = data.get(records_key, [])
105
116
  if not page_records:
106
117
  break
@@ -155,3 +166,26 @@ class GoogleWeatherApi:
155
166
  days,
156
167
  )
157
168
  return DailyForecastResponse.from_dict(data)
169
+
170
+ async def async_get_minute_forecast(
171
+ self, latitude: float, longitude: float, page_size: int = 360
172
+ ) -> MinuteForecastResponse:
173
+ """Fetch the precipitation nowcast for up to 6 hours.
174
+
175
+ Segments cover 2- or 15-minute intervals depending on the location. The endpoint
176
+ does not accept language_code (a reserved field in google.maps.weather.v1), so it
177
+ is omitted from these requests.
178
+
179
+ See https://developers.google.com/maps/documentation/weather/minute-forecast
180
+ """
181
+ data = await self._async_get_all_pages(
182
+ "forecast/minutes:lookup",
183
+ {
184
+ "location.latitude": latitude,
185
+ "location.longitude": longitude,
186
+ },
187
+ "segments",
188
+ page_size,
189
+ include_language_code=False,
190
+ )
191
+ return MinuteForecastResponse.from_dict(data)
@@ -639,3 +639,64 @@ class HourlyForecastResponse(BaseModel):
639
639
 
640
640
  next_page_token: str | None = field(default=None, metadata={"alias": "nextPageToken"})
641
641
  """The token to retrieve the next page."""
642
+
643
+
644
+ @dataclass
645
+ class PrecipitationSegment(BaseModel):
646
+ """Represents a segment of precipitation forecast data."""
647
+
648
+ class DominantPrecipitationType(ApiStrEnum):
649
+ """Represents the type of precipitation of a segment."""
650
+
651
+ DOMINANT_PRECIPITATION_TYPE_UNSPECIFIED = "DOMINANT_PRECIPITATION_TYPE_UNSPECIFIED"
652
+ NONE = "NONE"
653
+ RAIN = "RAIN"
654
+ SNOW = "SNOW"
655
+ HAIL = "HAIL"
656
+
657
+ class PrecipitationIntensity(ApiStrEnum):
658
+ """Represents the precipitation intensity of a segment."""
659
+
660
+ PRECIPITATION_INTENSITY_UNSPECIFIED = "PRECIPITATION_INTENSITY_UNSPECIFIED"
661
+ NO_INTENSITY = "NO_INTENSITY"
662
+ LIGHT = "LIGHT"
663
+ MID_LIGHT = "MID_LIGHT"
664
+ MODERATE = "MODERATE"
665
+ MID_MODERATE = "MID_MODERATE"
666
+ HEAVY = "HEAVY"
667
+ MID_HEAVY = "MID_HEAVY"
668
+
669
+ time_frame: Interval = field(metadata={"alias": "timeFrame"})
670
+ """The timeframe relevant to the segment."""
671
+
672
+ type: DominantPrecipitationType = DominantPrecipitationType.DOMINANT_PRECIPITATION_TYPE_UNSPECIFIED
673
+ """A code that indicates the type of precipitation."""
674
+
675
+ probability: int = 0
676
+ """The chance of precipitation (values from 0 to 100)."""
677
+
678
+ qpf: QuantitativePrecipitationForecast | None = None
679
+ """The amount of precipitation, measured as liquid water equivalent."""
680
+
681
+ snowfall_amount: QuantitativePrecipitationForecast | None = field(default=None, metadata={"alias": "snowfallAmount"})
682
+ """The amount of snow accumulation, measured as liquid water equivalent."""
683
+
684
+ intensity: PrecipitationIntensity = PrecipitationIntensity.PRECIPITATION_INTENSITY_UNSPECIFIED
685
+ """A code that indicates the precipitation intensity of the segment."""
686
+
687
+
688
+ @dataclass
689
+ class MinuteForecastResponse(BaseModel):
690
+ """Response model for the forecast.minutes.lookup method."""
691
+
692
+ overall_prediction_timeframe: Interval = field(metadata={"alias": "overallPredictionTimeframe"})
693
+ """The overall timeframe for the predictions."""
694
+
695
+ time_zone: TimeZone = field(metadata={"alias": "timeZone"})
696
+ """The time zone at the requested location."""
697
+
698
+ segments: list[PrecipitationSegment] = field(default_factory=list)
699
+ """The minute-level prediction segments."""
700
+
701
+ next_page_token: str | None = field(default=None, metadata={"alias": "nextPageToken"})
702
+ """The token to retrieve the next page."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-google-weather-api
3
- Version: 0.0.7
3
+ Version: 0.0.8
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=Su4PjNcbIJFYoFCEH4wwdtT-M2HSMtLNLvF-TWGHiNo,1601
2
+ google_weather_api/api.py,sha256=NOSVkaj7YKHy6DL8J6x2HnUfA0V_J4GRFcsMS_EpyCs,7235
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.8.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
+ python_google_weather_api-0.0.8.dist-info/METADATA,sha256=T33mjBiV5-p4PY__jHyKzYsvqI2sWOT-JGsskuGgYfk,1412
8
+ python_google_weather_api-0.0.8.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
9
+ python_google_weather_api-0.0.8.dist-info/top_level.txt,sha256=ZkFbtKDHz3vUJhH6DGEPVLj3PPeNpR9-Wg0f4HqGD78,19
10
+ python_google_weather_api-0.0.8.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- google_weather_api/__init__.py,sha256=mIsd7IvUJhucWozTmTKqDHT2wtGEPMG34Ux2Ve9q8QM,1489
2
- google_weather_api/api.py,sha256=7RzupmA5E0ft-Yn_ygGCIDxIz1bcB1iXuo0cbcU0vsY,5997
3
- google_weather_api/exceptions.py,sha256=AHHzb-cbE0bgzp4PH2IC5s3jHAJHONbL9iw5b-_MvoU,534
4
- google_weather_api/model.py,sha256=9dwgYTRLi37XPNDL0X5OZrBut6qBjxqO8BL4kFl-iL0,22500
5
- google_weather_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- python_google_weather_api-0.0.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
- python_google_weather_api-0.0.7.dist-info/METADATA,sha256=KJCoHyP64kq1mZdcH3r4_zUn955g3bbfwy3_b4MBwTw,1412
8
- python_google_weather_api-0.0.7.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
9
- python_google_weather_api-0.0.7.dist-info/top_level.txt,sha256=ZkFbtKDHz3vUJhH6DGEPVLj3PPeNpR9-Wg0f4HqGD78,19
10
- python_google_weather_api-0.0.7.dist-info/RECORD,,