Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove unit in labels #49 #60

Merged
merged 3 commits into from
Jan 3, 2024
Merged
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
1 change: 1 addition & 0 deletions custom_components/polestar_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
hass, conf[CONF_USERNAME], conf[CONF_PASSWORD])
try:
await polestarApi.init()
polestarApi.set_config_unit(hass.config.units)

hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][config_entry.entry_id] = polestarApi
Expand Down
7 changes: 7 additions & 0 deletions custom_components/polestar_api/polestar.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from urllib3 import disable_warnings

from homeassistant.core import HomeAssistant
from homeassistant.util.unit_system import METRIC_SYSTEM, UnitSystem

from .pypolestar.exception import PolestarApiException, PolestarAuthException
from .pypolestar.polestar import PolestarApi
Expand All @@ -25,6 +26,7 @@ def __init__(self,
self.name = "Polestar "
self.polestarApi = PolestarApi(username, password)
self.vin = None
self.unit_system = METRIC_SYSTEM
disable_warnings()

async def init(self):
Expand Down Expand Up @@ -71,6 +73,11 @@ async def async_update(self) -> None:
_LOGGER.error("Unexpected Error on update data %s", str(e))
self.polestarApi.next_update = datetime.now() + timedelta(seconds=60)

def set_config_unit(self, unit:UnitSystem):
self.unit_system = unit

def get_config_unit(self):
return self.unit_system

def get_value(self, query: str, field_name: str, skip_cache: bool = False):
data = self.polestarApi.get_cache_data(query, field_name, skip_cache)
Expand Down
7 changes: 4 additions & 3 deletions custom_components/polestar_api/pypolestar/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import httpx

from .const import HTTPX_TIMEOUT
from .exception import PolestarAuthException

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -46,7 +47,7 @@ async def get_token(self, refresh=False) -> None:
"operationName": operationName,
"variables": json.dumps({"token": token}),
}
result = await self._client_session.get("https://pc-api.polestar.com/eu-north-1/auth/", params=params, headers=headers)
result = await self._client_session.get("https://pc-api.polestar.com/eu-north-1/auth/", params=params, headers=headers, timeout=HTTPX_TIMEOUT)
self.latest_call_code = result.status_code
resultData = result.json()
if result.status_code != 200 or ("errors" in resultData and len(resultData["errors"])):
Expand Down Expand Up @@ -97,7 +98,7 @@ async def _get_code(self) -> None:
code = result.next_request.url.params.get('code')

# sign-in-callback
result = await self._client_session.get(result.next_request.url)
result = await self._client_session.get(result.next_request.url, timeout=HTTPX_TIMEOUT)
self.latest_call_code = result.status_code

if result.status_code != 200:
Expand All @@ -116,7 +117,7 @@ async def _get_resume_path(self):
"client_id": "polmystar",
"redirect_uri": "https://www.polestar.com/sign-in-callback"
}
result = await self._client_session.get("https://polestarid.eu.polestar.com/as/authorization.oauth2", params=params)
result = await self._client_session.get("https://polestarid.eu.polestar.com/as/authorization.oauth2", params=params, timeout=HTTPX_TIMEOUT)
if result.status_code != 303:
raise PolestarAuthException("Error getting resume path ", result.status_code)
return result.next_request.url.params
2 changes: 2 additions & 0 deletions custom_components/polestar_api/pypolestar/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
CAR_INFO_DATA = "getConsumerCarsV2"
ODO_METER_DATA = "getOdometerData"
BATTERY_DATA = "getBatteryData"

HTTPX_TIMEOUT = 30
85 changes: 51 additions & 34 deletions custom_components/polestar_api/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util.unit_system import METRIC_SYSTEM

from . import DOMAIN as POLESTAR_API_DOMAIN
from .entity import PolestarEntity
Expand Down Expand Up @@ -83,31 +84,32 @@ class PolestarSensorDescription(

POLESTAR_SENSOR_TYPES: Final[tuple[PolestarSensorDescription, ...]] = (
PolestarSensorDescription(
key="estimate_distance_to_empty_miles",
name="Distance Miles Remaining",
icon="mdi:map-marker-distance",
query="getBatteryData",
field_name="estimatedDistanceToEmptyMiles",
unit=UnitOfLength.MILES,
round_digits=None,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
max_value=410,
dict_data=None,
),
PolestarSensorDescription(
key="estimate_distance_to_empty_km",
name="Distance Km Remaining",
key="estimate_range",
name="Range",
icon="mdi:map-marker-distance",
query="getBatteryData",
field_name="estimatedDistanceToEmptyKm",
unit=UnitOfLength.KILOMETERS,
round_digits=None,
round_digits=2,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
max_value=660, # WLTP range max 655
max_value=660,
dict_data=None
),
# deprecated
# PolestarSensorDescription(
# key="estimate_distance_to_empty_miles",
# name="Distance Miles Remaining",
# icon="mdi:map-marker-distance",
# query="getBatteryData",
# field_name="estimatedDistanceToEmptyMiles",
# unit=UnitOfLength.MILES,
# round_digits=None,
# state_class=SensorStateClass.MEASUREMENT,
# device_class=SensorDeviceClass.DISTANCE,
# max_value=410,
# dict_data=None,
# ),
PolestarSensorDescription(
key="current_odometer_meters",
name="Odometer",
Expand All @@ -122,7 +124,7 @@ class PolestarSensorDescription(
dict_data=None
),
PolestarSensorDescription(
key="average_speed_km_per_hour",
key="average_speed_per_hour",
name="Avg. Speed",
icon="mdi:speedometer",
query="getOdometerData",
Expand Down Expand Up @@ -234,7 +236,7 @@ class PolestarSensorDescription(
dict_data=CHARGING_CONNECTION_STATUS_DICT
),
PolestarSensorDescription(
key="average_energy_consumption_kwh_per_100_km",
key="average_energy_consumption_kwh_per_100",
name="Avg. Energy Consumption",
icon="mdi:battery-clock",
query="getBatteryData",
Expand Down Expand Up @@ -331,22 +333,10 @@ class PolestarSensorDescription(
max_value=None,
dict_data=None
),
PolestarSensorDescription(
key="estimate_full_charge_range_miles",
name="Calc. Miles Full Charge",
icon="mdi:map-marker-distance",
query="getBatteryData",
field_name="estimatedDistanceToEmptyMiles",
unit=UnitOfLength.MILES,
round_digits=None,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
max_value=410,
dict_data=None
),

PolestarSensorDescription(
key="estimate_full_charge_range",
name="Calc. Km Full Charge",
name="Calc. Full Charge Range",
icon="mdi:map-marker-distance",
query="getBatteryData",
field_name="estimatedDistanceToEmptyKm",
Expand Down Expand Up @@ -477,6 +467,11 @@ def native_unit_of_measurement(self) -> str | None:
@property
def state(self) -> StateType:
"""Return the state of the sensor."""

if self._attr_native_value is None and self.entity_description.key in ('charging_current_amps', 'charging_power_watts', 'estimated_charging_time_minutes_to_target_distance'):
self.entity_description.unit = None
return "Not Supported Yet"

if self.entity_description.dict_data is not None:
# exception for api_status_code
if self.entity_description.key == 'api_status_code':
Expand All @@ -497,6 +492,26 @@ def state(self) -> StateType:
self._attr_native_value = int(
self._attr_native_value.replace('.0', ''))

is_metric = self._device.get_config_unit() == METRIC_SYSTEM
if self.entity_description.key in ('estimate_full_charge_range', 'estimate_range', 'current_trip_meter_manual', 'current_trip_meter_automatic', 'current_odometer_meters', 'average_speed_per_hour', 'average_energy_consumption_kwh_per_100'):
if self.entity_description.key == "average_speed_per_hour":
self.entity_description.unit = UnitOfSpeed.KILOMETERS_PER_HOUR if is_metric else UnitOfSpeed.MILES_PER_HOUR
elif self.entity_description.key == "average_energy_consumption_kwh_per_100":
self.entity_description.unit = 'kWh/100km' if is_metric else 'kWh/100mi'
else:
self.entity_description.unit = UnitOfLength.KILOMETERS if is_metric else UnitOfLength.MILES

if not is_metric:
# todo: need to check if current_trip should normally in KM
# todo: check if we need to convert current_odo_meter to miles
self._attr_native_value = self._attr_native_value * 0.621371

if self.entity_description.key == "estimate_range":
if not is_metric:
self.entity_description.max_value = 410
else:
self.entity_description.max_value = 660

# prevent exponentianal value, we only give state value that is lower than the max value
if self.entity_description.max_value is not None:
if isinstance(self._attr_native_value, str):
Expand All @@ -511,7 +526,7 @@ def state(self) -> StateType:
return datetime.now().replace(second=0, microsecond=0) + timedelta(minutes=round(value))
return 'Not charging'

if self.entity_description.key in ('estimate_full_charge_range', 'estimate_full_charge_range_miles'):
if self.entity_description.key in ('estimate_full_charge_range'):
battery_level = self._device.get_latest_data(
self.entity_description.query, 'batteryChargeLevelPercentage')
estimate_range = self._device.get_latest_data(
Expand All @@ -530,6 +545,8 @@ def state(self) -> StateType:

return estimate_range



if self.entity_description.key in ('current_odometer_meters'):
if int(self._attr_native_value) > 1000:
km = self._attr_native_value / 1000
Expand Down