Compare commits

...

3 Commits

Author SHA1 Message Date
Daniel Hjelseth Høyer
e55f89a800 Tibber, handle failed update
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-03-22 08:25:37 +01:00
tronikos
f9bd9f4982 Add diagnostics in Google Weather (#166105) 2026-03-21 18:43:45 +01:00
Jack Boswell
e4620a208d Update starlink-grpc-core to 1.2.4 (#165882) 2026-03-21 18:30:04 +01:00
8 changed files with 467 additions and 8 deletions

View File

@@ -0,0 +1,44 @@
"""Diagnostics support for Google Weather."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
from .const import CONF_REFERRER
from .coordinator import GoogleWeatherConfigEntry
TO_REDACT = {
CONF_API_KEY,
CONF_REFERRER,
CONF_LATITUDE,
CONF_LONGITUDE,
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: GoogleWeatherConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
diag_data: dict[str, Any] = {
"entry": entry.as_dict(),
"subentries": {},
}
for subentry_id, subentry_rt in entry.runtime_data.subentries_runtime_data.items():
diag_data["subentries"][subentry_id] = {
"observation_data": subentry_rt.coordinator_observation.data.to_dict()
if subentry_rt.coordinator_observation.data
else None,
"daily_forecast_data": subentry_rt.coordinator_daily_forecast.data.to_dict()
if subentry_rt.coordinator_daily_forecast.data
else None,
"hourly_forecast_data": subentry_rt.coordinator_hourly_forecast.data.to_dict()
if subentry_rt.coordinator_hourly_forecast.data
else None,
}
return async_redact_data(diag_data, TO_REDACT)

View File

@@ -43,7 +43,7 @@ rules:
# Gold
devices: done
diagnostics: todo
diagnostics: done
discovery-update-info:
status: exempt
comment: No discovery.

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/starlink",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["starlink-grpc-core==1.2.3"]
"requirements": ["starlink-grpc-core==1.2.4"]
}

View File

@@ -312,10 +312,13 @@ class TibberPriceCoordinator(DataUpdateCoordinator[dict[str, TibberHomeData]]):
await asyncio.gather(
*(home.update_info_and_price_info() for home in homes_to_update)
)
except tibber.RetryableHttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err.status})") from err
except tibber.FatalHttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err.status})") from err
except (
tibber.RetryableHttpExceptionError,
tibber.FatalHttpExceptionError,
) as err:
_LOGGER.error(
"Error communicating with Tibber API (%s): %s", err.status, err
)
result = {home.home_id: _build_home_data(home) for home in active_homes}

2
requirements_all.txt generated
View File

@@ -3014,7 +3014,7 @@ starline==0.1.5
starlingbank==3.2
# homeassistant.components.starlink
starlink-grpc-core==1.2.3
starlink-grpc-core==1.2.4
# homeassistant.components.statsd
statsd==3.2.1

View File

@@ -2547,7 +2547,7 @@ srpenergy==1.3.6
starline==0.1.5
# homeassistant.components.starlink
starlink-grpc-core==1.2.3
starlink-grpc-core==1.2.4
# homeassistant.components.statsd
statsd==3.2.1

View File

@@ -0,0 +1,375 @@
# serializer version: 1
# name: test_diagnostics
dict({
'entry': dict({
'created_at': '2026-03-20T21:22:23+00:00',
'data': dict({
'api_key': '**REDACTED**',
}),
'disabled_by': None,
'discovery_keys': dict({
}),
'domain': 'google_weather',
'minor_version': 1,
'modified_at': '2026-03-20T21:22:23+00:00',
'options': dict({
}),
'pref_disable_new_entities': False,
'pref_disable_polling': False,
'source': 'user',
'subentries': list([
dict({
'data': dict({
'latitude': '**REDACTED**',
'longitude': '**REDACTED**',
}),
'subentry_id': 'home-subentry-id',
'subentry_type': 'location',
'title': 'Home',
'unique_id': None,
}),
]),
'title': 'Google Weather',
'unique_id': None,
'version': 1,
}),
'subentries': dict({
'home-subentry-id': dict({
'daily_forecast_data': dict({
'forecast_days': list([
dict({
'daytime_forecast': dict({
'cloud_cover': 53,
'ice_thickness': None,
'interval': dict({
'end_time': '2025-02-11T03:00:00Z',
'start_time': '2025-02-10T15:00:00Z',
}),
'precipitation': dict({
'probability': dict({
'percent': 5,
'type': 'RAIN',
}),
'qpf': dict({
'quantity': 0.0,
'unit': 'MILLIMETERS',
}),
'snow_qpf': None,
}),
'relative_humidity': 54,
'thunderstorm_probability': 0,
'uv_index': 3,
'weather_condition': dict({
'description': dict({
'language_code': 'en',
'text': 'Partly sunny',
}),
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/party_cloudy',
'type': 'PARTLY_CLOUDY',
}),
'wind': dict({
'direction': dict({
'cardinal': 'WEST',
'degrees': 280,
}),
'gust': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 14.0,
}),
'speed': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 6.0,
}),
}),
}),
'display_date': dict({
'day': 10,
'month': 2,
'year': 2025,
}),
'feels_like_max_temperature': dict({
'degrees': 13.3,
'unit': 'CELSIUS',
}),
'feels_like_min_temperature': dict({
'degrees': 1.5,
'unit': 'CELSIUS',
}),
'ice_thickness': dict({
'thickness': 0.0,
'unit': 'MILLIMETERS',
}),
'interval': dict({
'end_time': '2025-02-11T15:00:00Z',
'start_time': '2025-02-10T15:00:00Z',
}),
'max_heat_index': dict({
'degrees': 13.3,
'unit': 'CELSIUS',
}),
'max_temperature': dict({
'degrees': 13.3,
'unit': 'CELSIUS',
}),
'min_temperature': dict({
'degrees': 1.5,
'unit': 'CELSIUS',
}),
'moon_events': dict({
'moon_phase': 'WAXING_GIBBOUS',
'moonrise_times': list([
'2025-02-10T23:54:17.713157984Z',
]),
'moonset_times': list([
'2025-02-10T14:13:58.625181191Z',
]),
}),
'nighttime_forecast': dict({
'cloud_cover': 70,
'ice_thickness': None,
'interval': dict({
'end_time': '2025-02-11T15:00:00Z',
'start_time': '2025-02-11T03:00:00Z',
}),
'precipitation': dict({
'probability': dict({
'percent': 10,
'type': 'RAIN_AND_SNOW',
}),
'qpf': dict({
'quantity': 0.0,
'unit': 'MILLIMETERS',
}),
'snow_qpf': None,
}),
'relative_humidity': 85,
'thunderstorm_probability': 0,
'uv_index': 0,
'weather_condition': dict({
'description': dict({
'language_code': 'en',
'text': 'Partly cloudy',
}),
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/partly_clear',
'type': 'PARTLY_CLOUDY',
}),
'wind': dict({
'direction': dict({
'cardinal': 'SOUTH_SOUTHWEST',
'degrees': 201,
}),
'gust': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 14.0,
}),
'speed': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 6.0,
}),
}),
}),
'sun_events': dict({
'sunrise_time': '2025-02-10T15:02:35.703929582Z',
'sunset_time': '2025-02-11T01:43:00.762932858Z',
}),
}),
]),
'next_page_token': None,
'time_zone': dict({
'id': 'America/Los_Angeles',
'version': None,
}),
}),
'hourly_forecast_data': dict({
'forecast_hours': list([
dict({
'air_pressure': dict({
'mean_sea_level_millibars': 1019.13,
}),
'cloud_cover': 0,
'dew_point': dict({
'degrees': 2.7,
'unit': 'CELSIUS',
}),
'display_date_time': dict({
'day': 5,
'hours': 15,
'minutes': None,
'month': 2,
'nanos': None,
'seconds': None,
'time_zone': None,
'utc_offset': '-28800s',
'year': 2025,
}),
'feels_like_temperature': dict({
'degrees': 12.0,
'unit': 'CELSIUS',
}),
'heat_index': dict({
'degrees': 12.7,
'unit': 'CELSIUS',
}),
'ice_thickness': dict({
'thickness': 0.0,
'unit': 'MILLIMETERS',
}),
'interval': dict({
'end_time': '2025-02-06T00:00:00Z',
'start_time': '2025-02-05T23:00:00Z',
}),
'is_daytime': True,
'precipitation': dict({
'probability': dict({
'percent': 0,
'type': 'RAIN',
}),
'qpf': dict({
'quantity': 0.0,
'unit': 'MILLIMETERS',
}),
'snow_qpf': None,
}),
'relative_humidity': 51,
'temperature': dict({
'degrees': 12.7,
'unit': 'CELSIUS',
}),
'thunderstorm_probability': 0,
'uv_index': 1,
'visibility': dict({
'distance': 16.0,
'unit': 'KILOMETERS',
}),
'weather_condition': dict({
'description': dict({
'language_code': 'en',
'text': 'Sunny',
}),
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/sunny',
'type': 'CLEAR',
}),
'wet_bulb_temperature': dict({
'degrees': 7.7,
'unit': 'CELSIUS',
}),
'wind': dict({
'direction': dict({
'cardinal': 'NORTH_NORTHWEST',
'degrees': 335,
}),
'gust': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 19.0,
}),
'speed': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 10.0,
}),
}),
'wind_chill': dict({
'degrees': 12.0,
'unit': 'CELSIUS',
}),
}),
]),
'next_page_token': None,
'time_zone': dict({
'id': 'America/Los_Angeles',
'version': None,
}),
}),
'observation_data': dict({
'air_pressure': dict({
'mean_sea_level_millibars': 1019.16,
}),
'cloud_cover': 0,
'current_conditions_history': dict({
'max_temperature': dict({
'degrees': 14.3,
'unit': 'CELSIUS',
}),
'min_temperature': dict({
'degrees': 3.7,
'unit': 'CELSIUS',
}),
'qpf': dict({
'quantity': 0.0,
'unit': 'MILLIMETERS',
}),
'temperature_change': dict({
'degrees': -0.6,
'unit': 'CELSIUS',
}),
}),
'current_time': '2025-01-28T22:04:12.025273178Z',
'dew_point': dict({
'degrees': 1.1,
'unit': 'CELSIUS',
}),
'feels_like_temperature': dict({
'degrees': 13.1,
'unit': 'CELSIUS',
}),
'heat_index': dict({
'degrees': 13.7,
'unit': 'CELSIUS',
}),
'is_daytime': True,
'precipitation': dict({
'probability': dict({
'percent': 0,
'type': 'RAIN',
}),
'qpf': dict({
'quantity': 0.0,
'unit': 'MILLIMETERS',
}),
'snow_qpf': None,
}),
'relative_humidity': 42,
'temperature': dict({
'degrees': 13.7,
'unit': 'CELSIUS',
}),
'thunderstorm_probability': 0,
'time_zone': dict({
'id': 'America/Los_Angeles',
'version': None,
}),
'uv_index': 1,
'visibility': dict({
'distance': 16.0,
'unit': 'KILOMETERS',
}),
'weather_condition': dict({
'description': dict({
'language_code': 'en',
'text': 'Sunny',
}),
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/sunny',
'type': 'CLEAR',
}),
'wind': dict({
'direction': dict({
'cardinal': 'NORTH_NORTHWEST',
'degrees': 335,
}),
'gust': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 18.0,
}),
'speed': dict({
'unit': 'KILOMETERS_PER_HOUR',
'value': 8.0,
}),
}),
'wind_chill': dict({
'degrees': 13.1,
'unit': 'CELSIUS',
}),
}),
}),
}),
})
# ---

View File

@@ -0,0 +1,37 @@
"""Tests for the diagnostics data provided by the Google Weather integration."""
from unittest.mock import AsyncMock
from freezegun import freeze_time
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@pytest.fixture(autouse=True)
def freeze_the_time():
"""Freeze the time."""
with freeze_time("2026-03-20 21:22:23", tz_offset=0):
yield
async def test_diagnostics(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
mock_config_entry: MockConfigEntry,
mock_google_weather_api: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test diagnostics."""
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == snapshot(exclude=props("entry_id"))