Skip to content

Commit b4bfacb

Browse files
authored
Fix formatting issues (#30)
1 parent 5125327 commit b4bfacb

File tree

4 files changed

+64
-42
lines changed

4 files changed

+64
-42
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ dmypy.json
130130

131131
# PyCharm
132132
.idea/
133+
*.iml
133134

134135
# Frigidaire creds
135136
config.ini

frigidaire/__init__.py

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
import urllib3
1212
from urllib.parse import quote_plus
1313
from urllib.parse import urlencode
14-
import uuid
1514
import time
1615

17-
from .signature_generator import get_signature
16+
from .signature_generator import get_signature
1817

1918
# Frigidaire uses a self-signed certificate, which forces us to disable SSL verification
2019
# To keep our logs free of spam, we disable warnings on insecure requests
@@ -156,7 +155,7 @@ class Alert(str, Enum):
156155
INDOOR_DEFROST_THERMISTOR_FAULT = "INDOOR_DEFROST_THERMISTOR_FAULT"
157156
PM25_SENSOR_FAULT = "PM25_SENSOR_FAULT"
158157
TUBE_HIGH_TEMPERATURE = "TUBE_HIGH_TEMPERATURE"
159-
UNKNOWN_STATE_ERROR = "UNKNOWN_STATE_ERROR"
158+
UNKNOWN_STATE_ERROR = "UNKNOWN_STATE_ERROR"
160159

161160

162161
class Mode(str, Enum):
@@ -226,11 +225,12 @@ class Frigidaire:
226225
This was reverse-engineered from the Frigidaire 2.0 App
227226
"""
228227

229-
def __init__(self, username: str, password: str, session_key: Optional[str] = None, timeout: Optional[float] = None, regional_base_url: Optional[str] = None):
228+
def __init__(self, username: str, password: str, session_key: Optional[str] = None, timeout: Optional[float] = None,
229+
regional_base_url: Optional[str] = None):
230230
"""
231231
Initializes a new instance of the Frigidaire API and authenticates against it
232-
:param username: The username to login to Frigidaire. Generally, this is an email
233-
:param password: The password to login to Frigidaire
232+
:param username: The username to log in to Frigidaire. Generally, this is an email
233+
:param password: The password to log in to Frigidaire
234234
:param session_key: The previously authenticated session key to connect to Frigidaire. If not specified,
235235
authentication is required
236236
:param timeout: The amount of time in seconds to wait before timing out a request
@@ -249,7 +249,8 @@ def __init__(self, username: str, password: str, session_key: Optional[str] = No
249249
def get_headers_frigidaire(self, method: str, include_bearer_token: bool) -> Dict[str, str]:
250250
to_return = {
251251
"x-api-key": FRIGIDAIRE_API_KEY,
252-
"Authorization": "Bearer" if not (self.session_key and include_bearer_token) else f"Bearer {self.session_key}",
252+
"Authorization": "Bearer" if not (
253+
self.session_key and include_bearer_token) else f"Bearer {self.session_key}",
253254
"Accept": "application/json",
254255
"Accept-Charset": "UTF-8",
255256
"User-Agent": FRIGIDAIRE_USER_AGENT
@@ -273,8 +274,8 @@ def test_connection(self) -> None:
273274
Tests for successful connectivity to the Frigidaire server
274275
:return:
275276
"""
276-
return self.get_request(self.regional_base_url, "/one-account-user/api/v1/users/current?countryDetails=true", self.get_headers_frigidaire("GET", include_bearer_token=True))
277-
277+
self.get_request(self.regional_base_url, "/one-account-user/api/v1/users/current?countryDetails=true",
278+
self.get_headers_frigidaire("GET", include_bearer_token=True))
278279

279280
def authenticate(self) -> None:
280281
"""
@@ -289,7 +290,7 @@ def authenticate(self) -> None:
289290
if not self.regional_base_url:
290291
self.session_key = None
291292

292-
## Remember to include "Context-Brand: frigidaire" in the headers for the "/api/v1/identity-providers" and "/api/v1/users/current" calls
293+
# Remember to include "Context-Brand: frigidaire" in the headers for the "/api/v1/identity-providers" and "/api/v1/users/current" calls
293294
if self.session_key:
294295
logging.debug('Authentication requested but session key is present, testing session key')
295296
try:
@@ -306,10 +307,13 @@ def authenticate(self) -> None:
306307
'clientSecret': CLIENT_SECRET,
307308
'scope': ''
308309
}
309-
session_key_response = self.post_request(GLOBAL_API_URL, '/one-account-authorization/api/v1/token', self.get_headers_frigidaire("POST", include_bearer_token=False), data)
310+
session_key_response = self.post_request(GLOBAL_API_URL, '/one-account-authorization/api/v1/token',
311+
self.get_headers_frigidaire("POST", include_bearer_token=False), data)
310312
self.session_key = session_key_response['accessToken']
311313

312-
identity_providers_response = self.get_request(GLOBAL_API_URL, f'/one-account-user/api/v1/identity-providers?brand=frigidaire&email={quote_plus(self.username)}&loginType=OTP', self.get_headers_frigidaire("GET", include_bearer_token=True))
314+
identity_providers_response = self.get_request(GLOBAL_API_URL,
315+
f'/one-account-user/api/v1/identity-providers?brand=frigidaire&email={quote_plus(self.username)}&loginType=OTP',
316+
self.get_headers_frigidaire("GET", include_bearer_token=True))
313317
identity_domain = identity_providers_response[0]['domain']
314318
identity_api_key = identity_providers_response[0]['apiKey']
315319
self.regional_base_url = identity_providers_response[0]['httpRegionalBaseUrl']
@@ -322,7 +326,8 @@ def authenticate(self) -> None:
322326
"sdk": "Android_6.2.1",
323327
"targetEnv": "mobile"
324328
}
325-
get_ids_response = self.post_request(f'https://socialize.{identity_domain}', '/socialize.getIDs', self.get_headers_auth("POST"), data, form_encoding=True)
329+
get_ids_response = self.post_request(f'https://socialize.{identity_domain}', '/socialize.getIDs',
330+
self.get_headers_auth("POST"), data, form_encoding=True)
326331

327332
auth_gmid = get_ids_response['gmid']
328333
auth_ucid = get_ids_response['ucid']
@@ -339,7 +344,8 @@ def authenticate(self) -> None:
339344
"targetEnv": "mobile",
340345
"ucid": auth_ucid
341346
}
342-
login_response = self.post_request(f'https://accounts.{identity_domain}', '/accounts.login', self.get_headers_auth("POST"), data, form_encoding=True)
347+
login_response = self.post_request(f'https://accounts.{identity_domain}', '/accounts.login',
348+
self.get_headers_auth("POST"), data, form_encoding=True)
343349

344350
auth_session_token = login_response['sessionInfo']['sessionToken']
345351
auth_session_secret = login_response['sessionInfo']['sessionSecret']
@@ -357,8 +363,10 @@ def authenticate(self) -> None:
357363
"timestamp": str(int(time.time())),
358364
"ucid": auth_ucid
359365
}
360-
data["sig"] = get_signature(auth_session_secret, "POST", f'https://accounts.{identity_domain}/accounts.getJWT', data)
361-
jwt_response = self.post_request(f'https://accounts.{identity_domain}', '/accounts.getJWT', self.get_headers_auth("POST"), data, form_encoding=True)
366+
data["sig"] = get_signature(auth_session_secret, "POST", f'https://accounts.{identity_domain}/accounts.getJWT',
367+
data)
368+
jwt_response = self.post_request(f'https://accounts.{identity_domain}', '/accounts.getJWT',
369+
self.get_headers_auth("POST"), data, form_encoding=True)
362370

363371
auth_jwt = jwt_response['id_token']
364372

@@ -368,10 +376,13 @@ def authenticate(self) -> None:
368376
"idToken": auth_jwt,
369377
"scope": ""
370378
}
371-
frigidaire_auth_response = self.post_request(self.regional_base_url, '/one-account-authorization/api/v1/token', self.get_headers_frigidaire("POST", include_bearer_token=False), data)
379+
frigidaire_auth_response = self.post_request(self.regional_base_url, '/one-account-authorization/api/v1/token',
380+
self.get_headers_frigidaire("POST", include_bearer_token=False),
381+
data)
372382

373383
if not frigidaire_auth_response.get('accessToken'):
374-
raise FrigidaireException(f'Failed to authenticate, accessToken was not in response: {frigidaire_auth_response}')
384+
raise FrigidaireException(
385+
f'Failed to authenticate, accessToken was not in response: {frigidaire_auth_response}')
375386

376387
logging.debug('Authentication successful, storing new session key')
377388
self.session_key = frigidaire_auth_response['accessToken']
@@ -408,8 +419,9 @@ def get_appliances_inner():
408419
to re-authenticate
409420
:return: The appliances that are associated with the Frigidaire account
410421
"""
411-
appliances = self.get_request(self.regional_base_url,
412-
'/appliance/api/v2/appliances?includeMetadata=true', self.get_headers_frigidaire("GET", include_bearer_token=True))
422+
appliances = self.get_request(self.regional_base_url,
423+
'/appliance/api/v2/appliances?includeMetadata=true',
424+
self.get_headers_frigidaire("GET", include_bearer_token=True))
413425

414426
return list(map(generate_appliance, appliances))
415427

@@ -430,12 +442,14 @@ def get_appliance_details(self, appliance: Appliance) -> Dict:
430442
logging.debug(f'Getting appliance details for appliance {appliance.nickname}')
431443

432444
try:
433-
appliances = self.get_request(self.regional_base_url,
434-
'/appliance/api/v2/appliances?includeMetadata=true', self.get_headers_frigidaire("GET", include_bearer_token=True))
445+
appliances = self.get_request(self.regional_base_url,
446+
'/appliance/api/v2/appliances?includeMetadata=true',
447+
self.get_headers_frigidaire("GET", include_bearer_token=True))
435448
except FrigidaireException:
436449
self.re_authenticate()
437-
appliances = self.get_request(self.regional_base_url,
438-
'/appliance/api/v2/appliances?includeMetadata=true', self.get_headers_frigidaire("GET", include_bearer_token=True))
450+
appliances = self.get_request(self.regional_base_url,
451+
'/appliance/api/v2/appliances?includeMetadata=true',
452+
self.get_headers_frigidaire("GET", include_bearer_token=True))
439453

440454
for raw_appliance in appliances:
441455
if raw_appliance['applianceId'] == appliance.appliance_id:
@@ -457,14 +471,14 @@ def execute_action(self, appliance: Appliance, action: List[Component]) -> None:
457471
}
458472

459473
try:
460-
self.put_request(self.regional_base_url,
461-
f'/appliance/api/v2/appliances/{appliance.appliance_id}/command',
462-
self.get_headers_frigidaire("PUT", include_bearer_token=True), data)
474+
self.put_request(self.regional_base_url,
475+
f'/appliance/api/v2/appliances/{appliance.appliance_id}/command',
476+
self.get_headers_frigidaire("PUT", include_bearer_token=True), data)
463477
except FrigidaireException:
464478
self.re_authenticate()
465-
self.put_request(self.regional_base_url,
466-
f'/appliance/api/v2/appliances/{appliance.appliance_id}/command',
467-
self.get_headers_frigidaire("PUT", include_bearer_token=True), data)
479+
self.put_request(self.regional_base_url,
480+
f'/appliance/api/v2/appliances/{appliance.appliance_id}/command',
481+
self.get_headers_frigidaire("PUT", include_bearer_token=True), data)
468482

469483
@staticmethod
470484
def parse_response(response: Response) -> Dict:
@@ -503,7 +517,7 @@ def handle_request_exception(self, e: Exception, method: str, fullpath: str, hea
503517
def get_request(self, url: str, path: str, headers: Dict[str, str]) -> Union[Dict, List]:
504518
"""
505519
Makes a get request to the Frigidaire API and parses the result
506-
:parm url: Base URL for the request (no slashes)
520+
:param url: Base URL for the request (no slashes)
507521
:param path: The path to the resource, including query params
508522
:param headers: Headers to include in the request
509523
:return: The contents of 'data' in the resulting json
@@ -514,34 +528,37 @@ def get_request(self, url: str, path: str, headers: Dict[str, str]) -> Union[Dic
514528
except Exception as e:
515529
self.handle_request_exception(e, "GET", f'{url}{path}', headers, "")
516530

517-
def post_request(self, url: str, path: str, headers: Dict[str, str], data: Dict, form_encoding: bool=False) -> Union[Dict, List]:
531+
def post_request(self, url: str, path: str, headers: Dict[str, str], data: Dict, form_encoding: bool = False) -> Union[Dict, List]:
518532
"""
519533
Makes a post request to the Frigidaire API and parses the result
520-
:parm url: Base URL for the request (no slashes)
534+
:param url: Base URL for the request (no slashes)
521535
:param path: The path to the resource, including query params
536+
:param headers: Headers to include in the request
522537
:param data: The data to include in the body of the request
523538
:param form_encoding: Whether to form-encode data. If false, encodes as json
524539
:return: The contents of 'data' in the resulting json
525540
"""
526541
try:
527542
encoded_data = urlencode(data) if form_encoding else json.dumps(data)
528543
response = requests.post(f'{url}{path}', data=encoded_data,
529-
headers=headers, verify=False, timeout=self.timeout)
544+
headers=headers, verify=False, timeout=self.timeout)
530545
return self.parse_response(response)
531546
except Exception as e:
532-
self.handle_request_exception(e, "POST", f'{url}{path}', headers, encoded_data)
547+
self.handle_request_exception(e, "POST", f'{url}{path}', headers, json.dumps(data))
533548

534-
def put_request(self, url: str, path: str, headers: Dict[str, str], data: Dict):
549+
def put_request(self, url: str, path: str, headers: Dict[str, str], data: Dict) -> Union[Dict, List]:
535550
"""
536551
Makes a put request to the Frigidaire API and parses the result
537-
:parm url: Base URL for the request (no slashes)
552+
:param url: Base URL for the request (no slashes)
553+
:param headers: Headers to include in the request
538554
:param path: The path to the resource, including query params
539555
:param data: The data to include in the body of the request
540556
:return: The contents of 'data' in the resulting json
541557
"""
558+
encoded_data = json.dumps(data)
542559
try:
543-
encoded_data = json.dumps(data)
544560
response = requests.put(f'{url}{path}', data=encoded_data,
545-
headers=headers, verify=False, timeout=self.timeout)
561+
headers=headers, verify=False, timeout=self.timeout)
562+
return self.parse_response(response)
546563
except Exception as e:
547564
self.handle_request_exception(e, "PUT", f'{url}{path}', headers, encoded_data)

frigidaire/signature_generator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
# Licensed under Apache 2.0, a copy of which is enclosed in this repository
33

44
import base64
5-
import gzip
65
import hmac
76
import urllib.parse
87
import hashlib
98
from collections import OrderedDict
9+
from typing import Optional
10+
1011

1112
def _build_encoded_query(params: dict) -> str:
1213
if not params:
@@ -15,15 +16,18 @@ def _build_encoded_query(params: dict) -> str:
1516
f"{key}={urllib.parse.quote_plus(str(value))}" for key, value in params.items() if value
1617
)
1718

19+
1820
def _url_encode(value: str) -> str:
1921
return urllib.parse.quote_plus(value).replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
2022

23+
2124
def _encode_signature(base_signature: str, secret: str) -> str:
2225
key = base64.b64decode(secret)
2326
signing_key = hmac.new(key, base_signature.encode("utf-8"), digestmod=hashlib.sha1)
2427
return base64.urlsafe_b64encode(signing_key.digest()).decode("utf-8")
2528

26-
def get_signature(secret: str, http_method: str, url: str, params: dict) -> str:
29+
30+
def get_signature(secret: str, http_method: str, url: str, params: dict) -> Optional[str]:
2731
if not all([params, url, http_method, secret]):
2832
return None
2933

test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import configparser
22
import logging
33

4-
from frigidaire import Action, Power, Mode, FanSpeed, Frigidaire, HaclCode, Component
4+
from frigidaire import Action, Power, Mode, FanSpeed, Frigidaire
55

66
if __name__ == '__main__':
77
logging.basicConfig(level=logging.DEBUG)

0 commit comments

Comments
 (0)