From b990c65a5cb901667c31f4cbdded5cffa1105b32 Mon Sep 17 00:00:00 2001 From: tronikos Date: Sun, 6 Sep 2026 23:30:46 -0700 Subject: [PATCH] Bump python-google-weather-api to 0.0.7 (#181482) --- .../components/google_weather/coordinator.py | 7 +- .../components/google_weather/manifest.json | 2 +- requirements_all.txt | 2 +- .../snapshots/test_diagnostics.ambr | 178 +++++++++--------- tests/components/google_weather/test_init.py | 17 ++ 5 files changed, 114 insertions(+), 92 deletions(-) diff --git a/homeassistant/components/google_weather/coordinator.py b/homeassistant/components/google_weather/coordinator.py index 8efb6f10e362..444d2d8d6648 100644 --- a/homeassistant/components/google_weather/coordinator.py +++ b/homeassistant/components/google_weather/coordinator.py @@ -3,6 +3,7 @@ from collections.abc import Awaitable, Callable from dataclasses import dataclass from datetime import timedelta +from functools import partial import logging from typing import TypeVar, override @@ -28,6 +29,10 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) +# The API returns at most 24 hourly records per request, so asking for more than +# that costs an extra request per update. Keep it to a single page. +HOURLY_FORECAST_HOURS = 24 + T = TypeVar( "T", bound=( @@ -182,5 +187,5 @@ class GoogleWeatherHourlyForecastCoordinator( subentry, "hourly weather forecast", timedelta(hours=1), - api.async_get_hourly_forecast, + partial(api.async_get_hourly_forecast, hours=HOURLY_FORECAST_HOURS), ) diff --git a/homeassistant/components/google_weather/manifest.json b/homeassistant/components/google_weather/manifest.json index e7ec2e05563d..d47c56f552d2 100644 --- a/homeassistant/components/google_weather/manifest.json +++ b/homeassistant/components/google_weather/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["google_weather_api"], "quality_scale": "platinum", - "requirements": ["python-google-weather-api==0.0.6"] + "requirements": ["python-google-weather-api==0.0.7"] } diff --git a/requirements_all.txt b/requirements_all.txt index ea109d84b9e1..8531638dc3cb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2729,7 +2729,7 @@ python-gitlab==1.6.0 python-google-drive-api==0.1.0 # homeassistant.components.google_weather -python-google-weather-api==0.0.6 +python-google-weather-api==0.0.7 # homeassistant.components.analytics_insights python-homeassistant-analytics==0.9.0 diff --git a/tests/components/google_weather/snapshots/test_diagnostics.ambr b/tests/components/google_weather/snapshots/test_diagnostics.ambr index 87f707a7bf32..d8e92ef683c1 100644 --- a/tests/components/google_weather/snapshots/test_diagnostics.ambr +++ b/tests/components/google_weather/snapshots/test_diagnostics.ambr @@ -36,14 +36,14 @@ 'subentries': dict({ 'home-subentry-id': dict({ 'daily_forecast_data': dict({ - 'forecast_days': list([ + 'forecastDays': list([ dict({ - 'daytime_forecast': dict({ - 'cloud_cover': 53, - 'ice_thickness': None, + 'daytimeForecast': dict({ + 'cloudCover': 53, + 'iceThickness': None, 'interval': dict({ - 'end_time': '2025-02-11T03:00:00Z', - 'start_time': '2025-02-10T15:00:00Z', + 'endTime': '2025-02-11T03:00:00Z', + 'startTime': '2025-02-10T15:00:00Z', }), 'precipitation': dict({ 'probability': dict({ @@ -54,17 +54,17 @@ 'quantity': 0.0, 'unit': 'MILLIMETERS', }), - 'snow_qpf': None, + 'snowQpf': None, }), - 'relative_humidity': 54, - 'thunderstorm_probability': 0, - 'uv_index': 3, - 'weather_condition': dict({ + 'relativeHumidity': 54, + 'thunderstormProbability': 0, + 'uvIndex': 3, + 'weatherCondition': dict({ 'description': dict({ - 'language_code': 'en', + 'languageCode': 'en', 'text': 'Partly sunny', }), - 'icon_base_uri': 'https://maps.gstatic.com/weather/v1/party_cloudy', + 'iconBaseUri': 'https://maps.gstatic.com/weather/v1/party_cloudy', 'type': 'PARTLY_CLOUDY', }), 'wind': dict({ @@ -82,54 +82,54 @@ }), }), }), - 'display_date': dict({ + 'displayDate': dict({ 'day': 10, 'month': 2, 'year': 2025, }), - 'feels_like_max_temperature': dict({ + 'feelsLikeMaxTemperature': dict({ 'degrees': 13.3, 'unit': 'CELSIUS', }), - 'feels_like_min_temperature': dict({ + 'feelsLikeMinTemperature': dict({ 'degrees': 1.5, 'unit': 'CELSIUS', }), - 'ice_thickness': dict({ + 'iceThickness': dict({ 'thickness': 0.0, 'unit': 'MILLIMETERS', }), 'interval': dict({ - 'end_time': '2025-02-11T15:00:00Z', - 'start_time': '2025-02-10T15:00:00Z', + 'endTime': '2025-02-11T15:00:00Z', + 'startTime': '2025-02-10T15:00:00Z', }), - 'max_heat_index': dict({ + 'maxHeatIndex': dict({ 'degrees': 13.3, 'unit': 'CELSIUS', }), - 'max_temperature': dict({ + 'maxTemperature': dict({ 'degrees': 13.3, 'unit': 'CELSIUS', }), - 'min_temperature': dict({ + 'minTemperature': dict({ 'degrees': 1.5, 'unit': 'CELSIUS', }), - 'moon_events': dict({ - 'moon_phase': 'WAXING_GIBBOUS', - 'moonrise_times': list([ + 'moonEvents': dict({ + 'moonPhase': 'WAXING_GIBBOUS', + 'moonriseTimes': list([ '2025-02-10T23:54:17.713157984Z', ]), - 'moonset_times': list([ + 'moonsetTimes': list([ '2025-02-10T14:13:58.625181191Z', ]), }), - 'nighttime_forecast': dict({ - 'cloud_cover': 70, - 'ice_thickness': None, + 'nighttimeForecast': dict({ + 'cloudCover': 70, + 'iceThickness': None, 'interval': dict({ - 'end_time': '2025-02-11T15:00:00Z', - 'start_time': '2025-02-11T03:00:00Z', + 'endTime': '2025-02-11T15:00:00Z', + 'startTime': '2025-02-11T03:00:00Z', }), 'precipitation': dict({ 'probability': dict({ @@ -140,17 +140,17 @@ 'quantity': 0.0, 'unit': 'MILLIMETERS', }), - 'snow_qpf': None, + 'snowQpf': None, }), - 'relative_humidity': 85, - 'thunderstorm_probability': 0, - 'uv_index': 0, - 'weather_condition': dict({ + 'relativeHumidity': 85, + 'thunderstormProbability': 0, + 'uvIndex': 0, + 'weatherCondition': dict({ 'description': dict({ - 'language_code': 'en', + 'languageCode': 'en', 'text': 'Partly cloudy', }), - 'icon_base_uri': 'https://maps.gstatic.com/weather/v1/partly_clear', + 'iconBaseUri': 'https://maps.gstatic.com/weather/v1/partly_clear', 'type': 'PARTLY_CLOUDY', }), 'wind': dict({ @@ -168,57 +168,57 @@ }), }), }), - 'sun_events': dict({ - 'sunrise_time': '2025-02-10T15:02:35.703929582Z', - 'sunset_time': '2025-02-11T01:43:00.762932858Z', + 'sunEvents': dict({ + 'sunriseTime': '2025-02-10T15:02:35.703929582Z', + 'sunsetTime': '2025-02-11T01:43:00.762932858Z', }), }), ]), - 'next_page_token': None, - 'time_zone': dict({ + 'nextPageToken': None, + 'timeZone': dict({ 'id': 'America/Los_Angeles', 'version': None, }), }), 'hourly_forecast_data': dict({ - 'forecast_hours': list([ + 'forecastHours': list([ dict({ - 'air_pressure': dict({ - 'mean_sea_level_millibars': 1019.13, + 'airPressure': dict({ + 'meanSeaLevelMillibars': 1019.13, }), - 'cloud_cover': 0, - 'dew_point': dict({ + 'cloudCover': 0, + 'dewPoint': dict({ 'degrees': 2.7, 'unit': 'CELSIUS', }), - 'display_date_time': dict({ + 'displayDateTime': dict({ 'day': 5, 'hours': 15, 'minutes': None, 'month': 2, 'nanos': None, 'seconds': None, - 'time_zone': None, - 'utc_offset': '-28800s', + 'timeZone': None, + 'utcOffset': '-28800s', 'year': 2025, }), - 'feels_like_temperature': dict({ + 'feelsLikeTemperature': dict({ 'degrees': 12.0, 'unit': 'CELSIUS', }), - 'heat_index': dict({ + 'heatIndex': dict({ 'degrees': 12.7, 'unit': 'CELSIUS', }), - 'ice_thickness': dict({ + 'iceThickness': dict({ 'thickness': 0.0, 'unit': 'MILLIMETERS', }), 'interval': dict({ - 'end_time': '2025-02-06T00:00:00Z', - 'start_time': '2025-02-05T23:00:00Z', + 'endTime': '2025-02-06T00:00:00Z', + 'startTime': '2025-02-05T23:00:00Z', }), - 'is_daytime': True, + 'isDaytime': True, 'precipitation': dict({ 'probability': dict({ 'percent': 0, @@ -228,28 +228,28 @@ 'quantity': 0.0, 'unit': 'MILLIMETERS', }), - 'snow_qpf': None, + 'snowQpf': None, }), - 'relative_humidity': 51, + 'relativeHumidity': 51, 'temperature': dict({ 'degrees': 12.7, 'unit': 'CELSIUS', }), - 'thunderstorm_probability': 0, - 'uv_index': 1, + 'thunderstormProbability': 0, + 'uvIndex': 1, 'visibility': dict({ 'distance': 16.0, 'unit': 'KILOMETERS', }), - 'weather_condition': dict({ + 'weatherCondition': dict({ 'description': dict({ - 'language_code': 'en', + 'languageCode': 'en', 'text': 'Sunny', }), - 'icon_base_uri': 'https://maps.gstatic.com/weather/v1/sunny', + 'iconBaseUri': 'https://maps.gstatic.com/weather/v1/sunny', 'type': 'CLEAR', }), - 'wet_bulb_temperature': dict({ + 'wetBulbTemperature': dict({ 'degrees': 7.7, 'unit': 'CELSIUS', }), @@ -267,29 +267,29 @@ 'value': 10.0, }), }), - 'wind_chill': dict({ + 'windChill': dict({ 'degrees': 12.0, 'unit': 'CELSIUS', }), }), ]), - 'next_page_token': None, - 'time_zone': dict({ + 'nextPageToken': None, + 'timeZone': dict({ 'id': 'America/Los_Angeles', 'version': None, }), }), 'observation_data': dict({ - 'air_pressure': dict({ - 'mean_sea_level_millibars': 1019.16, + 'airPressure': dict({ + 'meanSeaLevelMillibars': 1019.16, }), - 'cloud_cover': 0, - 'current_conditions_history': dict({ - 'max_temperature': dict({ + 'cloudCover': 0, + 'currentConditionsHistory': dict({ + 'maxTemperature': dict({ 'degrees': 14.3, 'unit': 'CELSIUS', }), - 'min_temperature': dict({ + 'minTemperature': dict({ 'degrees': 3.7, 'unit': 'CELSIUS', }), @@ -297,25 +297,25 @@ 'quantity': 0.0, 'unit': 'MILLIMETERS', }), - 'temperature_change': dict({ + 'temperatureChange': dict({ 'degrees': -0.6, 'unit': 'CELSIUS', }), }), - 'current_time': '2025-01-28T22:04:12.025273178Z', - 'dew_point': dict({ + 'currentTime': '2025-01-28T22:04:12.025273178Z', + 'dewPoint': dict({ 'degrees': 1.1, 'unit': 'CELSIUS', }), - 'feels_like_temperature': dict({ + 'feelsLikeTemperature': dict({ 'degrees': 13.1, 'unit': 'CELSIUS', }), - 'heat_index': dict({ + 'heatIndex': dict({ 'degrees': 13.7, 'unit': 'CELSIUS', }), - 'is_daytime': True, + 'isDaytime': True, 'precipitation': dict({ 'probability': dict({ 'percent': 0, @@ -325,29 +325,29 @@ 'quantity': 0.0, 'unit': 'MILLIMETERS', }), - 'snow_qpf': None, + 'snowQpf': None, }), - 'relative_humidity': 42, + 'relativeHumidity': 42, 'temperature': dict({ 'degrees': 13.7, 'unit': 'CELSIUS', }), - 'thunderstorm_probability': 0, - 'time_zone': dict({ + 'thunderstormProbability': 0, + 'timeZone': dict({ 'id': 'America/Los_Angeles', 'version': None, }), - 'uv_index': 1, + 'uvIndex': 1, 'visibility': dict({ 'distance': 16.0, 'unit': 'KILOMETERS', }), - 'weather_condition': dict({ + 'weatherCondition': dict({ 'description': dict({ - 'language_code': 'en', + 'languageCode': 'en', 'text': 'Sunny', }), - 'icon_base_uri': 'https://maps.gstatic.com/weather/v1/sunny', + 'iconBaseUri': 'https://maps.gstatic.com/weather/v1/sunny', 'type': 'CLEAR', }), 'wind': dict({ @@ -364,7 +364,7 @@ 'value': 8.0, }), }), - 'wind_chill': dict({ + 'windChill': dict({ 'degrees': 13.1, 'unit': 'CELSIUS', }), diff --git a/tests/components/google_weather/test_init.py b/tests/components/google_weather/test_init.py index 6bc1d3ccc893..dd329f1ad313 100644 --- a/tests/components/google_weather/test_init.py +++ b/tests/components/google_weather/test_init.py @@ -29,6 +29,23 @@ async def test_async_setup_entry( assert state.state == "sunny" +async def test_hourly_forecast_fits_in_one_request( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_google_weather_api: AsyncMock, +) -> None: + """Test the hourly forecast asks for no more than one page of records. + + The API returns at most 24 hourly records per request, so asking for more + costs an extra request against the quota on every update. + """ + await hass.config_entries.async_setup(mock_config_entry.entry_id) + + mock_google_weather_api.async_get_hourly_forecast.assert_called_once_with( + 10.1, 20.1, hours=24 + ) + + @pytest.mark.parametrize( "failing_api_method", [