Skip to content

Commit

Permalink
add comments, try catch http timeout issue
Browse files Browse the repository at this point in the history
  • Loading branch information
leeyuentuen committed Jan 3, 2024
1 parent b772574 commit 9dd5b55
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 7 deletions.
10 changes: 6 additions & 4 deletions custom_components/polestar_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,18 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:


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

_LOGGER.debug("async_setup_entry: %s", config_entry)
polestarApi = Polestar(
polestar = Polestar(
hass, conf[CONF_USERNAME], conf[CONF_PASSWORD])
try:
await polestarApi.init()
polestarApi.set_config_unit(hass.config.units)
await polestar.init()
polestar.set_config_unit(hass.config.units)

hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][config_entry.entry_id] = polestarApi
hass.data[DOMAIN][config_entry.entry_id] = polestar

await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)

Expand All @@ -61,6 +62,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
_LOGGER.exception("Read Timeout on update data %s", str(e))
except Exception as e:
_LOGGER.exception("Unexpected Error on update data %s", str(e))
polestar.polestarApi.latest_call_code = 500
return False

async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/polestar_api/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Config flow for the Polestar EV platform."""
import asyncio
from asyncio import timeout
import logging

from aiohttp import ClientError
from async_timeout import timeout
import voluptuous as vol

from homeassistant import config_entries
Expand Down
1 change: 1 addition & 0 deletions custom_components/polestar_api/const.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Constants for the Polestar API integration."""
DOMAIN = "polestar_api"
TIMEOUT = 90

Expand Down
2 changes: 2 additions & 0 deletions custom_components/polestar_api/entity.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Base class for Polestar entities."""
import logging

from homeassistant.helpers.entity import DeviceInfo, Entity
Expand All @@ -9,6 +10,7 @@


class PolestarEntity(Entity):
"""Base class for Polestar entities."""

def __init__(self, device: Polestar) -> None:
"""Initialize the Polestar entity."""
Expand Down
12 changes: 12 additions & 0 deletions custom_components/polestar_api/polestar.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@


class Polestar:
"""Polestar EV integration."""
def __init__(self,
hass: HomeAssistant,
username: str,
Expand All @@ -30,6 +31,7 @@ def __init__(self,
disable_warnings()

async def init(self):
"""Initialize the Polestar API."""
await self.polestarApi.init()
vin = self.get_value('getConsumerCarsV2', 'vin', True)
if vin:
Expand All @@ -39,26 +41,32 @@ async def init(self):
self.name = "Polestar " + vin[-4:]

def get_token_expiry(self):
"""Get the token expiry time."""
return self.polestarApi.auth.token_expiry

def get_latest_data(self, query: str, field_name: str):
""" Get the latest data from the Polestar API."""
return self.polestarApi.get_latest_data(query, field_name)

def get_latest_call_code(self):
"""Get the latest call code."""
# if AUTH code last code is not 200 then we return that error code,
# otherwise just give the call_code in API
if self.polestarApi.auth.latest_call_code != 200:
return self.polestarApi.auth.latest_call_code
return self.polestarApi.latest_call_code

async def async_update(self) -> None:
"""Update data from Polestar."""
try:
await self.polestarApi.get_ev_data(self.vin)
return
except PolestarApiException as e:
_LOGGER.warning("API Exception on update data %s", str(e))
self.polestarApi.next_update = datetime.now() + timedelta(seconds=5)
except PolestarAuthException as e:
_LOGGER.warning("Auth Exception on update data %s", str(e))
self.polestarApi.auth.get_token()
self.polestarApi.next_update = datetime.now() + timedelta(seconds=5)
except httpx.ConnectTimeout as e:
_LOGGER.warning("Connection Timeout on update data %s", str(e))
Expand All @@ -72,14 +80,18 @@ async def async_update(self) -> None:
except Exception as e:
_LOGGER.error("Unexpected Error on update data %s", str(e))
self.polestarApi.next_update = datetime.now() + timedelta(seconds=60)
self.polestarApi.latest_call_code = 500

def set_config_unit(self, unit:UnitSystem):
"""Set unit system for the device."""
self.unit_system = unit

def get_config_unit(self):
"""Get unit system for the device."""
return self.unit_system

def get_value(self, query: str, field_name: str, skip_cache: bool = False):
"""Get the latest value from the Polestar API."""
data = self.polestarApi.get_cache_data(query, field_name, skip_cache)
if data is None:
return
Expand Down
1 change: 1 addition & 0 deletions custom_components/polestar_api/pypolestar/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
"""Python Polestar API."""
__version__ = "1.1.0"
1 change: 1 addition & 0 deletions custom_components/polestar_api/pypolestar/const.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Constants for Polestar."""
CACHE_TIME = 30

CAR_INFO_DATA = "getConsumerCarsV2"
Expand Down
2 changes: 2 additions & 0 deletions custom_components/polestar_api/pypolestar/exception.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Exceptions for Polestar API."""
class PolestarApiException(Exception):
"""Base class for exceptions in this module."""

Expand All @@ -9,6 +10,7 @@ class PolestarAuthException(Exception):
message: str = None

def __init__(self, message, error_code) -> None:
"""Initialize the Polestar API."""
super().__init__(message)
self.error_code = error_code

Expand Down
12 changes: 11 additions & 1 deletion custom_components/polestar_api/pypolestar/polestar.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Asynchronous Python client for the Polestar API."""""
from datetime import datetime, timedelta
import logging

Expand All @@ -16,8 +17,10 @@


class PolestarApi:
"""Main class for handling connections with the Polestar API."""

def __init__(self, username: str, password: str) -> None:
"""Initialize the Polestar API."""
self.auth = PolestarAuth(username, password)
self.updating = False
self.cache_data = {}
Expand All @@ -27,6 +30,7 @@ def __init__(self, username: str, password: str) -> None:


async def init(self):
"""Initialize the Polestar API."""
try:
await self.auth.get_token()

Expand All @@ -39,6 +43,7 @@ async def init(self):
_LOGGER.exception("Auth Exception: %s", str(e))

def get_latest_data(self, query: str, field_name: str) -> dict or bool or None:
"""Get the latest data from the Polestar API."""
if self.cache_data and self.cache_data[query]:
data = self.cache_data[query]['data']
if data is None:
Expand All @@ -64,6 +69,7 @@ def _get_field_name_value(self, field_name: str, data: dict) -> str or bool or N
return None

async def _get_odometer_data(self, vin: str):
"""" Get the latest odometer data from the Polestar API."""
params = {
"query": "query GetOdometerData($vin: String!) { getOdometerData(vin: $vin) { averageSpeedKmPerHour eventUpdatedTimestamp { iso unix __typename } odometerMeters tripMeterAutomaticKm tripMeterManualKm __typename }}",
"operationName": "GetOdometerData",
Expand Down Expand Up @@ -91,6 +97,7 @@ async def _get_battery_data(self, vin: str):
'data': result['data'][BATTERY_DATA], 'timestamp': datetime.now()}

async def _get_vehicle_data(self):
"""" Get the latest vehicle data from the Polestar API."""
# get Vehicle Data
params = {
"query": "query getCars { getConsumerCarsV2 { vin internalVehicleIdentifier modelYear content { model { code name __typename } images { studio { url angles __typename } __typename } __typename } hasPerformancePackage registrationNo deliveryDate currentPlannedDeliveryDate __typename }}",
Expand All @@ -110,6 +117,7 @@ async def _get_vehicle_data(self):
'data': result['data'][CAR_INFO_DATA][0], 'timestamp': datetime.now()}

async def get_ev_data(self, vin: str):
"""Get the latest ev data from the Polestar API."""
if self.updating:
return

Expand All @@ -121,7 +129,7 @@ async def get_ev_data(self, vin: str):

try:
if self.auth.token_expiry is None:
raise PolestarAuthException("No token expiry found")
raise PolestarAuthException("No token expiry found", 500)
if (self.auth.token_expiry - datetime.now()).total_seconds() < 300:
await self.auth.get_token(refresh=True)
except PolestarAuthException as e:
Expand All @@ -147,6 +155,7 @@ async def call_api(func):
self.next_update = datetime.now() + timedelta(seconds=5)

def get_cache_data(self, query: str, field_name: str, skip_cache: bool = False):
"""" Get the latest data from the cache."""
if query is None:
return None

Expand All @@ -159,6 +168,7 @@ def get_cache_data(self, query: str, field_name: str, skip_cache: bool = False):
return None

async def get_graph_ql(self, params: dict):
"""Get the latest data from the Polestar API."""
headers = {
"Content-Type": "application/json",
"authorization": f"Bearer {self.auth.access_token}"
Expand Down
7 changes: 6 additions & 1 deletion custom_components/polestar_api/sensor.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Support for Polestar sensors."""
from dataclasses import dataclass
from datetime import datetime, timedelta
import logging
Expand Down Expand Up @@ -378,6 +379,7 @@ async def async_setup_platform(
config: ConfigEntry,
async_add_entities: AddEntitiesCallback,
discovery_info=None):
"""Set up the Polestar sensor."""
pass


Expand All @@ -389,6 +391,7 @@ async def async_setup_entry(
# get the device
device: Polestar
device = hass.data[POLESTAR_API_DOMAIN][entry.entry_id]
await device.init()

# put data in cache
await device.async_update()
Expand Down Expand Up @@ -570,8 +573,10 @@ async def async_update(self) -> None:
"""Get the latest data and updates the states."""
try:
await self._device.async_update()
self._attr_native_value = self._device.get_value(
value = self._device.get_value(
self.description.query, self.description.field_name, self.get_skip_cache())
if value is not None:
self._attr_native_value = value
except Exception:
_LOGGER.warning("Failed to update sensor async update")
self._device.polestarApi.next_update = datetime.now() + timedelta(seconds=60)

0 comments on commit 9dd5b55

Please sign in to comment.