Skip to content

Commit

Permalink
Add request sessions, change refresh token expiry time
Browse files Browse the repository at this point in the history
  • Loading branch information
klejejs committed Jan 2, 2023
1 parent fd85908 commit 93bf983
Showing 1 changed file with 37 additions and 18 deletions.
55 changes: 37 additions & 18 deletions ThermiaOnlineAPI/api/ThermiaAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from collections import ChainMap
from datetime import datetime, timedelta
import requests
from requests.adapters import HTTPAdapter, Retry
from requests import cookies
import json
import hashlib
Expand Down Expand Up @@ -59,6 +60,11 @@ def __init__(self, email, password, api_type):
"Content-Type": "application/json",
}

self.__session = requests.Session()
retry = Retry(connect=3, backoff_factor=0.5)
adapter = HTTPAdapter(max_retries=retry)
self.__session.mount("https://", adapter)

if api_type not in THERMIA_API_CONFIG_URLS_BY_API_TYPE:
raise ValueError("Unknown device type: " + api_type)

Expand All @@ -71,7 +77,7 @@ def get_devices(self):
self.__check_token_validity()

url = self.configuration["apiBaseUrl"] + "/api/v1/InstallationsInfo/own"
request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand All @@ -97,7 +103,7 @@ def get_device_info(self, device_id: str):
self.__check_token_validity()

url = self.configuration["apiBaseUrl"] + "/api/v1/installations/" + device_id
request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand All @@ -115,7 +121,7 @@ def get_device_status(self, device_id: str):
+ device_id
+ "/status"
)
request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand All @@ -133,7 +139,7 @@ def get_all_alarms(self, device_id: str):
+ str(device_id)
+ "/events?onlyActiveAlarms=false"
)
request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand All @@ -150,7 +156,7 @@ def get_historical_data_registers(self, device_id: str):
+ "/api/v1/DataHistory/installation/"
+ str(device_id)
)
request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand All @@ -175,7 +181,7 @@ def get_historical_data(
+ "&periodEnd="
+ end_date_str
)
request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand All @@ -196,7 +202,7 @@ def get_all_available_groups(self, installation_profile_id: int):
+ "/groups"
)

request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand Down Expand Up @@ -408,7 +414,7 @@ def __get_register_group(self, device_id: str, register_group: str) -> list:
+ "/Groups/"
+ register_group
)
request = requests.get(url, headers=self.__default_request_headers)
request = self.__session.get(url, headers=self.__default_request_headers)
status = request.status_code

if status != 200:
Expand Down Expand Up @@ -439,7 +445,9 @@ def __set_register_value(
"clientUuid": "api-client-uuid",
}

request = requests.post(url, headers=self.__default_request_headers, json=body)
request = self.__session.post(
url, headers=self.__default_request_headers, json=body
)

status = request.status_code
if status != 200:
Expand All @@ -451,7 +459,7 @@ def __set_register_value(
)

def __fetch_configuration(self):
request = requests.get(self.__api_config_url)
request = self.__session.get(self.__api_config_url)
status = request.status_code

if status != 200:
Expand All @@ -469,20 +477,24 @@ def __authenticate_refresh_token(self) -> Optional[str]:
"grant_type": "refresh_token",
}

request_token = requests.post(
request_token = self.__session.post(
AZURE_AUTH_GET_TOKEN_URL,
headers=azure_auth_request_headers,
data=request_token__data,
)

if request_token.status_code != 200:
self.__refresh_token = None
self.__refresh_token_valid_to = None

error_text = (
"Reauthentication request failed with previous refresh token. Status: "
+ str(request_token.status_code)
+ ", Response: "
+ request_token.text
)
_LOGGER.error(error_text)
_LOGGER.info(error_text)

return None

return request_token.text
Expand All @@ -498,6 +510,9 @@ def __authenticate(self) -> bool:
request_token_text = self.__authenticate_refresh_token()

if request_token_text is None: # New token, or refresh failed
self.__token = None
self.__token_valid_to = None

code_challenge = utils.generate_challenge(43)

request_auth__data = {
Expand All @@ -514,7 +529,9 @@ def __authenticate(self) -> bool:
"code_challenge_method": "S256",
}

request_auth = requests.get(AZURE_AUTH_AUTHORIZE_URL, request_auth__data)
request_auth = self.__session.get(
AZURE_AUTH_AUTHORIZE_URL, data=request_auth__data
)

state_code = ""
csrf_token = ""
Expand Down Expand Up @@ -549,7 +566,7 @@ def __authenticate(self) -> bool:
"p": "B2C_1A_SignUpOrSigninOnline",
}

request_self_asserted = requests.post(
request_self_asserted = self.__session.post(
AZURE_SELF_ASSERTED_URL,
cookies=request_auth.cookies,
data=request_self_asserted__data,
Expand Down Expand Up @@ -582,7 +599,7 @@ def __authenticate(self) -> bool:
"p": "B2C_1A_SignUpOrSigninOnline",
}

request_confirmed = requests.get(
request_confirmed = self.__session.get(
AZURE_AUTH_CONFIRM_URL,
cookies=request_confirmed__cookies,
params=request_confirmed__params,
Expand All @@ -597,7 +614,7 @@ def __authenticate(self) -> bool:
"grant_type": "authorization_code",
}

request_token = requests.post(
request_token = self.__session.post(
AZURE_AUTH_GET_TOKEN_URL,
headers=azure_auth_request_headers,
data=request_token__data,
Expand All @@ -620,9 +637,9 @@ def __authenticate(self) -> bool:
self.__token = token_data["access_token"]
self.__token_valid_to = token_data["expires_on"]

# refresh token valid for 24h, but we refresh it every 12h for safety
# refresh token valid for 24h (maybe), but we refresh it every 6h for safety
self.__refresh_token_valid_to = (
datetime.now() + timedelta(hours=12)
datetime.now() + timedelta(hours=6)
).timestamp()
self.__refresh_token = token_data.get("refresh_token")

Expand All @@ -639,6 +656,8 @@ def __check_token_validity(self):
if (
self.__token_valid_to is None
or self.__token_valid_to < datetime.now().timestamp()
or self.__refresh_token_valid_to is None
or self.__refresh_token_valid_to < datetime.now().timestamp()
):
_LOGGER.info("Token expired, re-authenticating.")
self.authenticated = self.__authenticate()

3 comments on commit 93bf983

@sezzor
Copy link

@sezzor sezzor commented on 93bf983 Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have used your API for a react/flask app running on raspberry pi with weather and electricity prices allowing scheduling and stuff. The latest update works great! Thank you for making this!

@wouterse
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sezzor, do you actually control the heat pump from the api? If so, which commands do you give? I tried to adjust the temperature (heat_pump.set_temperature(T)), but the heat pump overreacts even when the the temperature is changed by just 1C.

@sezzor
Copy link

@sezzor sezzor commented on 93bf983 Jan 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sezzor, do you actually control the heat pump from the api? If so, which commands do you give? I tried to adjust the temperature (heat_pump.set_temperature(T)), but the heat pump overreacts even when the the temperature is changed by just 1C.

@wouterse
Yeah apart from the information provided by the API i use heat_pump.set_temperature(degree) and heat_pump.set_hot_water_switch_state(int(set_water))
But for me this changes the heat-wheel, so on my heatpump i have set how many degrees each "degree" changes the outgoing temperature. I think default is 3.

I have the "genesis" version of heatpump API.
Although around 24h after i wrote this something went wrong. I've not had time to look at the logs but it might be related to one of the fixes @klejejs have commited.

But it was working flawlessly for the last months until 24/12-22.

Please sign in to comment.