pymammotion 0.0.37__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pymammotion might be problematic. Click here for more details.

Files changed (106) hide show
  1. pymammotion/__init__.py +43 -0
  2. pymammotion/aliyun/cloud_gateway.py +549 -0
  3. pymammotion/aliyun/cloud_service.py +65 -0
  4. pymammotion/aliyun/dataclass/aep_response.py +18 -0
  5. pymammotion/aliyun/dataclass/connect_response.py +51 -0
  6. pymammotion/aliyun/dataclass/dev_by_account_response.py +43 -0
  7. pymammotion/aliyun/dataclass/login_by_oauth_response.py +65 -0
  8. pymammotion/aliyun/dataclass/regions_response.py +26 -0
  9. pymammotion/aliyun/dataclass/session_by_authcode_response.py +18 -0
  10. pymammotion/aliyun/tmp_constant.py +175 -0
  11. pymammotion/bluetooth/__init__.py +1 -0
  12. pymammotion/bluetooth/ble.py +74 -0
  13. pymammotion/bluetooth/ble_message.py +430 -0
  14. pymammotion/bluetooth/const.py +27 -0
  15. pymammotion/bluetooth/data/__init__.py +0 -0
  16. pymammotion/bluetooth/data/convert.py +26 -0
  17. pymammotion/bluetooth/data/framectrldata.py +40 -0
  18. pymammotion/bluetooth/data/notifydata.py +63 -0
  19. pymammotion/const.py +9 -0
  20. pymammotion/data/__init__.py +0 -0
  21. pymammotion/data/model/__init__.py +8 -0
  22. pymammotion/data/model/device.py +157 -0
  23. pymammotion/data/model/enums.py +67 -0
  24. pymammotion/data/model/excute_boarder_params.py +48 -0
  25. pymammotion/data/model/execute_boarder.py +36 -0
  26. pymammotion/data/model/generate_route_information.py +133 -0
  27. pymammotion/data/model/hash_list.py +17 -0
  28. pymammotion/data/model/mowing_modes.py +37 -0
  29. pymammotion/data/model/plan.py +58 -0
  30. pymammotion/data/model/rapid_state.py +45 -0
  31. pymammotion/data/model/region_data.py +99 -0
  32. pymammotion/data/mqtt/__init__.py +1 -0
  33. pymammotion/data/mqtt/event.py +90 -0
  34. pymammotion/data/mqtt/properties.py +140 -0
  35. pymammotion/data/mqtt/status.py +52 -0
  36. pymammotion/event/__init__.py +6 -0
  37. pymammotion/event/event.py +50 -0
  38. pymammotion/http/_init_.py +0 -0
  39. pymammotion/http/http.py +76 -0
  40. pymammotion/luba/_init_.py +0 -0
  41. pymammotion/luba/base.py +52 -0
  42. pymammotion/mammotion/__init__.py +0 -0
  43. pymammotion/mammotion/commands/__init__.py +0 -0
  44. pymammotion/mammotion/commands/abstract_message.py +7 -0
  45. pymammotion/mammotion/commands/mammotion_command.py +34 -0
  46. pymammotion/mammotion/commands/messages/__init__.py +0 -0
  47. pymammotion/mammotion/commands/messages/driver.py +108 -0
  48. pymammotion/mammotion/commands/messages/media.py +36 -0
  49. pymammotion/mammotion/commands/messages/navigation.py +535 -0
  50. pymammotion/mammotion/commands/messages/network.py +236 -0
  51. pymammotion/mammotion/commands/messages/ota.py +34 -0
  52. pymammotion/mammotion/commands/messages/system.py +266 -0
  53. pymammotion/mammotion/commands/messages/video.py +27 -0
  54. pymammotion/mammotion/control/__init__.py +0 -0
  55. pymammotion/mammotion/control/joystick.py +184 -0
  56. pymammotion/mammotion/devices/__init__.py +1 -0
  57. pymammotion/mammotion/devices/luba.py +564 -0
  58. pymammotion/mqtt/mqtt.py +230 -0
  59. pymammotion/proto/__init__.py +0 -0
  60. pymammotion/proto/common.proto +7 -0
  61. pymammotion/proto/common.py +12 -0
  62. pymammotion/proto/common_pb2.py +25 -0
  63. pymammotion/proto/common_pb2.pyi +13 -0
  64. pymammotion/proto/dev_net.proto +297 -0
  65. pymammotion/proto/dev_net.py +381 -0
  66. pymammotion/proto/dev_net_pb2.py +107 -0
  67. pymammotion/proto/dev_net_pb2.pyi +472 -0
  68. pymammotion/proto/luba_msg.proto +73 -0
  69. pymammotion/proto/luba_msg.py +80 -0
  70. pymammotion/proto/luba_msg_pb2.py +40 -0
  71. pymammotion/proto/luba_msg_pb2.pyi +93 -0
  72. pymammotion/proto/luba_mul.proto +68 -0
  73. pymammotion/proto/luba_mul.py +76 -0
  74. pymammotion/proto/luba_mul_pb2.py +45 -0
  75. pymammotion/proto/luba_mul_pb2.pyi +91 -0
  76. pymammotion/proto/mctrl_driver.proto +67 -0
  77. pymammotion/proto/mctrl_driver.py +100 -0
  78. pymammotion/proto/mctrl_driver_pb2.py +45 -0
  79. pymammotion/proto/mctrl_driver_pb2.pyi +112 -0
  80. pymammotion/proto/mctrl_nav.proto +485 -0
  81. pymammotion/proto/mctrl_nav.py +589 -0
  82. pymammotion/proto/mctrl_nav_pb2.py +116 -0
  83. pymammotion/proto/mctrl_nav_pb2.pyi +875 -0
  84. pymammotion/proto/mctrl_ota.proto +42 -0
  85. pymammotion/proto/mctrl_ota.py +48 -0
  86. pymammotion/proto/mctrl_ota_pb2.py +35 -0
  87. pymammotion/proto/mctrl_ota_pb2.pyi +65 -0
  88. pymammotion/proto/mctrl_pept.proto +29 -0
  89. pymammotion/proto/mctrl_pept.py +41 -0
  90. pymammotion/proto/mctrl_pept_pb2.py +31 -0
  91. pymammotion/proto/mctrl_pept_pb2.pyi +50 -0
  92. pymammotion/proto/mctrl_sys.proto +487 -0
  93. pymammotion/proto/mctrl_sys.py +574 -0
  94. pymammotion/proto/mctrl_sys_pb2.py +142 -0
  95. pymammotion/proto/mctrl_sys_pb2.pyi +787 -0
  96. pymammotion/py.typed +0 -0
  97. pymammotion/utility/constant/__init__.py +1 -0
  98. pymammotion/utility/constant/device_constant.py +238 -0
  99. pymammotion/utility/datatype_converter.py +80 -0
  100. pymammotion/utility/device_type.py +152 -0
  101. pymammotion/utility/periodic.py +41 -0
  102. pymammotion/utility/rocker_util.py +135 -0
  103. pymammotion-0.0.37.dist-info/LICENSE +674 -0
  104. pymammotion-0.0.37.dist-info/METADATA +92 -0
  105. pymammotion-0.0.37.dist-info/RECORD +106 -0
  106. pymammotion-0.0.37.dist-info/WHEEL +4 -0
@@ -0,0 +1,43 @@
1
+ # __init__.py
2
+
3
+ # version of Luba API
4
+ # TODO export the three interface types
5
+ __version__ = "0.0.5"
6
+
7
+ import asyncio
8
+ import logging
9
+ import os
10
+
11
+ # works outside HA on its own
12
+ from pyluba.bluetooth.ble import LubaBLE
13
+ from pyluba.http.http import LubaHTTP, connect_http
14
+
15
+ # TODO make a working device that will work outside HA too.
16
+ from pyluba.mammotion.devices import MammotionBaseBLEDevice
17
+ from pyluba.mqtt.mqtt import LubaMQTT, logger
18
+
19
+ # TODO provide interface to pick between mqtt/cloud/bluetooth
20
+
21
+ if __name__ == "__main__":
22
+ """Values are generated from calls to aliyun APIs, can find what order is required in the login_test.py."""
23
+ logging.basicConfig(level=logging.DEBUG)
24
+ logger.getChild("paho").setLevel(logging.WARNING)
25
+ PRODUCT_KEY = os.environ.get("PRODUCT_KEY")
26
+ DEVICE_NAME = os.environ.get("DEVICE_NAME")
27
+ DEVICE_SECRET = os.environ.get("DEVICE_SECRET")
28
+ CLIENT_ID = os.environ.get("CLIENT_ID")
29
+ IOT_TOKEN = os.environ.get("IOT_TOKEN")
30
+ REGION = os.environ.get("REGION")
31
+ luba = LubaMQTT(
32
+ iot_token=IOT_TOKEN,
33
+ region_id=REGION,
34
+ product_key=PRODUCT_KEY,
35
+ device_name=DEVICE_NAME,
36
+ device_secret=DEVICE_SECRET,
37
+ client_id=CLIENT_ID,
38
+ )
39
+ luba.connect_async()
40
+
41
+ event_loop = asyncio.new_event_loop()
42
+ asyncio.set_event_loop(event_loop)
43
+ event_loop.run_forever()
@@ -0,0 +1,549 @@
1
+ import base64
2
+ import hashlib
3
+ import hmac
4
+ import itertools
5
+ import json
6
+ import random
7
+ import string
8
+ import time
9
+ import uuid
10
+ from logging import getLogger
11
+
12
+ from aiohttp import ClientSession
13
+ from alibabacloud_iot_api_gateway.client import Client
14
+ from alibabacloud_iot_api_gateway.models import CommonParams, Config, IoTApiRequest
15
+ from alibabacloud_tea_util.client import Client as UtilClient
16
+ from alibabacloud_tea_util.models import RuntimeOptions
17
+
18
+ from pyluba.aliyun.dataclass.aep_response import AepResponse
19
+ from pyluba.aliyun.dataclass.connect_response import ConnectResponse
20
+ from pyluba.aliyun.dataclass.dev_by_account_response import ListingDevByAccountResponse
21
+ from pyluba.aliyun.dataclass.login_by_oauth_response import LoginByOAuthResponse
22
+ from pyluba.aliyun.dataclass.regions_response import RegionResponse
23
+ from pyluba.aliyun.dataclass.session_by_authcode_response import (
24
+ SessionByAuthCodeResponse,
25
+ )
26
+ from pyluba.const import ALIYUN_DOMAIN, APP_KEY, APP_SECRET, APP_VERSION
27
+ from pyluba.utility.datatype_converter import DatatypeConverter
28
+
29
+ logger = getLogger(__name__)
30
+
31
+ # init client
32
+
33
+
34
+ MOVE_HEADERS = (
35
+ "x-ca-signature",
36
+ "x-ca-signature-headers",
37
+ "accept",
38
+ "content-md5",
39
+ "content-type",
40
+ "date",
41
+ "host",
42
+ "token",
43
+ "user-agent",
44
+ )
45
+
46
+
47
+ class CloudIOTGateway:
48
+ _client_id = ""
49
+ _device_sn = ""
50
+ _utdid = ""
51
+
52
+ _connect_response = None
53
+ _login_by_oauth_response = None
54
+ _aep_response = None
55
+ _session_by_authcode_response = None
56
+ _listing_dev_by_account_response = None
57
+ _region = None
58
+
59
+ converter = DatatypeConverter()
60
+
61
+ def __init__(self):
62
+ self._app_key = APP_KEY
63
+ self._app_secret = APP_SECRET
64
+ self.domain = ALIYUN_DOMAIN
65
+
66
+ self._client_id = self.generate_hardware_string(8) # 8 charatters
67
+ self._device_sn = self.generate_hardware_string(32) # 32 charatters
68
+ self._utdid = self.generate_hardware_string(32) # 32 charatters
69
+
70
+ @staticmethod
71
+ def generate_random_string(length):
72
+ characters = string.ascii_letters + string.digits
73
+ random_string = "".join(random.choice(characters) for _ in range(length))
74
+ return random_string
75
+
76
+ @staticmethod
77
+ def generate_hardware_string(length) -> str:
78
+ """Generate hardware string that is consistent per device."""
79
+ hashed_uuid = hashlib.sha1(f"{uuid.getnode()}".encode()).hexdigest()
80
+ return "".join(itertools.islice(itertools.cycle(hashed_uuid), length))
81
+
82
+ def sign(self, data):
83
+ keys = ["appKey", "clientId", "deviceSn", "timestamp"]
84
+ concatenated_str = ""
85
+ for key in keys:
86
+ concatenated_str += f"{key}{data.get(key, '')}"
87
+
88
+ logger.debug(f"sign(), toSignStr = {concatenated_str}")
89
+
90
+ sign = hmac.new(
91
+ self._app_secret.encode("utf-8"),
92
+ concatenated_str.encode("utf-8"),
93
+ hashlib.sha1,
94
+ ).hexdigest()
95
+
96
+ return sign
97
+
98
+ def get_region(self, country_code: str, auth_code: str):
99
+ # shim out the regions?
100
+ # https://api.link.aliyun.com/living/account/region/get?x-ca-request-id=59abc767-fbbc-4333-9127-e65d792133a8
101
+ # x-ca-request-id is a random UUID on each request
102
+
103
+ config = Config(
104
+ app_key=self._app_key, # correct
105
+ app_secret=self._app_secret,
106
+ domain=self.domain,
107
+ )
108
+ client = Client(config)
109
+
110
+ # build request
111
+ request = CommonParams(api_ver="1.0.2", language="en-US")
112
+ body = IoTApiRequest(
113
+ id=str(uuid.uuid4()),
114
+ params={
115
+ "authCode": auth_code,
116
+ "type": "THIRD_AUTHCODE",
117
+ "countryCode": country_code,
118
+ },
119
+ request=request,
120
+ version="1.0",
121
+ )
122
+
123
+ # send request
124
+ # possibly need to do this ourselves
125
+ response = client.do_request(
126
+ "/living/account/region/get", "https", "POST", None, body, RuntimeOptions()
127
+ )
128
+ logger.debug(response.status_message)
129
+ logger.debug(response.headers)
130
+ logger.debug(response.status_code)
131
+ logger.debug(response.body)
132
+
133
+ # Decodifica il corpo della risposta
134
+ response_body_str = response.body.decode("utf-8")
135
+
136
+ # Carica la stringa JSON in un dizionario
137
+ response_body_dict = json.loads(response_body_str)
138
+
139
+ if int(response_body_dict.get("code")) != 200:
140
+ raise Exception("Error in getting regions: " + response_body_dict["msg"])
141
+ else:
142
+ self._region = RegionResponse.from_dict(response_body_dict)
143
+ logger.debug("Endpoint : " + self._region.data.mqttEndpoint)
144
+
145
+ return response.body
146
+
147
+ def aep_handle(self):
148
+ # https://api.link.aliyun.com/app/aepauth/handle
149
+ aep_domain = self.domain
150
+
151
+ if self._region.data.apiGatewayEndpoint is not None:
152
+ aep_domain = self._region.data.apiGatewayEndpoint
153
+
154
+ config = Config(
155
+ app_key=self._app_key, # correct
156
+ app_secret=self._app_secret,
157
+ domain=aep_domain,
158
+ )
159
+ client = Client(config)
160
+
161
+ request = CommonParams(api_ver="1.0.0", language="en-US")
162
+ logger.debug("client id ", self._client_id)
163
+ time_now = time.time()
164
+ data_to_sign = {
165
+ "appKey": self._app_key,
166
+ "clientId": self._client_id, # needs to be unique to device
167
+ "deviceSn": self._device_sn, # same here
168
+ "timestamp": str(time_now),
169
+ }
170
+
171
+ body = IoTApiRequest(
172
+ id=str(uuid.uuid4()),
173
+ params={
174
+ "authInfo": {
175
+ "clientId": self._client_id,
176
+ "sign": self.sign(data_to_sign),
177
+ "deviceSn": self._device_sn,
178
+ "timestamp": str(time_now),
179
+ }
180
+ },
181
+ request=request,
182
+ version="1.0",
183
+ )
184
+
185
+ # send request
186
+ # possibly need to do this ourselves
187
+ response = client.do_request(
188
+ "/app/aepauth/handle", "https", "POST", None, body, RuntimeOptions()
189
+ )
190
+ logger.debug(response.status_message)
191
+ logger.debug(response.headers)
192
+ logger.debug(response.status_code)
193
+ logger.debug(response.body)
194
+
195
+ response_body_str = response.body.decode("utf-8")
196
+
197
+ response_body_dict = json.loads(response_body_str)
198
+
199
+ if int(response_body_dict.get("code")) != 200:
200
+ raise Exception(
201
+ "Error in getting mqtt credentials: " + response_body_dict["msg"]
202
+ )
203
+ else:
204
+ self._aep_response = AepResponse.from_dict(response_body_dict)
205
+
206
+ logger.debug(response_body_dict)
207
+
208
+ return response.body
209
+
210
+ # returns vid
211
+
212
+ async def connect(self):
213
+ region_url = "sdk.openaccount.aliyun.com"
214
+ async with ClientSession() as session:
215
+ headers = {
216
+ "host": region_url,
217
+ "date": UtilClient.get_date_utcstring(),
218
+ "x-ca-nonce": UtilClient.get_nonce(),
219
+ "x-ca-key": self._app_key,
220
+ "x-ca-signaturemethod": "HmacSHA256",
221
+ "accept": "application/json",
222
+ "content-type": "application/x-www-form-urlencoded",
223
+ "user-agent": UtilClient.get_user_agent(None),
224
+ }
225
+
226
+ _bodyParam = {
227
+ "context": {
228
+ "sdkVersion": "3.4.2",
229
+ "platformName": "android",
230
+ "netType": "wifi",
231
+ "appKey": self._app_key,
232
+ "yunOSId": "",
233
+ "appVersion": APP_VERSION,
234
+ "utDid": self._utdid,
235
+ "appAuthToken": self._utdid, # ???
236
+ "securityToken": self._utdid, # ???
237
+ },
238
+ "config": {"version": 0, "lastModify": 0},
239
+ "device": {
240
+ "model": "sdk_gphone_x86_arm",
241
+ "brand": "goldfish_x86",
242
+ "platformVersion": "30",
243
+ },
244
+ }
245
+
246
+ # Get sign header
247
+ dic = headers.copy()
248
+ for key in MOVE_HEADERS:
249
+ dic.pop(key, None)
250
+
251
+ keys = sorted(dic.keys())
252
+ sign_headers = ",".join(keys)
253
+ header = "".join(f"{k}:{dic[k]}\n" for k in keys).strip()
254
+
255
+ headers["x-ca-signature-headers"] = sign_headers
256
+ string_to_sign = (
257
+ "POST\n{}\n\n{}\n{}\n{}\n/api/prd/connect.json?request={}".format(
258
+ headers["accept"],
259
+ headers["content-type"],
260
+ headers["date"],
261
+ header,
262
+ json.dumps(_bodyParam, separators=(",", ":")),
263
+ )
264
+ )
265
+
266
+ hash_val = hmac.new(
267
+ self._app_secret.encode("utf-8"),
268
+ string_to_sign.encode("utf-8"),
269
+ hashlib.sha256,
270
+ ).digest()
271
+ signature = base64.b64encode(hash_val).decode("utf-8")
272
+ headers["x-ca-signature"] = signature
273
+
274
+ async with session.post(
275
+ f"https://{region_url}/api/prd/connect.json",
276
+ headers=headers,
277
+ params=dict(request=json.dumps(_bodyParam, separators=(",", ":"))),
278
+ ) as resp:
279
+ data = await resp.json()
280
+ self._connect_response = ConnectResponse.from_dict(data)
281
+ logger.debug(data)
282
+
283
+ async def login_by_oauth(self, country_code: str, auth_code: str):
284
+ """loginbyoauth.json."""
285
+
286
+ region_url = self._region.data.oaApiGatewayEndpoint
287
+
288
+ async with ClientSession() as session:
289
+ headers = {
290
+ "host": region_url,
291
+ "date": UtilClient.get_date_utcstring(),
292
+ "x-ca-nonce": UtilClient.get_nonce(),
293
+ "x-ca-key": self._app_key,
294
+ "x-ca-signaturemethod": "HmacSHA256",
295
+ "accept": "application/json",
296
+ "content-type": "application/x-www-form-urlencoded",
297
+ "user-agent": UtilClient.get_user_agent(None),
298
+ "vid": self._connect_response.data.vid,
299
+ }
300
+
301
+ _bodyParam = {
302
+ "country": country_code,
303
+ "authCode": auth_code,
304
+ "oauthPlateform": "23",
305
+ "oauthAppKey": self._app_key,
306
+ "appAuthToken": self._device_sn,
307
+ "riskControlInfo": {
308
+ "appID": "com.agilexrobotics",
309
+ "signType": "RSA",
310
+ "utdid": self._utdid,
311
+ "umidToken": self._utdid,
312
+ "USE_OA_PWD_ENCRYPT": "true",
313
+ "USE_H5_NC": "true",
314
+ },
315
+ }
316
+
317
+ # Get sign header
318
+ dic = headers.copy()
319
+ for key in MOVE_HEADERS:
320
+ dic.pop(key, None)
321
+
322
+ keys = sorted(dic.keys())
323
+ sign_headers = ",".join(keys)
324
+ header = "".join(f"{k}:{dic[k]}\n" for k in keys).strip()
325
+
326
+ headers["x-ca-signature-headers"] = sign_headers
327
+ string_to_sign = "POST\n{}\n\n{}\n{}\n{}\n/api/prd/loginbyoauth.json?loginByOauthRequest={}".format(
328
+ headers["accept"],
329
+ headers["content-type"],
330
+ headers["date"],
331
+ header,
332
+ json.dumps(_bodyParam, separators=(",", ":")),
333
+ )
334
+
335
+ hash_val = hmac.new(
336
+ self._app_secret.encode("utf-8"),
337
+ string_to_sign.encode("utf-8"),
338
+ hashlib.sha256,
339
+ ).digest()
340
+ signature = base64.b64encode(hash_val).decode("utf-8")
341
+ headers["x-ca-signature"] = signature
342
+
343
+ async with session.post(
344
+ f"https://{region_url}/api/prd/loginbyoauth.json",
345
+ headers=headers,
346
+ params=dict(
347
+ loginByOauthRequest=json.dumps(_bodyParam, separators=(",", ":"))
348
+ ),
349
+ ) as resp:
350
+ data = await resp.json()
351
+ logger.debug(data)
352
+
353
+ self._login_by_oauth_response = LoginByOAuthResponse.from_dict(data)
354
+
355
+ # self._region = response.body.data
356
+
357
+ # return response.body
358
+
359
+ # headers require sid vid or at a minimuim vid which comes from prd/connect.json
360
+
361
+ def session_by_auth_code(self):
362
+ config = Config(
363
+ app_key=self._app_key, # correct
364
+ app_secret=self._app_secret,
365
+ domain=self._region.data.apiGatewayEndpoint,
366
+ )
367
+ client = Client(config)
368
+
369
+ # build request
370
+ request = CommonParams(api_ver="1.0.4", language="en-US")
371
+ body = IoTApiRequest(
372
+ id=str(uuid.uuid4()),
373
+ params={
374
+ "request": {
375
+ "authCode": self._login_by_oauth_response.data.data.loginSuccessResult.sid,
376
+ "accountType": "OA_SESSION",
377
+ "appKey": self._app_key,
378
+ }
379
+ },
380
+ request=request,
381
+ version="1.0",
382
+ )
383
+
384
+ # send request
385
+ # possibly need to do this ourselves
386
+ response = client.do_request(
387
+ "/account/createSessionByAuthCode",
388
+ "https",
389
+ "POST",
390
+ None,
391
+ body,
392
+ RuntimeOptions(),
393
+ )
394
+ logger.debug(response.status_message)
395
+ logger.debug(response.headers)
396
+ logger.debug(response.status_code)
397
+ logger.debug(response.body)
398
+
399
+ # Decodifica il corpo della risposta
400
+ response_body_str = response.body.decode("utf-8")
401
+
402
+ # Carica la stringa JSON in un dizionario
403
+ response_body_dict = json.loads(response_body_str)
404
+
405
+ if int(response_body_dict.get("code")) != 200:
406
+ raise Exception("Error in creating session: " + response_body_dict["msg"])
407
+ else:
408
+ self._session_by_authcode_response = SessionByAuthCodeResponse.from_dict(
409
+ response_body_dict
410
+ )
411
+
412
+ return response.body
413
+
414
+ def check_or_refresh_session(self):
415
+ if self.load_saved_params() == False:
416
+ return False
417
+ config = Config(
418
+ app_key=self._app_key, # correct
419
+ app_secret=self._app_secret,
420
+ domain=self._region.data.apiGatewayEndpoint,
421
+ )
422
+ client = Client(config)
423
+
424
+ # build request
425
+ request = CommonParams(api_ver="1.0.4", language="en-US")
426
+ body = IoTApiRequest(
427
+ id=str(uuid.uuid4()),
428
+ params={
429
+ "request": {
430
+ "refreshToken": self._session_by_authcode_response.data.refreshToken,
431
+ "identityId": self._session_by_authcode_response.data.identityId,
432
+ }
433
+ },
434
+ request=request,
435
+ version="1.0",
436
+ )
437
+
438
+ # send request
439
+ # possibly need to do this ourselves
440
+ response = client.do_request(
441
+ "/account/checkOrRefreshSession",
442
+ "https",
443
+ "POST",
444
+ None,
445
+ body,
446
+ RuntimeOptions(),
447
+ )
448
+ logger.debug(response.status_message)
449
+ logger.debug(response.headers)
450
+ logger.debug(response.status_code)
451
+ logger.debug(response.body)
452
+
453
+ # self._region = response.body.data
454
+ # Decodifica il corpo della risposta
455
+ response_body_str = response.body.decode("utf-8")
456
+
457
+ # Carica la stringa JSON in un dizionario
458
+ response_body_dict = json.loads(response_body_str)
459
+
460
+ def list_binding_by_account(self):
461
+ config = Config(
462
+ app_key=self._app_key, # correct
463
+ app_secret=self._app_secret,
464
+ domain=self._region.data.apiGatewayEndpoint,
465
+ )
466
+
467
+ client = Client(config)
468
+
469
+ # build request
470
+ request = CommonParams(
471
+ api_ver="1.0.8",
472
+ language="en-US",
473
+ iot_token=self._session_by_authcode_response.data.iotToken,
474
+ )
475
+ body = IoTApiRequest(
476
+ id=str(uuid.uuid4()),
477
+ params={"pageSize": 100, "pageNo": 1},
478
+ request=request,
479
+ version="1.0",
480
+ )
481
+
482
+ # send request
483
+ # possibly need to do this ourselves
484
+ response = client.do_request(
485
+ "/uc/listBindingByAccount", "https", "POST", None, body, RuntimeOptions()
486
+ )
487
+ logger.debug(response.status_message)
488
+ logger.debug(response.headers)
489
+ logger.debug(response.status_code)
490
+ logger.debug(response.body)
491
+
492
+ # self._region = response.body.data
493
+ # Decodifica il corpo della risposta
494
+ response_body_str = response.body.decode("utf-8")
495
+
496
+ # Carica la stringa JSON in un dizionario
497
+ response_body_dict = json.loads(response_body_str)
498
+
499
+ if int(response_body_dict.get("code")) != 200:
500
+ raise Exception("Error in creating session: " + response_body_dict["msg"])
501
+ else:
502
+ self._listing_dev_by_account_response = (
503
+ ListingDevByAccountResponse.from_dict(response_body_dict)
504
+ )
505
+
506
+ def send_cloud_command(self, command: bytes):
507
+ config = Config(
508
+ app_key=self._app_key, # correct
509
+ app_secret=self._app_secret,
510
+ domain=self._region.data.apiGatewayEndpoint,
511
+ )
512
+
513
+ client = Client(config)
514
+
515
+ # build request
516
+ request = CommonParams(
517
+ api_ver="1.0.5",
518
+ language="en-US",
519
+ iot_token=self._session_by_authcode_response.data.iotToken,
520
+ )
521
+
522
+ # TODO move to using InvokeThingServiceRequest()
523
+ body = IoTApiRequest(
524
+ id=str(uuid.uuid4()),
525
+ params={
526
+ "args": {"content": self.converter.printBase64Binary(command)},
527
+ "identifier": "device_protobuf_sync_service",
528
+ "iotId": "MbXcDE2X63CENA0lPGIo000000", # TODO get iotId from listbybinding request
529
+ },
530
+ request=request,
531
+ version="1.0",
532
+ )
533
+
534
+ # send request
535
+ # possibly need to do this ourselves
536
+ response = client.do_request(
537
+ "/thing/service/invoke", "https", "POST", None, body, RuntimeOptions()
538
+ )
539
+ logger.debug(response.status_message)
540
+ logger.debug(response.headers)
541
+ logger.debug(response.status_code)
542
+ logger.debug(response.body)
543
+
544
+ # self._region = response.body.data
545
+ # Decodifica il corpo della risposta
546
+ response_body_str = response.body.decode("utf-8")
547
+
548
+ # Carica la stringa JSON in un dizionario
549
+ response_body_dict = json.loads(response_body_str)
@@ -0,0 +1,65 @@
1
+ import base64
2
+
3
+ from aliyunsdkcore import client
4
+ from aliyunsdkiot.request.v20180120.GetDeviceStatusRequest import GetDeviceStatusRequest
5
+ from aliyunsdkiot.request.v20180120.InvokeThingServiceRequest import (
6
+ InvokeThingServiceRequest,
7
+ )
8
+
9
+
10
+ class CloudService:
11
+ # com.aliyun.iot.aep.sdk
12
+ # https://domestic.mammotion.com/privacy/ - lists all aliyun packages
13
+ def __init__(self):
14
+ self.selectDeviceIOTID = ""
15
+ accessKeyId = "<your accessKey>"
16
+ accessKeySecret = "<your accessSecret>"
17
+ self.clt = client.AcsClient(accessKeyId, accessKeySecret, "ap-southeast")
18
+
19
+ """
20
+ String printBase64Binary = DatatypeConverter.printBase64Binary(byteArray);
21
+ JSONObject jSONObject = new JSONObject();
22
+ JSONObject jSONObject2 = new JSONObject();
23
+ try {
24
+ jSONObject2.put("content", printBase64Binary);
25
+ jSONObject.put("args", jSONObject2);
26
+ jSONObject.put("iotId", this.selectDeviceIOTID);
27
+ jSONObject.put("identifier", "device_protobuf_sync_service");
28
+ } catch (JSONException e) {
29
+ e.printStackTrace();
30
+ }
31
+
32
+ """
33
+
34
+ def invoke_thing_service(self, data: bytearray):
35
+ base64_encoded = base64.b64encode(data).decode("utf-8")
36
+
37
+ # Create a dictionary structure
38
+ data = {
39
+ "args": {"content": base64_encoded},
40
+ "DEVICE_IOTID": self.selectDeviceIOTID,
41
+ "identifier": "device_protobuf_sync_service",
42
+ }
43
+
44
+ request = InvokeThingServiceRequest()
45
+ request.set_accept_format("json")
46
+
47
+ request.set_Args("Args")
48
+ request.set_Identifier("Identifier")
49
+ request.set_IotId("IotId")
50
+ request.set_ProductKey("ProductKey")
51
+
52
+ response = self.clt.do_action_with_exception(request)
53
+ # python2: print(response)
54
+ print(response)
55
+
56
+ def get_device_status(self):
57
+ request = GetDeviceStatusRequest()
58
+ request.set_accept_format("json")
59
+
60
+ request.set_IotId("IotId")
61
+ request.set_ProductKey("ProductKey")
62
+ request.set_DeviceName("DeviceName")
63
+
64
+ response = self.clt.do_action_with_exception(request)
65
+ print(response)
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
5
+
6
+
7
+ @dataclass
8
+ class DeviceData(DataClassORJSONMixin):
9
+ deviceSecret: str
10
+ productKey: str
11
+ deviceName: str
12
+
13
+
14
+ @dataclass
15
+ class AepResponse(DataClassORJSONMixin):
16
+ code: int
17
+ data: DeviceData
18
+ id: Optional[str] = None