diff --git a/setup.py b/setup.py index b53a166..2d58a26 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="tesla_fleet_api", - version="0.7.0", + version="0.7.1", author="Brett Adams", author_email="hello@teslemetry.com", description="Tesla Fleet API library for Python", diff --git a/tesla_fleet_api/const.py b/tesla_fleet_api/const.py index f7930aa..59ab8b9 100644 --- a/tesla_fleet_api/const.py +++ b/tesla_fleet_api/const.py @@ -3,7 +3,7 @@ from enum import Enum import logging -VERSION = "0.7.0" +VERSION = "0.7.1" LOGGER = logging.getLogger(__package__) SERVERS = { "na": "https://fleet-api.prd.na.vn.cloud.tesla.com", diff --git a/tesla_fleet_api/exceptions.py b/tesla_fleet_api/exceptions.py index 020124a..bd4d550 100644 --- a/tesla_fleet_api/exceptions.py +++ b/tesla_fleet_api/exceptions.py @@ -363,7 +363,12 @@ async def raise_for_status(resp: aiohttp.ClientResponse) -> None: elif resp.status == 424: raise InvalidResponse(data) elif resp.status == 429: - raise RateLimited(data) + raise RateLimited( + { + "reset": resp.headers.get("RateLimit-Reset"), + "after": resp.headers.get("Retry-After"), + } + ) elif resp.status == 451: raise ResourceUnavailableForLegalReasons(data) elif resp.status == 499: diff --git a/tesla_fleet_api/teslafleetapi.py b/tesla_fleet_api/teslafleetapi.py index aa8a357..5b2f964 100644 --- a/tesla_fleet_api/teslafleetapi.py +++ b/tesla_fleet_api/teslafleetapi.py @@ -1,7 +1,7 @@ """Tesla Fleet API for Python.""" from json import dumps -from typing import Any +from typing import Any, Awaitable import aiohttp from .exceptions import raise_for_status, InvalidRegion, LibraryError, ResponseError @@ -22,6 +22,7 @@ class TeslaFleetApi: server: str | None = None session: aiohttp.ClientSession headers: dict[str, str] + refresh_hook: Awaitable | None def __init__( self, @@ -34,11 +35,13 @@ def __init__( partner_scope: bool = True, user_scope: bool = True, vehicle_scope: bool = True, + refresh_hook: Awaitable | None = None, ): """Initialize the Tesla Fleet API.""" self.session = session self.access_token = access_token + self.refresh_hook = refresh_hook if server is not None: self.server = server @@ -90,6 +93,10 @@ async def _request( if method == Method.GET and json is not None: raise ValueError("GET requests cannot have a body.") + # Call a pre-request hook if provided + if self.refresh_hook is not None: + await self.refresh_hook() + LOGGER.debug("Sending request to %s", path) # Remove None values from params and json @@ -114,8 +121,16 @@ async def _request( LOGGER.debug("Response Status: %s", resp.status) if "x-txid" in resp.headers: LOGGER.debug("Response TXID: %s", resp.headers["x-txid"]) + if "RateLimit-Reset" in resp.headers: + LOGGER.debug( + "Rate limit reset: %s", resp.headers.get("RateLimit-Reset") + ) + if "Retry-After" in resp.headers: + LOGGER.debug("Retry after: %s", resp.headers.get("Retry-After")) + if not resp.ok: await raise_for_status(resp) + if not resp.content_type.lower().startswith("application/json"): LOGGER.debug("Response type is: %s", resp.content_type) raise ResponseError(status=resp.status, data=await resp.text())