diff --git a/custom_components/polestar_api/const.py b/custom_components/polestar_api/const.py index 8e60b2b..dc41230 100644 --- a/custom_components/polestar_api/const.py +++ b/custom_components/polestar_api/const.py @@ -12,4 +12,4 @@ HEADER_AUTHORIZATION = "authorization" HEADER_VCC_API_KEY = "vcc-api-key" -CACHE_TIME = 15 +CACHE_TIME = 10 diff --git a/custom_components/polestar_api/polestar_api.py b/custom_components/polestar_api/polestar_api.py index 0dd6c43..e47f56d 100644 --- a/custom_components/polestar_api/polestar_api.py +++ b/custom_components/polestar_api/polestar_api.py @@ -73,6 +73,20 @@ async def get_token(self) -> None: _LOGGER.debug(f"Response {self.access_token}") + def get_latest_data(self, path: str, reponse_path: str = None) -> dict or bool or None: + # i don't care what cache is, just give me the latest data in the cache + # replace the string {vin} with the actual vin + path = path.replace('{vin}', self.vin) + + if self.cache_data and self.cache_data[path]: + data = self.cache_data[path]['data'] + if data is None: + return False + if reponse_path: + for key in reponse_path.split('.'): + data = data[key] + return data + def get_cache_data(self, path: str, reponse_path: str = None) -> dict or bool or None: # replace the string {vin} with the actual vin path = path.replace('{vin}', self.vin) @@ -101,7 +115,9 @@ async def get_data(self, path: str, reponse_path: str = None) -> dict or bool or # put as fast possible something in the cache otherwise we get a lot of requests if not self.cache_data: self.cache_data = {} - self.cache_data[path] = {'data': None, 'timestamp': datetime.now()} + self.cache_data[path] = {'data': None, 'timestamp': datetime.now()} + else: + self.cache_data[path]['timestamp'] = datetime.now() url = 'https://api.volvocars.com/energy/v1/vehicles/' + path headers = { diff --git a/custom_components/polestar_api/sensor.py b/custom_components/polestar_api/sensor.py index 4add5d9..59447d4 100644 --- a/custom_components/polestar_api/sensor.py +++ b/custom_components/polestar_api/sensor.py @@ -81,6 +81,18 @@ class PolestarSensorDescription( device_class=SensorDeviceClass.DISTANCE, max_value=None ), + PolestarSensorDescription( + key="estimate_full_charge_range_miles", + name="Est. full charge range", + icon="mdi:map-marker-distance", + path="{vin}/recharge-status", + response_path=None, + unit='miles', + round_digits=None, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.DISTANCE, + max_value=None + ), PolestarSensorDescription( key="battery_charge_level", name="Battery level", @@ -115,6 +127,18 @@ class PolestarSensorDescription( device_class=SensorDeviceClass.DISTANCE, max_value=570, # prevent spike value, and this should be the max range of polestar ), + PolestarSensorDescription( + key="electric_range_miles", + name="EV Range", + icon="mdi:map-marker-distance", + path="{vin}/recharge-status", + response_path="electricRange.value", + unit='miles', + round_digits=None, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.DISTANCE, + max_value=355, # prevent spike value, and this should be the max range of polestar + ), PolestarSensorDescription( key="estimated_charging_time", name="Charging time", @@ -289,10 +313,10 @@ def state(self) -> StateType: return int(self._attr_native_value) return round(float(self._attr_native_value), self.entity_description.round_digits) - if self.entity_description.key == 'estimate_full_charge_range': - battery_level = self._device.get_cache_data( + if self.entity_description.key in ('estimate_full_charge_range', 'estimate_full_charge_range_miles'): + battery_level = self._device.get_latest_data( self.entity_description.path, 'batteryChargeLevel.value') - estimate_range = self._device.get_cache_data( + estimate_range = self._device.get_latest_data( self.entity_description.path, 'electricRange.value') if battery_level is None or estimate_range is None: @@ -304,8 +328,25 @@ def state(self) -> StateType: battery_level = int(battery_level.replace('.0', '')) estimate_range = int(estimate_range) - return round(estimate_range / battery_level * 100) + estimate_range = round(estimate_range / battery_level * 100) + + if self.entity_description.key == 'estimate_full_charge_range_miles': + return round(estimate_range / 1.609344, self.entity_description.round_digits if self.entity_description.round_digits is not None else 0) + + return estimate_range + + if self.entity_description.key == 'electric_range_miles': + if self._attr_native_value is None: + return None + + if self._attr_native_value is False: + return None + + self._attr_native_value = int(self._attr_native_value) + miles = round(self._attr_native_value / 1.609344, + self.entity_description.round_digits if self.entity_description.round_digits is not None else 0) + return miles return self._attr_native_value @property