7
7
import json
8
8
import threading
9
9
from typing import List , Any
10
- import requests
10
+ import aiohttp
11
11
import urllib .parse
12
12
13
13
14
14
from .auth import Domains , Session
15
15
from .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
+ )
19
21
from .device import Device , DeviceProperties
20
22
from .exception import KarcherHomeAccessDenied , KarcherHomeException , handle_error_code
21
23
from .map import Map
22
24
from .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 ,
24
27
get_timestamp , get_timestamp_ms , is_email , md5
28
+ )
25
29
26
30
27
31
class KarcherHome :
28
32
"""Main class to access Karcher Home Robots API"""
29
33
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"""
32
37
33
- super (). __init__ ()
38
+ self = KarcherHome ()
34
39
self ._country = country .upper ()
35
40
self ._base_url = REGION_URLS [get_region_by_country (self ._country )]
36
- self ._mqtt_url = None
37
41
self ._language = language
38
- self ._session = None
39
- self ._mqtt = None
40
- self ._device_props = {}
41
- self ._wait_events = {}
42
42
43
- d = self .get_urls ()
43
+ d = await self .get_urls ()
44
44
# Update base URLs
45
45
if d .app_api != '' :
46
46
self ._base_url = d .app_api
47
47
if d .mqtt != '' :
48
48
self ._mqtt_url = d .mqtt
49
49
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 ()
52
67
# TODO: Fix SSL
53
- requests .packages .urllib3 .disable_warnings ()
54
- session .verify = False
68
+ # requests.packages.urllib3.disable_warnings()
69
+ # session.skip = False
55
70
56
71
headers = {}
57
72
if kwargs .get ('headers' ) is not None :
@@ -98,26 +113,27 @@ def _request(self, method: str, url: str, **kwargs) -> requests.Response:
98
113
headers ['nonce' ] = nonce
99
114
100
115
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 )
102
118
103
- def _download (self , url ) -> bytes :
104
- session = requests . Session ()
119
+ async def _download (self , url ) -> bytes :
120
+ session = aiohttp . ClientSession ()
105
121
headers = {
106
122
'User-Agent' : 'Android_' + TENANT_ID ,
107
123
}
108
124
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 :
111
127
raise KarcherHomeException (- 1 ,
112
128
'HTTP error: ' + str (resp .status_code ))
113
129
114
- return resp .content
130
+ return await resp .content . read ( - 1 )
115
131
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 :
118
134
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 ()
121
137
# Check for error response
122
138
if data ['code' ] != 0 :
123
139
handle_error_code (data ['code' ], data ['msg' ])
@@ -161,19 +177,19 @@ def _mqtt_connect(self, wait_for_connect=False):
161
177
event .wait ()
162
178
self ._mqtt .on_connect = None
163
179
164
- def get_urls (self ) -> Domains :
180
+ async def get_urls (self ) -> Domains :
165
181
"""Get URLs for API and MQTT."""
166
182
167
- resp = self ._request ('GET' , '/network-service/domains/list' , params = {
183
+ resp = await self ._request ('GET' , '/network-service/domains/list' , params = {
168
184
'tenantId' : TENANT_ID ,
169
185
'productModeCode' : PROJECT_TYPE ,
170
186
'version' : PROTOCOL_VERSION ,
171
187
})
172
188
173
- d = self ._process_response (resp , 'domain' )
189
+ d = await self ._process_response (resp , 'domain' )
174
190
return Domains (** d )
175
191
176
- def login (self , username , password , register_id = None ) -> Session :
192
+ async def login (self , username , password , register_id = None ) -> Session :
177
193
"""Login using provided credentials."""
178
194
179
195
if register_id is None or register_id == '' :
@@ -182,7 +198,7 @@ def login(self, username, password, register_id=None) -> Session:
182
198
if not is_email (username ):
183
199
username = '86-' + username
184
200
185
- resp = self ._request ('POST' , '/user-center/auth/login' , json = {
201
+ resp = await self ._request ('POST' , '/user-center/auth/login' , json = {
186
202
'tenantId' : TENANT_ID ,
187
203
'lang' : str (self ._language ),
188
204
'token' : None ,
@@ -201,7 +217,7 @@ def login(self, username, password, register_id=None) -> Session:
201
217
},
202
218
})
203
219
204
- d = self ._process_response (resp )
220
+ d = await self ._process_response (resp )
205
221
self ._session = Session (** d )
206
222
self ._session .register_id = register_id
207
223
@@ -222,7 +238,7 @@ def login_token(
222
238
223
239
return self ._session
224
240
225
- def logout (self ):
241
+ async def logout (self ):
226
242
"""End current session.
227
243
228
244
This will also reset the session object.
@@ -232,49 +248,49 @@ def logout(self):
232
248
self ._session = None
233
249
return
234
250
235
- self ._process_response (self ._request (
251
+ await self ._process_response (await self ._request (
236
252
'POST' , '/user-center/auth/logout' ))
237
253
self ._session = None
238
254
239
255
if self ._mqtt is not None :
240
256
self ._mqtt .disconnect ()
241
257
self ._mqtt = None
242
258
243
- def get_devices (self ) -> List [Device ]:
259
+ async def get_devices (self ) -> List [Device ]:
244
260
"""Get all user devices."""
245
261
246
262
if self ._session is None \
247
263
or self ._session .auth_token == '' or self ._session .user_id == '' :
248
264
raise KarcherHomeAccessDenied ('Not authorized' )
249
265
250
- resp = self ._request (
266
+ resp = await self ._request (
251
267
'GET' ,
252
268
'/smart-home-service/smartHome/user/getDeviceInfoByUserId/'
253
269
+ self ._session .user_id )
254
270
255
- return [Device (** d ) for d in self ._process_response (resp )]
271
+ return [Device (** d ) for d in await self ._process_response (resp )]
256
272
257
- def get_map_data (self , dev : Device , map : int = 1 ):
273
+ async def get_map_data (self , dev : Device , map : int = 1 ):
258
274
# <tenantId>/<modeType>/<deviceSn>/01-01-2022/map/temp/0046690461_<deviceSn>_1
259
275
mapDir = TENANT_ID + '/' + dev .product_mode_code + '/' + \
260
276
dev .sn + '/01-01-2022/map/temp/0046690461_' + \
261
277
dev .sn + '_' + str (map )
262
278
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
+ })
271
287
272
- d = self ._process_response (resp )
288
+ d = await self ._process_response (resp )
273
289
downloadUrl = d ['url' ]
274
290
if 'cdnDomain' in d and d ['cdnDomain' ] != '' :
275
291
downloadUrl = 'https://' + d ['cdnDomain' ] + '/' + d ['dir' ]
276
292
277
- d = self ._download (downloadUrl )
293
+ d = await self ._download (downloadUrl )
278
294
data = decrypt_map (dev .sn , dev .mac , dev .product_id , d )
279
295
if map == 1 or map == 2 :
280
296
return Map .parse (data )
0 commit comments