pymammotion 0.4.18__tar.gz → 0.4.19__tar.gz
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-0.4.18 → pymammotion-0.4.19}/PKG-INFO +1 -1
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/client.py +2 -1
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/cloud_gateway.py +7 -1
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/model/session_by_authcode_response.py +1 -0
- pymammotion-0.4.19/pymammotion/aliyun/tea/core.py +295 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/hash_list.py +46 -9
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/navigation.py +1 -1
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/devices/base.py +37 -13
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/devices/mammotion_bluetooth.py +1 -1
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/devices/mammotion_cloud.py +5 -29
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mqtt/mammotion_mqtt.py +7 -6
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_nav_pb2.py +1 -1
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pyproject.toml +2 -2
- {pymammotion-0.4.18 → pymammotion-0.4.19}/LICENSE +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/README.md +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/model/aep_response.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/model/connect_response.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/model/dev_by_account_response.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/model/login_by_oauth_response.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/model/regions_response.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/model/stream_subscription_response.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/aliyun/tmp_constant.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/ble.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/ble_message.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/const.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/data/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/data/convert.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/data/framectrldata.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/data/notifydata.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/model/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/bluetooth/model/atomic_integer.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/const.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/account.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/device.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/device_config.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/device_info.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/device_limits.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/enums.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/excute_boarder_params.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/execute_boarder.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/generate_route_information.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/location.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/mowing_modes.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/plan.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/rapid_state.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/raw_data.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/region_data.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/model/report_info.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/mqtt/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/mqtt/event.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/mqtt/properties.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/mqtt/status.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/data/state_manager.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/event/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/event/event.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/http/_init_.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/http/encryption.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/http/http.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/http/model/http.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/abstract_message.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/mammotion_command.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/driver.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/media.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/network.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/ota.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/system.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/video.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/control/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/control/joystick.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/devices/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/devices/mammotion.py +1 -1
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mqtt/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mqtt/linkkit/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mqtt/linkkit/h2client.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mqtt/linkkit/linkkit.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mqtt/mammotion_future.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/basestation.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/basestation_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/basestation_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/common.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/common_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/common_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/dev_net.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/dev_net_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/dev_net_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/luba_msg.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/luba_msg_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/luba_msg_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/luba_mul.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/luba_mul_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/luba_mul_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_driver.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_driver_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_driver_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_nav.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_nav_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_ota.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_ota_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_ota_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_pept.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_pept_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_pept_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_sys.proto +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_sys_pb2.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/proto/mctrl_sys_pb2.pyi +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/py.typed +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/constant/__init__.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/constant/device_constant.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/conversions.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/datatype_converter.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/device_config.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/device_type.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/map.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/movement.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/periodic.py +0 -0
- {pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/utility/rocker_util.py +0 -0
@@ -102,7 +102,6 @@ class CloudIOTGateway:
|
|
102
102
|
dev_by_account: ListingDevByAccountResponse | None = None,
|
103
103
|
) -> None:
|
104
104
|
"""Initialize the CloudIOTGateway."""
|
105
|
-
self._iot_token_issued_at = None
|
106
105
|
self.mammotion_http: MammotionHTTP | None = None
|
107
106
|
self._app_key = APP_KEY
|
108
107
|
self._app_secret = APP_SECRET
|
@@ -117,6 +116,13 @@ class CloudIOTGateway:
|
|
117
116
|
self._session_by_authcode_response = session_by_authcode_response
|
118
117
|
self._region_response = region_response
|
119
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
|
+
)
|
120
126
|
|
121
127
|
@staticmethod
|
122
128
|
def generate_random_string(length: int):
|
@@ -0,0 +1,295 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
import os
|
4
|
+
import ssl
|
5
|
+
import time
|
6
|
+
from typing import Any
|
7
|
+
from urllib.parse import urlencode, urlparse
|
8
|
+
|
9
|
+
import aiohttp
|
10
|
+
import certifi
|
11
|
+
from requests import PreparedRequest, adapters, status_codes
|
12
|
+
from Tea.exceptions import RequiredArgumentException, RetryError
|
13
|
+
from Tea.model import TeaModel
|
14
|
+
from Tea.request import TeaRequest
|
15
|
+
from Tea.response import TeaResponse
|
16
|
+
from Tea.stream import BaseStream
|
17
|
+
|
18
|
+
DEFAULT_CONNECT_TIMEOUT = 5000
|
19
|
+
DEFAULT_READ_TIMEOUT = 10000
|
20
|
+
DEFAULT_POOL_SIZE = 10
|
21
|
+
|
22
|
+
logger = logging.getLogger("alibabacloud-tea")
|
23
|
+
logger.setLevel(logging.DEBUG)
|
24
|
+
ch = logging.StreamHandler()
|
25
|
+
logger.addHandler(ch)
|
26
|
+
|
27
|
+
|
28
|
+
class TeaCore:
|
29
|
+
http_adapter = adapters.HTTPAdapter(pool_connections=DEFAULT_POOL_SIZE, pool_maxsize=DEFAULT_POOL_SIZE * 4)
|
30
|
+
https_adapter = adapters.HTTPAdapter(pool_connections=DEFAULT_POOL_SIZE, pool_maxsize=DEFAULT_POOL_SIZE * 4)
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def get_adapter(prefix):
|
34
|
+
if prefix.upper() == "HTTP":
|
35
|
+
return TeaCore.http_adapter
|
36
|
+
else:
|
37
|
+
return TeaCore.https_adapter
|
38
|
+
|
39
|
+
@staticmethod
|
40
|
+
def _prepare_http_debug(request, symbol):
|
41
|
+
base = ""
|
42
|
+
for key, value in request.headers.items():
|
43
|
+
base += f"\n{symbol} {key} : {value}"
|
44
|
+
return base
|
45
|
+
|
46
|
+
@staticmethod
|
47
|
+
def _do_http_debug(request, response) -> None:
|
48
|
+
# logger the request
|
49
|
+
url = urlparse(request.url)
|
50
|
+
request_base = f"\n> {request.method.upper()} {url.path + url.query} HTTP/1.1"
|
51
|
+
logger.debug(request_base + TeaCore._prepare_http_debug(request, ">"))
|
52
|
+
|
53
|
+
# logger the response
|
54
|
+
response_base = (
|
55
|
+
f"\n< HTTP/1.1 {response.status_code}" f" {status_codes._codes.get(response.status_code)[0].upper()}"
|
56
|
+
)
|
57
|
+
logger.debug(response_base + TeaCore._prepare_http_debug(response, "<"))
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def compose_url(request):
|
61
|
+
host = request.headers.get("host")
|
62
|
+
if not host:
|
63
|
+
raise RequiredArgumentException("endpoint")
|
64
|
+
else:
|
65
|
+
host = host.rstrip("/")
|
66
|
+
protocol = f"{request.protocol.lower()}://"
|
67
|
+
pathname = request.pathname
|
68
|
+
|
69
|
+
if host.startswith(("http://", "https://")):
|
70
|
+
protocol = ""
|
71
|
+
|
72
|
+
if request.port == 80:
|
73
|
+
port = ""
|
74
|
+
else:
|
75
|
+
port = f":{request.port}"
|
76
|
+
|
77
|
+
url = protocol + host + port + pathname
|
78
|
+
|
79
|
+
if request.query:
|
80
|
+
if "?" in url:
|
81
|
+
if not url.endswith("&"):
|
82
|
+
url += "&"
|
83
|
+
else:
|
84
|
+
url += "?"
|
85
|
+
|
86
|
+
encode_query = {}
|
87
|
+
for key in request.query:
|
88
|
+
value = request.query[key]
|
89
|
+
if value is not None:
|
90
|
+
encode_query[key] = str(value)
|
91
|
+
url += urlencode(encode_query)
|
92
|
+
return url.rstrip("?&")
|
93
|
+
|
94
|
+
@staticmethod
|
95
|
+
async def async_do_action(request: TeaRequest, runtime_option=None) -> TeaResponse:
|
96
|
+
runtime_option = runtime_option or {}
|
97
|
+
|
98
|
+
url = TeaCore.compose_url(request)
|
99
|
+
verify = not runtime_option.get("ignoreSSL", False)
|
100
|
+
|
101
|
+
timeout = runtime_option.get("timeout")
|
102
|
+
connect_timeout = runtime_option.get("connectTimeout") or timeout or DEFAULT_CONNECT_TIMEOUT
|
103
|
+
read_timeout = runtime_option.get("readTimeout") or timeout or DEFAULT_READ_TIMEOUT
|
104
|
+
|
105
|
+
connect_timeout, read_timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
|
106
|
+
|
107
|
+
proxy = None
|
108
|
+
if request.protocol.upper() == "HTTP":
|
109
|
+
proxy = runtime_option.get("httpProxy")
|
110
|
+
if not proxy:
|
111
|
+
proxy = os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")
|
112
|
+
elif request.protocol.upper() == "HTTPS":
|
113
|
+
proxy = runtime_option.get("httpsProxy")
|
114
|
+
if not proxy:
|
115
|
+
proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy")
|
116
|
+
|
117
|
+
connector = None
|
118
|
+
ca_cert = certifi.where()
|
119
|
+
if ca_cert and request.protocol.upper() == "HTTPS":
|
120
|
+
loop = asyncio.get_event_loop()
|
121
|
+
|
122
|
+
ssl_context = await loop.run_in_executor(None, ssl.create_default_context, ssl.Purpose.SERVER_AUTH)
|
123
|
+
await loop.run_in_executor(None, ssl_context.load_verify_locations, ca_cert)
|
124
|
+
connector = aiohttp.TCPConnector(
|
125
|
+
ssl=ssl_context,
|
126
|
+
)
|
127
|
+
else:
|
128
|
+
verify = False
|
129
|
+
|
130
|
+
timeout = aiohttp.ClientTimeout(sock_read=read_timeout, sock_connect=connect_timeout)
|
131
|
+
async with aiohttp.ClientSession(connector=connector) as s:
|
132
|
+
body = b""
|
133
|
+
if isinstance(request.body, BaseStream):
|
134
|
+
for content in request.body:
|
135
|
+
body += content
|
136
|
+
elif isinstance(request.body, str):
|
137
|
+
body = request.body.encode("utf-8")
|
138
|
+
else:
|
139
|
+
body = request.body
|
140
|
+
try:
|
141
|
+
async with s.request(
|
142
|
+
request.method, url, data=body, headers=request.headers, ssl=verify, proxy=proxy, timeout=timeout
|
143
|
+
) as response:
|
144
|
+
tea_resp = TeaResponse()
|
145
|
+
tea_resp.body = await response.read()
|
146
|
+
tea_resp.headers = {k.lower(): v for k, v in response.headers.items()}
|
147
|
+
tea_resp.status_code = response.status
|
148
|
+
tea_resp.status_message = response.reason
|
149
|
+
tea_resp.response = response
|
150
|
+
except OSError as e:
|
151
|
+
raise RetryError(str(e))
|
152
|
+
return tea_resp
|
153
|
+
|
154
|
+
@staticmethod
|
155
|
+
def do_action(request: TeaRequest, runtime_option=None) -> TeaResponse:
|
156
|
+
url = TeaCore.compose_url(request)
|
157
|
+
|
158
|
+
runtime_option = runtime_option or {}
|
159
|
+
|
160
|
+
verify = not runtime_option.get("ignoreSSL", False)
|
161
|
+
if verify:
|
162
|
+
verify = runtime_option.get("ca", True) if runtime_option.get("ca", True) is not None else True
|
163
|
+
cert = runtime_option.get("cert", None)
|
164
|
+
|
165
|
+
timeout = runtime_option.get("timeout")
|
166
|
+
connect_timeout = runtime_option.get("connectTimeout") or timeout or DEFAULT_CONNECT_TIMEOUT
|
167
|
+
read_timeout = runtime_option.get("readTimeout") or timeout or DEFAULT_READ_TIMEOUT
|
168
|
+
|
169
|
+
timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
|
170
|
+
|
171
|
+
if isinstance(request.body, str):
|
172
|
+
request.body = request.body.encode("utf-8")
|
173
|
+
|
174
|
+
p = PreparedRequest()
|
175
|
+
p.prepare(
|
176
|
+
method=request.method.upper(),
|
177
|
+
url=url,
|
178
|
+
data=request.body,
|
179
|
+
headers=request.headers,
|
180
|
+
)
|
181
|
+
|
182
|
+
proxies = {}
|
183
|
+
http_proxy = runtime_option.get("httpProxy")
|
184
|
+
https_proxy = runtime_option.get("httpsProxy")
|
185
|
+
no_proxy = runtime_option.get("noProxy")
|
186
|
+
|
187
|
+
if not http_proxy:
|
188
|
+
http_proxy = os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")
|
189
|
+
if not https_proxy:
|
190
|
+
https_proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy")
|
191
|
+
|
192
|
+
if http_proxy:
|
193
|
+
proxies["http"] = http_proxy
|
194
|
+
if https_proxy:
|
195
|
+
proxies["https"] = https_proxy
|
196
|
+
if no_proxy:
|
197
|
+
proxies["no_proxy"] = no_proxy
|
198
|
+
|
199
|
+
adapter = TeaCore.get_adapter(request.protocol)
|
200
|
+
try:
|
201
|
+
resp = adapter.send(
|
202
|
+
p,
|
203
|
+
proxies=proxies,
|
204
|
+
timeout=timeout,
|
205
|
+
verify=verify,
|
206
|
+
cert=cert,
|
207
|
+
)
|
208
|
+
except OSError as e:
|
209
|
+
raise RetryError(str(e))
|
210
|
+
|
211
|
+
debug = runtime_option.get("debug") or os.getenv("DEBUG")
|
212
|
+
if debug and debug.lower() == "sdk":
|
213
|
+
TeaCore._do_http_debug(p, resp)
|
214
|
+
|
215
|
+
response = TeaResponse()
|
216
|
+
response.status_message = resp.reason
|
217
|
+
response.status_code = resp.status_code
|
218
|
+
response.headers = {k.lower(): v for k, v in resp.headers.items()}
|
219
|
+
response.body = resp.content
|
220
|
+
response.response = resp
|
221
|
+
return response
|
222
|
+
|
223
|
+
@staticmethod
|
224
|
+
def get_response_body(resp) -> str:
|
225
|
+
return resp.content.decode("utf-8")
|
226
|
+
|
227
|
+
@staticmethod
|
228
|
+
def allow_retry(dic, retry_times, now=None) -> bool:
|
229
|
+
if retry_times == 0:
|
230
|
+
return True
|
231
|
+
if dic is None or not dic.__contains__("maxAttempts") or dic.get("retryable") is not True and retry_times >= 1:
|
232
|
+
return False
|
233
|
+
else:
|
234
|
+
retry = 0 if dic.get("maxAttempts") is None else int(dic.get("maxAttempts"))
|
235
|
+
return retry >= retry_times
|
236
|
+
|
237
|
+
@staticmethod
|
238
|
+
def get_backoff_time(dic, retry_times) -> int:
|
239
|
+
default_back_off_time = 0
|
240
|
+
if dic is None or not dic.get("policy") or dic.get("policy") == "no":
|
241
|
+
return default_back_off_time
|
242
|
+
|
243
|
+
back_off_time = dic.get("period", default_back_off_time)
|
244
|
+
if not isinstance(back_off_time, int) and not (isinstance(back_off_time, str) and back_off_time.isdigit()):
|
245
|
+
return default_back_off_time
|
246
|
+
|
247
|
+
back_off_time = int(back_off_time)
|
248
|
+
if back_off_time < 0:
|
249
|
+
return retry_times
|
250
|
+
|
251
|
+
return back_off_time
|
252
|
+
|
253
|
+
@staticmethod
|
254
|
+
async def sleep_async(t) -> None:
|
255
|
+
await asyncio.sleep(t)
|
256
|
+
|
257
|
+
@staticmethod
|
258
|
+
def sleep(t) -> None:
|
259
|
+
time.sleep(t)
|
260
|
+
|
261
|
+
@staticmethod
|
262
|
+
def is_retryable(ex) -> bool:
|
263
|
+
return isinstance(ex, RetryError)
|
264
|
+
|
265
|
+
@staticmethod
|
266
|
+
def bytes_readable(body):
|
267
|
+
return body
|
268
|
+
|
269
|
+
@staticmethod
|
270
|
+
def merge(*dic_list) -> dict:
|
271
|
+
dic_result = {}
|
272
|
+
for item in dic_list:
|
273
|
+
if isinstance(item, dict):
|
274
|
+
dic_result.update(item)
|
275
|
+
elif isinstance(item, TeaModel):
|
276
|
+
dic_result.update(item.to_map())
|
277
|
+
return dic_result
|
278
|
+
|
279
|
+
@staticmethod
|
280
|
+
def to_map(model: TeaModel | None) -> dict[str, Any]:
|
281
|
+
if isinstance(model, TeaModel):
|
282
|
+
return model.to_map()
|
283
|
+
else:
|
284
|
+
return dict()
|
285
|
+
|
286
|
+
@staticmethod
|
287
|
+
def from_map(model: TeaModel, dic: dict[str, Any]) -> TeaModel:
|
288
|
+
if isinstance(model, TeaModel):
|
289
|
+
try:
|
290
|
+
return model.from_map(dic)
|
291
|
+
except Exception:
|
292
|
+
model._map = dic
|
293
|
+
return model
|
294
|
+
else:
|
295
|
+
return model
|
@@ -12,6 +12,7 @@ class PathType(IntEnum):
|
|
12
12
|
AREA = 0
|
13
13
|
OBSTACLE = 1
|
14
14
|
PATH = 2
|
15
|
+
LINE = 10
|
15
16
|
DUMP = 12
|
16
17
|
SVG = 13
|
17
18
|
|
@@ -83,6 +84,32 @@ class FrameList(DataClassORJSONMixin):
|
|
83
84
|
data: list[NavGetCommData | SvgMessage] = field(default_factory=list)
|
84
85
|
|
85
86
|
|
87
|
+
@dataclass
|
88
|
+
class Plan(DataClassORJSONMixin):
|
89
|
+
sub_cmd: int = 2
|
90
|
+
version: str = ""
|
91
|
+
user_id: str = ""
|
92
|
+
device_id: str = ""
|
93
|
+
plan_id: str = ""
|
94
|
+
task_id: str = ""
|
95
|
+
start_time: str = "00:00"
|
96
|
+
knife_height: int = 0
|
97
|
+
model: int = 0
|
98
|
+
edge_mode: int = 0
|
99
|
+
route_model: int = 0
|
100
|
+
route_spacing: int = 0
|
101
|
+
ultrasonic_barrier: int = 0
|
102
|
+
total_plan_num: int = 0
|
103
|
+
speed: float = 0.0
|
104
|
+
task_name: str = ""
|
105
|
+
zone_hashs: list[str] = field(default_factory=list)
|
106
|
+
reserved: str = ""
|
107
|
+
start_date: str = ""
|
108
|
+
trigger_type: int = 0
|
109
|
+
remained_seconds: str = "-1"
|
110
|
+
toward_included_angle: int = 0
|
111
|
+
|
112
|
+
|
86
113
|
@dataclass(eq=False, repr=False)
|
87
114
|
class NavGetHashListData(DataClassORJSONMixin):
|
88
115
|
"""Dataclass for NavGetHashListData."""
|
@@ -124,8 +151,10 @@ class HashList(DataClassORJSONMixin):
|
|
124
151
|
area: dict[int, FrameList] = field(default_factory=dict) # type 0
|
125
152
|
path: dict[int, FrameList] = field(default_factory=dict) # type 2
|
126
153
|
obstacle: dict[int, FrameList] = field(default_factory=dict) # type 1
|
127
|
-
dump: dict[int, FrameList] = field(default_factory=dict) # type 12?
|
154
|
+
dump: dict[int, FrameList] = field(default_factory=dict) # type 12? / sub cmd 4
|
128
155
|
svg: dict[int, FrameList] = field(default_factory=dict) # type 13
|
156
|
+
line: dict[int, FrameList] = field(default_factory=dict) # type 10 possibly breakpoint? / sub cmd 3
|
157
|
+
plan: dict[int, Plan] = field(default_factory=dict)
|
129
158
|
area_name: list[AreaHashNameList] = field(default_factory=list)
|
130
159
|
|
131
160
|
def update_hash_lists(self, hashlist: list[int]) -> None:
|
@@ -152,6 +181,8 @@ class HashList(DataClassORJSONMixin):
|
|
152
181
|
all_hash_ids = set(self.area.keys()).union(
|
153
182
|
self.path.keys(), self.obstacle.keys(), self.dump.keys(), self.svg.keys()
|
154
183
|
)
|
184
|
+
if sub_cmd == 3:
|
185
|
+
all_hash_ids = set(self.line.keys())
|
155
186
|
return [
|
156
187
|
i
|
157
188
|
for root_list in self.root_hash_lists
|
@@ -174,7 +205,7 @@ class HashList(DataClassORJSONMixin):
|
|
174
205
|
if target_root_list is None:
|
175
206
|
return []
|
176
207
|
|
177
|
-
return self.
|
208
|
+
return self.find_missing_frames(target_root_list)
|
178
209
|
|
179
210
|
def update_root_hash_list(self, hash_list: NavGetHashListData) -> None:
|
180
211
|
target_root_list = next(
|
@@ -206,26 +237,29 @@ class HashList(DataClassORJSONMixin):
|
|
206
237
|
missing_frames = []
|
207
238
|
filtered_lists = [rl for rl in self.root_hash_lists if rl.sub_cmd == hash_ack.sub_cmd]
|
208
239
|
for root_list in filtered_lists:
|
209
|
-
missing = self.
|
240
|
+
missing = self.find_missing_frames(root_list)
|
210
241
|
if missing:
|
211
242
|
missing_frames.extend(missing)
|
212
243
|
return missing_frames
|
213
244
|
|
214
245
|
def missing_frame(self, hash_data: NavGetCommDataAck | SvgMessageAckT) -> list[int]:
|
215
246
|
if hash_data.type == PathType.AREA:
|
216
|
-
return self.
|
247
|
+
return self.find_missing_frames(self.area.get(hash_data.hash))
|
217
248
|
|
218
249
|
if hash_data.type == PathType.OBSTACLE:
|
219
|
-
return self.
|
250
|
+
return self.find_missing_frames(self.obstacle.get(hash_data.hash))
|
220
251
|
|
221
252
|
if hash_data.type == PathType.PATH:
|
222
|
-
return self.
|
253
|
+
return self.find_missing_frames(self.path.get(hash_data.hash))
|
254
|
+
|
255
|
+
if hash_data.type == PathType.LINE:
|
256
|
+
return self.find_missing_frames(self.line.get(hash_data.hash))
|
223
257
|
|
224
258
|
if hash_data.type == PathType.DUMP:
|
225
|
-
return self.
|
259
|
+
return self.find_missing_frames(self.dump.get(hash_data.hash))
|
226
260
|
|
227
261
|
if hash_data.type == PathType.SVG:
|
228
|
-
return self.
|
262
|
+
return self.find_missing_frames(self.svg.get(hash_data.data_hash))
|
229
263
|
|
230
264
|
return []
|
231
265
|
|
@@ -247,6 +281,9 @@ class HashList(DataClassORJSONMixin):
|
|
247
281
|
if hash_data.type == PathType.PATH:
|
248
282
|
return self._add_hash_data(self.path, hash_data)
|
249
283
|
|
284
|
+
if hash_data.type == PathType.LINE:
|
285
|
+
return self._add_hash_data(self.line, hash_data)
|
286
|
+
|
250
287
|
if hash_data.type == PathType.DUMP:
|
251
288
|
return self._add_hash_data(self.dump, hash_data)
|
252
289
|
|
@@ -256,7 +293,7 @@ class HashList(DataClassORJSONMixin):
|
|
256
293
|
return False
|
257
294
|
|
258
295
|
@staticmethod
|
259
|
-
def
|
296
|
+
def find_missing_frames(frame_list: FrameList | RootHashList) -> list[int]:
|
260
297
|
if frame_list is None:
|
261
298
|
return []
|
262
299
|
|
{pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/commands/messages/navigation.py
RENAMED
@@ -297,7 +297,7 @@ class MessageNavigation(AbstractMessage, ABC):
|
|
297
297
|
|
298
298
|
def synchronize_hash_data(self, hash_num: int) -> bytes:
|
299
299
|
build = MctlNav(todev_get_commondata=NavGetCommData(pver=1, action=8, hash=hash_num, sub_cmd=1))
|
300
|
-
logger.debug(f"Send command--209,hash synchronize area data hash:{
|
300
|
+
logger.debug(f"Send command--209,hash synchronize area data hash:{hash_num}")
|
301
301
|
return self.send_order_msg_nav(build)
|
302
302
|
|
303
303
|
def get_area_to_be_transferred(self) -> bytes:
|
@@ -44,19 +44,19 @@ class MammotionBaseDevice:
|
|
44
44
|
self._cloud_device = cloud_device
|
45
45
|
|
46
46
|
async def datahash_response(self, hash_ack: NavGetHashListAck) -> None:
|
47
|
-
"""Handle datahash responses."""
|
47
|
+
"""Handle datahash responses for root level hashs."""
|
48
|
+
current_frame = hash_ack.current_frame
|
48
49
|
|
49
50
|
missing_frames = self.mower.map.missing_root_hash_frame(hash_ack)
|
50
51
|
if len(missing_frames) == 0:
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
for data_hash in self.mower.map.missing_hashlist(hash_ack.sub_cmd):
|
55
|
-
await self.queue_command("synchronize_hash_data", hash_num=data_hash)
|
52
|
+
if len(self.mower.map.missing_hashlist(0)) > 0:
|
53
|
+
data_hash = self.mower.map.missing_hashlist(hash_ack.sub_cmd).pop()
|
54
|
+
await self.queue_command("synchronize_hash_data", hash_num=data_hash)
|
56
55
|
return
|
57
56
|
|
58
|
-
|
59
|
-
|
57
|
+
if current_frame != missing_frames[0] - 1:
|
58
|
+
current_frame = missing_frames[0] - 1
|
59
|
+
await self.queue_command("get_hash_response", total_frame=hash_ack.total_frame, current_frame=current_frame)
|
60
60
|
|
61
61
|
async def commdata_response(self, common_data: NavGetCommDataAck | SvgMessageAckT) -> None:
|
62
62
|
"""Handle common data responses."""
|
@@ -211,25 +211,49 @@ class MammotionBaseDevice:
|
|
211
211
|
|
212
212
|
self.mower.map.update_hash_lists(self.mower.map.hashlist)
|
213
213
|
|
214
|
+
await self.queue_command("send_todev_ble_sync", sync_type=3)
|
215
|
+
|
214
216
|
if self._cloud_device and len(self.mower.map.area_name) == 0 and not DeviceType.is_luba1(self.mower.name):
|
215
217
|
await self.queue_command("get_area_name_list", device_id=self._cloud_device.iotId)
|
216
218
|
|
217
219
|
await self.queue_command("read_plan", sub_cmd=2, plan_index=0)
|
218
220
|
|
219
|
-
|
221
|
+
if len(self.mower.map.root_hash_lists) == 0:
|
222
|
+
await self.queue_command("get_all_boundary_hash_list", sub_cmd=0)
|
223
|
+
|
224
|
+
for hash, frame in self.mower.map.area.items():
|
225
|
+
missing_frames = self.mower.map.find_missing_frames(frame)
|
226
|
+
if len(missing_frames) > 0:
|
227
|
+
del self.mower.map.area[hash]
|
228
|
+
|
229
|
+
for hash, frame in self.mower.map.path.items():
|
230
|
+
missing_frames = self.mower.map.find_missing_frames(frame)
|
231
|
+
if len(missing_frames) > 0:
|
232
|
+
del self.mower.map.path[hash]
|
233
|
+
|
234
|
+
for hash, frame in self.mower.map.obstacle.items():
|
235
|
+
missing_frames = self.mower.map.find_missing_frames(frame)
|
236
|
+
if len(missing_frames) > 0:
|
237
|
+
del self.mower.map.obstacle[hash]
|
238
|
+
|
239
|
+
# for hash, frame in self.mower.map.svg.items():
|
240
|
+
# missing_frames = self.mower.map.find_missing_frames(frame)
|
241
|
+
# if len(missing_frames) > 0:
|
242
|
+
# del self.mower.map.svg[hash]
|
243
|
+
|
220
244
|
if len(self.mower.map.missing_hashlist()) > 0:
|
221
245
|
data_hash = self.mower.map.missing_hashlist().pop()
|
222
246
|
await self.queue_command("synchronize_hash_data", hash_num=data_hash)
|
223
247
|
|
224
|
-
if len(self.mower.map.missing_hashlist(3)) > 0:
|
225
|
-
|
226
|
-
|
248
|
+
# if len(self.mower.map.missing_hashlist(3)) > 0:
|
249
|
+
# data_hash = self.mower.map.missing_hashlist(3).pop()
|
250
|
+
# await self.queue_command("synchronize_hash_data", hash_num=data_hash)
|
227
251
|
|
228
252
|
# sub_cmd 3 is job hashes??
|
229
253
|
# sub_cmd 4 is dump location (yuka)
|
230
254
|
# jobs list
|
231
255
|
#
|
232
|
-
await self.queue_command("get_all_boundary_hash_list", sub_cmd=3)
|
256
|
+
# await self.queue_command("get_all_boundary_hash_list", sub_cmd=3)
|
233
257
|
|
234
258
|
async def async_read_settings(self) -> None:
|
235
259
|
"""Read settings from device."""
|
{pymammotion-0.4.18 → pymammotion-0.4.19}/pymammotion/mammotion/devices/mammotion_bluetooth.py
RENAMED
@@ -368,7 +368,7 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
|
|
368
368
|
_LOGGER.debug("%s: Sending command: %s", self.name, key)
|
369
369
|
await self._message.post_custom_data_bytes(command)
|
370
370
|
|
371
|
-
timeout =
|
371
|
+
timeout = 1
|
372
372
|
timeout_handle = self.loop.call_at(self.loop.time() + timeout, _handle_timeout, self._notify_future)
|
373
373
|
timeout_expired = False
|
374
374
|
try:
|
@@ -5,7 +5,7 @@ from collections import deque
|
|
5
5
|
from collections.abc import Awaitable, Callable
|
6
6
|
import json
|
7
7
|
import logging
|
8
|
-
from typing import Any
|
8
|
+
from typing import Any
|
9
9
|
|
10
10
|
import betterproto
|
11
11
|
from Tea.exceptions import UnretryableException
|
@@ -20,7 +20,6 @@ from pymammotion.data.state_manager import StateManager
|
|
20
20
|
from pymammotion.event.event import DataEvent
|
21
21
|
from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
|
22
22
|
from pymammotion.mammotion.devices.base import MammotionBaseDevice
|
23
|
-
from pymammotion.mqtt.mammotion_future import MammotionFuture
|
24
23
|
from pymammotion.proto import LubaMsg, has_field
|
25
24
|
|
26
25
|
_LOGGER = logging.getLogger(__name__)
|
@@ -96,25 +95,12 @@ class MammotionCloud:
|
|
96
95
|
# Mark the task as done
|
97
96
|
self.command_queue.task_done()
|
98
97
|
|
99
|
-
async def _execute_command_locked(self, iot_id: str, key: str, command: bytes) ->
|
98
|
+
async def _execute_command_locked(self, iot_id: str, key: str, command: bytes) -> None:
|
100
99
|
"""Execute command and read response."""
|
101
100
|
assert self._mqtt_client is not None
|
102
101
|
self._key = key
|
103
102
|
_LOGGER.debug("Sending command: %s", key)
|
104
|
-
|
105
103
|
await self._mqtt_client.get_cloud_client().send_cloud_command(iot_id, command)
|
106
|
-
future = MammotionFuture(iot_id)
|
107
|
-
self._waiting_queue.append(future)
|
108
|
-
timeout = 5
|
109
|
-
try:
|
110
|
-
notify_msg = await future.async_get(timeout)
|
111
|
-
except asyncio.TimeoutError:
|
112
|
-
_LOGGER.debug("command_locked TimeoutError")
|
113
|
-
notify_msg = b""
|
114
|
-
|
115
|
-
_LOGGER.debug("%s: Message received", iot_id)
|
116
|
-
|
117
|
-
return notify_msg
|
118
104
|
|
119
105
|
async def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None:
|
120
106
|
"""Handle incoming MQTT messages."""
|
@@ -242,11 +228,11 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
242
228
|
|
243
229
|
async def _ble_sync(self) -> None:
|
244
230
|
command_bytes = self._commands.send_todev_ble_sync(3)
|
245
|
-
loop = asyncio.get_running_loop()
|
246
231
|
try:
|
247
232
|
await self._mqtt.send_command(self.iot_id, command_bytes)
|
248
233
|
except (CheckSessionException, SetupException):
|
249
|
-
self._ble_sync_task
|
234
|
+
if self._ble_sync_task:
|
235
|
+
self._ble_sync_task.cancel()
|
250
236
|
|
251
237
|
async def run_periodic_sync_task(self) -> None:
|
252
238
|
"""Send ble sync to robot."""
|
@@ -264,7 +250,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
264
250
|
160, lambda: asyncio.ensure_future(self.run_periodic_sync_task())
|
265
251
|
)
|
266
252
|
|
267
|
-
async def queue_command(self, key: str, **kwargs: Any) ->
|
253
|
+
async def queue_command(self, key: str, **kwargs: Any) -> None:
|
268
254
|
# Create a future to hold the result
|
269
255
|
_LOGGER.debug("Queueing command: %s", key)
|
270
256
|
future = asyncio.Future()
|
@@ -311,7 +297,6 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
311
297
|
self.state_manager.status(status)
|
312
298
|
|
313
299
|
async def _parse_message_for_device(self, event: ThingEventMessage) -> None:
|
314
|
-
_LOGGER.debug("_parse_message_for_device")
|
315
300
|
params = event.params
|
316
301
|
new_msg = LubaMsg()
|
317
302
|
if event.params.iotId != self.iot_id:
|
@@ -335,15 +320,6 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
335
320
|
|
336
321
|
await self._state_manager.notification(new_msg)
|
337
322
|
|
338
|
-
if len(self._mqtt.waiting_queue) > 0:
|
339
|
-
fut: MammotionFuture = self.dequeue_by_iot_id(self._mqtt.waiting_queue, self.iot_id)
|
340
|
-
if fut is None:
|
341
|
-
return
|
342
|
-
while fut is None or fut.fut.cancelled() and len(self._mqtt.waiting_queue) > 0:
|
343
|
-
fut = self.dequeue_by_iot_id(self._mqtt.waiting_queue, self.iot_id)
|
344
|
-
if fut is not None and not fut.fut.cancelled():
|
345
|
-
fut.resolve(cast(bytes, binary_data))
|
346
|
-
|
347
323
|
@property
|
348
324
|
def mqtt(self):
|
349
325
|
return self._mqtt
|
@@ -194,12 +194,13 @@ class MammotionMQTT:
|
|
194
194
|
|
195
195
|
def _on_disconnect(self, _client, _userdata) -> None:
|
196
196
|
"""Is called on disconnect."""
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
197
|
+
if self._linkkit_client.check_state() is LinkKit.LinkKitState.DISCONNECTED:
|
198
|
+
logger.info("Disconnected")
|
199
|
+
self.is_connected = False
|
200
|
+
self.is_ready = False
|
201
|
+
if self.on_disconnected:
|
202
|
+
future = asyncio.run_coroutine_threadsafe(self.on_disconnected(), self.loop)
|
203
|
+
asyncio.wrap_future(future, loop=self.loop)
|
203
204
|
|
204
205
|
def _on_message(self, _client, _userdata, message: MQTTMessage) -> None:
|
205
206
|
"""Is called when message is received."""
|