77import json
88import threading
99from typing import List , Any
10- import requests
10+ import aiohttp
1111import urllib .parse
1212
1313
1414from .auth import Domains , Session
1515from .countries import get_country_code , get_region_by_country
16- from .consts import APP_VERSION_CODE , APP_VERSION_NAME , PROJECT_TYPE , \
17- PROTOCOL_VERSION , REGION_URLS , ROBOT_PROPERTIES , TENANT_ID , \
18- Language
16+ from .consts import (
17+ APP_VERSION_CODE , APP_VERSION_NAME , PROJECT_TYPE ,
18+ PROTOCOL_VERSION , REGION_URLS , ROBOT_PROPERTIES , TENANT_ID ,
19+ Language , Region
20+ )
1921from .device import Device , DeviceProperties
2022from .exception import KarcherHomeAccessDenied , KarcherHomeException , handle_error_code
2123from .map import Map
2224from .mqtt import MqttClient , get_device_topic_property_get_reply , get_device_topics
23- from .utils import decrypt , decrypt_map , encrypt , get_nonce , get_random_string , \
25+ from .utils import (
26+ decrypt , decrypt_map , encrypt , get_nonce , get_random_string ,
2427 get_timestamp , get_timestamp_ms , is_email , md5
28+ )
2529
2630
2731class KarcherHome :
2832 """Main class to access Karcher Home Robots API"""
2933
30- def __init__ (self , country : str = 'GB' , language : Language = Language .EN ):
31- """Initialize Karcher Home Robots API"""
34+ @classmethod
35+ async def create (cls , country : str = 'GB' , language : Language = Language .EN ):
36+ """Create Karcher Home Robots API instance"""
3237
33- super (). __init__ ()
38+ self = KarcherHome ()
3439 self ._country = country .upper ()
3540 self ._base_url = REGION_URLS [get_region_by_country (self ._country )]
36- self ._mqtt_url = None
3741 self ._language = language
38- self ._session = None
39- self ._mqtt = None
40- self ._device_props = {}
41- self ._wait_events = {}
4242
43- d = self .get_urls ()
43+ d = await self .get_urls ()
4444 # Update base URLs
4545 if d .app_api != '' :
4646 self ._base_url = d .app_api
4747 if d .mqtt != '' :
4848 self ._mqtt_url = d .mqtt
4949
50- def _request (self , method : str , url : str , ** kwargs ) -> requests .Response :
51- session = requests .Session ()
50+ return self
51+
52+ def __init__ (self ):
53+ """Initialize Karcher Home Robots API"""
54+
55+ super ().__init__ ()
56+ self ._country = 'US'
57+ self ._base_url = REGION_URLS [Region .US ]
58+ self ._mqtt_url = None
59+ self ._language = Language .EN
60+ self ._session = None
61+ self ._mqtt = None
62+ self ._device_props = {}
63+ self ._wait_events = {}
64+
65+ async def _request (self , method : str , url : str , ** kwargs ) -> aiohttp .ClientResponse :
66+ session = aiohttp .ClientSession ()
5267 # TODO: Fix SSL
53- requests .packages .urllib3 .disable_warnings ()
54- session .verify = False
68+ # requests.packages.urllib3.disable_warnings()
69+ # session.skip = False
5570
5671 headers = {}
5772 if kwargs .get ('headers' ) is not None :
@@ -98,26 +113,27 @@ def _request(self, method: str, url: str, **kwargs) -> requests.Response:
98113 headers ['nonce' ] = nonce
99114
100115 kwargs ['headers' ] = headers
101- return session .request (method , self ._base_url + url , ** kwargs )
116+ kwargs ['verify_ssl' ] = False
117+ return await session .request (method , self ._base_url + url , ** kwargs )
102118
103- def _download (self , url ) -> bytes :
104- session = requests . Session ()
119+ async def _download (self , url ) -> bytes :
120+ session = aiohttp . ClientSession ()
105121 headers = {
106122 'User-Agent' : 'Android_' + TENANT_ID ,
107123 }
108124
109- resp = session .get (url , headers = headers )
110- if resp .status_code != 200 :
125+ resp = await session .get (url , headers = headers )
126+ if resp .status != 200 :
111127 raise KarcherHomeException (- 1 ,
112128 'HTTP error: ' + str (resp .status_code ))
113129
114- return resp .content
130+ return await resp .content . read ( - 1 )
115131
116- def _process_response (self , resp , prop = None ) -> Any :
117- if resp .status_code != 200 :
132+ async def _process_response (self , resp : aiohttp . ClientResponse , prop = None ) -> Any :
133+ if resp .status != 200 :
118134 raise KarcherHomeException (- 1 ,
119- 'HTTP error: ' + str (resp .status_code ))
120- data = resp .json ()
135+ 'HTTP error: ' + str (resp .status ))
136+ data = await resp .json ()
121137 # Check for error response
122138 if data ['code' ] != 0 :
123139 handle_error_code (data ['code' ], data ['msg' ])
@@ -161,19 +177,19 @@ def _mqtt_connect(self, wait_for_connect=False):
161177 event .wait ()
162178 self ._mqtt .on_connect = None
163179
164- def get_urls (self ) -> Domains :
180+ async def get_urls (self ) -> Domains :
165181 """Get URLs for API and MQTT."""
166182
167- resp = self ._request ('GET' , '/network-service/domains/list' , params = {
183+ resp = await self ._request ('GET' , '/network-service/domains/list' , params = {
168184 'tenantId' : TENANT_ID ,
169185 'productModeCode' : PROJECT_TYPE ,
170186 'version' : PROTOCOL_VERSION ,
171187 })
172188
173- d = self ._process_response (resp , 'domain' )
189+ d = await self ._process_response (resp , 'domain' )
174190 return Domains (** d )
175191
176- def login (self , username , password , register_id = None ) -> Session :
192+ async def login (self , username , password , register_id = None ) -> Session :
177193 """Login using provided credentials."""
178194
179195 if register_id is None or register_id == '' :
@@ -182,7 +198,7 @@ def login(self, username, password, register_id=None) -> Session:
182198 if not is_email (username ):
183199 username = '86-' + username
184200
185- resp = self ._request ('POST' , '/user-center/auth/login' , json = {
201+ resp = await self ._request ('POST' , '/user-center/auth/login' , json = {
186202 'tenantId' : TENANT_ID ,
187203 'lang' : str (self ._language ),
188204 'token' : None ,
@@ -201,7 +217,7 @@ def login(self, username, password, register_id=None) -> Session:
201217 },
202218 })
203219
204- d = self ._process_response (resp )
220+ d = await self ._process_response (resp )
205221 self ._session = Session (** d )
206222 self ._session .register_id = register_id
207223
@@ -222,7 +238,7 @@ def login_token(
222238
223239 return self ._session
224240
225- def logout (self ):
241+ async def logout (self ):
226242 """End current session.
227243
228244 This will also reset the session object.
@@ -232,49 +248,49 @@ def logout(self):
232248 self ._session = None
233249 return
234250
235- self ._process_response (self ._request (
251+ await self ._process_response (await self ._request (
236252 'POST' , '/user-center/auth/logout' ))
237253 self ._session = None
238254
239255 if self ._mqtt is not None :
240256 self ._mqtt .disconnect ()
241257 self ._mqtt = None
242258
243- def get_devices (self ) -> List [Device ]:
259+ async def get_devices (self ) -> List [Device ]:
244260 """Get all user devices."""
245261
246262 if self ._session is None \
247263 or self ._session .auth_token == '' or self ._session .user_id == '' :
248264 raise KarcherHomeAccessDenied ('Not authorized' )
249265
250- resp = self ._request (
266+ resp = await self ._request (
251267 'GET' ,
252268 '/smart-home-service/smartHome/user/getDeviceInfoByUserId/'
253269 + self ._session .user_id )
254270
255- return [Device (** d ) for d in self ._process_response (resp )]
271+ return [Device (** d ) for d in await self ._process_response (resp )]
256272
257- def get_map_data (self , dev : Device , map : int = 1 ):
273+ async def get_map_data (self , dev : Device , map : int = 1 ):
258274 # <tenantId>/<modeType>/<deviceSn>/01-01-2022/map/temp/0046690461_<deviceSn>_1
259275 mapDir = TENANT_ID + '/' + dev .product_mode_code + '/' + \
260276 dev .sn + '/01-01-2022/map/temp/0046690461_' + \
261277 dev .sn + '_' + str (map )
262278
263- resp = self ._request ('POST' ,
264- '/storage-management/storage/aws/getAccessUrl' ,
265- json = {
266- 'dir' : mapDir ,
267- 'countryCode' : get_country_code (self ._country ),
268- 'serviceType' : 2 ,
269- 'tenantId' : TENANT_ID ,
270- })
279+ resp = await self ._request ('POST' ,
280+ '/storage-management/storage/aws/getAccessUrl' ,
281+ json = {
282+ 'dir' : mapDir ,
283+ 'countryCode' : get_country_code (self ._country ),
284+ 'serviceType' : 2 ,
285+ 'tenantId' : TENANT_ID ,
286+ })
271287
272- d = self ._process_response (resp )
288+ d = await self ._process_response (resp )
273289 downloadUrl = d ['url' ]
274290 if 'cdnDomain' in d and d ['cdnDomain' ] != '' :
275291 downloadUrl = 'https://' + d ['cdnDomain' ] + '/' + d ['dir' ]
276292
277- d = self ._download (downloadUrl )
293+ d = await self ._download (downloadUrl )
278294 data = decrypt_map (dev .sn , dev .mac , dev .product_id , d )
279295 if map == 1 or map == 2 :
280296 return Map .parse (data )
0 commit comments