pymammotion 0.2.7__py3-none-any.whl → 0.2.8__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.
Potentially problematic release.
This version of pymammotion might be problematic. Click here for more details.
- pymammotion/aliyun/cloud_gateway.py +83 -23
- pymammotion/aliyun/dataclass/session_by_authcode_response.py +4 -4
- pymammotion/data/model/location.py +0 -4
- pymammotion/data/state_manager.py +6 -8
- pymammotion/mammotion/devices/mammotion.py +42 -26
- pymammotion/mqtt/mammotion_mqtt.py +4 -4
- {pymammotion-0.2.7.dist-info → pymammotion-0.2.8.dist-info}/METADATA +1 -1
- {pymammotion-0.2.7.dist-info → pymammotion-0.2.8.dist-info}/RECORD +10 -10
- {pymammotion-0.2.7.dist-info → pymammotion-0.2.8.dist-info}/LICENSE +0 -0
- {pymammotion-0.2.7.dist-info → pymammotion-0.2.8.dist-info}/WHEEL +0 -0
|
@@ -10,6 +10,7 @@ import string
|
|
|
10
10
|
import time
|
|
11
11
|
import uuid
|
|
12
12
|
from logging import getLogger, exception
|
|
13
|
+
from datetime import datetime
|
|
13
14
|
|
|
14
15
|
from aiohttp import ClientSession
|
|
15
16
|
from alibabacloud_iot_api_gateway.client import Client
|
|
@@ -48,6 +49,9 @@ MOVE_HEADERS = (
|
|
|
48
49
|
class SetupException(Exception):
|
|
49
50
|
pass
|
|
50
51
|
|
|
52
|
+
class AuthRefreshException(Exception):
|
|
53
|
+
"""Raise exception when library cannot refresh token."""
|
|
54
|
+
|
|
51
55
|
|
|
52
56
|
class CloudIOTGateway:
|
|
53
57
|
"""Class for interacting with Aliyun Cloud IoT Gateway."""
|
|
@@ -56,16 +60,18 @@ class CloudIOTGateway:
|
|
|
56
60
|
_device_sn = ""
|
|
57
61
|
_utdid = ""
|
|
58
62
|
|
|
59
|
-
_connect_response = None
|
|
60
|
-
_login_by_oauth_response = None
|
|
61
|
-
_aep_response = None
|
|
62
|
-
_session_by_authcode_response = None
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
_connect_response: ConnectResponse | None = None
|
|
64
|
+
_login_by_oauth_response: LoginByOAuthResponse | None = None
|
|
65
|
+
_aep_response: AepResponse | None = None
|
|
66
|
+
_session_by_authcode_response: SessionByAuthCodeResponse | None = None
|
|
67
|
+
_devices_by_account_response: ListingDevByAccountResponse | None = None
|
|
68
|
+
_region_response = None
|
|
69
|
+
|
|
70
|
+
_iot_token_issued_at : int = None
|
|
65
71
|
|
|
66
72
|
converter = DatatypeConverter()
|
|
67
73
|
|
|
68
|
-
def __init__(self):
|
|
74
|
+
def __init__(self, connect_response: ConnectResponse | None, login_by_oauth_response: LoginByOAuthResponse | None, aep_response: AepResponse | None, session_by_authcode_response: SessionByAuthCodeResponse | None, region_response: RegionResponse | None, dev_by_account: ListingDevByAccountResponse | None):
|
|
69
75
|
"""Initialize the CloudIOTGateway."""
|
|
70
76
|
self._app_key = APP_KEY
|
|
71
77
|
self._app_secret = APP_SECRET
|
|
@@ -74,6 +80,12 @@ class CloudIOTGateway:
|
|
|
74
80
|
self._client_id = self.generate_hardware_string(8) # 8 characters
|
|
75
81
|
self._device_sn = self.generate_hardware_string(32) # 32 characters
|
|
76
82
|
self._utdid = self.generate_hardware_string(32) # 32 characters
|
|
83
|
+
self._connect_response = connect_response
|
|
84
|
+
self._login_by_oauth_response = login_by_oauth_response
|
|
85
|
+
self._aep_response = aep_response
|
|
86
|
+
self._session_by_authcode_response = session_by_authcode_response
|
|
87
|
+
self._region_response = region_response
|
|
88
|
+
self._devices_by_account_response = dev_by_account
|
|
77
89
|
|
|
78
90
|
@staticmethod
|
|
79
91
|
def generate_random_string(length):
|
|
@@ -102,6 +114,24 @@ class CloudIOTGateway:
|
|
|
102
114
|
hashlib.sha1,
|
|
103
115
|
).hexdigest()
|
|
104
116
|
|
|
117
|
+
def get_connect_response(self):
|
|
118
|
+
return self._connect_response
|
|
119
|
+
|
|
120
|
+
def get_login_by_oauth_response(self):
|
|
121
|
+
return self._login_by_oauth_response
|
|
122
|
+
|
|
123
|
+
def get_aep_response(self):
|
|
124
|
+
return self._aep_response
|
|
125
|
+
|
|
126
|
+
def get_session_by_authcode_response(self):
|
|
127
|
+
return self._session_by_authcode_response
|
|
128
|
+
|
|
129
|
+
def get_devices_by_account_response(self):
|
|
130
|
+
return self._devices_by_account_response
|
|
131
|
+
|
|
132
|
+
def get_region_response(self):
|
|
133
|
+
return self._region_response
|
|
134
|
+
|
|
105
135
|
def get_region(self, country_code: str, auth_code: str):
|
|
106
136
|
"""Get the region based on country code and auth code."""
|
|
107
137
|
config = Config(
|
|
@@ -140,8 +170,8 @@ class CloudIOTGateway:
|
|
|
140
170
|
if int(response_body_dict.get("code")) != 200:
|
|
141
171
|
raise Exception("Error in getting regions: " + response_body_dict["msg"])
|
|
142
172
|
|
|
143
|
-
self.
|
|
144
|
-
logger.debug("Endpoint: %s", self.
|
|
173
|
+
self._region_response = RegionResponse.from_dict(response_body_dict)
|
|
174
|
+
logger.debug("Endpoint: %s", self._region_response.data.mqttEndpoint)
|
|
145
175
|
|
|
146
176
|
return response.body
|
|
147
177
|
|
|
@@ -149,8 +179,8 @@ class CloudIOTGateway:
|
|
|
149
179
|
"""Handle AEP authentication."""
|
|
150
180
|
aep_domain = self.domain
|
|
151
181
|
|
|
152
|
-
if self.
|
|
153
|
-
aep_domain = self.
|
|
182
|
+
if self._region_response.data.apiGatewayEndpoint is not None:
|
|
183
|
+
aep_domain = self._region_response.data.apiGatewayEndpoint
|
|
154
184
|
|
|
155
185
|
config = Config(
|
|
156
186
|
app_key=self._app_key,
|
|
@@ -275,7 +305,7 @@ class CloudIOTGateway:
|
|
|
275
305
|
|
|
276
306
|
async def login_by_oauth(self, country_code: str, auth_code: str):
|
|
277
307
|
"""Login by OAuth."""
|
|
278
|
-
region_url = self.
|
|
308
|
+
region_url = self._region_response.data.oaApiGatewayEndpoint
|
|
279
309
|
|
|
280
310
|
async with ClientSession() as session:
|
|
281
311
|
headers = {
|
|
@@ -347,7 +377,7 @@ class CloudIOTGateway:
|
|
|
347
377
|
config = Config(
|
|
348
378
|
app_key=self._app_key,
|
|
349
379
|
app_secret=self._app_secret,
|
|
350
|
-
domain=self.
|
|
380
|
+
domain=self._region_response.data.apiGatewayEndpoint,
|
|
351
381
|
)
|
|
352
382
|
client = Client(config)
|
|
353
383
|
|
|
@@ -390,15 +420,17 @@ class CloudIOTGateway:
|
|
|
390
420
|
raise Exception("Error in creating session: " + response_body_dict["msg"])
|
|
391
421
|
|
|
392
422
|
self._session_by_authcode_response = SessionByAuthCodeResponse.from_dict(response_body_dict)
|
|
423
|
+
self._iot_token_issued_at = int(time.time())
|
|
393
424
|
|
|
394
425
|
return response.body
|
|
395
426
|
|
|
396
427
|
def check_or_refresh_session(self):
|
|
397
428
|
"""Check or refresh the session."""
|
|
429
|
+
logger.debug("Try to refresh token")
|
|
398
430
|
config = Config(
|
|
399
431
|
app_key=self._app_key,
|
|
400
432
|
app_secret=self._app_secret,
|
|
401
|
-
domain=self.
|
|
433
|
+
domain=self._region_response.data.apiGatewayEndpoint,
|
|
402
434
|
)
|
|
403
435
|
client = Client(config)
|
|
404
436
|
|
|
@@ -431,19 +463,36 @@ class CloudIOTGateway:
|
|
|
431
463
|
logger.debug(response.status_code)
|
|
432
464
|
logger.debug(response.body)
|
|
433
465
|
|
|
434
|
-
#
|
|
435
|
-
# Decodifica il corpo della risposta
|
|
466
|
+
# Decode the response body
|
|
436
467
|
response_body_str = response.body.decode("utf-8")
|
|
437
468
|
|
|
438
|
-
#
|
|
439
|
-
json.loads(response_body_str)
|
|
469
|
+
# Load the JSON string into a dictionary
|
|
470
|
+
response_body_dict = json.loads(response_body_str)
|
|
471
|
+
|
|
472
|
+
if int(response_body_dict.get("code")) != 200:
|
|
473
|
+
raise Exception("Error check or refresh token: " + response_body_dict.get('msg', ''))
|
|
474
|
+
|
|
475
|
+
identityId = response_body_dict.get('data', {}).get('identityId', None)
|
|
476
|
+
refreshTokenExpire = response_body_dict.get('data', {}).get('refreshTokenExpire', None)
|
|
477
|
+
iotToken = response_body_dict.get('data', {}).get('iotToken', None)
|
|
478
|
+
iotTokenExpire = response_body_dict.get('data', {}).get('iotTokenExpire', None)
|
|
479
|
+
refreshToken = response_body_dict.get('data', {}).get('refreshToken', None)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
if (identityId is None or refreshTokenExpire is None or iotToken is None or iotTokenExpire is None or refreshToken is None):
|
|
483
|
+
raise Exception("Error check or refresh token: Parameters not correct")
|
|
484
|
+
|
|
485
|
+
self._session_by_authcode_response = SessionByAuthCodeResponse.from_dict(response_body_dict)
|
|
486
|
+
self._iot_token_issued_at = int(time.time())
|
|
487
|
+
|
|
488
|
+
|
|
440
489
|
|
|
441
490
|
def list_binding_by_account(self) -> ListingDevByAccountResponse:
|
|
442
491
|
"""List bindings by account."""
|
|
443
492
|
config = Config(
|
|
444
493
|
app_key=self._app_key,
|
|
445
494
|
app_secret=self._app_secret,
|
|
446
|
-
domain=self.
|
|
495
|
+
domain=self._region_response.data.apiGatewayEndpoint,
|
|
447
496
|
)
|
|
448
497
|
|
|
449
498
|
client = Client(config)
|
|
@@ -477,15 +526,26 @@ class CloudIOTGateway:
|
|
|
477
526
|
if int(response_body_dict.get("code")) != 200:
|
|
478
527
|
raise Exception("Error in creating session: " + response_body_dict["msg"])
|
|
479
528
|
|
|
480
|
-
self.
|
|
481
|
-
return self.
|
|
529
|
+
self._devices_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict)
|
|
530
|
+
return self._devices_by_account_response
|
|
482
531
|
|
|
483
532
|
def send_cloud_command(self, iot_id: str, command: bytes) -> str:
|
|
484
533
|
"""Send a cloud command to the specified IoT device."""
|
|
534
|
+
|
|
535
|
+
"""Check if iotToken is expired"""
|
|
536
|
+
if self._iot_token_issued_at + self._session_by_authcode_response.data.iotTokenExpire <= (int(time.time()) + (5 * 3600)):
|
|
537
|
+
"""Token expired - Try to refresh - Check if refreshToken is not expired"""
|
|
538
|
+
if self._iot_token_issued_at + self._session_by_authcode_response.data.refreshTokenExpire > (int(time.time())):
|
|
539
|
+
self.check_or_refresh_session()
|
|
540
|
+
else:
|
|
541
|
+
raise AuthRefreshException("Refresh token expired. Please re-login")
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
|
|
485
545
|
config = Config(
|
|
486
546
|
app_key=self._app_key,
|
|
487
547
|
app_secret=self._app_secret,
|
|
488
|
-
domain=self.
|
|
548
|
+
domain=self._region_response.data.apiGatewayEndpoint,
|
|
489
549
|
)
|
|
490
550
|
|
|
491
551
|
client = Client(config)
|
|
@@ -537,4 +597,4 @@ class CloudIOTGateway:
|
|
|
537
597
|
|
|
538
598
|
@property
|
|
539
599
|
def listing_dev_by_account_response(self):
|
|
540
|
-
return self.
|
|
600
|
+
return self._devices_by_account_response
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from datetime import time
|
|
2
3
|
|
|
3
4
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
@dataclass
|
|
7
|
-
class
|
|
8
|
+
class SessionOauthToken(DataClassORJSONMixin):
|
|
8
9
|
identityId: str
|
|
9
10
|
refreshTokenExpire: int
|
|
10
11
|
iotToken: str
|
|
11
12
|
iotTokenExpire: int
|
|
12
13
|
refreshToken: str
|
|
13
14
|
|
|
14
|
-
|
|
15
15
|
@dataclass
|
|
16
16
|
class SessionByAuthCodeResponse(DataClassORJSONMixin):
|
|
17
17
|
code: int
|
|
18
|
-
data:
|
|
18
|
+
data: SessionOauthToken
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
"""Manage state from notifications into MowingDevice."""
|
|
2
|
+
from typing import Optional, Callable, Awaitable
|
|
2
3
|
|
|
3
4
|
import betterproto
|
|
4
5
|
|
|
5
6
|
from pymammotion.data.model.device import MowingDevice
|
|
6
|
-
from pymammotion.event.event import DataEvent
|
|
7
7
|
from pymammotion.proto.luba_msg import LubaMsg
|
|
8
|
-
from pymammotion.proto.mctrl_nav import NavGetCommDataAck
|
|
8
|
+
from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class StateManager:
|
|
12
12
|
"""Manage state."""
|
|
13
13
|
|
|
14
14
|
_device: MowingDevice
|
|
15
|
-
gethash_ack_callback: DataEvent
|
|
16
|
-
get_commondata_ack_callback: DataEvent
|
|
17
15
|
|
|
18
16
|
def __init__(self, device: MowingDevice):
|
|
19
17
|
self._device = device
|
|
20
|
-
self.gethash_ack_callback =
|
|
21
|
-
self.get_commondata_ack_callback =
|
|
18
|
+
self.gethash_ack_callback: Optional[Callable[[NavGetHashListAck],Awaitable[None]]] = None
|
|
19
|
+
self.get_commondata_ack_callback: Optional[Callable[[NavGetCommDataAck],Awaitable[None]]] = None
|
|
22
20
|
|
|
23
21
|
def get_device(self) -> MowingDevice:
|
|
24
22
|
"""Get device."""
|
|
@@ -54,12 +52,12 @@ class StateManager:
|
|
|
54
52
|
self._device.map.obstacle = dict()
|
|
55
53
|
self._device.map.area = dict()
|
|
56
54
|
self._device.map.path = dict()
|
|
57
|
-
await self.gethash_ack_callback
|
|
55
|
+
await self.gethash_ack_callback(nav_msg[1])
|
|
58
56
|
case "toapp_get_commondata_ack":
|
|
59
57
|
common_data: NavGetCommDataAck = nav_msg[1]
|
|
60
58
|
updated = self._device.map.update(common_data)
|
|
61
59
|
if updated:
|
|
62
|
-
await self.get_commondata_ack_callback
|
|
60
|
+
await self.get_commondata_ack_callback(common_data)
|
|
63
61
|
|
|
64
62
|
def _update_sys_data(self, message):
|
|
65
63
|
"""Update system."""
|
|
@@ -13,7 +13,7 @@ from abc import abstractmethod
|
|
|
13
13
|
from collections import deque
|
|
14
14
|
from enum import Enum
|
|
15
15
|
from functools import cache
|
|
16
|
-
from typing import Any, Callable, Optional, cast
|
|
16
|
+
from typing import Any, Callable, Optional, cast, Awaitable
|
|
17
17
|
from uuid import UUID
|
|
18
18
|
|
|
19
19
|
import betterproto
|
|
@@ -212,7 +212,8 @@ class Mammotion(object):
|
|
|
212
212
|
"""Represents a Mammotion device."""
|
|
213
213
|
|
|
214
214
|
devices = MammotionDevices()
|
|
215
|
-
|
|
215
|
+
cloud_client: CloudIOTGateway | None = None
|
|
216
|
+
mqtt: MammotionMQTT | None = None
|
|
216
217
|
|
|
217
218
|
|
|
218
219
|
|
|
@@ -229,25 +230,25 @@ class Mammotion(object):
|
|
|
229
230
|
self._preference = preference
|
|
230
231
|
|
|
231
232
|
async def initiate_cloud_connection(self, cloud_client: CloudIOTGateway) -> None:
|
|
232
|
-
if self.
|
|
233
|
-
if self.
|
|
233
|
+
if self.mqtt is not None:
|
|
234
|
+
if self.mqtt.is_connected:
|
|
234
235
|
return
|
|
235
236
|
|
|
236
|
-
|
|
237
|
-
self.
|
|
237
|
+
self.cloud_client = cloud_client
|
|
238
|
+
self.mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId,
|
|
238
239
|
product_key=cloud_client._aep_response.data.productKey,
|
|
239
240
|
device_name=cloud_client._aep_response.data.deviceName,
|
|
240
241
|
device_secret=cloud_client._aep_response.data.deviceSecret,
|
|
241
242
|
iot_token=cloud_client._session_by_authcode_response.data.iotToken,
|
|
242
243
|
client_id=cloud_client._client_id)
|
|
243
244
|
|
|
244
|
-
self.
|
|
245
|
+
self.mqtt._cloud_client = cloud_client
|
|
245
246
|
loop = asyncio.get_running_loop()
|
|
246
|
-
await loop.run_in_executor(None, self.
|
|
247
|
+
await loop.run_in_executor(None, self.mqtt.connect_async)
|
|
247
248
|
|
|
248
249
|
for device in cloud_client.listing_dev_by_account_response.data.data:
|
|
249
250
|
if device.deviceName.startswith(("Luba-", "Yuka-")):
|
|
250
|
-
self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=self.
|
|
251
|
+
self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=self.mqtt))
|
|
251
252
|
|
|
252
253
|
def set_disconnect_strategy(self, disconnect: bool):
|
|
253
254
|
for device_name, device in self.devices.devices:
|
|
@@ -338,8 +339,8 @@ class MammotionBaseDevice:
|
|
|
338
339
|
self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE)
|
|
339
340
|
self._mower = device
|
|
340
341
|
self._state_manager = StateManager(self._mower)
|
|
341
|
-
self._state_manager.gethash_ack_callback
|
|
342
|
-
self._state_manager.get_commondata_ack_callback
|
|
342
|
+
self._state_manager.gethash_ack_callback = self.datahash_response
|
|
343
|
+
self._state_manager.get_commondata_ack_callback = self.commdata_response
|
|
343
344
|
self._notify_future: asyncio.Future[bytes] | None = None
|
|
344
345
|
|
|
345
346
|
async def datahash_response(self, hash_ack: NavGetHashListAck):
|
|
@@ -947,8 +948,9 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
947
948
|
self._command_futures = {}
|
|
948
949
|
self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName)
|
|
949
950
|
self.currentID = ""
|
|
950
|
-
self.on_ready_callback: Optional[Callable[[], None]] = None
|
|
951
|
+
self.on_ready_callback: Optional[Callable[[], Awaitable[None]]] = None
|
|
951
952
|
self._waiting_queue = deque()
|
|
953
|
+
self._operation_lock = threading.Lock()
|
|
952
954
|
|
|
953
955
|
self._mqtt_client.on_connected = self.on_connected
|
|
954
956
|
self._mqtt_client.on_disconnected = self.on_disconnected
|
|
@@ -998,7 +1000,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
998
1000
|
|
|
999
1001
|
async def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None:
|
|
1000
1002
|
"""Handle incoming MQTT messages."""
|
|
1001
|
-
_LOGGER.debug("MQTT message received on topic %s: %s", topic, payload, iot_id)
|
|
1003
|
+
_LOGGER.debug("MQTT message received on topic %s: %s, iot_id: %s", topic, payload, iot_id)
|
|
1002
1004
|
|
|
1003
1005
|
json_str = json.dumps(payload)
|
|
1004
1006
|
payload = json.loads(json_str)
|
|
@@ -1007,13 +1009,18 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
1007
1009
|
|
|
1008
1010
|
async def _send_command(self, key: str, retry: int | None = None) -> bytes | None:
|
|
1009
1011
|
"""Send command to device via MQTT and read response."""
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1012
|
+
if self._operation_lock.locked():
|
|
1013
|
+
_LOGGER.debug(
|
|
1014
|
+
"%s: Operation already in progress, waiting for it to complete;",
|
|
1015
|
+
self.device.nickName
|
|
1016
|
+
)
|
|
1017
|
+
with self._operation_lock:
|
|
1018
|
+
try:
|
|
1019
|
+
command_bytes = getattr(self._commands, key)()
|
|
1020
|
+
return await self._send_command_locked(key, command_bytes)
|
|
1021
|
+
except Exception as ex:
|
|
1022
|
+
_LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex)
|
|
1023
|
+
raise
|
|
1017
1024
|
|
|
1018
1025
|
async def _send_command_locked(self, key: str, command: bytes) -> bytes:
|
|
1019
1026
|
"""Send command to device and read response."""
|
|
@@ -1049,12 +1056,18 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
1049
1056
|
|
|
1050
1057
|
async def _send_command_with_args(self, key: str, **kwargs: any) -> bytes | None:
|
|
1051
1058
|
"""Send command with arguments to device via MQTT and read response."""
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1059
|
+
if self._operation_lock.locked():
|
|
1060
|
+
_LOGGER.debug(
|
|
1061
|
+
"%s: Operation already in progress, waiting for it to complete;",
|
|
1062
|
+
self.device.nickName
|
|
1063
|
+
)
|
|
1064
|
+
with self._operation_lock:
|
|
1065
|
+
try:
|
|
1066
|
+
command_bytes = getattr(self._commands, key)(**kwargs)
|
|
1067
|
+
return await self._send_command_locked(key, command_bytes)
|
|
1068
|
+
except Exception as ex:
|
|
1069
|
+
_LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex)
|
|
1070
|
+
raise
|
|
1058
1071
|
|
|
1059
1072
|
def _extract_message_id(self, payload: dict) -> str:
|
|
1060
1073
|
"""Extract the message ID from the payload."""
|
|
@@ -1091,6 +1104,8 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
1091
1104
|
|
|
1092
1105
|
if len(self._waiting_queue) > 0:
|
|
1093
1106
|
fut: MammotionFuture = self._waiting_queue.popleft()
|
|
1107
|
+
while fut.fut.cancelled():
|
|
1108
|
+
fut: MammotionFuture = self._waiting_queue.popleft()
|
|
1094
1109
|
fut.resolve(cast(bytes, binary_data))
|
|
1095
1110
|
await self._state_manager.notification(new_msg)
|
|
1096
1111
|
|
|
@@ -1102,3 +1117,4 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
1102
1117
|
"""Disconnect the MQTT client."""
|
|
1103
1118
|
self._mqtt_client.disconnect()
|
|
1104
1119
|
|
|
1120
|
+
|
|
@@ -80,6 +80,8 @@ class MammotionMQTT:
|
|
|
80
80
|
def connect_async(self):
|
|
81
81
|
"""Connect async to MQTT Server."""
|
|
82
82
|
logger.info("Connecting...")
|
|
83
|
+
if self._linkkit_client.check_state() is LinkKit.LinkKitState.INITIALIZED:
|
|
84
|
+
self._linkkit_client.thing_setup()
|
|
83
85
|
self._linkkit_client.connect_async()
|
|
84
86
|
|
|
85
87
|
|
|
@@ -121,8 +123,7 @@ class MammotionMQTT:
|
|
|
121
123
|
|
|
122
124
|
if self.on_ready:
|
|
123
125
|
self.is_ready = True
|
|
124
|
-
|
|
125
|
-
asyncio.wrap_future(future, loop=self.loop)
|
|
126
|
+
asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop).result()
|
|
126
127
|
# self._linkkit_client.query_ota_firmware()
|
|
127
128
|
# command = MammotionCommand(device_name="Luba")
|
|
128
129
|
# self._cloud_client.send_cloud_command(command.get_report_cfg())
|
|
@@ -138,8 +139,7 @@ class MammotionMQTT:
|
|
|
138
139
|
payload = json.loads(payload)
|
|
139
140
|
iot_id = payload.get("params", {}).get("iotId", "")
|
|
140
141
|
if iot_id != "" and self.on_message:
|
|
141
|
-
|
|
142
|
-
return asyncio.wrap_future(future, loop=self.loop)
|
|
142
|
+
asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop).result()
|
|
143
143
|
|
|
144
144
|
|
|
145
145
|
def _thing_on_connect(self, session_flag, rc, user_data):
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
pymammotion/__init__.py,sha256=kmnjdt3AEMejIz5JK7h1tTJj5ZriAgKwZBa3ScA4-Ao,1516
|
|
2
2
|
pymammotion/aliyun/__init__.py,sha256=T1lkX7TRYiL4nqYanG4l4MImV-SlavSbuooC-W-uUGw,29
|
|
3
|
-
pymammotion/aliyun/cloud_gateway.py,sha256=
|
|
3
|
+
pymammotion/aliyun/cloud_gateway.py,sha256=sOqewx_aRxlRGj9FvPYJ0VPmheSTDpH3e1mCAX-HGko,21788
|
|
4
4
|
pymammotion/aliyun/cloud_service.py,sha256=YWcKuKK6iRWy5mTnBYgHxcCusiRGGzQt3spSf7dGDss,2183
|
|
5
5
|
pymammotion/aliyun/dataclass/aep_response.py,sha256=8f6GIP58ve8gd6AL3HBoXxsy0n2q4ygWvjELGnoOnVc,452
|
|
6
6
|
pymammotion/aliyun/dataclass/connect_response.py,sha256=Yz-fEbDzgGPTo5Of2oAjmFkSv08T7ze80pQU4k-gKIU,824
|
|
7
7
|
pymammotion/aliyun/dataclass/dev_by_account_response.py,sha256=DtGeMCzoiFrfSniHoL6db9xcheQgmcTvGySkGBjKdKE,977
|
|
8
8
|
pymammotion/aliyun/dataclass/login_by_oauth_response.py,sha256=6TQYAMyQ1jf_trsnTST007qlNXve3BqsvpV0Dwp7CFA,1245
|
|
9
9
|
pymammotion/aliyun/dataclass/regions_response.py,sha256=CVPpdFhDD6_emWHyLRzOdp2j3HLPtP8tlNyzGnr8AcI,690
|
|
10
|
-
pymammotion/aliyun/dataclass/session_by_authcode_response.py,sha256=
|
|
10
|
+
pymammotion/aliyun/dataclass/session_by_authcode_response.py,sha256=_5vsyIxocoecEqygPTmhOQnzjwaK845ssIqbTeaLcmk,406
|
|
11
11
|
pymammotion/aliyun/tmp_constant.py,sha256=M4Hq_lrGB3LZdX6R2XohRPFoK1NDnNV-pTJwJcJ9838,6650
|
|
12
12
|
pymammotion/bluetooth/__init__.py,sha256=LAl8jqZ1fPh-3mLmViNQsP3s814C1vsocYUa6oSaXt0,36
|
|
13
13
|
pymammotion/bluetooth/ble.py,sha256=9fA8yw5xXNJRSbC68SdOMf2QJnHoD7RhhCjLFoF0c0I,2404
|
|
@@ -28,7 +28,7 @@ pymammotion/data/model/excute_boarder_params.py,sha256=kadSth4y-VXlXIZ6R-Ng-kDvB
|
|
|
28
28
|
pymammotion/data/model/execute_boarder.py,sha256=oDb2h5tFtOQIa8OCNYaDugqCgCZBLjQRzQTNVcJVAGQ,1072
|
|
29
29
|
pymammotion/data/model/generate_route_information.py,sha256=5w1MM1-gXGXb_AoEap_I5xTxAFbNSzXuqQFEkw4NmDs,4301
|
|
30
30
|
pymammotion/data/model/hash_list.py,sha256=z4y0mzbW8LQ5nsaHbMlt1y6uS4suB4D_VljAyIEjFR0,1171
|
|
31
|
-
pymammotion/data/model/location.py,sha256=
|
|
31
|
+
pymammotion/data/model/location.py,sha256=qO3G0U_eWP9alswbZXTpmYImIcXJeteBVHX1cGZGbHg,729
|
|
32
32
|
pymammotion/data/model/mowing_modes.py,sha256=2GAF-xaHpv6CSGobYoNtfSi2if8_jW0nonCqN50SNx0,665
|
|
33
33
|
pymammotion/data/model/plan.py,sha256=7JvqAo0a9Yg1Vtifd4J3Dx3StEppxrMOfmq2-877kYg,2891
|
|
34
34
|
pymammotion/data/model/rapid_state.py,sha256=_e9M-65AbkvIqXyMYzLKBxbNvpso42qD8R-JSt66THY,986
|
|
@@ -38,7 +38,7 @@ pymammotion/data/mqtt/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdr
|
|
|
38
38
|
pymammotion/data/mqtt/event.py,sha256=7hq-dl-HdKJB-TQ2kKfYfrebR_8qWE_lF2nQLlPyphU,3437
|
|
39
39
|
pymammotion/data/mqtt/properties.py,sha256=HkBPghr26L9_b4QaOi1DtPgb0UoPIOGSe9wb3kgnM6Y,2815
|
|
40
40
|
pymammotion/data/mqtt/status.py,sha256=zqnlo-MzejEQZszl0i0Wucoc3E76x6UtI9JLxoBnu54,1067
|
|
41
|
-
pymammotion/data/state_manager.py,sha256=
|
|
41
|
+
pymammotion/data/state_manager.py,sha256=mNIAhP8f1Cb6-wFm2R5e-NlZauj-71iDGz8oZvdTq3Y,2826
|
|
42
42
|
pymammotion/event/__init__.py,sha256=mgATR6vPHACNQ-0zH5fi7NdzeTCDV1CZyaWPmtUusi8,115
|
|
43
43
|
pymammotion/event/event.py,sha256=Fy5-I1p92AO_D67VW4eHQqA4pOt7MZsrP--tVfIVUz8,1820
|
|
44
44
|
pymammotion/http/_init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -58,10 +58,10 @@ pymammotion/mammotion/commands/messages/video.py,sha256=_8lJsU4sLm2CGnc7RDkueA0A
|
|
|
58
58
|
pymammotion/mammotion/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
59
|
pymammotion/mammotion/control/joystick.py,sha256=EWV20MMzQuhbLlNlXbsyZKSEpeM7x1CQL7saU4Pn0-g,6165
|
|
60
60
|
pymammotion/mammotion/devices/__init__.py,sha256=T72jt0ejtMjo1rPmn_FeMF3pmp0LLeRRpc9WcDKEYYY,126
|
|
61
|
-
pymammotion/mammotion/devices/mammotion.py,sha256=
|
|
61
|
+
pymammotion/mammotion/devices/mammotion.py,sha256=akUzxCSKPVgg2mG2-UQ6kjDHcZEz9Irs6p1Xh-_C51w,45017
|
|
62
62
|
pymammotion/mqtt/__init__.py,sha256=Ocs5e-HLJvTuDpVXyECEsWIvwsUaxzj7lZ9mSYutNDY,105
|
|
63
63
|
pymammotion/mqtt/mammotion_future.py,sha256=WKnHqeHiS2Ut-SaDBNOxqh1jDLeTiyLTsJ7PNUexrjk,687
|
|
64
|
-
pymammotion/mqtt/mammotion_mqtt.py,sha256=
|
|
64
|
+
pymammotion/mqtt/mammotion_mqtt.py,sha256=cBx-T_gsck8bEGaaE-oxdV1ADoPxJi8FKbSntRCSlVQ,8013
|
|
65
65
|
pymammotion/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
66
|
pymammotion/proto/basestation.proto,sha256=_x5gAz3FkZXS1jtq4GgZgaDCuRU-UV-7HTFdsfQ3zbo,1034
|
|
67
67
|
pymammotion/proto/basestation.py,sha256=js64_N2xQYRxWPRdVNEapO0qe7vBlfYnjW5sE8hi7hw,2026
|
|
@@ -111,7 +111,7 @@ pymammotion/utility/device_type.py,sha256=KYawu2glZMVlPmxRbA4kVFujXz3miHp3rJiOWR
|
|
|
111
111
|
pymammotion/utility/map.py,sha256=aoi-Luzuph02hKynTofMoq3mnPstanx75MDAVv49CuY,2211
|
|
112
112
|
pymammotion/utility/periodic.py,sha256=9wJMfwXPlx6Mbp3Fws7LLTI34ZDKphH1bva_Ggyk32g,3281
|
|
113
113
|
pymammotion/utility/rocker_util.py,sha256=syPL0QN4zMzHiTIkUKS7RXBBptjdbkfNlPddwUD5V3A,7171
|
|
114
|
-
pymammotion-0.2.
|
|
115
|
-
pymammotion-0.2.
|
|
116
|
-
pymammotion-0.2.
|
|
117
|
-
pymammotion-0.2.
|
|
114
|
+
pymammotion-0.2.8.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
115
|
+
pymammotion-0.2.8.dist-info/METADATA,sha256=oPDS7unJxPTKajrvuT1s7MIK7c6fzKy6t-tKcLT2JP0,3968
|
|
116
|
+
pymammotion-0.2.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
117
|
+
pymammotion-0.2.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|