fix(remote_calendar): provide diagnostics for entries that failed setup (#182958)

This commit is contained in:
Evan Severson
2026-09-24 21:08:36 +02:00
committed by GitHub
parent c9204dba38
commit 8df9e61c68
5 changed files with 44 additions and 2 deletions
@@ -18,8 +18,8 @@ async def async_setup_entry(
) -> bool:
"""Set up Remote Calendar from a config entry."""
coordinator = RemoteCalendarDataUpdateCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await coordinator.async_config_entry_first_refresh()
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@@ -27,7 +27,6 @@ class RemoteCalendarDataUpdateCoordinator(DataUpdateCoordinator[Calendar]):
"""Class to manage fetching calendar data."""
config_entry: RemoteCalendarConfigEntry
ics: str
def __init__(
self,
@@ -49,6 +48,7 @@ class RemoteCalendarDataUpdateCoordinator(DataUpdateCoordinator[Calendar]):
self._url = config_entry.data[CONF_URL]
self._username: str | None = config_entry.data.get(CONF_USERNAME)
self._password: str | None = config_entry.data.get(CONF_PASSWORD)
self.ics = ""
@override
async def _async_update_data(self) -> Calendar:
@@ -15,10 +15,19 @@ async def async_get_config_entry_diagnostics(
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinator = entry.runtime_data
last_exception: dict[str, Any] | None = None
# Exception messages may embed the calendar URL, which often carries a secret
if (err := coordinator.last_exception) is not None:
last_exception = {
"type": type(err).__name__,
"translation_key": getattr(err, "translation_key", None),
}
payload: dict[str, Any] = {
"now": dt_util.now().isoformat(),
"timezone": str(dt_util.get_default_time_zone()),
"system_timezone": str(dt_util.naive_now().astimezone().tzinfo),
"last_update_success": coordinator.last_update_success,
"last_exception": last_exception,
}
payload["ics"] = "\n".join(redact_ics(coordinator.ics))
return payload
@@ -10,6 +10,21 @@
END:VEVENT
END:VCALENDAR
''',
'last_exception': None,
'last_update_success': True,
'now': '2023-06-04T18:00:00-06:00',
'system_timezone': 'tzlocal()',
'timezone': 'America/Regina',
})
# ---
# name: test_entry_diagnostics_setup_failed
dict({
'ics': '',
'last_exception': dict({
'translation_key': 'unable_to_fetch',
'type': 'UpdateFailed',
}),
'last_update_success': False,
'now': '2023-06-04T18:00:00-06:00',
'system_timezone': 'tzlocal()',
'timezone': 'America/Regina',
@@ -7,6 +7,7 @@ import pytest
import respx
from syrupy.assertion import SnapshotAssertion
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from . import setup_integration
@@ -37,3 +38,20 @@ async def test_entry_diagnostics(
await hass.async_block_till_done()
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot
@respx.mock
@pytest.mark.freeze_time(datetime.datetime(2023, 6, 5))
async def test_entry_diagnostics_setup_failed(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
snapshot: SnapshotAssertion,
config_entry: MockConfigEntry,
) -> None:
"""Test diagnostics are available for an entry that failed to set up."""
respx.get(CALENDER_URL).mock(return_value=Response(status_code=500))
await setup_integration(hass, config_entry)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.SETUP_RETRY
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert result == snapshot