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.
Files changed (154) hide show
  1. pymammotion/__init__.py +53 -0
  2. pymammotion/agora/__init__.py +0 -0
  3. pymammotion/agora/agora_api.py +755 -0
  4. pymammotion/agora/agora_rtc_capabilities.py +748 -0
  5. pymammotion/agora/agora_websockets.py +1175 -0
  6. pymammotion/aliyun/__init__.py +1 -0
  7. pymammotion/aliyun/client.py +235 -0
  8. pymammotion/aliyun/cloud_gateway.py +982 -0
  9. pymammotion/aliyun/model/aep_response.py +21 -0
  10. pymammotion/aliyun/model/connect_response.py +51 -0
  11. pymammotion/aliyun/model/dev_by_account_response.py +195 -0
  12. pymammotion/aliyun/model/login_by_oauth_response.py +64 -0
  13. pymammotion/aliyun/model/regions_response.py +29 -0
  14. pymammotion/aliyun/model/session_by_authcode_response.py +19 -0
  15. pymammotion/aliyun/model/thing_response.py +12 -0
  16. pymammotion/aliyun/regions.py +62 -0
  17. pymammotion/aliyun/tea/core.py +297 -0
  18. pymammotion/aliyun/tmp_constant.py +171 -0
  19. pymammotion/bluetooth/__init__.py +1 -0
  20. pymammotion/bluetooth/ble.py +62 -0
  21. pymammotion/bluetooth/ble_message.py +676 -0
  22. pymammotion/bluetooth/const.py +27 -0
  23. pymammotion/bluetooth/data/__init__.py +0 -0
  24. pymammotion/bluetooth/data/convert.py +25 -0
  25. pymammotion/bluetooth/data/framectrldata.py +40 -0
  26. pymammotion/bluetooth/data/notifydata.py +62 -0
  27. pymammotion/bluetooth/model/__init__.py +0 -0
  28. pymammotion/bluetooth/model/atomic_integer.py +54 -0
  29. pymammotion/const.py +13 -0
  30. pymammotion/data/__init__.py +0 -0
  31. pymammotion/data/model/__init__.py +8 -0
  32. pymammotion/data/model/account.py +8 -0
  33. pymammotion/data/model/device.py +192 -0
  34. pymammotion/data/model/device_config.py +72 -0
  35. pymammotion/data/model/device_info.py +60 -0
  36. pymammotion/data/model/device_limits.py +49 -0
  37. pymammotion/data/model/enums.py +77 -0
  38. pymammotion/data/model/errors.py +12 -0
  39. pymammotion/data/model/events.py +14 -0
  40. pymammotion/data/model/generate_geojson.py +565 -0
  41. pymammotion/data/model/generate_route_information.py +26 -0
  42. pymammotion/data/model/hash_list.py +475 -0
  43. pymammotion/data/model/location.py +36 -0
  44. pymammotion/data/model/mowing_modes.py +77 -0
  45. pymammotion/data/model/rapid_state.py +45 -0
  46. pymammotion/data/model/raw_data.py +215 -0
  47. pymammotion/data/model/region_data.py +102 -0
  48. pymammotion/data/model/report_info.py +182 -0
  49. pymammotion/data/model/work.py +27 -0
  50. pymammotion/data/mower_state_manager.py +369 -0
  51. pymammotion/data/mqtt/__init__.py +1 -0
  52. pymammotion/data/mqtt/event.py +227 -0
  53. pymammotion/data/mqtt/mammotion_properties.py +276 -0
  54. pymammotion/data/mqtt/properties.py +203 -0
  55. pymammotion/data/mqtt/status.py +57 -0
  56. pymammotion/event/__init__.py +6 -0
  57. pymammotion/event/event.py +96 -0
  58. pymammotion/homeassistant/__init__.py +3 -0
  59. pymammotion/homeassistant/mower_api.py +514 -0
  60. pymammotion/homeassistant/rtk_api.py +54 -0
  61. pymammotion/http/__init__.py +0 -0
  62. pymammotion/http/encryption.py +220 -0
  63. pymammotion/http/http.py +673 -0
  64. pymammotion/http/model/__init__.py +0 -0
  65. pymammotion/http/model/camera_stream.py +31 -0
  66. pymammotion/http/model/http.py +249 -0
  67. pymammotion/http/model/response_factory.py +61 -0
  68. pymammotion/http/model/rtk.py +16 -0
  69. pymammotion/mammotion/__init__.py +0 -0
  70. pymammotion/mammotion/commands/__init__.py +0 -0
  71. pymammotion/mammotion/commands/abstract_message.py +24 -0
  72. pymammotion/mammotion/commands/mammotion_command.py +81 -0
  73. pymammotion/mammotion/commands/messages/__init__.py +0 -0
  74. pymammotion/mammotion/commands/messages/basestation.py +43 -0
  75. pymammotion/mammotion/commands/messages/driver.py +122 -0
  76. pymammotion/mammotion/commands/messages/media.py +87 -0
  77. pymammotion/mammotion/commands/messages/navigation.py +564 -0
  78. pymammotion/mammotion/commands/messages/network.py +205 -0
  79. pymammotion/mammotion/commands/messages/ota.py +38 -0
  80. pymammotion/mammotion/commands/messages/system.py +330 -0
  81. pymammotion/mammotion/commands/messages/video.py +33 -0
  82. pymammotion/mammotion/control/__init__.py +0 -0
  83. pymammotion/mammotion/control/joystick.py +145 -0
  84. pymammotion/mammotion/devices/__init__.py +29 -0
  85. pymammotion/mammotion/devices/base.py +163 -0
  86. pymammotion/mammotion/devices/mammotion.py +571 -0
  87. pymammotion/mammotion/devices/mammotion_bluetooth.py +496 -0
  88. pymammotion/mammotion/devices/mammotion_cloud.py +355 -0
  89. pymammotion/mammotion/devices/mammotion_mower_ble.py +48 -0
  90. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  91. pymammotion/mammotion/devices/managers/managers.py +81 -0
  92. pymammotion/mammotion/devices/mower_device.py +120 -0
  93. pymammotion/mammotion/devices/mower_manager.py +107 -0
  94. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  95. pymammotion/mammotion/devices/rtk_cloud.py +115 -0
  96. pymammotion/mammotion/devices/rtk_device.py +50 -0
  97. pymammotion/mammotion/devices/rtk_manager.py +125 -0
  98. pymammotion/mqtt/__init__.py +6 -0
  99. pymammotion/mqtt/aliyun_mqtt.py +237 -0
  100. pymammotion/mqtt/linkkit/__init__.py +5 -0
  101. pymammotion/mqtt/linkkit/h2client.py +585 -0
  102. pymammotion/mqtt/linkkit/linkkit.py +3025 -0
  103. pymammotion/mqtt/mammotion_future.py +26 -0
  104. pymammotion/mqtt/mammotion_mqtt.py +214 -0
  105. pymammotion/mqtt/mqtt_models.py +66 -0
  106. pymammotion/proto/__init__.py +4841 -0
  107. pymammotion/proto/basestation.proto +51 -0
  108. pymammotion/proto/basestation_pb2.py +35 -0
  109. pymammotion/proto/basestation_pb2.pyi +89 -0
  110. pymammotion/proto/common.proto +7 -0
  111. pymammotion/proto/common_pb2.py +25 -0
  112. pymammotion/proto/common_pb2.pyi +13 -0
  113. pymammotion/proto/dev_net.proto +321 -0
  114. pymammotion/proto/dev_net_pb2.py +111 -0
  115. pymammotion/proto/dev_net_pb2.pyi +515 -0
  116. pymammotion/proto/luba_msg.proto +76 -0
  117. pymammotion/proto/luba_msg_pb2.py +41 -0
  118. pymammotion/proto/luba_msg_pb2.pyi +97 -0
  119. pymammotion/proto/luba_mul.proto +129 -0
  120. pymammotion/proto/luba_mul_pb2.py +61 -0
  121. pymammotion/proto/luba_mul_pb2.pyi +178 -0
  122. pymammotion/proto/mctrl_driver.proto +107 -0
  123. pymammotion/proto/mctrl_driver_pb2.py +57 -0
  124. pymammotion/proto/mctrl_driver_pb2.pyi +167 -0
  125. pymammotion/proto/mctrl_nav.proto +591 -0
  126. pymammotion/proto/mctrl_nav_pb2.py +136 -0
  127. pymammotion/proto/mctrl_nav_pb2.pyi +1067 -0
  128. pymammotion/proto/mctrl_ota.proto +80 -0
  129. pymammotion/proto/mctrl_ota_pb2.py +45 -0
  130. pymammotion/proto/mctrl_ota_pb2.pyi +128 -0
  131. pymammotion/proto/mctrl_pept.proto +34 -0
  132. pymammotion/proto/mctrl_pept_pb2.py +33 -0
  133. pymammotion/proto/mctrl_pept_pb2.pyi +58 -0
  134. pymammotion/proto/mctrl_sys.proto +741 -0
  135. pymammotion/proto/mctrl_sys_pb2.py +206 -0
  136. pymammotion/proto/mctrl_sys_pb2.pyi +1213 -0
  137. pymammotion/proto/message_pool.py +3 -0
  138. pymammotion/proto/py.typed +0 -0
  139. pymammotion/py.typed +0 -0
  140. pymammotion/utility/constant/__init__.py +3 -0
  141. pymammotion/utility/constant/device_constant.py +315 -0
  142. pymammotion/utility/conversions.py +5 -0
  143. pymammotion/utility/datatype_converter.py +124 -0
  144. pymammotion/utility/device_config.py +755 -0
  145. pymammotion/utility/device_type.py +489 -0
  146. pymammotion/utility/map.py +259 -0
  147. pymammotion/utility/movement.py +18 -0
  148. pymammotion/utility/mur_mur_hash.py +159 -0
  149. pymammotion/utility/periodic.py +106 -0
  150. pymammotion/utility/rocker_util.py +194 -0
  151. pymammotion-0.5.69.dist-info/METADATA +93 -0
  152. pymammotion-0.5.69.dist-info/RECORD +154 -0
  153. pymammotion-0.5.69.dist-info/WHEEL +4 -0
  154. 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