mlowijs / tesla_api

Lightweight Python API client for the Tesla API.
MIT License
80 stars 31 forks source link

Any suggestions? #44

Open jackkorber opened 3 years ago

jackkorber commented 3 years ago

I am getting the error below. Any suggestions?

Traceback (most recent call last): File "/Users/jackkorber/Desktop/Python/GetPowerwallCapacity/powerwallCapacity.py", line 31, in asyncio.run(main()) File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/runners.py", line 43, in run return loop.run_until_complete(main) File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 608, in run_until_complete return future.result() File "/Users/jackkorber/Desktop/Python/GetPowerwallCapacity/powerwallCapacity.py", line 12, in main energy_sites = await client.list_energy_sites() File "/Users/jackkorber/Desktop/Python/GetPowerwallCapacity/tesla_api/init.py", line 123, in list_energy_sites for product in await self.get("products") if "energy_site_id" in product] File "/Users/jackkorber/Desktop/Python/GetPowerwallCapacity/tesla_api/init.py", line 91, in get await self.authenticate() File "/Users/jackkorber/Desktop/Python/GetPowerwallCapacity/tesla_api/init.py", line 81, in authenticate expiry_time = timedelta(seconds=self._token["expires_in"]) KeyError: 'expires_in' Unclosed client session client_session: <aiohttp.client.ClientSession object at 0x7fa22a881ac0> Unclosed connector connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x7fa22a8a5520>, 0.69530855)]'] connector: <aiohttp.connector.TCPConnector object at 0x7fa22a881af0>

jackkorber commented 3 years ago

<ClientResponse(https://owner-api.teslamotors.com/oauth/token) [400 Bad Request]>

jakkaj commented 3 years ago

In my experience, errors like this come and go. They seem to play with the API from time to time.

I think there is a new problem now though with Captcha being added as discussed here: https://github.com/timdorr/tesla-api/discussions/390

jakkaj commented 3 years ago

I managed to work around the captcha by grabbing a token in Postman using the built in authentication stuff, then popping it in a json file and removing direct user and pass. Works for MFA too as you're just logging in a browser as you would in the app - and the tokens refresh anyway, so should never have to do it again hypothetically.

Settings for Postman that worked:

Client secret:c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3

t1

t2

jakkaj commented 3 years ago

This is the rest.

The START_TOKEN variable is a json token from .env to get things started (the one manually generated in postman)

It looks like this:

{"resfresh_token": "", "access_token": "", "expires_in": 28800, "created_at": 1622326394.014721}
async def save_token(token):
    open("token_file.json", "w").write(token)

async def load_token():
    try:
        with open('token_file.json', "r") as f:
            return f.read()        
    except IOError:
        await save_token(START_TOKEN)
        return await load_token()    

async def getreserve():
    token = await load_token()
    client = TeslaApiClient(token=token, on_new_token=save_token) 
jakkaj commented 3 years ago

Just create a new request and switch to the authorization tab and you should see it. BTW you don't need an account in Postman!

Capture

bchristian14 commented 3 years ago

Just create a new request and switch to the authorization tab and you should see it. BTW you don't need an account in Postman!

I deleted my previous response out of embarrassment but thank very much for holding my hand. I don't know why I couldn't fumble my way through the UI to find this!

jakkaj commented 3 years ago

I find it to be unintuitive... could be a better UI .

There is a problem I've come across now that the code does not refresh the tokens properly, gives an invalid_grant error with a grant type of refresh_token. Have not had time to really pull it apart yet. Will create a new issue.

bchristian14 commented 3 years ago

the code does not refresh the tokens properly

well that's unfortunate. I think I maybe saw this when I first wrote my script, but probably just attributed to my lack of understanding/knowledge, and just pass the credentials each time (the script only has 1 user - me!)

jakkaj commented 3 years ago

The auth token works fine, just when it expires the refresh step fails.

fracai commented 3 years ago

Thanks @jakkaj, your token suggestion is working for me and but I'm also seeing the refresh step failing. The error I get is:

...
  File ".../.venv/lib/python3.8/site-packages/tesla_api/__init__.py", line 61, in _get_token
    raise AuthenticationError(response_json)
tesla_api.exceptions.AuthenticationError: Authentication to the Tesla API failed: {'error': 'invalid_grant', 'error_description': 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'}

The "issued to another client" makes me wonder if the OAUTH_CLIENT_ID is the problem. I note that tesla_api uses 81527cff…, but the Postman process uses ownerapi. I tried getting a token using the 81527cff… ID, but that resulted in an error in the authorization popup. I tried changing OAUTH_CLIENT_ID to ownerapi for just the _get_token method, but resulted in the same error response.

fracai commented 3 years ago

I was able to refresh the token!

I used ownerapi for the client id, https://auth.tesla.com/oauth2/v3/token for the URL, and added "scope": "openid email offline_access" to the refresh data. I'm not sure if scope is actually required here. I saw it used in a refresh on another project and haven't tried it without. I then also added created_at to the response_json to support the refresh check.

I can make a proper PR, but for now my changes are below:

diff --git a/tesla_api/__init__.py b/tesla_api/__init__.py
index 6d5ab02..775a18d 100644
--- a/tesla_api/__init__.py
+++ b/tesla_api/__init__.py
@@ -9,9 +9,11 @@ from .exceptions import ApiError, AuthenticationError, VehicleUnavailableError
 from .vehicle import Vehicle

 TESLA_API_BASE_URL = "https://owner-api.teslamotors.com/"
-TOKEN_URL = TESLA_API_BASE_URL + "oauth/token"
+TESLA_TOKEN_BASE_URL = "https://auth.tesla.com/"
+TOKEN_URL = TESLA_TOKEN_BASE_URL + "oauth2/v3/token"
 API_URL = TESLA_API_BASE_URL + "api/1"

+TOKEN_CLIENT_ID = "ownerapi"
 OAUTH_CLIENT_ID = "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384"
 OAUTH_CLIENT_SECRET = "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3"

@@ -50,7 +52,7 @@ class TeslaApiClient:

     async def _get_token(self, data):
         request_data = {
-            "client_id": OAUTH_CLIENT_ID,
+            "client_id": TOKEN_CLIENT_ID,
             "client_secret": OAUTH_CLIENT_SECRET,
         }
         request_data.update(data)
@@ -60,6 +62,7 @@ class TeslaApiClient:
             if resp.status == 401:
                 raise AuthenticationError(response_json)

+        response_json['created_at'] = datetime.now().timestamp()
         # Send token to application via callback.
         if self._new_token_callback:
             asyncio.create_task(self._new_token_callback(json.dumps(response_json)))
@@ -71,7 +74,7 @@ class TeslaApiClient:
         return await self._get_token(data)

     async def _refresh_token(self, refresh_token):
-        data = {"grant_type": "refresh_token", "refresh_token": refresh_token}
+        data = {"grant_type": "refresh_token", "refresh_token": refresh_token, "scope": "openid email offline_access"}
         return await self._get_token(data)

     async def authenticate(self):
fracai commented 3 years ago

Looks like some of my changes are covered by the v3 PRs that already exist. https://github.com/mlowijs/tesla_api/pull/38/ https://github.com/mlowijs/tesla_api/pull/39/

jcam commented 2 years ago

This project seems to handle the captcha pretty well, might be useful to lift some of their work: https://github.com/tdorssers/TeslaPy/commit/22540aba05f41dfc3da12ccf20683d43c82c9e06

jakkaj commented 2 years ago

@fracai sweet!

jakkaj commented 2 years ago

@jcam Seems like a lot of extra work around to handle Capcha. This really looks like a lot of work when perhaps a one time interactive login is needed + decent token refreshing.

jcam commented 2 years ago

Yeah it is a lot. Probably way more than needed. The built in interactive login (kicks out to system web browser) and token refresh handling seems pretty solid though. Not really suggesting to duplicate all the work, but since it all works it might be a useful resource for getting the flows right.

jakkaj commented 2 years ago

The thing I like best about built in flows is that no need to update things when Telsla changes their flows a little.

On Tue, Sep 28, 2021 at 9:17 AM Jesse Campbell @.***> wrote:

Yeah it is a lot. Probably way more than needed. The built in interactive login (kicks out to system web browser) and token refresh handling seems pretty solid though. Not really suggesting to duplicate all the work, but since it all works it might be a useful resource for getting the flows right.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mlowijs/tesla_api/issues/44#issuecomment-928426036, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABH32NVM3NUGHLHPIWI5QCTUED3RZANCNFSM45XWK3OQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.