From 3bda6e8099ede924d7718ab6d77123a43ab8e3f5 Mon Sep 17 00:00:00 2001 From: rleidner Date: Tue, 2 Dec 2025 12:59:41 +0100 Subject: [PATCH 1/4] soc_vwid: make status query thread safe --- packages/modules/vehicles/vwid/libvwid.py | 142 +++++++++++----------- 1 file changed, 74 insertions(+), 68 deletions(-) diff --git a/packages/modules/vehicles/vwid/libvwid.py b/packages/modules/vehicles/vwid/libvwid.py index ebfa1768b3..b2235edf56 100755 --- a/packages/modules/vehicles/vwid/libvwid.py +++ b/packages/modules/vehicles/vwid/libvwid.py @@ -17,6 +17,7 @@ # from aiohttp.hdrs import METH_GET, METH_POST, METH_PUT import bs4 import jwt +import threading ANDROID_PACKAGE_NAME = "com.volkswagen.weconnect" APP_URI = "weconnect://authenticated" @@ -56,6 +57,9 @@ JWT_ALGORITHMS = ["RS256"] +lock = threading.Lock() + + class Services: """Service names that are used in `capabilities` and `selectivestatus` calls.""" @@ -1407,75 +1411,77 @@ def set_jobs(self, jobs): self.jobs_string = ','.join(jobs) async def get_status(self): - global connection - async with aiohttp.ClientSession(headers={'Connection': 'keep-alive'}) as session: - _now = datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ') - data = {} - data['charging'] = {} - data['charging']['batteryStatus'] = {} - data['charging']['batteryStatus']['value'] = {} - data['charging']['batteryStatus']['value']['currentSOC_pct'] = str(0) - data['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] = str(0) - data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = _now - - if 'connection' not in globals() or connection is None: - connection = {} - if self.username not in connection or connection[self.username] != self.username: - _LOGGER.debug("create new connection") - connection[self.username] = Connection(session, self.username, self.password) - connection[self.username]._session_tokens['identity'] = {} - connection[self.username]._session_tokens['Legacy'] = {} - for token in self.tokens: - connection[self.username]._session_tokens['identity'][token] = self.tokens[token] - connection[self.username]._session_tokens['Legacy'][token] = self.tokens[token] - _conn_reuse = False - else: - _LOGGER.debug("reuse existing connection") - connection[self.username]._session = session - _conn_reuse = True - try: - if not _conn_reuse: - _doLogin_result = await connection[self.username].doLogin() - _LOGGER.debug("after 1st doLogin, result=" + str(_doLogin_result)) - if _doLogin_result: - _update_result = True + global connection, lock + with lock: + async with aiohttp.ClientSession(headers={'Connection': 'keep-alive'}) as session: + _now = datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ') + data = {} + data['charging'] = {} + data['charging']['batteryStatus'] = {} + data['charging']['batteryStatus']['value'] = {} + data['charging']['batteryStatus']['value']['currentSOC_pct'] = str(0) + data['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] = str(0) + data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = _now + + if 'connection' not in globals() or connection is None: + connection = {} + if self.username not in connection or connection[self.username] != self.username: + _LOGGER.debug("create new connection") + connection[self.username] = Connection(session, self.username, self.password) + connection[self.username]._session_tokens['identity'] = {} + connection[self.username]._session_tokens['Legacy'] = {} + for token in self.tokens: + connection[self.username]._session_tokens['identity'][token] = self.tokens[token] + connection[self.username]._session_tokens['Legacy'][token] = self.tokens[token] + _conn_reuse = False else: - _update_result = await connection[self.username].update() - _LOGGER.debug("after 1st connection.update without doLogin, result=" + str(_update_result)) - if not _update_result: + _LOGGER.debug("reuse existing connection") + connection[self.username]._session = session + _conn_reuse = True + try: + if not _conn_reuse: _doLogin_result = await connection[self.username].doLogin() - _LOGGER.debug("after 2nd doLogin, result=" + str(_doLogin_result)) + _LOGGER.debug("after 1st doLogin, result=" + str(_doLogin_result)) if _doLogin_result: - _update_result = await connection[self.username].update() - _LOGGER.debug("after 2nd connection.update, result=" + str(_update_result)) - else: - _LOGGER.debug("retry doLogin failed, exit") - return data - if _update_result: - _LOGGER.debug("update/doLogin look OK, get results") - for vehicle in connection[self.username].vehicles: - _LOGGER.debug("vehicle loop: " + str(vehicle) + ", self.vin=" + str(self.vin)) - if str(vehicle) == str(self.vin): - _LOGGER.debug("vehicle loop match: " + str(vehicle) + ", self.vin=" + str(self.vin)) - soc = vehicle._states['charging']['batteryStatus']['value']['currentSOC_pct'] - range = vehicle._states['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] - ts = vehicle._states['charging']['batteryStatus']['value']['carCapturedTimestamp'] - _LOGGER.debug("vehicle =" + str(vehicle)) - _LOGGER.debug("soc =" + str(soc)) - _LOGGER.debug("range =" + str(range)) - _LOGGER.debug("timestamp=" + str(ts)) - tsxx = ts.strftime('%Y-%m-%dT%H:%M:%SZ') - _LOGGER.debug("timestampxx=" + str(tsxx)) - data['charging']['batteryStatus']['value']['currentSOC_pct'] = str(soc) - data['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] = str(range) - data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = str(tsxx) - _LOGGER.debug("return data =" + to_json(data, indent=4)) - for token in connection[self.username]._session_tokens['identity']: - self.tokens[token] = connection[self.username]._session_tokens['identity'][token] - return data - else: - _LOGGER.warning("get_status rsp. update failed, return soc 0") + _update_result = True + else: + _update_result = await connection[self.username].update() + _LOGGER.debug("after 1st connection.update without doLogin, result=" + str(_update_result)) + if not _update_result: + _doLogin_result = await connection[self.username].doLogin() + _LOGGER.debug("after 2nd doLogin, result=" + str(_doLogin_result)) + if _doLogin_result: + _update_result = await connection[self.username].update() + _LOGGER.debug("after 2nd connection.update, result=" + str(_update_result)) + else: + _LOGGER.debug("retry doLogin failed, exit") + return data + if _update_result: + _LOGGER.debug("update/doLogin look OK, get results") + for vehicle in connection[self.username].vehicles: + _LOGGER.debug("vehicle loop: " + str(vehicle) + ", self.vin=" + str(self.vin)) + if str(vehicle) == str(self.vin): + _LOGGER.debug("vehicle loop match: " + str(vehicle) + ", self.vin=" + str(self.vin)) + soc = vehicle._states['charging']['batteryStatus']['value']['currentSOC_pct'] + range =\ + vehicle._states['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] + ts = vehicle._states['charging']['batteryStatus']['value']['carCapturedTimestamp'] + _LOGGER.debug("vehicle =" + str(vehicle)) + _LOGGER.debug("soc =" + str(soc)) + _LOGGER.debug("range =" + str(range)) + _LOGGER.debug("timestamp=" + str(ts)) + tsxx = ts.strftime('%Y-%m-%dT%H:%M:%SZ') + _LOGGER.debug("timestampxx=" + str(tsxx)) + data['charging']['batteryStatus']['value']['currentSOC_pct'] = str(soc) + data['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] = str(range) + data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = str(tsxx) + _LOGGER.debug("return data =" + to_json(data, indent=4)) + for token in connection[self.username]._session_tokens['identity']: + self.tokens[token] = connection[self.username]._session_tokens['identity'][token] + return data + else: + _LOGGER.warning("get_status rsp. update failed, return soc 0") + return data + except Exception as error: + _LOGGER.exception("get_status failed, return soc 0, exception=" + str(error)) return data - except Exception as error: - _LOGGER.exception("get_status failed, return soc 0, exception=" + str(error)) - return data From 7d066303761473118b0b25d3b83c2e6cf2b98250 Mon Sep 17 00:00:00 2001 From: rleidner Date: Wed, 3 Dec 2025 22:22:01 +0100 Subject: [PATCH 2/4] fix connection context and reentrance problem --- packages/modules/vehicles/vwid/api.py | 49 +++++++++++------ packages/modules/vehicles/vwid/libvwid.py | 65 ++++++++++++----------- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/packages/modules/vehicles/vwid/api.py b/packages/modules/vehicles/vwid/api.py index 30d69946b9..96807ca5ce 100755 --- a/packages/modules/vehicles/vwid/api.py +++ b/packages/modules/vehicles/vwid/api.py @@ -6,28 +6,47 @@ from modules.vehicles.vwid import libvwid from modules.vehicles.vwid.config import VWId from modules.vehicles.vwgroup.vwgroup import VwGroup +import logging + + +log = logging.getLogger(__name__) class api(VwGroup): def __init__(self, conf: VWId, vehicle: int): + self.vwid = None super().__init__(conf, vehicle) # async method, called from sync fetch_soc, required because libvwid expect async environment async def _fetch_soc(self) -> Union[int, float, str]: async with aiohttp.ClientSession() as self.session: - vwid = libvwid.vwid(self.session) - return await super().request_data(vwid) - - -def fetch_soc(conf: VWId, vehicle: int) -> Union[int, float, str]: - - # prepare and call async method - loop = new_event_loop() - set_event_loop(loop) - - # get soc, range from server - a = api(conf, vehicle) - soc, range, soc_ts, soc_tsX = loop.run_until_complete(a._fetch_soc()) - - return soc, range, soc_ts, soc_tsX + # instantiate a single instance of vwid + if not self.vwid: + self.vwid = libvwid.vwid(self.session) + return await super().request_data(self.vwid) + + +def fetch_soc(conf: VWId, vehicle: int) -> Union[int, float, str, float]: + global a + try: + if 'a' not in globals(): + a = None + # prepare and call async method + loop = new_event_loop() + set_event_loop(loop) + + # instantiate a single instance of class api as a if not done yet + if not a: + log.debug(f"vwid.api.fetch_soc: create api for vehicle {vehicle}") + a = api(conf, vehicle) + else: + log.debug(f"vwid.api.fetch_soc: reuse api for vehicle {vehicle}") + + # get soc, range from server + soc, range, soc_ts, soc_tsX = loop.run_until_complete(a._fetch_soc()) + + return soc, range, soc_ts, soc_tsX + except Exception as e: + log.exception(f"vwid.api.fetch_soc: exception {e}") + raise Exception(f"vwid.api.fetch_soc: exception {e}") diff --git a/packages/modules/vehicles/vwid/libvwid.py b/packages/modules/vehicles/vwid/libvwid.py index b2235edf56..91269514a8 100755 --- a/packages/modules/vehicles/vwid/libvwid.py +++ b/packages/modules/vehicles/vwid/libvwid.py @@ -17,7 +17,6 @@ # from aiohttp.hdrs import METH_GET, METH_POST, METH_PUT import bs4 import jwt -import threading ANDROID_PACKAGE_NAME = "com.volkswagen.weconnect" APP_URI = "weconnect://authenticated" @@ -57,9 +56,6 @@ JWT_ALGORITHMS = ["RS256"] -lock = threading.Lock() - - class Services: """Service names that are used in `capabilities` and `selectivestatus` calls.""" @@ -1399,6 +1395,7 @@ class vwid: def __init__(self, session): self.session = session self.log = logging.getLogger(__name__) + self.connection = {} def set_vin(self, vin): self.vin = vin @@ -1411,8 +1408,7 @@ def set_jobs(self, jobs): self.jobs_string = ','.join(jobs) async def get_status(self): - global connection, lock - with lock: + try: async with aiohttp.ClientSession(headers={'Connection': 'keep-alive'}) as session: _now = datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ') data = {} @@ -1423,42 +1419,43 @@ async def get_status(self): data['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] = str(0) data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = _now - if 'connection' not in globals() or connection is None: - connection = {} - if self.username not in connection or connection[self.username] != self.username: - _LOGGER.debug("create new connection") - connection[self.username] = Connection(session, self.username, self.password) - connection[self.username]._session_tokens['identity'] = {} - connection[self.username]._session_tokens['Legacy'] = {} - for token in self.tokens: - connection[self.username]._session_tokens['identity'][token] = self.tokens[token] - connection[self.username]._session_tokens['Legacy'][token] = self.tokens[token] - _conn_reuse = False - else: - _LOGGER.debug("reuse existing connection") - connection[self.username]._session = session - _conn_reuse = True try: + _k = str(self.connection.keys()) + _LOGGER.debug(f"libvwid.get_status connections at entry: connections.keys={_k}") + if self.username not in self.connection: + _LOGGER.debug(f"create new connection, key={self.username}") + self.connection[self.username] = Connection(session, self.username, self.password) + self._connection = self.connection[self.username] + self.connection[self.username]._session_tokens['identity'] = {} + self.connection[self.username]._session_tokens['Legacy'] = {} + for token in self.tokens: + self.connection[self.username]._session_tokens['identity'][token] = self.tokens[token] + self.connection[self.username]._session_tokens['Legacy'][token] = self.tokens[token] + _conn_reuse = False + else: + _LOGGER.debug(f"reuse existing connection, key={self.username}") + self.connection[self.username]._session = session + _conn_reuse = True if not _conn_reuse: - _doLogin_result = await connection[self.username].doLogin() + _doLogin_result = await self.connection[self.username].doLogin() _LOGGER.debug("after 1st doLogin, result=" + str(_doLogin_result)) if _doLogin_result: _update_result = True else: - _update_result = await connection[self.username].update() + _update_result = await self.connection[self.username].update() _LOGGER.debug("after 1st connection.update without doLogin, result=" + str(_update_result)) if not _update_result: - _doLogin_result = await connection[self.username].doLogin() + _doLogin_result = await self.connection[self.username].doLogin() _LOGGER.debug("after 2nd doLogin, result=" + str(_doLogin_result)) if _doLogin_result: - _update_result = await connection[self.username].update() + _update_result = await self.connection[self.username].update() _LOGGER.debug("after 2nd connection.update, result=" + str(_update_result)) else: _LOGGER.debug("retry doLogin failed, exit") return data if _update_result: _LOGGER.debug("update/doLogin look OK, get results") - for vehicle in connection[self.username].vehicles: + for vehicle in self.connection[self.username].vehicles: _LOGGER.debug("vehicle loop: " + str(vehicle) + ", self.vin=" + str(self.vin)) if str(vehicle) == str(self.vin): _LOGGER.debug("vehicle loop match: " + str(vehicle) + ", self.vin=" + str(self.vin)) @@ -1476,12 +1473,16 @@ async def get_status(self): data['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] = str(range) data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = str(tsxx) _LOGGER.debug("return data =" + to_json(data, indent=4)) - for token in connection[self.username]._session_tokens['identity']: - self.tokens[token] = connection[self.username]._session_tokens['identity'][token] + for token in self.connection[self.username]._session_tokens['identity']: + self.tokens[token] =\ + self.connection[self.username]._session_tokens['identity'][token] return data else: - _LOGGER.warning("get_status rsp. update failed, return soc 0") - return data + _LOGGER.warning("get_status rsp. update failed, raise exception") + raise Exception("get_status: keine Daten empfangen") except Exception as error: - _LOGGER.exception("get_status failed, return soc 0, exception=" + str(error)) - return data + _LOGGER.exception("get_status failed 1, raise exception, exception=" + str(error)) + raise Exception(error) + except Exception as error: + _LOGGER.exception("get_status failed 0, raise exception=" + str(error)) + raise Exception(error) From f445c397615164bcae4acfdd200deb94c10be852 Mon Sep 17 00:00:00 2001 From: rleidner Date: Thu, 4 Dec 2025 11:53:37 +0100 Subject: [PATCH 3/4] handle modified parameters --- packages/modules/vehicles/vwid/api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/modules/vehicles/vwid/api.py b/packages/modules/vehicles/vwid/api.py index 96807ca5ce..dd504abbae 100755 --- a/packages/modules/vehicles/vwid/api.py +++ b/packages/modules/vehicles/vwid/api.py @@ -43,6 +43,10 @@ def fetch_soc(conf: VWId, vehicle: int) -> Union[int, float, str, float]: else: log.debug(f"vwid.api.fetch_soc: reuse api for vehicle {vehicle}") + a.user_id = conf.configuration.user_id + a.password = conf.configuration.password + a.vin = conf.configuration.vin + # get soc, range from server soc, range, soc_ts, soc_tsX = loop.run_until_complete(a._fetch_soc()) From b81c2216de0c612c3bf079d2b199b1aefec3ca43 Mon Sep 17 00:00:00 2001 From: rleidner Date: Thu, 4 Dec 2025 14:58:53 +0100 Subject: [PATCH 4/4] rework soc.py,libvwid.py, remove api.py --- packages/modules/vehicles/vwid/api.py | 56 ----------------------- packages/modules/vehicles/vwid/libvwid.py | 53 +++++++++++---------- packages/modules/vehicles/vwid/soc.py | 44 +++++++++--------- 3 files changed, 49 insertions(+), 104 deletions(-) delete mode 100755 packages/modules/vehicles/vwid/api.py diff --git a/packages/modules/vehicles/vwid/api.py b/packages/modules/vehicles/vwid/api.py deleted file mode 100755 index dd504abbae..0000000000 --- a/packages/modules/vehicles/vwid/api.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 - -import aiohttp -from asyncio import new_event_loop, set_event_loop -from typing import Union -from modules.vehicles.vwid import libvwid -from modules.vehicles.vwid.config import VWId -from modules.vehicles.vwgroup.vwgroup import VwGroup -import logging - - -log = logging.getLogger(__name__) - - -class api(VwGroup): - - def __init__(self, conf: VWId, vehicle: int): - self.vwid = None - super().__init__(conf, vehicle) - - # async method, called from sync fetch_soc, required because libvwid expect async environment - async def _fetch_soc(self) -> Union[int, float, str]: - async with aiohttp.ClientSession() as self.session: - # instantiate a single instance of vwid - if not self.vwid: - self.vwid = libvwid.vwid(self.session) - return await super().request_data(self.vwid) - - -def fetch_soc(conf: VWId, vehicle: int) -> Union[int, float, str, float]: - global a - try: - if 'a' not in globals(): - a = None - # prepare and call async method - loop = new_event_loop() - set_event_loop(loop) - - # instantiate a single instance of class api as a if not done yet - if not a: - log.debug(f"vwid.api.fetch_soc: create api for vehicle {vehicle}") - a = api(conf, vehicle) - else: - log.debug(f"vwid.api.fetch_soc: reuse api for vehicle {vehicle}") - - a.user_id = conf.configuration.user_id - a.password = conf.configuration.password - a.vin = conf.configuration.vin - - # get soc, range from server - soc, range, soc_ts, soc_tsX = loop.run_until_complete(a._fetch_soc()) - - return soc, range, soc_ts, soc_tsX - except Exception as e: - log.exception(f"vwid.api.fetch_soc: exception {e}") - raise Exception(f"vwid.api.fetch_soc: exception {e}") diff --git a/packages/modules/vehicles/vwid/libvwid.py b/packages/modules/vehicles/vwid/libvwid.py index 91269514a8..74ddaf5256 100755 --- a/packages/modules/vehicles/vwid/libvwid.py +++ b/packages/modules/vehicles/vwid/libvwid.py @@ -1391,11 +1391,14 @@ async def validate_login(self): return True -class vwid: +class vwid(): + + connection = {} + def __init__(self, session): self.session = session - self.log = logging.getLogger(__name__) - self.connection = {} + # self.log = logging.getLogger(__name__) + # self.connection = {} def set_vin(self, vin): self.vin = vin @@ -1420,42 +1423,42 @@ async def get_status(self): data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = _now try: - _k = str(self.connection.keys()) - _LOGGER.debug(f"libvwid.get_status connections at entry: connections.keys={_k}") - if self.username not in self.connection: - _LOGGER.debug(f"create new connection, key={self.username}") - self.connection[self.username] = Connection(session, self.username, self.password) - self._connection = self.connection[self.username] - self.connection[self.username]._session_tokens['identity'] = {} - self.connection[self.username]._session_tokens['Legacy'] = {} + _k = str(vwid.connection.keys()) + _LOGGER.info(f"libvwid.get_status connections at entry: vwid.connections.keys={_k}") + if self.username not in vwid.connection: + _LOGGER.info(f"create new connection, key={self.username}") + vwid.connection[self.username] = Connection(session, self.username, self.password) + self._connection = vwid.connection[self.username] + vwid.connection[self.username]._session_tokens['identity'] = {} + vwid.connection[self.username]._session_tokens['Legacy'] = {} for token in self.tokens: - self.connection[self.username]._session_tokens['identity'][token] = self.tokens[token] - self.connection[self.username]._session_tokens['Legacy'][token] = self.tokens[token] + vwid.connection[self.username]._session_tokens['identity'][token] = self.tokens[token] + vwid.connection[self.username]._session_tokens['Legacy'][token] = self.tokens[token] _conn_reuse = False else: - _LOGGER.debug(f"reuse existing connection, key={self.username}") - self.connection[self.username]._session = session + _LOGGER.info(f"reuse existing connection, key={self.username}") + vwid.connection[self.username]._session = session _conn_reuse = True if not _conn_reuse: - _doLogin_result = await self.connection[self.username].doLogin() + _doLogin_result = await vwid.connection[self.username].doLogin() _LOGGER.debug("after 1st doLogin, result=" + str(_doLogin_result)) if _doLogin_result: _update_result = True else: - _update_result = await self.connection[self.username].update() + _update_result = await vwid.connection[self.username].update() _LOGGER.debug("after 1st connection.update without doLogin, result=" + str(_update_result)) if not _update_result: - _doLogin_result = await self.connection[self.username].doLogin() + _doLogin_result = await vwid.connection[self.username].doLogin() _LOGGER.debug("after 2nd doLogin, result=" + str(_doLogin_result)) if _doLogin_result: - _update_result = await self.connection[self.username].update() + _update_result = await vwid.connection[self.username].update() _LOGGER.debug("after 2nd connection.update, result=" + str(_update_result)) else: - _LOGGER.debug("retry doLogin failed, exit") - return data + _LOGGER.error("retry doLogin failed, exit") + raise Exception("Login failed") if _update_result: _LOGGER.debug("update/doLogin look OK, get results") - for vehicle in self.connection[self.username].vehicles: + for vehicle in vwid.connection[self.username].vehicles: _LOGGER.debug("vehicle loop: " + str(vehicle) + ", self.vin=" + str(self.vin)) if str(vehicle) == str(self.vin): _LOGGER.debug("vehicle loop match: " + str(vehicle) + ", self.vin=" + str(self.vin)) @@ -1473,12 +1476,12 @@ async def get_status(self): data['charging']['batteryStatus']['value']['cruisingRangeElectric_km'] = str(range) data['charging']['batteryStatus']['value']['carCapturedTimestamp'] = str(tsxx) _LOGGER.debug("return data =" + to_json(data, indent=4)) - for token in self.connection[self.username]._session_tokens['identity']: + for token in vwid.connection[self.username]._session_tokens['identity']: self.tokens[token] =\ - self.connection[self.username]._session_tokens['identity'][token] + vwid.connection[self.username]._session_tokens['identity'][token] return data else: - _LOGGER.warning("get_status rsp. update failed, raise exception") + _LOGGER.error("get_status rsp. update failed, raise exception") raise Exception("get_status: keine Daten empfangen") except Exception as error: _LOGGER.exception("get_status failed 1, raise exception, exception=" + str(error)) diff --git a/packages/modules/vehicles/vwid/soc.py b/packages/modules/vehicles/vwid/soc.py index 0042072c44..57e31d4c2f 100755 --- a/packages/modules/vehicles/vwid/soc.py +++ b/packages/modules/vehicles/vwid/soc.py @@ -1,43 +1,41 @@ -from typing import List - +import aiohttp import logging +from asyncio import new_event_loop, set_event_loop +from typing import Union -from helpermodules.cli import run_using_positional_cli_args -from modules.common import store from modules.common.abstract_device import DeviceDescriptor from modules.common.abstract_vehicle import VehicleUpdateData from modules.common.component_state import CarState from modules.common.configurable_vehicle import ConfigurableVehicle -from modules.vehicles.vwid import api -from modules.vehicles.vwid.config import VWId, VWIdConfiguration - +from modules.vehicles.vwid.config import VWId +from modules.vehicles.vwid import libvwid +from modules.vehicles.vwgroup.vwgroup import VwGroup log = logging.getLogger(__name__) -def fetch(vehicle_update_data: VehicleUpdateData, config: VWId, vehicle: int) -> CarState: - 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=soc, range=range, soc_timestamp=soc_tsX) +def create_vehicle(vehicle_config: VWId, vehicle: int): + def fetch() -> CarState: + nonlocal vw_group + # async method, called from sync fetch_soc, required because libvwid expect async environment + async def _fetch_soc() -> Union[int, float, str]: + async with aiohttp.ClientSession() as session: + return await vw_group.request_data(libvwid.vwid(session)) + + loop = new_event_loop() + set_event_loop(loop) + soc, range, soc_ts, soc_tsX = loop.run_until_complete(_fetch_soc()) + return CarState(soc=soc, range=range, soc_timestamp=soc_tsX) + + vw_group = VwGroup(vehicle_config, vehicle) -def create_vehicle(vehicle_config: VWId, vehicle: int): def updater(vehicle_update_data: VehicleUpdateData) -> CarState: - return fetch(vehicle_update_data, vehicle_config, vehicle) + return fetch() 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): - log.debug("vwid: user_id="+user_id+"vin="+vin+"charge_point="+str(charge_point)) - store.get_car_value_store(charge_point).store.set( - fetch(None, VWId(configuration=VWIdConfiguration(user_id, password, vin, refreshToken)), charge_point)) - - -def main(argv: List[str]): - run_using_positional_cli_args(vwid_update, argv) - - device_descriptor = DeviceDescriptor(configuration_factory=VWId)