Skip to content

Commit 683abf6

Browse files
authored
fix: refresh flow was not DST compliant (#484)
For long-running clients (i.e. where the `Tado` object is not constructed each time the API is called), there appears to be an issue with the refresh tokens and DST. This was first reported by Tado in PyTado, see wmalgadey/PyTado#188 A fix has since been made, mainly for use with HA. Since the Oauth code is mostly taken over from PyTado, applied the same changes (see wmalgadey/PyTado#189) to make our codebase compliant as well.
1 parent a5d3e04 commit 683abf6

File tree

1 file changed

+9
-7
lines changed

1 file changed

+9
-7
lines changed

libtado/api.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
import json
3737
import os
3838
import sys
39-
from datetime import timedelta, datetime
39+
from datetime import timedelta, datetime, timezone
4040
from pathlib import Path
4141
from urllib.parse import urlencode
4242

@@ -69,7 +69,7 @@ class Tado:
6969
device_verification_url = None
7070
device_verification_url_expires_at = None
7171
id = None
72-
refresh_at = datetime.now() + timedelta(minutes=10)
72+
refresh_at = datetime.now(timezone.utc) + timedelta(minutes=10)
7373
refresh_token = None
7474
timeout = 15
7575
user_code = None
@@ -98,7 +98,7 @@ def set_oauth_token(self, response) -> str:
9898
self.refresh_token = refresh_token
9999
# We subtract 30 seconds from the correct refresh time.
100100
# Then we have a 30 seconds timespan to get a new refresh_token
101-
self.refresh_at = datetime.now() + timedelta(seconds=expires_in) - timedelta(seconds=30)
101+
self.refresh_at = datetime.now(timezone.utc) + timedelta(seconds=expires_in) - timedelta(seconds=30)
102102

103103
self.access_headers = {
104104
'Authorization': f'Bearer {access_token}',
@@ -126,7 +126,7 @@ def load_token(self) -> bool:
126126

127127

128128
def refresh_auth(self, refresh_token: str = None, force_refresh = False) -> bool:
129-
if self.refresh_at >= datetime.now() and not force_refresh:
129+
if self.refresh_at >= datetime.now(timezone.utc) and not force_refresh:
130130
return True
131131

132132
url='https://login.tado.com/oauth2/token'
@@ -182,17 +182,19 @@ def login_device_flow(self) -> DeviceActivationStatus:
182182
print("Please visit the following URL in your Web browser to log in to your Tado account:", visit_url)
183183

184184
expires_in_seconds = response["expires_in"]
185-
self.device_verification_url_expires_at = datetime.now() + timedelta(seconds=expires_in_seconds)
185+
self.device_verification_url_expires_at = datetime.now(timezone.utc) + timedelta(seconds=expires_in_seconds)
186+
# print the expiry time in the user's local timezone.
187+
device_verification_url_expires_at_local_tz = datetime.now() + timedelta(seconds=expires_in_seconds)
186188

187189
print(
188190
"Waiting for you to complete logging in. You have until",
189-
self.device_verification_url_expires_at.strftime("%Y-%m-%d %H:%M:%S"),
191+
device_verification_url_expires_at_local_tz.strftime("%Y-%m-%d %H:%M:%S"),
190192
)
191193

192194
return DeviceActivationStatus.PENDING
193195

194196
def check_device_activation(self) -> bool:
195-
if self.device_verification_url_expires_at is not None and datetime.timestamp(datetime.now()) > datetime.timestamp(self.device_verification_url_expires_at):
197+
if self.device_verification_url_expires_at is not None and datetime.timestamp(datetime.now(timezone.utc)) > datetime.timestamp(self.device_verification_url_expires_at):
196198
raise Exception("User took too long to enter key")
197199

198200
# Await the desired interval, before polling the API again

0 commit comments

Comments
 (0)