From 4d5c5de12aeb19a5b9222aa9c375e13056f8ad33 Mon Sep 17 00:00:00 2001 From: rleidner Date: Mon, 10 Feb 2025 21:12:46 +0100 Subject: [PATCH 1/4] soc module vwid improvements --- packages/modules/vehicles/vwid/api.py | 38 ++++++++++++++------------- packages/modules/vehicles/vwid/soc.py | 4 +-- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/modules/vehicles/vwid/api.py b/packages/modules/vehicles/vwid/api.py index f8dd9e2b08..19519c8ce7 100755 --- a/packages/modules/vehicles/vwid/api.py +++ b/packages/modules/vehicles/vwid/api.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -import logging +from logging import getLogger from typing import Union from modules.vehicles.vwid import libvwid -import aiohttp -import asyncio -import time +from aiohttp import ClientSession +from asyncio import new_event_loop, set_event_loop +from time import time, mktime from datetime import datetime -import json +from json import dumps from modules.common.store import RAMDISK_PATH from modules.vehicles.vwid.config import VWId from modules.vehicles.vwid.socutils import socUtils @@ -17,11 +17,11 @@ refreshToken_exp_days = 7 # 7 days before refreshToken expires a new refreshToken shall be stored initialToken = '1.2.3' -log = logging.getLogger(__name__) +log = getLogger(__name__) def utc2local(utc): - epoch = time.mktime(utc.timetuple()) + epoch = mktime(utc.timetuple()) offset = datetime.fromtimestamp(epoch) - datetime.utcfromtimestamp(epoch) return utc + offset @@ -44,7 +44,7 @@ async def _fetch_soc(self, self.accessTokenFile = str(RAMDISK_PATH) + '/soc_vwid_accessToken_vh_' + str(vehicle) self.accessToken_old = {} - async with aiohttp.ClientSession() as self.session: + async with ClientSession() as self.session: self.w = libvwid.vwid(self.session) self.w.set_vin(self.vin) self.w.set_credentials(self.user_id, self.password) @@ -79,13 +79,13 @@ async def _fetch_soc(self, if (self.data): if self.su.keys_exist(self.data, 'userCapabilities', 'capabilitiesStatus', 'error'): log.error("Server Error: \n" - + json.dumps(self.data['userCapabilities']['capabilitiesStatus']['error'], - ensure_ascii=False, indent=4)) + + dumps(self.data['userCapabilities']['capabilitiesStatus']['error'], + ensure_ascii=False, indent=4)) if self.su.keys_exist(self.data, 'charging', 'batteryStatus'): log.debug("batteryStatus: \n" + - json.dumps(self.data['charging']['batteryStatus'], - ensure_ascii=False, indent=4)) + dumps(self.data['charging']['batteryStatus'], + ensure_ascii=False, indent=4)) try: self.soc = int(self.data['charging']['batteryStatus']['value']['currentSOC_pct']) @@ -93,12 +93,14 @@ async def _fetch_soc(self, soc_tsZ = self.data['charging']['batteryStatus']['value']['carCapturedTimestamp'] soc_tsdtZ = datetime.strptime(soc_tsZ, ts_fmt + "Z") soc_tsdtL = utc2local(soc_tsdtZ) + self.soc_tsX = datetime.timestamp(soc_tsdtL) self.soc_ts = datetime.strftime(soc_tsdtL, ts_fmt) except Exception as e: log.exception("soc/range/soc_ts field missing exception: e=" + str(e)) self.soc = 0 self.range = 0.0 self.soc_ts = "" + self.soc_tsX = time() # decision logic - shall a new refreshToken be stored? self.store_refreshToken = False @@ -107,7 +109,7 @@ async def _fetch_soc(self, if self.refreshTokenOld != initialToken: try: self.expOld, self.expOld_dt = self.su.get_token_expiration(self.refreshTokenOld, date_fmt) - self.now = int(time.time()) + self.now = int(time()) expirationThreshold = self.expOld - refreshToken_exp_days * 86400 if expirationThreshold < self.now: @@ -142,17 +144,17 @@ async def _fetch_soc(self, if (self.w.tokens['accessToken'] != self.accessTokenOld): # modified accessToken? self.su.write_token_file(self.accessTokenFile, self.w.tokens['accessToken']) - return self.soc, self.range, self.soc_ts + return self.soc, self.range, self.soc_ts, self.soc_tsX def fetch_soc(conf: VWId, vehicle: int) -> Union[int, float, str]: # prepare and call async method - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) + loop = new_event_loop() + set_event_loop(loop) # get soc, range from server a = api() - soc, range, soc_ts = loop.run_until_complete(a._fetch_soc(conf, vehicle)) + soc, range, soc_ts, soc_tsX = loop.run_until_complete(a._fetch_soc(conf, vehicle)) - return soc, range, soc_ts + return soc, range, soc_ts, soc_tsX diff --git a/packages/modules/vehicles/vwid/soc.py b/packages/modules/vehicles/vwid/soc.py index d0bea63269..3344b6b87e 100755 --- a/packages/modules/vehicles/vwid/soc.py +++ b/packages/modules/vehicles/vwid/soc.py @@ -16,9 +16,9 @@ def fetch(vehicle_update_data: VehicleUpdateData, config: VWId, vehicle: int) -> CarState: - soc, range, soc_ts = api.fetch_soc(config, vehicle) + soc, range, soc_ts, soc_tsX = api.fetch_soc(config, vehicle) log.info("Result: soc=" + str(soc)+", range=" + str(range) + "@" + soc_ts) - return CarState(soc, range) + return CarState(soc=soc, range=range, soc_timestamp=soc_tsX) def create_vehicle(vehicle_config: VWId, vehicle: int): From 2a301956af97e6c47fe3b6124b3e6e84a72e3769 Mon Sep 17 00:00:00 2001 From: rleidner Date: Mon, 10 Feb 2025 21:22:23 +0100 Subject: [PATCH 2/4] fix pytest error --- packages/modules/vehicles/vwid/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/modules/vehicles/vwid/api.py b/packages/modules/vehicles/vwid/api.py index 19519c8ce7..960e9e41b4 100755 --- a/packages/modules/vehicles/vwid/api.py +++ b/packages/modules/vehicles/vwid/api.py @@ -20,6 +20,7 @@ log = getLogger(__name__) +# convert utc timestamp to local time def utc2local(utc): epoch = mktime(utc.timetuple()) offset = datetime.fromtimestamp(epoch) - datetime.utcfromtimestamp(epoch) From fbe7e71fc14889f9f2181ff149ec1d1a08723e7d Mon Sep 17 00:00:00 2001 From: rleidner Date: Tue, 11 Feb 2025 17:16:54 +0100 Subject: [PATCH 3/4] fix pytest assert import problem --- packages/modules/vehicles/vwid/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/modules/vehicles/vwid/api.py b/packages/modules/vehicles/vwid/api.py index 960e9e41b4..bf4198c294 100755 --- a/packages/modules/vehicles/vwid/api.py +++ b/packages/modules/vehicles/vwid/api.py @@ -3,7 +3,7 @@ from logging import getLogger from typing import Union from modules.vehicles.vwid import libvwid -from aiohttp import ClientSession +import aiohttp from asyncio import new_event_loop, set_event_loop from time import time, mktime from datetime import datetime @@ -45,7 +45,7 @@ async def _fetch_soc(self, self.accessTokenFile = str(RAMDISK_PATH) + '/soc_vwid_accessToken_vh_' + str(vehicle) self.accessToken_old = {} - async with ClientSession() as self.session: + async with aiohttp.ClientSession() as self.session: self.w = libvwid.vwid(self.session) self.w.set_vin(self.vin) self.w.set_credentials(self.user_id, self.password) From df5b57c94627303e399e6ce01163aeaeb752e0eb Mon Sep 17 00:00:00 2001 From: rleidner Date: Thu, 20 Feb 2025 11:15:13 +0100 Subject: [PATCH 4/4] add calculate_soc option --- packages/modules/vehicles/vwid/config.py | 4 +++- packages/modules/vehicles/vwid/soc.py | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/modules/vehicles/vwid/config.py b/packages/modules/vehicles/vwid/config.py index 64b4a5ef3b..2460ac1f98 100755 --- a/packages/modules/vehicles/vwid/config.py +++ b/packages/modules/vehicles/vwid/config.py @@ -6,12 +6,14 @@ def __init__(self, user_id: Optional[str] = None, # show in UI password: Optional[str] = None, # show in UI vin: Optional[str] = None, # show in UI - refreshToken: Optional[str] = None # DON'T show in UI! + refreshToken: Optional[str] = None, # DON'T show in UI! + calculate_soc: bool = False # show in UI ): self.user_id = user_id self.password = password self.vin = vin self.refreshToken = refreshToken + self.calculate_soc = calculate_soc class VWId: diff --git a/packages/modules/vehicles/vwid/soc.py b/packages/modules/vehicles/vwid/soc.py index 3344b6b87e..0042072c44 100755 --- a/packages/modules/vehicles/vwid/soc.py +++ b/packages/modules/vehicles/vwid/soc.py @@ -24,7 +24,10 @@ def fetch(vehicle_update_data: VehicleUpdateData, config: VWId, vehicle: int) -> def create_vehicle(vehicle_config: VWId, vehicle: int): def updater(vehicle_update_data: VehicleUpdateData) -> CarState: return fetch(vehicle_update_data, vehicle_config, vehicle) - return ConfigurableVehicle(vehicle_config=vehicle_config, component_updater=updater, vehicle=vehicle) + return ConfigurableVehicle(vehicle_config=vehicle_config, + component_updater=updater, + vehicle=vehicle, + calc_while_charging=vehicle_config.configuration.calculate_soc) def vwid_update(user_id: str, password: str, vin: str, refreshToken: str, charge_point: int):