1111import urllib3
1212from urllib .parse import quote_plus
1313from urllib .parse import urlencode
14- import uuid
1514import 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
162161class 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 )
0 commit comments