Add repair issue for unknown station in WAQI (#182612)

This commit is contained in:
Simone Chemelli
2026-09-30 12:32:13 +02:00
committed by GitHub
parent f42ca7f9f5
commit f0ce605c48
4 changed files with 181 additions and 3 deletions
+23 -3
View File
@@ -3,13 +3,14 @@
from datetime import timedelta
from typing import override
from aiowaqi import WAQIAirQuality, WAQIClient, WAQIError
from aiowaqi import WAQIAirQuality, WAQIClient, WAQIError, WAQIUnknownStationError
from homeassistant.config_entries import ConfigEntry, ConfigSubentry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_STATION_NUMBER, LOGGER
from .const import CONF_STATION_NUMBER, DOMAIN, LOGGER
type WAQIConfigEntry = ConfigEntry[dict[str, WAQIDataUpdateCoordinator]]
@@ -39,9 +40,28 @@ class WAQIDataUpdateCoordinator(DataUpdateCoordinator[WAQIAirQuality]):
@override
async def _async_update_data(self) -> WAQIAirQuality:
issue_id = f"station_not_found_{self.subentry.subentry_id}"
try:
return await self._client.get_by_station_number(
air_quality = await self._client.get_by_station_number(
self.subentry.data[CONF_STATION_NUMBER]
)
except WAQIUnknownStationError as exc:
ir.async_create_issue(
self.hass,
DOMAIN,
issue_id,
is_fixable=True,
severity=ir.IssueSeverity.ERROR,
translation_key="station_not_found",
translation_placeholders={"name": self.subentry.title},
data={
"entry_id": self.config_entry.entry_id,
"subentry_id": self.subentry.subentry_id,
"name": self.subentry.title,
},
)
raise UpdateFailed(str(exc)) from exc
except WAQIError as exc:
raise UpdateFailed(str(exc)) from exc
ir.async_delete_issue(self.hass, DOMAIN, issue_id)
return air_quality
+56
View File
@@ -0,0 +1,56 @@
"""Repairs for the World Air Quality Index (WAQI) integration."""
from typing import TYPE_CHECKING
import probatio
from homeassistant.components.repairs import RepairsFlow, RepairsFlowResult
from homeassistant.core import HomeAssistant
class StationNotFoundRepairFlow(RepairsFlow):
"""Handler to remove a measuring station that can no longer be found."""
def __init__(self, entry_id: str, subentry_id: str, name: str) -> None:
"""Initialize the flow."""
self._entry_id = entry_id
self._subentry_id = subentry_id
self._name = name
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> RepairsFlowResult:
"""Handle the first step of a fix flow."""
return await self.async_step_confirm()
async def async_step_confirm(
self, user_input: dict[str, str] | None = None
) -> RepairsFlowResult:
"""Handle the confirm step of a fix flow."""
if user_input is not None:
entry = self.hass.config_entries.async_get_entry(self._entry_id)
if TYPE_CHECKING:
assert entry is not None
self.hass.config_entries.async_remove_subentry(entry, self._subentry_id)
return self.async_create_entry(data={})
return self.async_show_form(
step_id="confirm",
data_schema=probatio.Schema({}),
description_placeholders={"name": self._name},
)
async def async_create_fix_flow(
hass: HomeAssistant,
issue_id: str,
data: dict[str, str] | None,
) -> RepairsFlow:
"""Create flow."""
if TYPE_CHECKING:
assert data is not None
return StationNotFoundRepairFlow(
entry_id=data["entry_id"],
subentry_id=data["subentry_id"],
name=data["name"],
)
@@ -92,5 +92,18 @@
"name": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]"
}
}
},
"issues": {
"station_not_found": {
"fix_flow": {
"step": {
"confirm": {
"description": "The measuring station \"{name}\" can no longer be found by the World Air Quality Index API. This usually means the station has been permanently taken offline. A different station may report different metrics, so it is not automatically substituted.\n\nSelect **Submit** to remove this measuring station. You can add a replacement station afterwards.",
"title": "[%key:component::waqi::issues::station_not_found::title%]"
}
}
},
"title": "Measuring station \"{name}\" not found"
}
}
}
+89
View File
@@ -0,0 +1,89 @@
"""Test the World Air Quality Index (WAQI) repairs."""
from unittest.mock import AsyncMock
from aiowaqi import WAQIUnknownStationError
from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN
from homeassistant.components.waqi.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from homeassistant.setup import async_setup_component
from . import setup_integration
from tests.common import MockConfigEntry
from tests.components.repairs import process_repair_fix_flow, start_repair_fix_flow
from tests.typing import ClientSessionGenerator
async def test_station_not_found_fix_flow(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
issue_registry: ir.IssueRegistry,
mock_waqi: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the station not found fix flow removes the subentry."""
assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}})
subentry_id = list(mock_config_entry.subentries)[0]
mock_waqi.get_by_station_number.side_effect = WAQIUnknownStationError(
"Could not find station @4585"
)
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
issue_id = f"station_not_found_{subentry_id}"
issue = issue_registry.async_get_issue(DOMAIN, issue_id)
assert issue is not None
assert issue.translation_key == "station_not_found"
assert issue.is_fixable is True
assert issue.data == {
"entry_id": mock_config_entry.entry_id,
"subentry_id": subentry_id,
"name": "de Jongweg, Utrecht",
}
http_client = await hass_client()
data = await start_repair_fix_flow(http_client, DOMAIN, issue_id)
flow_id = data["flow_id"]
assert data["step_id"] == "confirm"
assert data["description_placeholders"] == {"name": "de Jongweg, Utrecht"}
data = await process_repair_fix_flow(http_client, flow_id, json={})
assert data["type"] == "create_entry"
await hass.async_block_till_done()
assert subentry_id not in mock_config_entry.subentries
assert not issue_registry.async_get_issue(DOMAIN, issue_id)
async def test_issue_cleared_on_recovery(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
mock_waqi: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the issue is removed once the station is reachable again."""
subentry_id = list(mock_config_entry.subentries)[0]
mock_waqi.get_by_station_number.side_effect = WAQIUnknownStationError(
"Could not find station @4585"
)
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
issue_id = f"station_not_found_{subentry_id}"
assert issue_registry.async_get_issue(DOMAIN, issue_id) is not None
mock_waqi.get_by_station_number.side_effect = None
await hass.config_entries.async_reload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.LOADED
assert issue_registry.async_get_issue(DOMAIN, issue_id) is None