mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 22:38:02 -04:00
Add repair issue for unknown station in WAQI (#182612)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user