Skip to content

Commit

Permalink
migrate to async api
Browse files Browse the repository at this point in the history
  • Loading branch information
denysdovhan committed Sep 20, 2024
1 parent dd28120 commit edcada6
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 55 deletions.
3 changes: 2 additions & 1 deletion custom_components/lun_misto_air/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import TYPE_CHECKING

from homeassistant.const import Platform
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .api import LUNMistoAirApi
from .coordinator import LUNMistoAirCoordinator
Expand All @@ -23,7 +24,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a new entry."""
LOGGER.debug("Entry data: %s", entry.data)

api = LUNMistoAirApi()
api = LUNMistoAirApi(session=async_get_clientsession(hass))
coordinator = LUNMistoAirCoordinator(hass, api, entry)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
Expand Down
96 changes: 58 additions & 38 deletions custom_components/lun_misto_air/api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""API for LUN Misto Air."""
"""Asynchronous API for LUN Misto Air."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Self

import requests
from aiohttp import ClientError, ClientSession, ClientTimeout


class LUNMistoAirError(Exception):
Expand All @@ -23,7 +25,7 @@ class LUNMistoAirStationNotFoundError(LUNMistoAirError):


class LUNMistoAirCityNotFoundError(LUNMistoAirError):
"""Raised for station errors."""
"""Raised when a city is not found."""


@dataclass(slots=True)
Expand All @@ -41,7 +43,7 @@ class LUNMistoAirStation:
updated: str

@classmethod
def from_dict(cls: type[Self], data: dict) -> Self:
def from_dict(cls: type[Self], data: dict[str, Any]) -> Self:
"""Initialize from a dict."""
return cls(
name=data["name"],
Expand All @@ -57,48 +59,66 @@ def from_dict(cls: type[Self], data: dict) -> Self:


class LUNMistoAirApi:
"""API for LUN Misto Air."""
"""Asynchronous API for LUN Misto Air."""

base_url = "https://misto.lun.ua/api/v1/air/stations"
timeout = 60

def _request(self, url: str) -> list[dict[str, Any]]:
"""Private method to handle HTTP requests."""
def __init__(
self,
session: ClientSession | None = None,
timeout: int = 60,
) -> None:
"""Initialize the API."""
self.session = session or ClientSession()
self.close_session = session is None
self.timeout = ClientTimeout(total=timeout)

async def close(self) -> None:
"""Close the client session if we created it."""
if self.close_session and not self.session.closed:
await self.session.close()

async def _request(self, url: str) -> Any:
"""Make an asynchronous HTTP request."""
try:
response = requests.get(url, timeout=self.timeout)
response.raise_for_status() # Raise an error for bad status codes
return response.json() # Return JSON data
except requests.exceptions.HTTPError as http_err:
msg = f"HTTP error occurred: {http_err}"
raise LUNMistoAirResponseError(msg) from http_err
except requests.exceptions.Timeout as timeout_err:
msg = f"Request timed out: {timeout_err}"
raise LUNMistoAirConnectionError(msg) from timeout_err
except requests.exceptions.RequestException as req_err:
msg = f"Request error occurred: {req_err}"
raise LUNMistoAirConnectionError(msg) from req_err
async with self.session.get(url, timeout=self.timeout) as response:
http_ok = 200
if response.status != http_ok:
text = await response.text()
msg = f"HTTP error {response.status}: {text}"
raise LUNMistoAirResponseError(msg) # noqa: TRY301
return await response.json()
except TimeoutError as err:
msg = "Request timed out"
raise LUNMistoAirConnectionError(msg) from err
except ClientError as err:
msg = f"Connection error: {err}"
raise LUNMistoAirConnectionError(msg) from err
except Exception as err:
msg = f"An unexpected error occurred: {err}"
msg = f"Unexpected error: {err}"
raise LUNMistoAirError(msg) from err

def get_all_stations(self) -> list[LUNMistoAirStation]:
async def get_all_stations(self) -> list[LUNMistoAirStation]:
"""Fetch and return data for all stations."""
if data := self._request(self.base_url):
return [LUNMistoAirStation.from_dict(station) for station in data]
return []
data = await self._request(self.base_url)
return [LUNMistoAirStation.from_dict(station) for station in data]

def get_by_station_name(self, station_name: str) -> LUNMistoAirStation:
async def get_station_by_name(self, station_name: str) -> LUNMistoAirStation:
"""Fetch and return data for a specific station by its name."""
if stations := self.get_all_stations():
for station in stations:
if station.name == station_name:
return station
raise LUNMistoAirStationNotFoundError

def get_by_city(self, city: str) -> list[LUNMistoAirStation]:
stations = await self.get_all_stations()
for station in stations:
if station.name == station_name:
return station
msg = f"Station with name '{station_name}' not found."
raise LUNMistoAirStationNotFoundError(msg)

async def get_stations_by_city(self, city: str) -> list[LUNMistoAirStation]:
"""Fetch and return data for all stations in a specific city."""
if stations := self.get_all_stations():
return [
station for station in stations if station.city.lower() == city.lower()
]
raise LUNMistoAirCityNotFoundError
stations = await self.get_all_stations()
matching_stations = [
station for station in stations if station.city.lower() == city.lower()
]
if not matching_stations:
msg = f"No stations found in city '{city}'."
raise LUNMistoAirCityNotFoundError(msg)
return matching_stations
18 changes: 6 additions & 12 deletions custom_components/lun_misto_air/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
CONF_METHOD,
)
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import (
LocationSelector,
SelectOptionDict,
Expand Down Expand Up @@ -58,7 +59,7 @@ class LUNMistoAirOptionsFlow(OptionsFlow):
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
self.api = LUNMistoAirApi()
self.api = LUNMistoAirApi(session=async_get_clientsession(self.hass))

async def async_step_init(self, user_input: dict | None = None) -> ConfigFlowResult:
"""Handle the station flow."""
Expand All @@ -77,9 +78,7 @@ async def async_step_init(self, user_input: dict | None = None) -> ConfigFlowRes
},
)

stations = await self.hass.async_add_executor_job(
self.api.get_all_stations,
)
stations = await self.api.get_all_stations()

return self.async_show_form(
step_id="init",
Expand All @@ -106,7 +105,7 @@ class LUNMistoAirConfigFlow(ConfigFlow, domain=DOMAIN):

def __init__(self) -> None:
"""Initialize config flow."""
self.api = LUNMistoAirApi()
self.api = LUNMistoAirApi(session=async_get_clientsession(self.hass))
self.data: dict[str, Any] = {}

@staticmethod
Expand Down Expand Up @@ -148,10 +147,7 @@ async def async_step_map(
if user_input is not None:
LOGGER.debug("User data: %s", user_input)

stations = await self.hass.async_add_executor_job(
self.api.get_all_stations,
)

stations = await self.api.get_all_stations()
if len(stations) == 0:
errors["base"] = "no_stations"

Expand Down Expand Up @@ -209,9 +205,7 @@ async def async_step_station_name(
},
)

stations = await self.hass.async_add_executor_job(
self.api.get_all_stations,
)
stations = await self.api.get_all_stations()

return self.async_show_form(
step_id=STEP_STATION_NAME,
Expand Down
5 changes: 1 addition & 4 deletions custom_components/lun_misto_air/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ def __init__(

async def _async_update_data(self) -> LUNMistoAirStation:
try:
return await self.hass.async_add_executor_job(
self._api.get_by_station_name,
self.station_name,
)
return await self._api.get_station_by_name(self.station_name)
except LUNMistoAirStationNotFoundError as exc:
msg = f"Station '{self.station_name}' not found"
raise UpdateFailed(msg) from exc
Expand Down

0 comments on commit edcada6

Please sign in to comment.