DasBasti / SmartHashtag

Homeassistant component for Smart #1/#3 API
MIT License
34 stars 1 forks source link

Volvo EX30 Integration #114

Open denkyem opened 3 months ago

denkyem commented 3 months ago

Recently, I replaced my XC40 with an EX30. The Volvo integration/API is not compatible with the EX30.

Upon further exploration, I noticed that it uses the same endpoints as the Smart cars, except (I think) for the initial authentication part.

I don't have enough knowledge to implement an integration myself. Would you be willing to adapt this Smart integration for the Volvo EX30?

I can help with testing and maybe even a bit of programming.

These requests were captured using Postman.

image

Thank you

DasBasti commented 3 months ago

sure if you can send me the captured logs I might be able to integrate the EX30 its all Geely platform so this might work

denkyem commented 2 months ago

For some reason, I can't login on the app with the proxy activated I'll try to figure out why, to capture all packages starting with authorisation, that is the major difference from Smart#. The API endpoints services to get/set info seems to be exactly for the Smart and the EX30.

In the meanwhile, the authorisation part is well documented on Volvo website and I believe it's the same used with the EX30 API, if you want to take a look https://developer.volvocars.com/apis/docs/authorisation/

Thank you so much

michaelmorris commented 2 months ago

I did some digging into this, it seems like it does a normal login via volvo's oauth endpoints, then uses the access token it gets from that to then do a login onto geely's platform:

Here's an example request:

POST /auth/account/session/secure?identity_type=volvo-global HTTP/2 Host: api-vlv.ecloudeu.com X-Api-Signature-Nonce: F0E9DF3E-E5C7-45DF-B63F-178E881FE601 X-App-Id: volvo_global_app X-Timestamp: 1721217814650 User-Agent: volvocar/1.6.0 (iPhone; iOS 17.5.1; Scale/3.00) X-Device-Model: iPhone X-Device-Type: mobile X-Device-Manufacture: Apple X-Timezone: Europe/London X-Agent-Type: iOS X-Api-Signature-Version: 1.0 Content-Length: 1130 Platform: NON-CMA X-Env-Type: production X-Signature: tlKZV3B3ZvPlVNMXfUan31+EpVs= Accept-Language: en-GB;q=1 X-Agent-Version: 17.5.1 Accept: application/json;responseformat=3 Content-Type: application/json Accept-Encoding: gzip, deflate, br X-Device-Brand: Apple X-Operator-Code: VOLVO-GLOBAL X-Device-Identifier: uuid, redacted as i don't know if thats unique to my device

{"accessToken":"redacted"}

Response back:

HTTP/2 200 OK Date: Wed, 17 Jul 2024 12:03:40 GMT Content-Type: application/json; responseformat=3;charset=UTF-8 Access-Control-Allow-Origin: * X-Request-Id: 8fb173a1bcd839cea59d8d1090f6230a Cache-Control: no-cache, no-store, max-age=0, must-revalidate Pragma: no-cache Expires: 0 X-Content-Type-Options: nosniff X-Frame-Options: DENY X-Xss-Protection: 1 ; mode=block Server: api-server

{"code":1000,"data":{"expiresIn":7200,"deviceType":"mobile","clientId":"APPLE0000APP00IPHONEredacted","idToken":"","resultCode":"0","alias":"","accessToken":"redacted","tcToken":"","resultMessage":"Success","userId":"redacted","refreshToken":"redacted"},"success":true,"hint":null,"sessionId":"uuid redacted","message":"Successful."}

Then it uses that to send a request into what appears to be a similar API as smart cars use (don't know if its identical or not), here's an example:

GET /remote-control/user/authorization/vehicle/status/vingoeshere?latest=False&target=basic%2Cmore&userId=redacted HTTP/2 Host: api-vlv.ecloudeu.com X-App-Id: volvo_global_app X-Env-Type: production Accept: application/json;responseformat=3 Authorization: redacted X-Timestamp: 1721216874193 X-Timezone: Europe/London X-Agent-Type: iOS X-Signature: plihw2Asn1xhapVnn5Qz1v5Eswg= X-Api-Signature-Version: 1.0 Accept-Language: en-GB;q=1 Accept-Encoding: gzip, deflate, br Platform: NON-CMA User-Agent: volvocar/1.6.0 (iPhone; iOS 17.5.1; Scale/3.00) X-Operator-Code: VOLVO-GLOBAL X-Client-Id: APPLE0000APP00IPHONE(truncated as i dont know if this is unique) X-Api-Signature-Nonce: 1B7D8DB4-2CFB-4FA4-AE25-7830B7E49892 X-Device-Type: mobile

I did try to see if i could hack something together using this code as a base, but I hit a stumbling block with getting the secret value to generate correct X-Signature header values - any tips on how I could get it? I had a look at the EX30 app APK but didn't have much luck finding it (although I was able to extract the client secrets for the volvo identity API) - any hints? How were you able to find the secret for the smart car endpoints?

michaelmorris commented 2 months ago

For some reason, I can't login on the app with the proxy activated I'll try to figure out why, to capture all packages starting with authorisation, that is the major difference from Smart#. The API endpoints services to get/set info seems to be exactly for the Smart and the EX30.

I had some weird behaviour from the app when proxying TLS - i think it uses either mutual TLS or cert pinning during some part of the volvo login process or some of its built in analytics. I had to configure these hosts to passthrough TLS: volvoid.eu.volvocars.com volvo.md-apis.medallia.com appserver-external.appbksvceu.com appserver-external.appbksvckr.com appserver-external.appbksvcus.com

Not all of these may have been necesarry - i added passthroughs until the app's login flow started to at least partially work.

michaelmorris commented 2 months ago

Update: found the magic value, its MGU0MzFhZGY0YmY5NGE2YWI3YmUyYzY4NjhkNGMwNjQ=

To test this, I hacked up the authentication code in pysmarthashtag, essentially putting in a hardcoded access token from the volvo id service I got myself manually, changed the endpoint urls to the volvo endpoints, made a few other changes to the hardcoded headers the python library sends and was able to successfully request car info. I did not need to touch any code other than the authentication, hardcoded headers and signature generation code.

I think the main task to make the python library work is therefore to implement the volvo id login process. I have the client secrets that the official app uses, but volvo also lets you sign up for your own dev account and get your own secrets so will try that way first.

michaelmorris commented 2 months ago

I've forked the pysmarthashtag repo and committed my hack to it just to show the basics of what is necessary to make it work with EX30, see: https://github.com/michaelmorris/pySmartHashtag/commit/5a099f288a73e9daa6528fdc50e727b23ce19466

You can test it via the cli, i replaced the username and password arguments with an accesstoken argument, grab a currently valid access token from the volvo id service somehow (so far I have only tested this via grabbing one from a capture from the app itself, next step would be try one generated via the proper auth process), and then you can use the commands available from the cli - most i tried seemed to work.

michaelmorris commented 2 months ago

I've played around with this some more. I've attempted to modify the pySmartHashtag code so that it in theory supports both volvo and smart cars - see: https://github.com/michaelmorris/pySmartHashtag

This has just been for my own experimentation really, and I've put this code out there in case its useful to someone. I don't know if I have the time to actually build and maintain an EX30 home assistant addon, nor do I know if @DasBasti is at all interested in using any of this work. There are a lot of practical problems with trying to do an actual shared addon - I assume he doesn't have an EX30 to test changes against, and I don't have a Smart car to test against.

Having said that, the state of that code is that it appears to be mostly functional via the CLI for my EX30. There is some weirdness with setting climate when the car is asleep, but I observe very similar behaviour with the official app. Interestingly, the API requests to turn the seat heaters on appeared to work, despite that not being something you can do in the EX30 app. I couldn't tell if they physically turned on though - the sun was out today so my seats were already warm.

The quality of my changes are pretty rough, partly because this is just me experimenting at this point but mostly because I'm not a python developer.

Currently this code is not compatible with the home assistant plugin. The reason for this is that unlike the smart login flow (at least from what I could see looking at the code), Volvo ID's login flow has an interactive element - its a two step process where the username and password is sent, if that is validated then an email with an OTP code is sent, and you can't complete the login until that code has been entered. This means that the home assistant side will need to be changed to support this, as well as flag to the user when an interactive reauthentication is needed when the refresh token expires. I may do this in the future, if I have time.

denkyem commented 2 months ago

@michaelmorris that's an amazing work

I've question Volvo if they are going to provide access to EX30 cars with the current Public API that they already have (and there's already an official Home Assistant integration). They said there will be some news on week 33, after summer vacations If thats the case, maybe adapt this integration it's might not be necessary.

DasBasti commented 2 months ago

@michaelmorris nice work, I will have a look into it. But I am not sure if it makes sense to have it in pysmarthashtag. Maybe it makes sense to move the project to somethings along the line of pygeely. I think integrating a slightly different login workflow will not be that hard. I do not have an overview of the geely platform and currently only testing against one smart #1 :)