pymammotion 0.5.69__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/__init__.py +53 -0
- pymammotion/agora/__init__.py +0 -0
- pymammotion/agora/agora_api.py +755 -0
- pymammotion/agora/agora_rtc_capabilities.py +748 -0
- pymammotion/agora/agora_websockets.py +1175 -0
- pymammotion/aliyun/__init__.py +1 -0
- pymammotion/aliyun/client.py +235 -0
- pymammotion/aliyun/cloud_gateway.py +982 -0
- pymammotion/aliyun/model/aep_response.py +21 -0
- pymammotion/aliyun/model/connect_response.py +51 -0
- pymammotion/aliyun/model/dev_by_account_response.py +195 -0
- pymammotion/aliyun/model/login_by_oauth_response.py +64 -0
- pymammotion/aliyun/model/regions_response.py +29 -0
- pymammotion/aliyun/model/session_by_authcode_response.py +19 -0
- pymammotion/aliyun/model/thing_response.py +12 -0
- pymammotion/aliyun/regions.py +62 -0
- pymammotion/aliyun/tea/core.py +297 -0
- pymammotion/aliyun/tmp_constant.py +171 -0
- pymammotion/bluetooth/__init__.py +1 -0
- pymammotion/bluetooth/ble.py +62 -0
- pymammotion/bluetooth/ble_message.py +676 -0
- pymammotion/bluetooth/const.py +27 -0
- pymammotion/bluetooth/data/__init__.py +0 -0
- pymammotion/bluetooth/data/convert.py +25 -0
- pymammotion/bluetooth/data/framectrldata.py +40 -0
- pymammotion/bluetooth/data/notifydata.py +62 -0
- pymammotion/bluetooth/model/__init__.py +0 -0
- pymammotion/bluetooth/model/atomic_integer.py +54 -0
- pymammotion/const.py +13 -0
- pymammotion/data/__init__.py +0 -0
- pymammotion/data/model/__init__.py +8 -0
- pymammotion/data/model/account.py +8 -0
- pymammotion/data/model/device.py +192 -0
- pymammotion/data/model/device_config.py +72 -0
- pymammotion/data/model/device_info.py +60 -0
- pymammotion/data/model/device_limits.py +49 -0
- pymammotion/data/model/enums.py +77 -0
- pymammotion/data/model/errors.py +12 -0
- pymammotion/data/model/events.py +14 -0
- pymammotion/data/model/generate_geojson.py +565 -0
- pymammotion/data/model/generate_route_information.py +26 -0
- pymammotion/data/model/hash_list.py +475 -0
- pymammotion/data/model/location.py +36 -0
- pymammotion/data/model/mowing_modes.py +77 -0
- pymammotion/data/model/rapid_state.py +45 -0
- pymammotion/data/model/raw_data.py +215 -0
- pymammotion/data/model/region_data.py +102 -0
- pymammotion/data/model/report_info.py +182 -0
- pymammotion/data/model/work.py +27 -0
- pymammotion/data/mower_state_manager.py +369 -0
- pymammotion/data/mqtt/__init__.py +1 -0
- pymammotion/data/mqtt/event.py +227 -0
- pymammotion/data/mqtt/mammotion_properties.py +276 -0
- pymammotion/data/mqtt/properties.py +203 -0
- pymammotion/data/mqtt/status.py +57 -0
- pymammotion/event/__init__.py +6 -0
- pymammotion/event/event.py +96 -0
- pymammotion/homeassistant/__init__.py +3 -0
- pymammotion/homeassistant/mower_api.py +514 -0
- pymammotion/homeassistant/rtk_api.py +54 -0
- pymammotion/http/__init__.py +0 -0
- pymammotion/http/encryption.py +220 -0
- pymammotion/http/http.py +673 -0
- pymammotion/http/model/__init__.py +0 -0
- pymammotion/http/model/camera_stream.py +31 -0
- pymammotion/http/model/http.py +249 -0
- pymammotion/http/model/response_factory.py +61 -0
- pymammotion/http/model/rtk.py +16 -0
- pymammotion/mammotion/__init__.py +0 -0
- pymammotion/mammotion/commands/__init__.py +0 -0
- pymammotion/mammotion/commands/abstract_message.py +24 -0
- pymammotion/mammotion/commands/mammotion_command.py +81 -0
- pymammotion/mammotion/commands/messages/__init__.py +0 -0
- pymammotion/mammotion/commands/messages/basestation.py +43 -0
- pymammotion/mammotion/commands/messages/driver.py +122 -0
- pymammotion/mammotion/commands/messages/media.py +87 -0
- pymammotion/mammotion/commands/messages/navigation.py +564 -0
- pymammotion/mammotion/commands/messages/network.py +205 -0
- pymammotion/mammotion/commands/messages/ota.py +38 -0
- pymammotion/mammotion/commands/messages/system.py +330 -0
- pymammotion/mammotion/commands/messages/video.py +33 -0
- pymammotion/mammotion/control/__init__.py +0 -0
- pymammotion/mammotion/control/joystick.py +145 -0
- pymammotion/mammotion/devices/__init__.py +29 -0
- pymammotion/mammotion/devices/base.py +163 -0
- pymammotion/mammotion/devices/mammotion.py +571 -0
- pymammotion/mammotion/devices/mammotion_bluetooth.py +496 -0
- pymammotion/mammotion/devices/mammotion_cloud.py +355 -0
- pymammotion/mammotion/devices/mammotion_mower_ble.py +48 -0
- pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
- pymammotion/mammotion/devices/managers/managers.py +81 -0
- pymammotion/mammotion/devices/mower_device.py +120 -0
- pymammotion/mammotion/devices/mower_manager.py +107 -0
- pymammotion/mammotion/devices/rtk_ble.py +89 -0
- pymammotion/mammotion/devices/rtk_cloud.py +115 -0
- pymammotion/mammotion/devices/rtk_device.py +50 -0
- pymammotion/mammotion/devices/rtk_manager.py +125 -0
- pymammotion/mqtt/__init__.py +6 -0
- pymammotion/mqtt/aliyun_mqtt.py +237 -0
- pymammotion/mqtt/linkkit/__init__.py +5 -0
- pymammotion/mqtt/linkkit/h2client.py +585 -0
- pymammotion/mqtt/linkkit/linkkit.py +3025 -0
- pymammotion/mqtt/mammotion_future.py +26 -0
- pymammotion/mqtt/mammotion_mqtt.py +214 -0
- pymammotion/mqtt/mqtt_models.py +66 -0
- pymammotion/proto/__init__.py +4841 -0
- pymammotion/proto/basestation.proto +51 -0
- pymammotion/proto/basestation_pb2.py +35 -0
- pymammotion/proto/basestation_pb2.pyi +89 -0
- pymammotion/proto/common.proto +7 -0
- pymammotion/proto/common_pb2.py +25 -0
- pymammotion/proto/common_pb2.pyi +13 -0
- pymammotion/proto/dev_net.proto +321 -0
- pymammotion/proto/dev_net_pb2.py +111 -0
- pymammotion/proto/dev_net_pb2.pyi +515 -0
- pymammotion/proto/luba_msg.proto +76 -0
- pymammotion/proto/luba_msg_pb2.py +41 -0
- pymammotion/proto/luba_msg_pb2.pyi +97 -0
- pymammotion/proto/luba_mul.proto +129 -0
- pymammotion/proto/luba_mul_pb2.py +61 -0
- pymammotion/proto/luba_mul_pb2.pyi +178 -0
- pymammotion/proto/mctrl_driver.proto +107 -0
- pymammotion/proto/mctrl_driver_pb2.py +57 -0
- pymammotion/proto/mctrl_driver_pb2.pyi +167 -0
- pymammotion/proto/mctrl_nav.proto +591 -0
- pymammotion/proto/mctrl_nav_pb2.py +136 -0
- pymammotion/proto/mctrl_nav_pb2.pyi +1067 -0
- pymammotion/proto/mctrl_ota.proto +80 -0
- pymammotion/proto/mctrl_ota_pb2.py +45 -0
- pymammotion/proto/mctrl_ota_pb2.pyi +128 -0
- pymammotion/proto/mctrl_pept.proto +34 -0
- pymammotion/proto/mctrl_pept_pb2.py +33 -0
- pymammotion/proto/mctrl_pept_pb2.pyi +58 -0
- pymammotion/proto/mctrl_sys.proto +741 -0
- pymammotion/proto/mctrl_sys_pb2.py +206 -0
- pymammotion/proto/mctrl_sys_pb2.pyi +1213 -0
- pymammotion/proto/message_pool.py +3 -0
- pymammotion/proto/py.typed +0 -0
- pymammotion/py.typed +0 -0
- pymammotion/utility/constant/__init__.py +3 -0
- pymammotion/utility/constant/device_constant.py +315 -0
- pymammotion/utility/conversions.py +5 -0
- pymammotion/utility/datatype_converter.py +124 -0
- pymammotion/utility/device_config.py +755 -0
- pymammotion/utility/device_type.py +489 -0
- pymammotion/utility/map.py +259 -0
- pymammotion/utility/movement.py +18 -0
- pymammotion/utility/mur_mur_hash.py +159 -0
- pymammotion/utility/periodic.py +106 -0
- pymammotion/utility/rocker_util.py +194 -0
- pymammotion-0.5.69.dist-info/METADATA +93 -0
- pymammotion-0.5.69.dist-info/RECORD +154 -0
- pymammotion-0.5.69.dist-info/WHEEL +4 -0
- pymammotion-0.5.69.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import socket
|
|
5
|
+
import ssl
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any
|
|
8
|
+
from urllib.parse import urlencode, urlparse
|
|
9
|
+
|
|
10
|
+
import aiohttp
|
|
11
|
+
import certifi
|
|
12
|
+
from requests import PreparedRequest, adapters, status_codes
|
|
13
|
+
from Tea.exceptions import RequiredArgumentException, RetryError
|
|
14
|
+
from Tea.model import TeaModel
|
|
15
|
+
from Tea.request import TeaRequest
|
|
16
|
+
from Tea.response import TeaResponse
|
|
17
|
+
from Tea.stream import BaseStream
|
|
18
|
+
|
|
19
|
+
DEFAULT_CONNECT_TIMEOUT = 5000
|
|
20
|
+
DEFAULT_READ_TIMEOUT = 10000
|
|
21
|
+
DEFAULT_POOL_SIZE = 10
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger("alibabacloud-tea")
|
|
24
|
+
logger.setLevel(logging.DEBUG)
|
|
25
|
+
ch = logging.StreamHandler()
|
|
26
|
+
logger.addHandler(ch)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TeaCore:
|
|
30
|
+
http_adapter = adapters.HTTPAdapter(pool_connections=DEFAULT_POOL_SIZE, pool_maxsize=DEFAULT_POOL_SIZE * 4)
|
|
31
|
+
https_adapter = adapters.HTTPAdapter(pool_connections=DEFAULT_POOL_SIZE, pool_maxsize=DEFAULT_POOL_SIZE * 4)
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def get_adapter(prefix):
|
|
35
|
+
if prefix.upper() == "HTTP":
|
|
36
|
+
return TeaCore.http_adapter
|
|
37
|
+
else:
|
|
38
|
+
return TeaCore.https_adapter
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _prepare_http_debug(request, symbol):
|
|
42
|
+
base = ""
|
|
43
|
+
for key, value in request.headers.items():
|
|
44
|
+
base += f"\n{symbol} {key} : {value}"
|
|
45
|
+
return base
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _do_http_debug(request, response) -> None:
|
|
49
|
+
# logger the request
|
|
50
|
+
url = urlparse(request.url)
|
|
51
|
+
request_base = f"\n> {request.method.upper()} {url.path + url.query} HTTP/1.1"
|
|
52
|
+
logger.debug(request_base + TeaCore._prepare_http_debug(request, ">"))
|
|
53
|
+
|
|
54
|
+
# logger the response
|
|
55
|
+
response_base = (
|
|
56
|
+
f"\n< HTTP/1.1 {response.status_code}" f" {status_codes._codes.get(response.status_code)[0].upper()}"
|
|
57
|
+
)
|
|
58
|
+
logger.debug(response_base + TeaCore._prepare_http_debug(response, "<"))
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def compose_url(request):
|
|
62
|
+
host = request.headers.get("host")
|
|
63
|
+
if not host:
|
|
64
|
+
raise RequiredArgumentException("endpoint")
|
|
65
|
+
else:
|
|
66
|
+
host = host.rstrip("/")
|
|
67
|
+
protocol = f"{request.protocol.lower()}://"
|
|
68
|
+
pathname = request.pathname
|
|
69
|
+
|
|
70
|
+
if host.startswith(("http://", "https://")):
|
|
71
|
+
protocol = ""
|
|
72
|
+
|
|
73
|
+
if request.port == 80:
|
|
74
|
+
port = ""
|
|
75
|
+
else:
|
|
76
|
+
port = f":{request.port}"
|
|
77
|
+
|
|
78
|
+
url = protocol + host + port + pathname
|
|
79
|
+
|
|
80
|
+
if request.query:
|
|
81
|
+
if "?" in url:
|
|
82
|
+
if not url.endswith("&"):
|
|
83
|
+
url += "&"
|
|
84
|
+
else:
|
|
85
|
+
url += "?"
|
|
86
|
+
|
|
87
|
+
encode_query = {}
|
|
88
|
+
for key in request.query:
|
|
89
|
+
value = request.query[key]
|
|
90
|
+
if value is not None:
|
|
91
|
+
encode_query[key] = str(value)
|
|
92
|
+
url += urlencode(encode_query)
|
|
93
|
+
return url.rstrip("?&")
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
async def async_do_action(request: TeaRequest, runtime_option=None) -> TeaResponse:
|
|
97
|
+
runtime_option = runtime_option or {}
|
|
98
|
+
|
|
99
|
+
url = TeaCore.compose_url(request)
|
|
100
|
+
verify = not runtime_option.get("ignoreSSL", False)
|
|
101
|
+
|
|
102
|
+
timeout = runtime_option.get("timeout")
|
|
103
|
+
connect_timeout = runtime_option.get("connectTimeout") or timeout or DEFAULT_CONNECT_TIMEOUT
|
|
104
|
+
read_timeout = runtime_option.get("readTimeout") or timeout or DEFAULT_READ_TIMEOUT
|
|
105
|
+
|
|
106
|
+
connect_timeout, read_timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
|
|
107
|
+
|
|
108
|
+
proxy = None
|
|
109
|
+
if request.protocol.upper() == "HTTP":
|
|
110
|
+
proxy = runtime_option.get("httpProxy")
|
|
111
|
+
if not proxy:
|
|
112
|
+
proxy = os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")
|
|
113
|
+
elif request.protocol.upper() == "HTTPS":
|
|
114
|
+
proxy = runtime_option.get("httpsProxy")
|
|
115
|
+
if not proxy:
|
|
116
|
+
proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy")
|
|
117
|
+
|
|
118
|
+
connector = None
|
|
119
|
+
ca_cert = certifi.where()
|
|
120
|
+
if ca_cert and request.protocol.upper() == "HTTPS":
|
|
121
|
+
loop = asyncio.get_event_loop()
|
|
122
|
+
|
|
123
|
+
ssl_context = await loop.run_in_executor(None, ssl.create_default_context, ssl.Purpose.SERVER_AUTH)
|
|
124
|
+
await loop.run_in_executor(None, ssl_context.load_verify_locations, ca_cert)
|
|
125
|
+
connector = aiohttp.TCPConnector(
|
|
126
|
+
ssl=ssl_context,
|
|
127
|
+
family=socket.AF_INET,
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
verify = False
|
|
131
|
+
|
|
132
|
+
timeout = aiohttp.ClientTimeout(sock_read=read_timeout, sock_connect=connect_timeout)
|
|
133
|
+
async with aiohttp.ClientSession(connector=connector) as s:
|
|
134
|
+
body = b""
|
|
135
|
+
if isinstance(request.body, BaseStream):
|
|
136
|
+
for content in request.body:
|
|
137
|
+
body += content
|
|
138
|
+
elif isinstance(request.body, str):
|
|
139
|
+
body = request.body.encode("utf-8")
|
|
140
|
+
else:
|
|
141
|
+
body = request.body
|
|
142
|
+
try:
|
|
143
|
+
async with s.request(
|
|
144
|
+
request.method, url, data=body, headers=request.headers, ssl=verify, proxy=proxy, timeout=timeout
|
|
145
|
+
) as response:
|
|
146
|
+
tea_resp = TeaResponse()
|
|
147
|
+
tea_resp.body = await response.read()
|
|
148
|
+
tea_resp.headers = {k.lower(): v for k, v in response.headers.items()}
|
|
149
|
+
tea_resp.status_code = response.status
|
|
150
|
+
tea_resp.status_message = response.reason
|
|
151
|
+
tea_resp.response = response
|
|
152
|
+
except OSError as e:
|
|
153
|
+
raise RetryError(str(e))
|
|
154
|
+
return tea_resp
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def do_action(request: TeaRequest, runtime_option=None) -> TeaResponse:
|
|
158
|
+
url = TeaCore.compose_url(request)
|
|
159
|
+
|
|
160
|
+
runtime_option = runtime_option or {}
|
|
161
|
+
|
|
162
|
+
verify = not runtime_option.get("ignoreSSL", False)
|
|
163
|
+
if verify:
|
|
164
|
+
verify = runtime_option.get("ca", True) if runtime_option.get("ca", True) is not None else True
|
|
165
|
+
cert = runtime_option.get("cert", None)
|
|
166
|
+
|
|
167
|
+
timeout = runtime_option.get("timeout")
|
|
168
|
+
connect_timeout = runtime_option.get("connectTimeout") or timeout or DEFAULT_CONNECT_TIMEOUT
|
|
169
|
+
read_timeout = runtime_option.get("readTimeout") or timeout or DEFAULT_READ_TIMEOUT
|
|
170
|
+
|
|
171
|
+
timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
|
|
172
|
+
|
|
173
|
+
if isinstance(request.body, str):
|
|
174
|
+
request.body = request.body.encode("utf-8")
|
|
175
|
+
|
|
176
|
+
p = PreparedRequest()
|
|
177
|
+
p.prepare(
|
|
178
|
+
method=request.method.upper(),
|
|
179
|
+
url=url,
|
|
180
|
+
data=request.body,
|
|
181
|
+
headers=request.headers,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
proxies = {}
|
|
185
|
+
http_proxy = runtime_option.get("httpProxy")
|
|
186
|
+
https_proxy = runtime_option.get("httpsProxy")
|
|
187
|
+
no_proxy = runtime_option.get("noProxy")
|
|
188
|
+
|
|
189
|
+
if not http_proxy:
|
|
190
|
+
http_proxy = os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")
|
|
191
|
+
if not https_proxy:
|
|
192
|
+
https_proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy")
|
|
193
|
+
|
|
194
|
+
if http_proxy:
|
|
195
|
+
proxies["http"] = http_proxy
|
|
196
|
+
if https_proxy:
|
|
197
|
+
proxies["https"] = https_proxy
|
|
198
|
+
if no_proxy:
|
|
199
|
+
proxies["no_proxy"] = no_proxy
|
|
200
|
+
|
|
201
|
+
adapter = TeaCore.get_adapter(request.protocol)
|
|
202
|
+
try:
|
|
203
|
+
resp = adapter.send(
|
|
204
|
+
p,
|
|
205
|
+
proxies=proxies,
|
|
206
|
+
timeout=timeout,
|
|
207
|
+
verify=verify,
|
|
208
|
+
cert=cert,
|
|
209
|
+
)
|
|
210
|
+
except OSError as e:
|
|
211
|
+
raise RetryError(str(e))
|
|
212
|
+
|
|
213
|
+
debug = runtime_option.get("debug") or os.getenv("DEBUG")
|
|
214
|
+
if debug and debug.lower() == "sdk":
|
|
215
|
+
TeaCore._do_http_debug(p, resp)
|
|
216
|
+
|
|
217
|
+
response = TeaResponse()
|
|
218
|
+
response.status_message = resp.reason
|
|
219
|
+
response.status_code = resp.status_code
|
|
220
|
+
response.headers = {k.lower(): v for k, v in resp.headers.items()}
|
|
221
|
+
response.body = resp.content
|
|
222
|
+
response.response = resp
|
|
223
|
+
return response
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def get_response_body(resp) -> str:
|
|
227
|
+
return resp.content.decode("utf-8")
|
|
228
|
+
|
|
229
|
+
@staticmethod
|
|
230
|
+
def allow_retry(dic, retry_times, now=None) -> bool:
|
|
231
|
+
if retry_times == 0:
|
|
232
|
+
return True
|
|
233
|
+
if dic is None or not dic.__contains__("maxAttempts") or dic.get("retryable") is not True and retry_times >= 1:
|
|
234
|
+
return False
|
|
235
|
+
else:
|
|
236
|
+
retry = 0 if dic.get("maxAttempts") is None else int(dic.get("maxAttempts"))
|
|
237
|
+
return retry >= retry_times
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def get_backoff_time(dic, retry_times) -> int:
|
|
241
|
+
default_back_off_time = 0
|
|
242
|
+
if dic is None or not dic.get("policy") or dic.get("policy") == "no":
|
|
243
|
+
return default_back_off_time
|
|
244
|
+
|
|
245
|
+
back_off_time = dic.get("period", default_back_off_time)
|
|
246
|
+
if not isinstance(back_off_time, int) and not (isinstance(back_off_time, str) and back_off_time.isdigit()):
|
|
247
|
+
return default_back_off_time
|
|
248
|
+
|
|
249
|
+
back_off_time = int(back_off_time)
|
|
250
|
+
if back_off_time < 0:
|
|
251
|
+
return retry_times
|
|
252
|
+
|
|
253
|
+
return back_off_time
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
async def sleep_async(t) -> None:
|
|
257
|
+
await asyncio.sleep(t)
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def sleep(t) -> None:
|
|
261
|
+
time.sleep(t)
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def is_retryable(ex) -> bool:
|
|
265
|
+
return isinstance(ex, RetryError)
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def bytes_readable(body):
|
|
269
|
+
return body
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
def merge(*dic_list) -> dict:
|
|
273
|
+
dic_result = {}
|
|
274
|
+
for item in dic_list:
|
|
275
|
+
if isinstance(item, dict):
|
|
276
|
+
dic_result.update(item)
|
|
277
|
+
elif isinstance(item, TeaModel):
|
|
278
|
+
dic_result.update(item.to_map())
|
|
279
|
+
return dic_result
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def to_map(model: TeaModel | None) -> dict[str, Any]:
|
|
283
|
+
if isinstance(model, TeaModel):
|
|
284
|
+
return model.to_map()
|
|
285
|
+
else:
|
|
286
|
+
return dict()
|
|
287
|
+
|
|
288
|
+
@staticmethod
|
|
289
|
+
def from_map(model: TeaModel, dic: dict[str, Any]) -> TeaModel:
|
|
290
|
+
if isinstance(model, TeaModel):
|
|
291
|
+
try:
|
|
292
|
+
return model.from_map(dic)
|
|
293
|
+
except Exception:
|
|
294
|
+
model._map = dic
|
|
295
|
+
return model
|
|
296
|
+
else:
|
|
297
|
+
return model
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
class tmp_constant:
|
|
2
|
+
ALGECC = "ecc"
|
|
3
|
+
ALGHMACMD5 = "hmacmd5"
|
|
4
|
+
ALGNONE = "none"
|
|
5
|
+
ALINK_SECURE_URI_SCHEME = "coaps"
|
|
6
|
+
ALINK_URI_SCHEME = "coap"
|
|
7
|
+
AUTH_VER = "1.0"
|
|
8
|
+
CONFIG_TYPE_CLIENT = "ClientAuthInfo"
|
|
9
|
+
CONFIG_TYPE_PROVISION_RECEIVER = "SetupData"
|
|
10
|
+
CONFIG_TYPE_SERVER = "ServerAuthInfo"
|
|
11
|
+
DATA_KEY_DEVICENAME = "MAC"
|
|
12
|
+
DATA_KEY_DEVICE_WIFI_STATUS = "wifiEnabled"
|
|
13
|
+
DEFAULT_COAP_SECURE_PORT = 5684
|
|
14
|
+
DEFAULT_COAP_UDP_PORT = 5683
|
|
15
|
+
DEFAULT_MULTI_CAST_ADDRESS = "224.0.1.187"
|
|
16
|
+
DEVICES = "devices"
|
|
17
|
+
DEVICE_DATA_FORMAT_ALINK = "ALINK_FORMAT"
|
|
18
|
+
DEVICE_IOTID = "iotId"
|
|
19
|
+
DEVICE_MODEL = "deviceModel"
|
|
20
|
+
DEVICE_MODEL_EVENTS = "events"
|
|
21
|
+
DEVICE_MODEL_PROPERTIES = "properties"
|
|
22
|
+
DEVICE_MODEL_SERVICES = "services"
|
|
23
|
+
DEVICE_MODEL_TYPE = "model"
|
|
24
|
+
DEVICE_NAME = "deviceName"
|
|
25
|
+
DEVICE_NAME_POSITION = 2
|
|
26
|
+
DEVICE_PRODUCT_KEY = "productKey"
|
|
27
|
+
DEVICE_PROFILE = "profile"
|
|
28
|
+
EVENT_PROPERTY_URI_POST = "post"
|
|
29
|
+
EVENT_PROPERTY_URI_POST_REPLY = "post_reply"
|
|
30
|
+
EVENT_PROPERTY_URI_PRE = "thing/event/property"
|
|
31
|
+
EVENT_URI_POST = "post"
|
|
32
|
+
EVENT_URI_PRE = "thing/event"
|
|
33
|
+
EXPAND_SPLITE = "."
|
|
34
|
+
GROUP_CLOUD_ROLE_COTROLLER = "COTROLLER"
|
|
35
|
+
GROUP_CLOUD_ROLE_DEVICE = "DEVICE"
|
|
36
|
+
GROUP_DEVICE_LOCAL_STATUS_OFFLINE = 3
|
|
37
|
+
GROUP_DEVICE_LOCAL_STATUS_ONLINE = 1
|
|
38
|
+
GROUP_LOCAL_SATUS_ALLOFFLINE = 2
|
|
39
|
+
GROUP_LOCAL_SATUS_ALLONLINE = 0
|
|
40
|
+
GROUP_LOCAL_SATUS_PARTONLINE = 1
|
|
41
|
+
GROUP_OP_ADD = "add"
|
|
42
|
+
GROUP_OP_DEL = "del"
|
|
43
|
+
GROUP_ROLE_CONTROLLER = "cloud_ctl"
|
|
44
|
+
GROUP_ROLE_DEVICE = "cloud_dev"
|
|
45
|
+
GROUP_ROLE_UNKNOWN = "null"
|
|
46
|
+
HEARTBEAT_DEFAULT_TIME = 600000
|
|
47
|
+
HEARTBEAT_MAX_RETRY_TIME = 3
|
|
48
|
+
IDENTIFIER_DEVICENOTIFY = "devnotify"
|
|
49
|
+
IDENTIFIER_RAW_DATA_DOWN = "raw_data_down"
|
|
50
|
+
IDENTIFIER_SETUP = "setup"
|
|
51
|
+
IDENTITY_ADMIN = "0"
|
|
52
|
+
IDENTITY_GUEST = "1"
|
|
53
|
+
IDENTITY_PROVISION = "2"
|
|
54
|
+
KEY_CLIENT_ID = "clientId"
|
|
55
|
+
KEY_IOT_NeedRsp = "NeedRsp"
|
|
56
|
+
KEY_IOT_NeedRsp_EX = "NeedRspEx"
|
|
57
|
+
KEY_IOT_PERFORMANCE_EVENT_REQ = "req"
|
|
58
|
+
KEY_IOT_PERFORMANCE_EVENT_RES = "res"
|
|
59
|
+
KEY_IOT_PERFORMANCE_ID = "IotPerformanceId"
|
|
60
|
+
KEY_IOT_QOS = "QosLevel"
|
|
61
|
+
KEY_IOT_QOS_EX = "QosLevelEx"
|
|
62
|
+
KEY_SIGN_METHOD = "signMethod"
|
|
63
|
+
KEY_SIGN_VALUE = "sign"
|
|
64
|
+
LINK_DEFAULT_CONNECT_ID = "LINK_DEAFULT"
|
|
65
|
+
MAX_BLOCK_SIZE = 3072
|
|
66
|
+
MAX_DEVICE_MODEL_LENGTH = 3072
|
|
67
|
+
METHOD_AUTH_USER = "core.service.user"
|
|
68
|
+
METHOD_DEVICE_NOTIFY = "core.service.notify"
|
|
69
|
+
METHOD_HEART_BEAT = "core.service.heartBeat"
|
|
70
|
+
METHOD_IDENTIFIER_DEV = "dev"
|
|
71
|
+
METHOD_PROPERTY_GET = "thing.service.property.get"
|
|
72
|
+
METHOD_PROPERTY_POST = "thing.event.property.post"
|
|
73
|
+
METHOD_PROPERTY_SET = "thing.service.property.set"
|
|
74
|
+
METHOD_SERVICE_AUTH = "core.service.auth"
|
|
75
|
+
METHOD_SERVICE_AUTH_INFO = "core.service.auth_info"
|
|
76
|
+
METHOD_SERVICE_DISCOVERY = "core.service.dev"
|
|
77
|
+
METHOD_SERVICE_PRE = "thing.service."
|
|
78
|
+
METHOD_SETUP = "core.service.setup"
|
|
79
|
+
METHOD_TSL_TEMPLATE = "thing.dsltemplate.get"
|
|
80
|
+
METHOD_URI_PRE = "thing/service"
|
|
81
|
+
METHOD_USER_SERVICE = "core.service.user"
|
|
82
|
+
MODEL_TYPE_ALI_LCA_CLOUD = "4"
|
|
83
|
+
MODE_VALUE_RANDW = "rw"
|
|
84
|
+
MODE_VALUE_READ = "r"
|
|
85
|
+
MODE_VALUE_WRITE = "w"
|
|
86
|
+
MQTT_NOTIFY_TOPIC_PREFIX = "/app/down/_thing"
|
|
87
|
+
MQTT_TOPIC_EVENTS = "/app/down/thing/events"
|
|
88
|
+
MQTT_TOPIC_NOTIFY = "/app/down/_thing/event/notify"
|
|
89
|
+
MQTT_TOPIC_PREFIX = "/app/down/thing"
|
|
90
|
+
MQTT_TOPIC_PROPERTIES = "/app/down/thing/properties"
|
|
91
|
+
MQTT_TOPIC_SERVICE_REPLY = "/app/down/thing/service/invoke/reply"
|
|
92
|
+
MQTT_TOPIC_STATUS = "/app/down/thing/status"
|
|
93
|
+
OPTIONNUMREGISTRY_ACCESSKEY = 36 # deprecated
|
|
94
|
+
PATH_DISCOVERY = "/dev/core/service/dev"
|
|
95
|
+
PATH_GROUP_PRE = "/thing/service/"
|
|
96
|
+
PATH_NOTIFY = "/dev/core/service/notify"
|
|
97
|
+
PATH_SETUP = "/core/service/setup"
|
|
98
|
+
PAYLOAD_CODE = "code"
|
|
99
|
+
PRODUCT_KEY_POSITION = 1
|
|
100
|
+
PROPERTY_IDENTIFIER = "identifier"
|
|
101
|
+
PROPERTY_IDENTIFIER_GET = "get"
|
|
102
|
+
PROPERTY_IDENTIFIER_SET = "set"
|
|
103
|
+
PROPERTY_NAME = "name"
|
|
104
|
+
PROPERTY_TIME = "time"
|
|
105
|
+
PROPERTY_URI_PRE = "thing/service/property"
|
|
106
|
+
PROPERTY_VALUE = "value"
|
|
107
|
+
REQUEST_DELAYTIME = "delayTime"
|
|
108
|
+
REQUEST_ID = "id"
|
|
109
|
+
REQUEST_METHOD = "method"
|
|
110
|
+
REQUEST_PARAMS = "params"
|
|
111
|
+
REQUEST_SESSIONKEY = "sessionKey"
|
|
112
|
+
REQUEST_VERSION = "version"
|
|
113
|
+
RESULT_FAIL = "Failed"
|
|
114
|
+
RESULT_OK = "Success"
|
|
115
|
+
RRESPONSE_DATA = "data"
|
|
116
|
+
RRESPONSE_MESSAGEID = "messageId"
|
|
117
|
+
SERVICE_CALLTYPE = "callType"
|
|
118
|
+
SERVICE_DESC = "desc"
|
|
119
|
+
SERVICE_IDENTIFIER = "identifier"
|
|
120
|
+
SERVICE_INPUTDATA = "inputData"
|
|
121
|
+
SERVICE_METHOD = "method"
|
|
122
|
+
SERVICE_NAME = "name"
|
|
123
|
+
SERVICE_OUTPUTDATA = "outputData"
|
|
124
|
+
SESSION_VALID_TIME = 86400 # deprecated
|
|
125
|
+
TAG = "[Tmp]"
|
|
126
|
+
THING_WIFI_STATUS_NOTIFY = "thing/wifi/status/notify"
|
|
127
|
+
TMP_MODEL_TYPE_ALI_BREEZE = "breeze"
|
|
128
|
+
TMP_MODEL_TYPE_ALI_LCA_CLOUD = "cloudlca"
|
|
129
|
+
TMP_MODEL_TYPE_ALI_THIRD_PART = "third"
|
|
130
|
+
TMP_MODEL_TYPE_ALI_WIFI = "wifi"
|
|
131
|
+
TYPE_VALUE_ARRAY = "array"
|
|
132
|
+
TYPE_VALUE_BOOLEAN = "bool"
|
|
133
|
+
TYPE_VALUE_DATE = "date"
|
|
134
|
+
TYPE_VALUE_DOUBLE = "double"
|
|
135
|
+
TYPE_VALUE_ENUM = "enum"
|
|
136
|
+
TYPE_VALUE_FLOAT = "float"
|
|
137
|
+
TYPE_VALUE_INTEGER = "int"
|
|
138
|
+
TYPE_VALUE_STRING = "string"
|
|
139
|
+
TYPE_VALUE_STRUCT = "struct"
|
|
140
|
+
TYPE_VALUE_TEXT = "text"
|
|
141
|
+
UNKNOWN_CODE = 0 # deprecated
|
|
142
|
+
URI_ALINK_DEVICE_DATA = "alink/deviceStatus" # deprecated
|
|
143
|
+
URI_ALINK_SERVICE = "alink/service" # deprecated
|
|
144
|
+
URI_AUTH = "/auth" # deprecated
|
|
145
|
+
URI_AUTHEN_REGISTER = "/sys/{productKey}/{deviceName}/thing/authen/sub/register"
|
|
146
|
+
URI_AUTHEN_REGISTER_REPLY = "/sys/{productKey}/{deviceName}/thing/authen/sub/register_reply"
|
|
147
|
+
URI_BLACKLIST_UPDATE_POST = "/thing/lan/blacklist/update"
|
|
148
|
+
URI_BLACKLIST_UPDATE_REPLY_POST = "/thing/lan/blacklist/update_reply"
|
|
149
|
+
URI_DEVICE = "/device/core/dev"
|
|
150
|
+
URI_DEV_PATH_PRE = "dev"
|
|
151
|
+
URI_METHOD = "/device/core/method"
|
|
152
|
+
URI_MODEL = "/model"
|
|
153
|
+
URI_PATH_SPLITER = "/"
|
|
154
|
+
URI_PERMISSION = "/permission"
|
|
155
|
+
URI_PREFIX_ALCS_SERVICE = "core/service"
|
|
156
|
+
URI_PREFIX_UPDATE_POST = "/thing/lan/prefix/update"
|
|
157
|
+
URI_PREFIX_UPDATE_REPLY_POST = "/thing/lan/prefix/update_reply"
|
|
158
|
+
URI_PRODUCT_DEVICE_REPLACE = "{deviceName}"
|
|
159
|
+
URI_PRODUCT_PRODUCT_REPLACE = "{productKey}"
|
|
160
|
+
URI_PROPERTY_SET = "/thing/service/property/set"
|
|
161
|
+
URI_SYS_PATH_PRE = "sys"
|
|
162
|
+
URI_THING = "/thing"
|
|
163
|
+
URI_TOPIC_LOCALDEVICE_STATECHANGE = "group/localstatechange"
|
|
164
|
+
URI_TOPIC_REPLY_POST = "_reply"
|
|
165
|
+
URI_UPDATE_DEVICE_INFO = "/sys/{productKey}/{deviceName}/thing/deviceinfo/update"
|
|
166
|
+
URI_UPDATE_DEVICE_INFO_REPLY = "/sys/{productKey}/{deviceName}/thing/deviceinfo/update_reply"
|
|
167
|
+
URI_USER = "/user"
|
|
168
|
+
VALUE_SHA256 = "sha256"
|
|
169
|
+
VERSION = "1.0"
|
|
170
|
+
WIFI_STATUS_KEY = "status"
|
|
171
|
+
WIFI_STATUS_NOTIFY_PARAMS = "params"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .ble_message import BleMessage
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from bleak import BleakClient, BleakScanner, BLEDevice
|
|
2
|
+
from bleak.backends.characteristic import BleakGATTCharacteristic
|
|
3
|
+
|
|
4
|
+
from pymammotion.bluetooth.const import SERVICE_CHANGED_CHARACTERISTIC, UUID_NOTIFICATION_CHARACTERISTIC
|
|
5
|
+
from pymammotion.event.event import BleNotificationEvent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MammotionBLE:
|
|
9
|
+
"""Class for basic ble connections to mowers."""
|
|
10
|
+
|
|
11
|
+
client: BleakClient
|
|
12
|
+
|
|
13
|
+
def __init__(self, bleEvt: BleNotificationEvent) -> None:
|
|
14
|
+
self._bleEvt = bleEvt
|
|
15
|
+
|
|
16
|
+
async def scanForLubaAndConnect(self) -> bool:
|
|
17
|
+
scanner = BleakScanner()
|
|
18
|
+
|
|
19
|
+
def scanCallback(device, advertising_data) -> bool:
|
|
20
|
+
# TODO: do something with incoming data
|
|
21
|
+
print(device)
|
|
22
|
+
print(advertising_data)
|
|
23
|
+
if advertising_data.local_name and (
|
|
24
|
+
"Luba-" in advertising_data.local_name or "Yuka-" in advertising_data.local_name
|
|
25
|
+
):
|
|
26
|
+
return True
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
device = await scanner.find_device_by_filter(scanCallback)
|
|
30
|
+
if device is not None:
|
|
31
|
+
return await self.create_client(device)
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
async def create_client(self, device: BLEDevice):
|
|
35
|
+
self.client = BleakClient(device.address)
|
|
36
|
+
return await self.connect()
|
|
37
|
+
|
|
38
|
+
async def connect(self) -> bool:
|
|
39
|
+
return await self.client.connect() if self.client is not None else False
|
|
40
|
+
|
|
41
|
+
async def disconnect(self) -> bool:
|
|
42
|
+
return await self.client.disconnect() if self.client is not None else False
|
|
43
|
+
|
|
44
|
+
async def notification_handler(self, _characteristic: BleakGATTCharacteristic, data: bytearray) -> None:
|
|
45
|
+
"""Simple notification handler which prints the data received."""
|
|
46
|
+
await self._bleEvt.BleNotification(data)
|
|
47
|
+
|
|
48
|
+
def service_changed_handler(self, characteristic: BleakGATTCharacteristic, data: bytearray) -> None:
|
|
49
|
+
"""Simple notification handler which prints the data received."""
|
|
50
|
+
print(f"Response 2 {characteristic.description}: {data}")
|
|
51
|
+
print(data.decode("utf-8"))
|
|
52
|
+
# BlufiNotifyData
|
|
53
|
+
# run an event handler back to somewhere
|
|
54
|
+
|
|
55
|
+
async def notifications(self) -> None:
|
|
56
|
+
if self.client.is_connected:
|
|
57
|
+
await self.client.start_notify(UUID_NOTIFICATION_CHARACTERISTIC, self.notification_handler)
|
|
58
|
+
await self.client.start_notify(SERVICE_CHANGED_CHARACTERISTIC, self.service_changed_handler)
|
|
59
|
+
|
|
60
|
+
def get_client(self):
|
|
61
|
+
"""Returns the ble client."""
|
|
62
|
+
return self.client
|