Skip to content

Commit

Permalink
Add config flow to WAQI (#98220)
Browse files Browse the repository at this point in the history
* Migrate WAQI to aiowaqi library

* Migrate WAQI to aiowaqi library

* Migrate WAQI to aiowaqi library

* Add config flow to WAQI

* Finish config flow

* Add tests

* Add tests

* Fix ruff

* Add issues on failing to import

* Add issues on failing to import

* Add issues on failing to import

* Add importing issue

* Finish coverage

* Remove url from translation string

* Fix feedback

* Fix feedback
  • Loading branch information
joostlek authored Sep 9, 2023
1 parent fdddbd7 commit 9be16d9
Show file tree
Hide file tree
Showing 17 changed files with 825 additions and 79 deletions.
3 changes: 2 additions & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -1391,7 +1391,8 @@ build.json @home-assistant/supervisor
/tests/components/wake_word/ @home-assistant/core @synesthesiam
/homeassistant/components/wallbox/ @hesselonline
/tests/components/wallbox/ @hesselonline
/homeassistant/components/waqi/ @andrey-git
/homeassistant/components/waqi/ @joostlek
/tests/components/waqi/ @joostlek
/homeassistant/components/water_heater/ @home-assistant/core
/tests/components/water_heater/ @home-assistant/core
/homeassistant/components/watson_tts/ @rutkai
Expand Down
38 changes: 37 additions & 1 deletion homeassistant/components/waqi/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,37 @@
"""The waqi component."""
"""The World Air Quality Index (WAQI) integration."""
from __future__ import annotations

from aiowaqi import WAQIClient

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN
from .coordinator import WAQIDataUpdateCoordinator

PLATFORMS: list[Platform] = [Platform.SENSOR]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up World Air Quality Index (WAQI) from a config entry."""

client = WAQIClient(session=async_get_clientsession(hass))
client.authenticate(entry.data[CONF_API_KEY])

waqi_coordinator = WAQIDataUpdateCoordinator(hass, client)
await waqi_coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = waqi_coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
135 changes: 135 additions & 0 deletions homeassistant/components/waqi/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""Config flow for World Air Quality Index (WAQI) integration."""
from __future__ import annotations

import logging
from typing import Any

from aiowaqi import (
WAQIAirQuality,
WAQIAuthenticationError,
WAQIClient,
WAQIConnectionError,
)
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow
from homeassistant.const import (
CONF_API_KEY,
CONF_LATITUDE,
CONF_LOCATION,
CONF_LONGITUDE,
CONF_NAME,
)
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN
from homeassistant.data_entry_flow import AbortFlow, FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.selector import LocationSelector
from homeassistant.helpers.typing import ConfigType

from .const import CONF_STATION_NUMBER, DOMAIN, ISSUE_PLACEHOLDER

_LOGGER = logging.getLogger(__name__)


class WAQIConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for World Air Quality Index (WAQI)."""

VERSION = 1

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
async with WAQIClient(
session=async_get_clientsession(self.hass)
) as waqi_client:
waqi_client.authenticate(user_input[CONF_API_KEY])
location = user_input[CONF_LOCATION]
try:
measuring_station: WAQIAirQuality = (
await waqi_client.get_by_coordinates(
location[CONF_LATITUDE], location[CONF_LONGITUDE]
)
)
except WAQIAuthenticationError:
errors["base"] = "invalid_auth"
except WAQIConnectionError:
errors["base"] = "cannot_connect"
except Exception as exc: # pylint: disable=broad-except
_LOGGER.exception(exc)
errors["base"] = "unknown"
else:
await self.async_set_unique_id(str(measuring_station.station_id))
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=measuring_station.city.name,
data={
CONF_API_KEY: user_input[CONF_API_KEY],
CONF_STATION_NUMBER: measuring_station.station_id,
},
)

return self.async_show_form(
step_id="user",
data_schema=self.add_suggested_values_to_schema(
vol.Schema(
{
vol.Required(CONF_API_KEY): str,
vol.Required(
CONF_LOCATION,
): LocationSelector(),
}
),
user_input
or {
CONF_LOCATION: {
CONF_LATITUDE: self.hass.config.latitude,
CONF_LONGITUDE: self.hass.config.longitude,
}
},
),
errors=errors,
)

async def async_step_import(self, import_config: ConfigType) -> FlowResult:
"""Handle importing from yaml."""
await self.async_set_unique_id(str(import_config[CONF_STATION_NUMBER]))
try:
self._abort_if_unique_id_configured()
except AbortFlow as exc:
async_create_issue(
self.hass,
DOMAIN,
"deprecated_yaml_import_issue_already_configured",
breaks_in_ha_version="2024.4.0",
is_fixable=False,
severity=IssueSeverity.ERROR,
translation_key="deprecated_yaml_import_issue_already_configured",
translation_placeholders=ISSUE_PLACEHOLDER,
)
raise exc

async_create_issue(
self.hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_yaml_{DOMAIN}",
breaks_in_ha_version="2024.4.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "World Air Quality Index",
},
)
return self.async_create_entry(
title=import_config[CONF_NAME],
data={
CONF_API_KEY: import_config[CONF_API_KEY],
CONF_STATION_NUMBER: import_config[CONF_STATION_NUMBER],
},
)
10 changes: 10 additions & 0 deletions homeassistant/components/waqi/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Constants for the World Air Quality Index (WAQI) integration."""
import logging

DOMAIN = "waqi"

LOGGER = logging.getLogger(__package__)

CONF_STATION_NUMBER = "station_number"

ISSUE_PLACEHOLDER = {"url": "/config/integrations/dashboard/add?domain=waqi"}
36 changes: 36 additions & 0 deletions homeassistant/components/waqi/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Coordinator for the World Air Quality Index (WAQI) integration."""
from __future__ import annotations

from datetime import timedelta

from aiowaqi import WAQIAirQuality, WAQIClient, WAQIError

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import CONF_STATION_NUMBER, DOMAIN, LOGGER


class WAQIDataUpdateCoordinator(DataUpdateCoordinator[WAQIAirQuality]):
"""The WAQI Data Update Coordinator."""

config_entry: ConfigEntry

def __init__(self, hass: HomeAssistant, client: WAQIClient) -> None:
"""Initialize the WAQI data coordinator."""
super().__init__(
hass,
LOGGER,
name=DOMAIN,
update_interval=timedelta(minutes=5),
)
self._client = client

async def _async_update_data(self) -> WAQIAirQuality:
try:
return await self._client.get_by_station_number(
self.config_entry.data[CONF_STATION_NUMBER]
)
except WAQIError as exc:
raise UpdateFailed from exc
3 changes: 2 additions & 1 deletion homeassistant/components/waqi/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"domain": "waqi",
"name": "World Air Quality Index (WAQI)",
"codeowners": ["@andrey-git"],
"codeowners": ["@joostlek"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/waqi",
"iot_class": "cloud_polling",
"loggers": ["waqiasync"],
Expand Down
Loading

0 comments on commit 9be16d9

Please sign in to comment.