Skip to content
This repository was archived by the owner on Mar 15, 2024. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,8 @@ omit =
homeassistant/components/vulcan/calendar.py
homeassistant/components/vulcan/fetch_data.py
homeassistant/components/w800rf32/*
homeassistant/components/waqi/__init__.py
homeassistant/components/waqi/const.py
homeassistant/components/waqi/sensor.py
homeassistant/components/waterfurnace/*
homeassistant/components/watson_iot/*
Expand Down
2 changes: 1 addition & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,7 @@ build.json @home-assistant/supervisor
/tests/components/wake_on_lan/ @ntilley905
/homeassistant/components/wallbox/ @hesselonline
/tests/components/wallbox/ @hesselonline
/homeassistant/components/waqi/ @andrey-git
/homeassistant/components/waqi/ @sbach
/homeassistant/components/water_heater/ @home-assistant/core
/tests/components/water_heater/ @home-assistant/core
/homeassistant/components/watson_tts/ @rutkai
Expand Down
73 changes: 72 additions & 1 deletion homeassistant/components/waqi/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,72 @@
"""The waqi component."""
from __future__ import annotations

from datetime import timedelta

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from waqi_client_async import WAQIClient

from .const import (
CONF_API_TOKEN,
CONF_UPDATE_INTERVAL,
DEFAULT_UPDATE_INTERVAL,
DOMAIN,
LOGGER,
)

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


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

client = WAQIClient(
token=entry.options[CONF_API_TOKEN],
session=async_get_clientsession(hass),
)

async def coordinator_async_get():
try:
return await client.feed(f"@{entry.unique_id}")
except e:
raise UpdateFailed(e) from e

coordinator = DataUpdateCoordinator(
hass,
LOGGER,
name="WAQI",
update_method=coordinator_async_get,
update_interval=timedelta(seconds=entry.options[CONF_UPDATE_INTERVAL]),
)

await coordinator.async_config_entry_first_refresh()

hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator,
}

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

entry.async_on_unload(entry.add_update_listener(async_reload_entry))

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok


async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle an options update."""
await hass.config_entries.async_reload(entry.entry_id)
146 changes: 146 additions & 0 deletions homeassistant/components/waqi/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
from __future__ import annotations

import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_API_TOKEN
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession

import waqi_client_async as waqi

from .const import (
CONF_KEYWORD,
CONF_STATION,
CONF_UPDATE_INTERVAL,
DEFAULT_UPDATE_INTERVAL,
DOMAIN,
LOGGER,
)


class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""

VERSION = 1

_api_token: str
_stations: dict[str, str]
_update_interval: int

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:

try:
client = waqi.WAQIClient(
user_input[CONF_API_TOKEN], async_get_clientsession(self.hass)
)
found = await client.search(user_input[CONF_KEYWORD])
if not found:
errors[CONF_KEYWORD] = "no_matching_stations_found"
except waqi.OverQuota:
errors[CONF_API_TOKEN] = "api_over_quota"
except waqi.InvalidToken:
errors[CONF_API_TOKEN] = "api_token_invalid"
except:
return self.async_abort(reason="unknown")
else:
if found:
self._stations = {}
for station in found:
unique_id = station["uid"]
self._stations[unique_id] = station["station"]["name"]

self._api_token = user_input[CONF_API_TOKEN]
self._update_interval = user_input[CONF_UPDATE_INTERVAL]

return await self.async_step_pick_station()

return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_API_TOKEN): str,
vol.Required(CONF_KEYWORD): str,
vol.Optional(
CONF_UPDATE_INTERVAL,
default=DEFAULT_UPDATE_INTERVAL,
): int,
}
),
errors=errors,
)

async def async_step_pick_station(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the station selection step."""
errors: dict[str, str] = {}

if user_input is not None:
unique_id = user_input[CONF_STATION]

await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()

return self.async_create_entry(
title=self._stations[unique_id],
data={},
options={
CONF_API_TOKEN: self._api_token,
CONF_UPDATE_INTERVAL: self._update_interval,
},
)

return self.async_show_form(
step_id="pick_station",
data_schema=vol.Schema(
{vol.Required(CONF_STATION): vol.In(self._stations)}
),
errors=errors,
)

@staticmethod
@callback
def async_get_options_flow(config_entry: config_entries.ConfigEntry) -> OptionsFlow:
"""Get the options flow."""
return OptionsFlow(config_entry)


class OptionsFlow(config_entries.OptionsFlow):
"""Handle an options flow."""

def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize the options flow."""
self._config_entry = config_entry

async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the options configuration step."""
errors: dict[str, str] = {}

if user_input is not None:
return self.async_create_entry(title="", data=user_input)

options = self._config_entry.options
options_schema = vol.Schema(
{
vol.Required(CONF_API_TOKEN): str,
vol.Optional(
CONF_UPDATE_INTERVAL,
default=self._config_entry.options.get(
CONF_UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL
),
): int,
}
)

return self.async_show_form(
step_id="init", data_schema=options_schema, errors=errors
)
11 changes: 11 additions & 0 deletions homeassistant/components/waqi/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from logging import Logger, getLogger

LOGGER: Logger = getLogger(__package__)

DOMAIN = "_waqi"

CONF_KEYWORD = "keyword"
CONF_STATION = "station"
CONF_UPDATE_INTERVAL = "update_interval"

DEFAULT_UPDATE_INTERVAL = 900
8 changes: 4 additions & 4 deletions homeassistant/components/waqi/manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"domain": "waqi",
"name": "World Air Quality Index (WAQI)",
"name": "World's Air Quality Index (WAQI)",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/waqi",
"requirements": ["waqiasync==1.0.0"],
"codeowners": ["@andrey-git"],
"requirements": ["waqi-client-async==1.0.0"],
"codeowners": ["@sbach"],
"iot_class": "cloud_polling",
"loggers": ["waqiasync"]
}
Loading