import asyncio import logging import aiohttp from volkswagencarnet.vw_connection import Connection from volkswagencarnet.vw_exceptions import AuthenticationError # noqa: F401 — re-exported for collect.py log = logging.getLogger(__name__) class _Session: """Holds the asyncio event loop, aiohttp session, and Connection together.""" def __init__( self, loop: asyncio.AbstractEventLoop, session: aiohttp.ClientSession, connection: Connection, ) -> None: self._loop = loop self._http_session = session self.connection = connection def run(self, coro): return self._loop.run_until_complete(coro) @property def vehicles(self): return self.connection.vehicles def connect(username: str, password: str) -> _Session: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) async def _do(): session = aiohttp.ClientSession(headers={"Connection": "keep-alive"}) conn = Connection(session, username, password) ok = await conn.doLogin() if not ok: await session.close() raise AuthenticationError("Login failed") return session, conn session, conn = loop.run_until_complete(_do()) return _Session(loop, session, conn) def select_vehicle(wc: _Session, vin: str | None): """Return (vehicle, error_message). error_message is None on success.""" vehicles = wc.vehicles if not vehicles: return None, "No vehicles found in this account" if vin: vehicle = next((v for v in vehicles if v.vin == vin), None) if not vehicle: available = [v.vin for v in vehicles] return None, f"VIN {vin} not found. Available: {available}" return vehicle, None return vehicles[0], None def update(wc: _Session) -> bool: """Run one update cycle. Returns True if the library reported success.""" ok = wc.run(wc.connection.update()) if not ok: log.warning("connection.update() returned False — data may not have refreshed") else: log.debug("connection.update() succeeded") return bool(ok)