From 79743cdf6ebd1479c09852591f3b31aa95b973f6 Mon Sep 17 00:00:00 2001 From: Mike Bishop Date: Thu, 16 May 2024 09:35:26 -0400 Subject: [PATCH] Avoid refresh loops --- lib/TWCManager/Vehicle/TeslaAPI.py | 64 ++++++++++++++++-------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/lib/TWCManager/Vehicle/TeslaAPI.py b/lib/TWCManager/Vehicle/TeslaAPI.py index 3ebf0fa2..8e91d3af 100644 --- a/lib/TWCManager/Vehicle/TeslaAPI.py +++ b/lib/TWCManager/Vehicle/TeslaAPI.py @@ -131,19 +131,32 @@ def apiRefresh(self): try: req = requests.post(self.refreshURL, headers=headers, json=data) logger.log(logging.INFO2, "Car API request" + str(req)) + req.raise_for_status() apiResponseDict = json.loads(req.text) except requests.exceptions.RequestException: - logger.log( - logging.INFO2, "Request Exception parsing API Token Refresh Response" - ) - pass - except ValueError: - pass + if req.status_code == 401: + logger.log( + logging.INFO2, + "TeslaAPI", + "ERROR: Can't access Tesla car via API. Please supply fresh tokens.", + ) + self.setCarApiBearerToken("") + self.setCarApiRefreshToken("") + self.updateCarApiLastErrorTime() + # Instead of just setting carApiLastErrorTime, erase tokens to + # prevent further authorization attempts until user enters password + # on web interface. I feel this is safer than trying to log in every + # ten minutes with a bad token because Tesla might decide to block + # remote access to your car after too many authorization errors. + self.master.queue_background_task({"cmd": "saveSettings"}) + return False except json.decoder.JSONDecodeError: logger.log( logging.INFO2, "JSON Decode Error parsing API Token Refresh Response" ) pass + except ValueError: + pass try: logger.log(logging.INFO4, "Car API auth response" + str(apiResponseDict)) @@ -151,25 +164,13 @@ def apiRefresh(self): self.setCarApiRefreshToken(apiResponseDict["refresh_token"]) self.setCarApiTokenExpireTime(now + apiResponseDict["expires_in"]) self.master.queue_background_task({"cmd": "saveSettings"}) + return True - except KeyError: - logger.log( - logging.INFO2, - "TeslaAPI", - "ERROR: Can't access Tesla car via API. Please log in again via web interface.", - ) - self.updateCarApiLastErrorTime() - # Instead of just setting carApiLastErrorTime, erase tokens to - # prevent further authorization attempts until user enters password - # on web interface. I feel this is safer than trying to log in every - # ten minutes with a bad token because Tesla might decide to block - # remote access to your car after too many authorization errors. - self.setCarApiBearerToken("") - self.setCarApiRefreshToken("") - self.master.queue_background_task({"cmd": "saveSettings"}) - except UnboundLocalError: + except: pass + return False + def car_api_available( self, email=None, password=None, charge=None, applyLimit=None ): @@ -1149,7 +1150,7 @@ def setCarApiRefreshToken(self, token): self.getCarApiBearerToken() == "" or self.getCarApiTokenExpireTime() - time.time() < 60 * 60 ): - self.apiRefresh() + return self.apiRefresh() return True def setCarApiTokenExpireTime(self, value): @@ -1244,8 +1245,11 @@ def wakeVehicle(self, vehicle): except requests.exceptions.RequestException: if req.status_code == 401 and "expired" in req.text: # If the token is expired, refresh it and try again - self.apiRefresh() - return self.wakeVehicle(vehicle) + if self.apiRefresh(): + return self.wakeVehicle(vehicle) + elif req.status_code == 429: + # We're explicitly being told to back off + self.errorCount = max(30, self.errorCount) return False except json.decoder.JSONDecodeError: return False @@ -1411,10 +1415,12 @@ def get_car_api(self, url, checkReady=True, provesOnline=True): except requests.exceptions.RequestException: if req.status_code == 401 and "expired" in req.text: # If the token is expired, refresh it and try again - self.apiRefresh() - continue - else: - pass + if self.apiRefresh(): + continue + elif req.status_code == 429: + # We're explicitly being told to back off + self.errorCount = max(30, self.errorCount) + return False, None except json.decoder.JSONDecodeError: pass