Support Porsche mobile auth flow and Macan EV data - #84
Conversation
|
Thank you for this PR, it looks promising! |
| def _normalize_engine(vehicle: dict) -> str: | ||
| """Best-effort mapping of the portal vehicle payload to drivetrain type.""" | ||
| model_type = vehicle.get("modelType", {}) | ||
| if model_type.get("engine"): | ||
| return model_type["engine"] | ||
| description = str(vehicle.get("modelDescription", "")).lower() | ||
| if description in {"macan", "taycan"}: | ||
| return "BEV" | ||
| return "COMBUSTION" | ||
|
|
||
|
|
||
| def _normalize_vehicle(vehicle: dict) -> dict: | ||
| """Normalize the portal vehicle payload to the legacy library shape.""" | ||
| model_name = vehicle.get("modelDescription") or vehicle.get("modelName") or vehicle.get("vin", "Porsche") | ||
| return { | ||
| "vin": vehicle["vin"], | ||
| "name": model_name, | ||
| "modelName": model_name, | ||
| "modelType": { | ||
| "year": vehicle.get("modelYear") or vehicle.get("modelType", {}).get("year", "not available"), | ||
| "engine": _normalize_engine(vehicle), | ||
| }, | ||
| "systemInfo": vehicle.get("systemInfo", {}), | ||
| "timestamp": vehicle.get("validFrom") or vehicle.get("timestamp"), | ||
| "portalVehicle": vehicle, | ||
| } | ||
|
|
||
|
|
There was a problem hiding this comment.
I don't see why this is needed, should be removed?
| v = PorscheVehicle( | ||
| vin=vehicle["vin"], | ||
| data=vehicle, | ||
| data=_normalize_vehicle(vehicle), |
| def _normalize_engine(vehicle: dict) -> str: | ||
| """Best-effort mapping of the portal vehicle payload to drivetrain type.""" | ||
| description = str(vehicle.get("modelDescription", "")).lower() | ||
| if description in {"macan", "taycan"}: | ||
| return "BEV" | ||
| return "COMBUSTION" | ||
|
|
||
|
|
||
| def _normalize_portal_vehicle(vehicle: dict) -> dict: | ||
| """Normalize the portal vehicle payload to the legacy library shape.""" | ||
| model_name = vehicle.get("modelDescription") or vehicle.get("modelName") or vehicle.get("vin", "Porsche") | ||
| return { | ||
| "vin": vehicle["vin"], | ||
| "name": model_name, | ||
| "modelName": model_name, | ||
| "modelType": { | ||
| "year": vehicle.get("modelYear", "not available"), | ||
| "engine": _normalize_engine(vehicle), | ||
| }, | ||
| "systemInfo": {}, | ||
| "timestamp": vehicle.get("validFrom"), | ||
| "portalVehicle": vehicle, | ||
| } | ||
|
|
There was a problem hiding this comment.
Same issue, this is not needed (and does not work to determine the type of drivetrain anyway).
| overview = await self.connection.get(f"/connect/v1/vehicles/{self.vin}") | ||
| self.status = { | ||
| "appVehicle": overview, | ||
| } | ||
| normalized = _normalize_portal_vehicle(overview) | ||
| self.data = self.data | normalized |
There was a problem hiding this comment.
This change breaks the entire library, because no data will come out of it in the structure required for the remaining functions to work. Please provide an explaination of why this change is needed?
| "Could not get capabilities, error communicating with API: %s", | ||
| err.message, | ||
| ) | ||
| _LOGGER.debug("Skipping picture lookup for vehicle %s; legacy picture endpoint is no longer used", self.vin) |
There was a problem hiding this comment.
It seems this endpoint is still providing the URLs, so why remove it? Unless there is a better place to retrieve them from?
|
It looks to me like the auth flow is working, but there are some mistakes made in vehicle.py that prevents the library from working as intended. And I don't understand the reasons for those changes. Also, @tietjen, I am curious about what was actually not working for you with your Macan? I was under the impression the library mostly worked fine with the newer Macan as well? But I notice you have tested this to get "live" data, is that through polling current overview regularly, or through some other interface? |
|
I noted now that the captcha flow isn't working.. |
| async def portal_get(self, url, params=None): | ||
| """Make a GET request to the Porsche DCGW portal API.""" | ||
| return await self.absolute_request("GET", f"{DCGW_BASE_URL}{url}", params=params) | ||
|
|
||
| async def get_portal_config(self): | ||
| """Fetch and cache portal localization/config data.""" | ||
| config = await self.portal_get(f"/core/config/v1/{self.country_code}/{self.country_code}/") | ||
| localization = config.get("localization", {}) | ||
| self.country_code = str(localization.get("countryCode", self.country_code)).lower() | ||
| self.language_code = localization.get("languageCode", self.language_code) | ||
| return config | ||
|
|
|
Core functionality of this PR has been refactored into #96. Closing this. |
Summary
This updates the library to work with Porsche's current mobile auth flow and restores live vehicle data for newer vehicles such as the Macan EV.
The previous implementation relied on the older native app auth assumptions and older vehicle status endpoints. Porsche now uses a different mobile client flow and current vehicle data is available through
app/connect/v1/vehicles/....What changed
Auth flow
code_verifieracross captcha/login continuationcode_verifierConnection handling
Vehicle/account data
app/connect/v1/vehicles/...for current overview dataTested
Tested against a real Porsche account and Macan EV payloads:
Notes
This PR intentionally focuses on the underlying library. Home Assistant integration changes should be handled separately once the updated library is available.