import logging import aiohttp from typing import Optional, Tuple from aiohttp.client import ClientTimeout from homeassistant.helpers.update_coordinator import UpdateFailed from .const import GEOAPI_GOUV_URL, ADDRESS_API_URL, VIGIEAU_API_URL import re DEFAULT_TIMEOUT = 120 CLIENT_TIMEOUT = ClientTimeout(total=DEFAULT_TIMEOUT) _LOGGER = logging.getLogger(__name__) class InseeApiError(RuntimeError): pass class InseeApi: """Api to get INSEE data""" def __init__( self, session: Optional[aiohttp.ClientSession] = None, timeout=CLIENT_TIMEOUT ) -> None: self._timeout = timeout self._session = session or aiohttp.ClientSession() async def get_insee_list(self): """Get all insee codes""" session = aiohttp.ClientSession() resp = await session.get(GEOAPI_GOUV_URL) if resp.status != 200: raise InseeApiError( f"Unable to list all INSEE codes. API status was {resp.status}" ) return await resp.json() async def get_data(self, zipcode) -> dict: """Get INSEE code for a given zip code""" url = f"{GEOAPI_GOUV_URL}&codePostal={zipcode}&format=json&geometry=centre" resp = await self._session.get(url) if resp.status != 200: raise InseeApiError(f"Unable to get Insee Code for zip {zipcode}") data = await resp.json() _LOGGER.debug("Got Data GEOAPI data : %s ", data) if len(data) == 0: raise InseeApiError("No data received with GeoApi") return data class AddressApiError(RuntimeError): pass class AddressApi: """API for Reverse geocoding""" def __init__( self, session: Optional[aiohttp.ClientSession] = None, timeout=CLIENT_TIMEOUT ) -> None: self._timeout = timeout self._session = session or aiohttp.ClientSession() async def get_data(self, lat: float, lon: float) -> Tuple[str, str, float, float]: url = f"{ADDRESS_API_URL}/reverse/?lat={lat}&lon={lon}&type=housenumber" resp = await self._session.get(url) if resp.status != 200: raise AddressApiError( "Failed to fetch address from api-adresse.data.gouv.fr api" ) data = await resp.json() _LOGGER.debug(f"Data received from {ADDRESS_API_URL}: {data}") if len(data["features"]) == 0: _LOGGER.warn( "Data received from api-adresse.data.gouv.fr is empty for those coordinates: (%s, %s). Either coordinates are not located in France or the governement geocoding database has no record for them.", lat, lon, ) raise AddressApiError( "Impossible to find approximate address of the current HA instance. API returned no result." ) properties = data["features"][0]["properties"] return (properties["citycode"], properties["city"], lat, lon) class VigieauApiError(RuntimeError): def __init__(self, message, text): super().__init__(message) self._text = text @property def text(self) -> str: return self._text class VigieauApi: def __init__( self, session: Optional[aiohttp.ClientSession] = None, timeout=CLIENT_TIMEOUT ) -> None: self._timeout = timeout self._session = session or aiohttp.ClientSession() async def get_data( self, lat: Optional[float], long: Optional[float], insee_code: str, profil: str ) -> dict: url = f"{VIGIEAU_API_URL}/api/zones?commune={insee_code}&profil={profil}&zoneType=SUP" if lat is not None and long is not None: url += f"&lat={lat}&lon={long}" _LOGGER.debug(f"Requesting restrictions from {url}") resp = await self._session.get(url) if ( resp.status == 404 and "message" in await resp.json() and re.match("Aucune zone.+en vigueur", (await resp.json())["message"]) ): _LOGGER.debug(f"Vigieau replied with no restriction, faking data") data = {"niveauGravite": "vigilance", "usages": [], "arrete": {}} elif resp.status == 200 and (await resp.text()) == "": _LOGGER.debug(f"Vigieau replied with no data at all, faking data") data = {"niveauGravite": "vigilance", "usages": [], "arrete": {}} elif resp.status in range(200, 300): data = await resp.json() else: raise VigieauApiError(f"Failed fetching vigieau data", resp.text) _LOGGER.debug(f"Data fetched from vigieau: {data}") return data