From 2daf2f3eda49a23c9914846a7109b672a8bc6a14 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Mon, 24 Aug 2026 20:52:52 +0200 Subject: [PATCH] fix(renault): reuse HTTP session across the login/data flow, guard cockpit odometer req.get_http_session() created a fresh session (and cookie jar) for every one of the 7 requests in fetch_soc(), breaking Renault's Gigya/Kamereon login chain for some accounts and causing SoC reads to fail entirely. Reuse a single session for the whole flow instead. Also guard the cockpit odometer lookup: some vehicles don't report mileage, and odometer is already optional on CarState, so a missing/failing cockpit response should no longer take down the SoC/range read with it. --- packages/modules/vehicles/renault/api.py | 34 +++++++++++++++--------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/modules/vehicles/renault/api.py b/packages/modules/vehicles/renault/api.py index 173f07e20c..f2675c2637 100755 --- a/packages/modules/vehicles/renault/api.py +++ b/packages/modules/vehicles/renault/api.py @@ -15,32 +15,35 @@ def fetch_soc(config: RenaultConfiguration) -> CarState: country_data = {'country': config.country} + # eine Session für den gesamten Login-/Abfrageablauf verwenden, da Renault/Gigya + # sitzungsübergreifend gesetzte Cookies für die Folgeaufrufe benötigt + session = req.get_http_session() # Gigya-Login: Zugangsdaten gegen Session-Cookie tauschen payload = {'loginID': config.user_id, 'password': config.password, 'apiKey': GIGYA_API} - gigya_session = req.get_http_session().post(f"{GIGYA_ROOTURL}/accounts.login", data=payload).json() + gigya_session = session.post(f"{GIGYA_ROOTURL}/accounts.login", data=payload).json() gigyacookievalue = gigya_session['sessionInfo']['cookieValue'] # Account-Infos abrufen (liefert personId) payload = {'login_token': gigyacookievalue, 'apiKey': GIGYA_API} - gigya_account = req.get_http_session().post(f"{GIGYA_ROOTURL}/accounts.getAccountInfo", data=payload).json() + gigya_account = session.post(f"{GIGYA_ROOTURL}/accounts.getAccountInfo", data=payload).json() # JWT für Kamereon-API anfordern payload = {'login_token': gigyacookievalue, 'apiKey': GIGYA_API, 'fields': 'data.personId,data.gigyaDataCenter', 'expiration': 900} - gigya_jwt = req.get_http_session().post(f"{GIGYA_ROOTURL}/accounts.getJWT", data=payload).json() + gigya_jwt = session.post(f"{GIGYA_ROOTURL}/accounts.getJWT", data=payload).json() gigya_jwttoken = gigya_jwt['id_token'] # Kamereon-Account anhand der personId ermitteln kamereonpersonid = gigya_account['data']['personId'] headers = {'x-gigya-id_token': gigya_jwttoken, 'apikey': KAMEREON_API_KEY} - kamereon_per = req.get_http_session().get(f"{KAMEREON_ROOTURL}/commerce/v1/persons/{kamereonpersonid}", - headers=headers, params=country_data).json() + kamereon_per = session.get(f"{KAMEREON_ROOTURL}/commerce/v1/persons/{kamereonpersonid}", + headers=headers, params=country_data).json() kamereonaccountid = kamereon_per['accounts'][0]['accountId'] log.debug(f"account id {kamereonaccountid}") # Fahrzeugliste abrufen, um VIN zu ermitteln, falls nicht konfiguriert - vehic = req.get_http_session().get( + vehic = session.get( f"{KAMEREON_ROOTURL}/commerce/v1/accounts/{kamereonaccountid}/vehicles", headers=headers, params=country_data).json() if config.vin is None or len(config.vin) < 10: @@ -49,17 +52,22 @@ def fetch_soc(config: RenaultConfiguration) -> CarState: vin = config.vin # Batteriestatus abrufen (SoC, Reichweite) - batt = req.get_http_session().get( + batt = session.get( f"{KAMEREON_ROOTURL}/commerce/v1/accounts/{kamereonaccountid}/kamereon/kca/" f"car-adapter/v2/cars/{vin}/battery-status", headers=headers, params=country_data).json() - # Cockpit-Daten abrufen (Kilometerstand) - cockpit = req.get_http_session().get( - f"{KAMEREON_ROOTURL}/commerce/v1/accounts/{kamereonaccountid}/kamereon/kca/" - f"car-adapter/v1/cars/{vin}/cockpit", - headers=headers, params=country_data).json() + # Cockpit-Daten abrufen (Kilometerstand); nicht jedes Fahrzeug liefert diesen Wert + odometer = None + try: + cockpit = session.get( + f"{KAMEREON_ROOTURL}/commerce/v1/accounts/{kamereonaccountid}/kamereon/kca/" + f"car-adapter/v1/cars/{vin}/cockpit", + headers=headers, params=country_data).json() + odometer = float(cockpit['data']['attributes']['totalMileage']) + except Exception: + log.debug("Kilometerstand konnte nicht abgerufen werden.", exc_info=True) return CarState(soc=float(batt['data']['attributes']['batteryLevel']), range=float(batt['data']['attributes']['batteryAutonomy']), - odometer=float(cockpit['data']['attributes']['totalMileage'])) + odometer=odometer)