pymammotion 0.4.17__py3-none-any.whl → 0.4.19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymammotion/aliyun/client.py +231 -0
- pymammotion/aliyun/cloud_gateway.py +32 -20
- pymammotion/aliyun/model/session_by_authcode_response.py +1 -0
- pymammotion/aliyun/tea/core.py +295 -0
- pymammotion/data/model/hash_list.py +46 -9
- pymammotion/http/http.py +9 -2
- pymammotion/mammotion/commands/messages/navigation.py +1 -1
- pymammotion/mammotion/devices/base.py +37 -13
- pymammotion/mammotion/devices/mammotion.py +21 -11
- pymammotion/mammotion/devices/mammotion_bluetooth.py +1 -2
- pymammotion/mammotion/devices/mammotion_cloud.py +9 -33
- pymammotion/mqtt/mammotion_mqtt.py +7 -6
- pymammotion/proto/mctrl_nav_pb2.py +1 -1
- {pymammotion-0.4.17.dist-info → pymammotion-0.4.19.dist-info}/METADATA +1 -1
- {pymammotion-0.4.17.dist-info → pymammotion-0.4.19.dist-info}/RECORD +17 -15
- {pymammotion-0.4.17.dist-info → pymammotion-0.4.19.dist-info}/LICENSE +0 -0
- {pymammotion-0.4.17.dist-info → pymammotion-0.4.19.dist-info}/WHEEL +0 -0
@@ -0,0 +1,231 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
from Tea.exceptions import UnretryableException
|
4
|
+
from Tea.request import TeaRequest
|
5
|
+
|
6
|
+
from pymammotion.aliyun.tea.core import TeaCore
|
7
|
+
|
8
|
+
try:
|
9
|
+
from typing import Dict
|
10
|
+
except ImportError:
|
11
|
+
pass
|
12
|
+
|
13
|
+
from alibabacloud_apigateway_util.client import Client as APIGatewayUtilClient
|
14
|
+
from alibabacloud_tea_util.client import Client as UtilClient
|
15
|
+
|
16
|
+
|
17
|
+
class Client:
|
18
|
+
"""test"""
|
19
|
+
|
20
|
+
_app_key = None # type: str
|
21
|
+
_app_secret = None # type: str
|
22
|
+
_protocol = None # type: str
|
23
|
+
_user_agent = None # type: str
|
24
|
+
_read_timeout = None # type: int
|
25
|
+
_connect_timeout = None # type: int
|
26
|
+
_http_proxy = None # type: str
|
27
|
+
_https_proxy = None # type: str
|
28
|
+
_no_proxy = None # type: str
|
29
|
+
_max_idle_conns = None # type: int
|
30
|
+
_domain = None # type: str
|
31
|
+
|
32
|
+
def __init__(self, config) -> None:
|
33
|
+
self._domain = config.domain
|
34
|
+
self._app_key = config.app_key
|
35
|
+
self._app_secret = config.app_secret
|
36
|
+
self._protocol = config.protocol
|
37
|
+
self._read_timeout = config.read_timeout
|
38
|
+
self._connect_timeout = config.connect_timeout
|
39
|
+
self._http_proxy = config.http_proxy
|
40
|
+
self._https_proxy = config.https_proxy
|
41
|
+
self._no_proxy = config.no_proxy
|
42
|
+
self._max_idle_conns = config.max_idle_conns
|
43
|
+
|
44
|
+
def do_request(self, pathname, protocol, method, header, body, runtime):
|
45
|
+
"""Send request
|
46
|
+
|
47
|
+
@type pathname: str
|
48
|
+
@param pathname: the url path
|
49
|
+
|
50
|
+
@type protocol: str
|
51
|
+
@param protocol: http or https
|
52
|
+
|
53
|
+
@type method: str
|
54
|
+
@param method: example GET
|
55
|
+
|
56
|
+
@type header: Dict[str, str]
|
57
|
+
@param header: request header
|
58
|
+
|
59
|
+
@type body: iot_api_gateway_models.IoTApiRequest
|
60
|
+
@param body: the object of IoTApiRequest
|
61
|
+
|
62
|
+
@type runtime: util_models.RuntimeOptions
|
63
|
+
@param runtime: which controls some details of call api, such as retry times
|
64
|
+
|
65
|
+
@rtype: TeaResponse
|
66
|
+
@return: the response
|
67
|
+
"""
|
68
|
+
body.validate()
|
69
|
+
runtime.validate()
|
70
|
+
_runtime = {
|
71
|
+
"timeouted": "retry",
|
72
|
+
"readTimeout": UtilClient.default_number(runtime.read_timeout, self._read_timeout),
|
73
|
+
"connectTimeout": UtilClient.default_number(runtime.connect_timeout, self._connect_timeout),
|
74
|
+
"httpProxy": UtilClient.default_string(runtime.http_proxy, self._http_proxy),
|
75
|
+
"httpsProxy": UtilClient.default_string(runtime.https_proxy, self._https_proxy),
|
76
|
+
"noProxy": UtilClient.default_string(runtime.no_proxy, self._no_proxy),
|
77
|
+
"maxIdleConns": UtilClient.default_number(runtime.max_idle_conns, self._max_idle_conns),
|
78
|
+
"retry": {
|
79
|
+
"retryable": runtime.autoretry,
|
80
|
+
"maxAttempts": UtilClient.default_number(runtime.max_attempts, 3),
|
81
|
+
},
|
82
|
+
"backoff": {
|
83
|
+
"policy": UtilClient.default_string(runtime.backoff_policy, "no"),
|
84
|
+
"period": UtilClient.default_number(runtime.backoff_period, 1),
|
85
|
+
},
|
86
|
+
"ignoreSSL": runtime.ignore_ssl,
|
87
|
+
}
|
88
|
+
_last_request = None
|
89
|
+
_last_exception = None
|
90
|
+
_now = time.time()
|
91
|
+
_retry_times = 0
|
92
|
+
while TeaCore.allow_retry(_runtime.get("retry"), _retry_times, _now):
|
93
|
+
if _retry_times > 0:
|
94
|
+
_backoff_time = TeaCore.get_backoff_time(_runtime.get("backoff"), _retry_times)
|
95
|
+
if _backoff_time > 0:
|
96
|
+
TeaCore.sleep(_backoff_time)
|
97
|
+
_retry_times = _retry_times + 1
|
98
|
+
try:
|
99
|
+
_request = TeaRequest()
|
100
|
+
_request.protocol = UtilClient.default_string(self._protocol, protocol)
|
101
|
+
_request.method = UtilClient.default_string(method, "POST")
|
102
|
+
_request.pathname = pathname
|
103
|
+
_request.headers = TeaCore.merge(
|
104
|
+
{
|
105
|
+
"host": self._domain,
|
106
|
+
"date": UtilClient.get_date_utcstring(),
|
107
|
+
"x-ca-nonce": UtilClient.get_nonce(),
|
108
|
+
"x-ca-key": self._app_key,
|
109
|
+
"x-ca-signaturemethod": "HmacSHA256",
|
110
|
+
"accept": "application/json",
|
111
|
+
"user-agent": self.get_user_agent(),
|
112
|
+
},
|
113
|
+
header,
|
114
|
+
)
|
115
|
+
if UtilClient.empty(body.id):
|
116
|
+
body.id = UtilClient.get_nonce()
|
117
|
+
if not UtilClient.is_unset(body):
|
118
|
+
_request.headers["content-type"] = "application/octet-stream"
|
119
|
+
_request.headers["content-md5"] = APIGatewayUtilClient.get_content_md5(
|
120
|
+
UtilClient.to_jsonstring(TeaCore.to_map(body))
|
121
|
+
)
|
122
|
+
_request.body = UtilClient.to_jsonstring(TeaCore.to_map(body))
|
123
|
+
_request.headers["x-ca-signature"] = APIGatewayUtilClient.get_signature(_request, self._app_secret)
|
124
|
+
_last_request = _request
|
125
|
+
_response = TeaCore.do_action(_request, _runtime)
|
126
|
+
return _response
|
127
|
+
except Exception as e:
|
128
|
+
if TeaCore.is_retryable(e):
|
129
|
+
_last_exception = e
|
130
|
+
continue
|
131
|
+
raise e
|
132
|
+
raise UnretryableException(_last_request, _last_exception)
|
133
|
+
|
134
|
+
async def async_do_request(self, pathname, protocol, method, header, body, runtime):
|
135
|
+
"""Send request
|
136
|
+
|
137
|
+
@type pathname: str
|
138
|
+
@param pathname: the url path
|
139
|
+
|
140
|
+
@type protocol: str
|
141
|
+
@param protocol: http or https
|
142
|
+
|
143
|
+
@type method: str
|
144
|
+
@param method: example GET
|
145
|
+
|
146
|
+
@type header: Dict[str, str]
|
147
|
+
@param header: request header
|
148
|
+
|
149
|
+
@type body: iot_api_gateway_models.IoTApiRequest
|
150
|
+
@param body: the object of IoTApiRequest
|
151
|
+
|
152
|
+
@type runtime: util_models.RuntimeOptions
|
153
|
+
@param runtime: which controls some details of call api, such as retry times
|
154
|
+
|
155
|
+
@rtype: TeaResponse
|
156
|
+
@return: the response
|
157
|
+
"""
|
158
|
+
body.validate()
|
159
|
+
runtime.validate()
|
160
|
+
_runtime = {
|
161
|
+
"timeouted": "retry",
|
162
|
+
"readTimeout": UtilClient.default_number(runtime.read_timeout, self._read_timeout),
|
163
|
+
"connectTimeout": UtilClient.default_number(runtime.connect_timeout, self._connect_timeout),
|
164
|
+
"httpProxy": UtilClient.default_string(runtime.http_proxy, self._http_proxy),
|
165
|
+
"httpsProxy": UtilClient.default_string(runtime.https_proxy, self._https_proxy),
|
166
|
+
"noProxy": UtilClient.default_string(runtime.no_proxy, self._no_proxy),
|
167
|
+
"maxIdleConns": UtilClient.default_number(runtime.max_idle_conns, self._max_idle_conns),
|
168
|
+
"retry": {
|
169
|
+
"retryable": runtime.autoretry,
|
170
|
+
"maxAttempts": UtilClient.default_number(runtime.max_attempts, 3),
|
171
|
+
},
|
172
|
+
"backoff": {
|
173
|
+
"policy": UtilClient.default_string(runtime.backoff_policy, "no"),
|
174
|
+
"period": UtilClient.default_number(runtime.backoff_period, 1),
|
175
|
+
},
|
176
|
+
"ignoreSSL": runtime.ignore_ssl,
|
177
|
+
}
|
178
|
+
_last_request = None
|
179
|
+
_last_exception = None
|
180
|
+
_now = time.time()
|
181
|
+
_retry_times = 0
|
182
|
+
while TeaCore.allow_retry(_runtime.get("retry"), _retry_times, _now):
|
183
|
+
if _retry_times > 0:
|
184
|
+
_backoff_time = TeaCore.get_backoff_time(_runtime.get("backoff"), _retry_times)
|
185
|
+
if _backoff_time > 0:
|
186
|
+
TeaCore.sleep(_backoff_time)
|
187
|
+
_retry_times = _retry_times + 1
|
188
|
+
try:
|
189
|
+
_request = TeaRequest()
|
190
|
+
_request.protocol = UtilClient.default_string(self._protocol, protocol)
|
191
|
+
_request.method = UtilClient.default_string(method, "POST")
|
192
|
+
_request.pathname = pathname
|
193
|
+
_request.headers = TeaCore.merge(
|
194
|
+
{
|
195
|
+
"host": self._domain,
|
196
|
+
"date": UtilClient.get_date_utcstring(),
|
197
|
+
"x-ca-nonce": UtilClient.get_nonce(),
|
198
|
+
"x-ca-key": self._app_key,
|
199
|
+
"x-ca-signaturemethod": "HmacSHA256",
|
200
|
+
"accept": "application/json",
|
201
|
+
"user-agent": self.get_user_agent(),
|
202
|
+
},
|
203
|
+
header,
|
204
|
+
)
|
205
|
+
if UtilClient.empty(body.id):
|
206
|
+
body.id = UtilClient.get_nonce()
|
207
|
+
if not UtilClient.is_unset(body):
|
208
|
+
_request.headers["content-type"] = "application/octet-stream"
|
209
|
+
_request.headers["content-md5"] = APIGatewayUtilClient.get_content_md5(
|
210
|
+
UtilClient.to_jsonstring(TeaCore.to_map(body))
|
211
|
+
)
|
212
|
+
_request.body = UtilClient.to_jsonstring(TeaCore.to_map(body))
|
213
|
+
_request.headers["x-ca-signature"] = APIGatewayUtilClient.get_signature(_request, self._app_secret)
|
214
|
+
_last_request = _request
|
215
|
+
_response = await TeaCore.async_do_action(_request, _runtime)
|
216
|
+
return _response
|
217
|
+
except Exception as e:
|
218
|
+
if TeaCore.is_retryable(e):
|
219
|
+
_last_exception = e
|
220
|
+
continue
|
221
|
+
raise e
|
222
|
+
raise UnretryableException(_last_request, _last_exception)
|
223
|
+
|
224
|
+
def get_user_agent(self):
|
225
|
+
"""Get user agent
|
226
|
+
|
227
|
+
@rtype: str
|
228
|
+
@return: user agent
|
229
|
+
"""
|
230
|
+
user_agent = UtilClient.get_user_agent(self._user_agent)
|
231
|
+
return user_agent
|
@@ -12,12 +12,12 @@ import time
|
|
12
12
|
import uuid
|
13
13
|
|
14
14
|
from aiohttp import ClientSession
|
15
|
-
from alibabacloud_iot_api_gateway.client import Client
|
16
15
|
from alibabacloud_iot_api_gateway.models import CommonParams, Config, IoTApiRequest
|
17
16
|
from alibabacloud_tea_util.client import Client as UtilClient
|
18
17
|
from alibabacloud_tea_util.models import RuntimeOptions
|
19
18
|
from Tea.exceptions import UnretryableException
|
20
19
|
|
20
|
+
from pymammotion.aliyun.client import Client
|
21
21
|
from pymammotion.aliyun.model.aep_response import AepResponse
|
22
22
|
from pymammotion.aliyun.model.connect_response import ConnectResponse
|
23
23
|
from pymammotion.aliyun.model.dev_by_account_response import ListingDevByAccountResponse
|
@@ -116,6 +116,13 @@ class CloudIOTGateway:
|
|
116
116
|
self._session_by_authcode_response = session_by_authcode_response
|
117
117
|
self._region_response = region_response
|
118
118
|
self._devices_by_account_response = dev_by_account
|
119
|
+
self._iot_token_issued_at = int(time.time())
|
120
|
+
if self._session_by_authcode_response:
|
121
|
+
self._iot_token_issued_at = (
|
122
|
+
self._session_by_authcode_response.token_issued_at
|
123
|
+
if self._session_by_authcode_response.token_issued_at is not None
|
124
|
+
else int(time.time())
|
125
|
+
)
|
119
126
|
|
120
127
|
@staticmethod
|
121
128
|
def generate_random_string(length: int):
|
@@ -144,7 +151,7 @@ class CloudIOTGateway:
|
|
144
151
|
hashlib.sha1,
|
145
152
|
).hexdigest()
|
146
153
|
|
147
|
-
def get_region(self, country_code: str, auth_code: str):
|
154
|
+
async def get_region(self, country_code: str, auth_code: str):
|
148
155
|
"""Get the region based on country code and auth code."""
|
149
156
|
|
150
157
|
if self._region_response is not None:
|
@@ -171,7 +178,9 @@ class CloudIOTGateway:
|
|
171
178
|
)
|
172
179
|
|
173
180
|
# send request
|
174
|
-
response = client.
|
181
|
+
response = await client.async_do_request(
|
182
|
+
"/living/account/region/get", "https", "POST", None, body, RuntimeOptions()
|
183
|
+
)
|
175
184
|
logger.debug(response.status_message)
|
176
185
|
logger.debug(response.headers)
|
177
186
|
logger.debug(response.status_code)
|
@@ -191,7 +200,7 @@ class CloudIOTGateway:
|
|
191
200
|
|
192
201
|
return response.body
|
193
202
|
|
194
|
-
def aep_handle(self):
|
203
|
+
async def aep_handle(self):
|
195
204
|
"""Handle AEP authentication."""
|
196
205
|
aep_domain = self.domain
|
197
206
|
|
@@ -230,7 +239,7 @@ class CloudIOTGateway:
|
|
230
239
|
)
|
231
240
|
|
232
241
|
# send request
|
233
|
-
response = client.
|
242
|
+
response = await client.async_do_request("/app/aepauth/handle", "https", "POST", None, body, RuntimeOptions())
|
234
243
|
logger.debug(response.status_message)
|
235
244
|
logger.debug(response.headers)
|
236
245
|
logger.debug(response.status_code)
|
@@ -395,7 +404,7 @@ class CloudIOTGateway:
|
|
395
404
|
return self._login_by_oauth_response
|
396
405
|
raise LoginException(data)
|
397
406
|
|
398
|
-
def session_by_auth_code(self):
|
407
|
+
async def session_by_auth_code(self):
|
399
408
|
"""Create a session by auth code."""
|
400
409
|
config = Config(
|
401
410
|
app_key=self._app_key,
|
@@ -420,7 +429,7 @@ class CloudIOTGateway:
|
|
420
429
|
)
|
421
430
|
|
422
431
|
# send request
|
423
|
-
response = client.
|
432
|
+
response = await client.async_do_request(
|
424
433
|
"/account/createSessionByAuthCode",
|
425
434
|
"https",
|
426
435
|
"POST",
|
@@ -452,7 +461,7 @@ class CloudIOTGateway:
|
|
452
461
|
|
453
462
|
return response.body
|
454
463
|
|
455
|
-
def sign_out(self) -> None:
|
464
|
+
async def sign_out(self) -> None:
|
456
465
|
config = Config(
|
457
466
|
app_key=self._app_key,
|
458
467
|
app_secret=self._app_secret,
|
@@ -476,7 +485,7 @@ class CloudIOTGateway:
|
|
476
485
|
|
477
486
|
# send request
|
478
487
|
# possibly need to do this ourselves
|
479
|
-
response = client.
|
488
|
+
response = await client.async_do_request(
|
480
489
|
"/iotx/account/invalidSession",
|
481
490
|
"https",
|
482
491
|
"POST",
|
@@ -497,7 +506,7 @@ class CloudIOTGateway:
|
|
497
506
|
logger.debug(response_body_dict)
|
498
507
|
return response_body_dict
|
499
508
|
|
500
|
-
def check_or_refresh_session(self):
|
509
|
+
async def check_or_refresh_session(self):
|
501
510
|
"""Check or refresh the session."""
|
502
511
|
logger.debug("Trying to refresh token")
|
503
512
|
config = Config(
|
@@ -523,7 +532,7 @@ class CloudIOTGateway:
|
|
523
532
|
|
524
533
|
# send request
|
525
534
|
# possibly need to do this ourselves
|
526
|
-
response = client.
|
535
|
+
response = await client.async_do_request(
|
527
536
|
"/account/checkOrRefreshSession",
|
528
537
|
"https",
|
529
538
|
"POST",
|
@@ -544,7 +553,7 @@ class CloudIOTGateway:
|
|
544
553
|
|
545
554
|
if int(response_body_dict.get("code")) != 200:
|
546
555
|
logger.error(response_body_dict)
|
547
|
-
self.sign_out()
|
556
|
+
await self.sign_out()
|
548
557
|
raise CheckSessionException("Error check or refresh token: " + response_body_dict.__str__())
|
549
558
|
|
550
559
|
session = SessionByAuthCodeResponse.from_dict(response_body_dict)
|
@@ -562,7 +571,7 @@ class CloudIOTGateway:
|
|
562
571
|
self._session_by_authcode_response = session
|
563
572
|
self._iot_token_issued_at = int(time.time())
|
564
573
|
|
565
|
-
def list_binding_by_account(self) -> ListingDevByAccountResponse:
|
574
|
+
async def list_binding_by_account(self) -> ListingDevByAccountResponse:
|
566
575
|
"""List bindings by account."""
|
567
576
|
config = Config(
|
568
577
|
app_key=self._app_key,
|
@@ -586,7 +595,9 @@ class CloudIOTGateway:
|
|
586
595
|
)
|
587
596
|
|
588
597
|
# send request
|
589
|
-
response = client.
|
598
|
+
response = await client.async_do_request(
|
599
|
+
"/uc/listBindingByAccount", "https", "POST", None, body, RuntimeOptions()
|
600
|
+
)
|
590
601
|
logger.debug(response.status_message)
|
591
602
|
logger.debug(response.headers)
|
592
603
|
logger.debug(response.status_code)
|
@@ -604,7 +615,7 @@ class CloudIOTGateway:
|
|
604
615
|
self._devices_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict)
|
605
616
|
return self._devices_by_account_response
|
606
617
|
|
607
|
-
def list_binding_by_dev(self, iot_id: str):
|
618
|
+
async def list_binding_by_dev(self, iot_id: str):
|
608
619
|
config = Config(
|
609
620
|
app_key=self._app_key,
|
610
621
|
app_secret=self._app_secret,
|
@@ -627,7 +638,7 @@ class CloudIOTGateway:
|
|
627
638
|
)
|
628
639
|
|
629
640
|
# send request
|
630
|
-
response = client.
|
641
|
+
response = await client.async_do_request("/uc/listBindingByDev", "https", "POST", None, body, RuntimeOptions())
|
631
642
|
logger.debug(response.status_message)
|
632
643
|
logger.debug(response.headers)
|
633
644
|
logger.debug(response.status_code)
|
@@ -645,7 +656,7 @@ class CloudIOTGateway:
|
|
645
656
|
self._devices_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict)
|
646
657
|
return self._devices_by_account_response
|
647
658
|
|
648
|
-
def send_cloud_command(self, iot_id: str, command: bytes) -> str:
|
659
|
+
async def send_cloud_command(self, iot_id: str, command: bytes) -> str:
|
649
660
|
"""Send a cloud command to the specified IoT device."""
|
650
661
|
|
651
662
|
if command is None:
|
@@ -659,7 +670,7 @@ class CloudIOTGateway:
|
|
659
670
|
if self._iot_token_issued_at + self._session_by_authcode_response.data.refreshTokenExpire > (
|
660
671
|
int(time.time())
|
661
672
|
):
|
662
|
-
self.check_or_refresh_session()
|
673
|
+
await self.check_or_refresh_session()
|
663
674
|
else:
|
664
675
|
raise AuthRefreshException("Refresh token expired. Please re-login")
|
665
676
|
|
@@ -694,7 +705,8 @@ class CloudIOTGateway:
|
|
694
705
|
)
|
695
706
|
logger.debug(self.converter.printBase64Binary(command))
|
696
707
|
# send request
|
697
|
-
|
708
|
+
runtime_options = RuntimeOptions(autoretry=True, backoff_policy="yes")
|
709
|
+
response = await client.async_do_request("/thing/service/invoke", "https", "POST", None, body, runtime_options)
|
698
710
|
logger.debug(response.status_message)
|
699
711
|
logger.debug(response.headers)
|
700
712
|
logger.debug(response.status_code)
|
@@ -716,7 +728,7 @@ class CloudIOTGateway:
|
|
716
728
|
|
717
729
|
if response_body_dict.get("code") == 29003:
|
718
730
|
logger.debug(self._session_by_authcode_response.data.identityId)
|
719
|
-
self.sign_out()
|
731
|
+
await self.sign_out()
|
720
732
|
raise SetupException(response_body_dict.get("code"), iot_id)
|
721
733
|
if response_body_dict.get("code") == 6205:
|
722
734
|
raise DeviceOfflineException(response_body_dict.get("code"), iot_id)
|