pymammotion 0.0.40__tar.gz → 0.0.41__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.

Potentially problematic release.


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

Files changed (114) hide show
  1. {pymammotion-0.0.40 → pymammotion-0.0.41}/PKG-INFO +10 -4
  2. {pymammotion-0.0.40 → pymammotion-0.0.41}/README.md +7 -1
  3. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/__init__.py +4 -2
  4. pymammotion-0.0.41/pymammotion/aliyun/__init__.py +1 -0
  5. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/cloud_gateway.py +74 -95
  6. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/tmp_constant.py +2 -6
  7. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/ble.py +4 -12
  8. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/ble_message.py +12 -36
  9. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/data/convert.py +1 -3
  10. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/data/notifydata.py +0 -1
  11. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/device.py +62 -3
  12. pymammotion-0.0.41/pymammotion/data/model/hash_list.py +37 -0
  13. pymammotion-0.0.41/pymammotion/data/model/location.py +40 -0
  14. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/rapid_state.py +1 -5
  15. pymammotion-0.0.41/pymammotion/data/state_manager.py +84 -0
  16. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/event/event.py +18 -3
  17. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/http/http.py +2 -6
  18. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/mammotion_command.py +1 -3
  19. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/driver.py +7 -21
  20. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/media.py +4 -9
  21. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/navigation.py +42 -107
  22. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/network.py +10 -30
  23. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/system.py +11 -26
  24. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/video.py +1 -3
  25. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/control/joystick.py +9 -33
  26. pymammotion-0.0.41/pymammotion/mammotion/devices/__init__.py +5 -0
  27. pymammotion-0.0.40/pymammotion/mammotion/devices/luba.py → pymammotion-0.0.41/pymammotion/mammotion/devices/mammotion.py +299 -110
  28. pymammotion-0.0.41/pymammotion/mqtt/__init__.py +5 -0
  29. pymammotion-0.0.40/pymammotion/mqtt/mqtt.py → pymammotion-0.0.41/pymammotion/mqtt/mammotion_mqtt.py +46 -50
  30. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/utility/constant/device_constant.py +14 -0
  31. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/utility/datatype_converter.py +52 -9
  32. pymammotion-0.0.41/pymammotion/utility/device_type.py +261 -0
  33. pymammotion-0.0.41/pymammotion/utility/periodic.py +106 -0
  34. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/utility/rocker_util.py +63 -4
  35. {pymammotion-0.0.40 → pymammotion-0.0.41}/pyproject.toml +6 -4
  36. pymammotion-0.0.40/pymammotion/data/model/hash_list.py +0 -17
  37. pymammotion-0.0.40/pymammotion/luba/_init_.py +0 -0
  38. pymammotion-0.0.40/pymammotion/luba/base.py +0 -52
  39. pymammotion-0.0.40/pymammotion/mammotion/devices/__init__.py +0 -1
  40. pymammotion-0.0.40/pymammotion/utility/device_type.py +0 -152
  41. pymammotion-0.0.40/pymammotion/utility/periodic.py +0 -41
  42. {pymammotion-0.0.40 → pymammotion-0.0.41}/LICENSE +0 -0
  43. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/cloud_service.py +0 -0
  44. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/dataclass/aep_response.py +0 -0
  45. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/dataclass/connect_response.py +0 -0
  46. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/dataclass/dev_by_account_response.py +0 -0
  47. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/dataclass/login_by_oauth_response.py +0 -0
  48. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/dataclass/regions_response.py +0 -0
  49. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/aliyun/dataclass/session_by_authcode_response.py +0 -0
  50. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/__init__.py +0 -0
  51. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/const.py +0 -0
  52. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/data/__init__.py +0 -0
  53. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/bluetooth/data/framectrldata.py +0 -0
  54. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/const.py +0 -0
  55. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/__init__.py +0 -0
  56. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/__init__.py +0 -0
  57. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/enums.py +0 -0
  58. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/excute_boarder_params.py +0 -0
  59. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/execute_boarder.py +0 -0
  60. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/generate_route_information.py +0 -0
  61. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/mowing_modes.py +0 -0
  62. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/plan.py +0 -0
  63. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/model/region_data.py +0 -0
  64. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/mqtt/__init__.py +0 -0
  65. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/mqtt/event.py +0 -0
  66. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/mqtt/properties.py +0 -0
  67. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/data/mqtt/status.py +0 -0
  68. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/event/__init__.py +0 -0
  69. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/http/_init_.py +0 -0
  70. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/__init__.py +0 -0
  71. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/__init__.py +0 -0
  72. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/abstract_message.py +0 -0
  73. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/__init__.py +0 -0
  74. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/commands/messages/ota.py +0 -0
  75. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/mammotion/control/__init__.py +0 -0
  76. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/__init__.py +0 -0
  77. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/common.proto +0 -0
  78. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/common.py +0 -0
  79. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/common_pb2.py +0 -0
  80. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/common_pb2.pyi +0 -0
  81. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/dev_net.proto +0 -0
  82. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/dev_net.py +0 -0
  83. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/dev_net_pb2.py +0 -0
  84. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/dev_net_pb2.pyi +0 -0
  85. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_msg.proto +0 -0
  86. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_msg.py +0 -0
  87. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_msg_pb2.py +0 -0
  88. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_msg_pb2.pyi +0 -0
  89. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_mul.proto +0 -0
  90. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_mul.py +0 -0
  91. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_mul_pb2.py +0 -0
  92. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/luba_mul_pb2.pyi +0 -0
  93. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_driver.proto +0 -0
  94. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_driver.py +0 -0
  95. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_driver_pb2.py +0 -0
  96. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_driver_pb2.pyi +0 -0
  97. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_nav.proto +0 -0
  98. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_nav.py +0 -0
  99. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_nav_pb2.py +0 -0
  100. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_nav_pb2.pyi +0 -0
  101. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_ota.proto +0 -0
  102. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_ota.py +0 -0
  103. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_ota_pb2.py +0 -0
  104. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_ota_pb2.pyi +0 -0
  105. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_pept.proto +0 -0
  106. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_pept.py +0 -0
  107. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_pept_pb2.py +0 -0
  108. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_pept_pb2.pyi +0 -0
  109. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_sys.proto +0 -0
  110. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_sys.py +0 -0
  111. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_sys_pb2.py +0 -0
  112. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/proto/mctrl_sys_pb2.pyi +0 -0
  113. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/py.typed +0 -0
  114. {pymammotion-0.0.40 → pymammotion-0.0.41}/pymammotion/utility/constant/__init__.py +0 -0
@@ -1,14 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pymammotion
3
- Version: 0.0.40
3
+ Version: 0.0.41
4
4
  Summary:
5
5
  License: GNU-3.0
6
6
  Author: Michael Arthur
7
7
  Author-email: michael@jumblesoft.co.nz
8
- Requires-Python: >=3.11,<3.13
8
+ Requires-Python: >=3.12.0,<3.13.0
9
9
  Classifier: License :: Other/Proprietary License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.11
12
11
  Classifier: Programming Language :: Python :: 3.12
13
12
  Requires-Dist: aiohttp (>=3.9.1,<4.0.0)
14
13
  Requires-Dist: alibabacloud-apigateway-util (>=0.0.2,<0.0.3)
@@ -27,6 +26,7 @@ Requires-Dist: paho-mqtt (>=1.6.1,<2.0.0)
27
26
  Requires-Dist: protobuf (>=4.23.1)
28
27
  Requires-Dist: py-jsonic (>=0.0.2,<0.0.3)
29
28
  Requires-Dist: pyjoystick (>=1.2.4,<2.0.0)
29
+ Requires-Dist: pyproj (>=3.6.1,<4.0.0)
30
30
  Description-Content-Type: text/markdown
31
31
 
32
32
  # PyMammotion - Python API for Mammotion Mowers [![Discord](https://img.shields.io/discord/1247286396297678879)](https://discord.gg/vpZdWhJX8x)
@@ -35,7 +35,7 @@ Description-Content-Type: text/markdown
35
35
  [![PyPI Releases][img_pypi]][url_pypi]
36
36
  [![Supported Python Versions][img_pyversions]][url_pyversions]
37
37
 
38
- [img_version]: https://img.shields.io/static/v1.svg?label=SemVer&message=0.0.1&color=blue
38
+ [img_version]: https://img.shields.io/static/v1.svg?label=SemVer&message=0.4.0&color=blue
39
39
  [url_version]: https://pypi.org/project/pymammotion/
40
40
 
41
41
  [img_pypi]: https://img.shields.io/badge/PyPI-wheels-green.svg
@@ -90,3 +90,9 @@ If you encounter any issues:
90
90
 
91
91
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
92
92
 
93
+
94
+ ## Trademark Notice
95
+
96
+ The trademarks "Mammotion," "Luba," and "Yuka" referenced herein are registered trademarks of their respective owners. The author of this software repository is not affiliated with, endorsed by, or connected to these trademark owners in any way.
97
+
98
+
@@ -4,7 +4,7 @@
4
4
  [![PyPI Releases][img_pypi]][url_pypi]
5
5
  [![Supported Python Versions][img_pyversions]][url_pyversions]
6
6
 
7
- [img_version]: https://img.shields.io/static/v1.svg?label=SemVer&message=0.0.1&color=blue
7
+ [img_version]: https://img.shields.io/static/v1.svg?label=SemVer&message=0.4.0&color=blue
8
8
  [url_version]: https://pypi.org/project/pymammotion/
9
9
 
10
10
  [img_pypi]: https://img.shields.io/badge/PyPI-wheels-green.svg
@@ -58,3 +58,9 @@ If you encounter any issues:
58
58
  ## License 📄
59
59
 
60
60
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
61
+
62
+
63
+ ## Trademark Notice
64
+
65
+ The trademarks "Mammotion," "Luba," and "Yuka" referenced herein are registered trademarks of their respective owners. The author of this software repository is not affiliated with, endorsed by, or connected to these trademark owners in any way.
66
+
@@ -14,7 +14,9 @@ from pymammotion.http.http import LubaHTTP, connect_http
14
14
 
15
15
  # TODO make a working device that will work outside HA too.
16
16
  from pymammotion.mammotion.devices import MammotionBaseBLEDevice
17
- from pymammotion.mqtt.mqtt import LubaMQTT, logger
17
+ from pymammotion.mqtt import MammotionMQTT
18
+
19
+ logger = logging.getLogger(__name__)
18
20
 
19
21
  # TODO provide interface to pick between mqtt/cloud/bluetooth
20
22
 
@@ -28,7 +30,7 @@ if __name__ == "__main__":
28
30
  CLIENT_ID = os.environ.get("CLIENT_ID")
29
31
  IOT_TOKEN = os.environ.get("IOT_TOKEN")
30
32
  REGION = os.environ.get("REGION")
31
- luba = LubaMQTT(
33
+ luba = MammotionMQTT(
32
34
  iot_token=IOT_TOKEN,
33
35
  region_id=REGION,
34
36
  product_key=PRODUCT_KEY,
@@ -0,0 +1 @@
1
+ """Aliyun API interfaces."""
@@ -1,3 +1,5 @@
1
+ """Module for interacting with Aliyun Cloud IoT Gateway."""
2
+
1
3
  import base64
2
4
  import hashlib
3
5
  import hmac
@@ -30,9 +32,6 @@ from pymammotion.utility.datatype_converter import DatatypeConverter
30
32
 
31
33
  logger = getLogger(__name__)
32
34
 
33
- # init client
34
-
35
-
36
35
  MOVE_HEADERS = (
37
36
  "x-ca-signature",
38
37
  "x-ca-signature-headers",
@@ -47,6 +46,8 @@ MOVE_HEADERS = (
47
46
 
48
47
 
49
48
  class CloudIOTGateway:
49
+ """Class for interacting with Aliyun Cloud IoT Gateway."""
50
+
50
51
  _client_id = ""
51
52
  _device_sn = ""
52
53
  _utdid = ""
@@ -61,19 +62,20 @@ class CloudIOTGateway:
61
62
  converter = DatatypeConverter()
62
63
 
63
64
  def __init__(self):
65
+ """Initialize the CloudIOTGateway."""
64
66
  self._app_key = APP_KEY
65
67
  self._app_secret = APP_SECRET
66
68
  self.domain = ALIYUN_DOMAIN
67
69
 
68
- self._client_id = self.generate_hardware_string(8) # 8 charatters
69
- self._device_sn = self.generate_hardware_string(32) # 32 charatters
70
- self._utdid = self.generate_hardware_string(32) # 32 charatters
70
+ self._client_id = self.generate_hardware_string(8) # 8 characters
71
+ self._device_sn = self.generate_hardware_string(32) # 32 characters
72
+ self._utdid = self.generate_hardware_string(32) # 32 characters
71
73
 
72
74
  @staticmethod
73
75
  def generate_random_string(length):
76
+ """Generate a random string of specified length."""
74
77
  characters = string.ascii_letters + string.digits
75
- random_string = "".join(random.choice(characters) for _ in range(length))
76
- return random_string
78
+ return "".join(random.choice(characters) for _ in range(length))
77
79
 
78
80
  @staticmethod
79
81
  def generate_hardware_string(length) -> str:
@@ -82,28 +84,24 @@ class CloudIOTGateway:
82
84
  return "".join(itertools.islice(itertools.cycle(hashed_uuid), length))
83
85
 
84
86
  def sign(self, data):
87
+ """Generate signature for the given data."""
85
88
  keys = ["appKey", "clientId", "deviceSn", "timestamp"]
86
89
  concatenated_str = ""
87
90
  for key in keys:
88
91
  concatenated_str += f"{key}{data.get(key, '')}"
89
92
 
90
- logger.debug(f"sign(), toSignStr = {concatenated_str}")
93
+ logger.debug("sign(), toSignStr = %s", concatenated_str)
91
94
 
92
- sign = hmac.new(
95
+ return hmac.new(
93
96
  self._app_secret.encode("utf-8"),
94
97
  concatenated_str.encode("utf-8"),
95
98
  hashlib.sha1,
96
99
  ).hexdigest()
97
100
 
98
- return sign
99
-
100
101
  def get_region(self, country_code: str, auth_code: str):
101
- # shim out the regions?
102
- # https://api.link.aliyun.com/living/account/region/get?x-ca-request-id=59abc767-fbbc-4333-9127-e65d792133a8
103
- # x-ca-request-id is a random UUID on each request
104
-
102
+ """Get the region based on country code and auth code."""
105
103
  config = Config(
106
- app_key=self._app_key, # correct
104
+ app_key=self._app_key,
107
105
  app_secret=self._app_secret,
108
106
  domain=self.domain,
109
107
  )
@@ -123,45 +121,42 @@ class CloudIOTGateway:
123
121
  )
124
122
 
125
123
  # send request
126
- # possibly need to do this ourselves
127
- response = client.do_request(
128
- "/living/account/region/get", "https", "POST", None, body, RuntimeOptions()
129
- )
124
+ response = client.do_request("/living/account/region/get", "https", "POST", None, body, RuntimeOptions())
130
125
  logger.debug(response.status_message)
131
126
  logger.debug(response.headers)
132
127
  logger.debug(response.status_code)
133
128
  logger.debug(response.body)
134
129
 
135
- # Decodifica il corpo della risposta
130
+ # Decode the response body
136
131
  response_body_str = response.body.decode("utf-8")
137
132
 
138
- # Carica la stringa JSON in un dizionario
133
+ # Load the JSON string into a dictionary
139
134
  response_body_dict = json.loads(response_body_str)
140
135
 
141
136
  if int(response_body_dict.get("code")) != 200:
142
137
  raise Exception("Error in getting regions: " + response_body_dict["msg"])
143
- else:
144
- self._region = RegionResponse.from_dict(response_body_dict)
145
- logger.debug("Endpoint : " + self._region.data.mqttEndpoint)
138
+
139
+ self._region = RegionResponse.from_dict(response_body_dict)
140
+ logger.debug("Endpoint: %s", self._region.data.mqttEndpoint)
146
141
 
147
142
  return response.body
148
143
 
149
144
  def aep_handle(self):
150
- # https://api.link.aliyun.com/app/aepauth/handle
145
+ """Handle AEP authentication."""
151
146
  aep_domain = self.domain
152
147
 
153
148
  if self._region.data.apiGatewayEndpoint is not None:
154
149
  aep_domain = self._region.data.apiGatewayEndpoint
155
150
 
156
151
  config = Config(
157
- app_key=self._app_key, # correct
152
+ app_key=self._app_key,
158
153
  app_secret=self._app_secret,
159
154
  domain=aep_domain,
160
155
  )
161
156
  client = Client(config)
162
157
 
163
158
  request = CommonParams(api_ver="1.0.0", language="en-US")
164
- logger.debug("client id ", self._client_id)
159
+ logger.debug("client id %s", self._client_id)
165
160
  time_now = time.time()
166
161
  data_to_sign = {
167
162
  "appKey": self._app_key,
@@ -185,10 +180,7 @@ class CloudIOTGateway:
185
180
  )
186
181
 
187
182
  # send request
188
- # possibly need to do this ourselves
189
- response = client.do_request(
190
- "/app/aepauth/handle", "https", "POST", None, body, RuntimeOptions()
191
- )
183
+ response = client.do_request("/app/aepauth/handle", "https", "POST", None, body, RuntimeOptions())
192
184
  logger.debug(response.status_message)
193
185
  logger.debug(response.headers)
194
186
  logger.debug(response.status_code)
@@ -199,19 +191,16 @@ class CloudIOTGateway:
199
191
  response_body_dict = json.loads(response_body_str)
200
192
 
201
193
  if int(response_body_dict.get("code")) != 200:
202
- raise Exception(
203
- "Error in getting mqtt credentials: " + response_body_dict["msg"]
204
- )
205
- else:
206
- self._aep_response = AepResponse.from_dict(response_body_dict)
194
+ raise Exception("Error in getting mqtt credentials: " + response_body_dict["msg"])
195
+
196
+ self._aep_response = AepResponse.from_dict(response_body_dict)
207
197
 
208
198
  logger.debug(response_body_dict)
209
199
 
210
200
  return response.body
211
201
 
212
- # returns vid
213
-
214
202
  async def connect(self):
203
+ """Connect to the Aliyun Cloud IoT Gateway."""
215
204
  region_url = "sdk.openaccount.aliyun.com"
216
205
  async with ClientSession() as session:
217
206
  headers = {
@@ -255,14 +244,12 @@ class CloudIOTGateway:
255
244
  header = "".join(f"{k}:{dic[k]}\n" for k in keys).strip()
256
245
 
257
246
  headers["x-ca-signature-headers"] = sign_headers
258
- string_to_sign = (
259
- "POST\n{}\n\n{}\n{}\n{}\n/api/prd/connect.json?request={}".format(
260
- headers["accept"],
261
- headers["content-type"],
262
- headers["date"],
263
- header,
264
- json.dumps(_bodyParam, separators=(",", ":")),
265
- )
247
+ string_to_sign = "POST\n{}\n\n{}\n{}\n{}\n/api/prd/connect.json?request={}".format(
248
+ headers["accept"],
249
+ headers["content-type"],
250
+ headers["date"],
251
+ header,
252
+ json.dumps(_bodyParam, separators=(",", ":")),
266
253
  )
267
254
 
268
255
  hash_val = hmac.new(
@@ -276,15 +263,14 @@ class CloudIOTGateway:
276
263
  async with session.post(
277
264
  f"https://{region_url}/api/prd/connect.json",
278
265
  headers=headers,
279
- params=dict(request=json.dumps(_bodyParam, separators=(",", ":"))),
266
+ params={"request": json.dumps(_bodyParam, separators=(",", ":"))},
280
267
  ) as resp:
281
268
  data = await resp.json()
282
269
  self._connect_response = ConnectResponse.from_dict(data)
283
270
  logger.debug(data)
284
271
 
285
272
  async def login_by_oauth(self, country_code: str, auth_code: str):
286
- """loginbyoauth.json."""
287
-
273
+ """Login by OAuth."""
288
274
  region_url = self._region.data.oaApiGatewayEndpoint
289
275
 
290
276
  async with ClientSession() as session:
@@ -345,24 +331,17 @@ class CloudIOTGateway:
345
331
  async with session.post(
346
332
  f"https://{region_url}/api/prd/loginbyoauth.json",
347
333
  headers=headers,
348
- params=dict(
349
- loginByOauthRequest=json.dumps(_bodyParam, separators=(",", ":"))
350
- ),
334
+ params={"loginByOauthRequest": json.dumps(_bodyParam, separators=(",", ":"))},
351
335
  ) as resp:
352
336
  data = await resp.json()
353
337
  logger.debug(data)
354
338
 
355
339
  self._login_by_oauth_response = LoginByOAuthResponse.from_dict(data)
356
340
 
357
- # self._region = response.body.data
358
-
359
- # return response.body
360
-
361
- # headers require sid vid or at a minimuim vid which comes from prd/connect.json
362
-
363
341
  def session_by_auth_code(self):
342
+ """Create a session by auth code."""
364
343
  config = Config(
365
- app_key=self._app_key, # correct
344
+ app_key=self._app_key,
366
345
  app_secret=self._app_secret,
367
346
  domain=self._region.data.apiGatewayEndpoint,
368
347
  )
@@ -384,7 +363,6 @@ class CloudIOTGateway:
384
363
  )
385
364
 
386
365
  # send request
387
- # possibly need to do this ourselves
388
366
  response = client.do_request(
389
367
  "/account/createSessionByAuthCode",
390
368
  "https",
@@ -398,26 +376,25 @@ class CloudIOTGateway:
398
376
  logger.debug(response.status_code)
399
377
  logger.debug(response.body)
400
378
 
401
- # Decodifica il corpo della risposta
379
+ # Decode the response body
402
380
  response_body_str = response.body.decode("utf-8")
403
381
 
404
- # Carica la stringa JSON in un dizionario
382
+ # Load the JSON string into a dictionary
405
383
  response_body_dict = json.loads(response_body_str)
406
384
 
407
385
  if int(response_body_dict.get("code")) != 200:
408
386
  raise Exception("Error in creating session: " + response_body_dict["msg"])
409
- else:
410
- self._session_by_authcode_response = SessionByAuthCodeResponse.from_dict(
411
- response_body_dict
412
- )
387
+
388
+ self._session_by_authcode_response = SessionByAuthCodeResponse.from_dict(response_body_dict)
413
389
 
414
390
  return response.body
415
391
 
416
392
  def check_or_refresh_session(self):
417
- if self.load_saved_params() == False:
393
+ """Check or refresh the session."""
394
+ if self.load_saved_params() is False:
418
395
  return False
419
396
  config = Config(
420
- app_key=self._app_key, # correct
397
+ app_key=self._app_key,
421
398
  app_secret=self._app_secret,
422
399
  domain=self._region.data.apiGatewayEndpoint,
423
400
  )
@@ -457,11 +434,12 @@ class CloudIOTGateway:
457
434
  response_body_str = response.body.decode("utf-8")
458
435
 
459
436
  # Carica la stringa JSON in un dizionario
460
- response_body_dict = json.loads(response_body_str)
437
+ json.loads(response_body_str)
461
438
 
462
439
  def list_binding_by_account(self):
440
+ """List bindings by account."""
463
441
  config = Config(
464
- app_key=self._app_key, # correct
442
+ app_key=self._app_key,
465
443
  app_secret=self._app_secret,
466
444
  domain=self._region.data.apiGatewayEndpoint,
467
445
  )
@@ -482,32 +460,27 @@ class CloudIOTGateway:
482
460
  )
483
461
 
484
462
  # send request
485
- # possibly need to do this ourselves
486
- response = client.do_request(
487
- "/uc/listBindingByAccount", "https", "POST", None, body, RuntimeOptions()
488
- )
463
+ response = client.do_request("/uc/listBindingByAccount", "https", "POST", None, body, RuntimeOptions())
489
464
  logger.debug(response.status_message)
490
465
  logger.debug(response.headers)
491
466
  logger.debug(response.status_code)
492
467
  logger.debug(response.body)
493
468
 
494
- # self._region = response.body.data
495
- # Decodifica il corpo della risposta
469
+ # Decode the response body
496
470
  response_body_str = response.body.decode("utf-8")
497
471
 
498
- # Carica la stringa JSON in un dizionario
472
+ # Load the JSON string into a dictionary
499
473
  response_body_dict = json.loads(response_body_str)
500
474
 
501
475
  if int(response_body_dict.get("code")) != 200:
502
476
  raise Exception("Error in creating session: " + response_body_dict["msg"])
503
- else:
504
- self._listing_dev_by_account_response = (
505
- ListingDevByAccountResponse.from_dict(response_body_dict)
506
- )
507
477
 
508
- def send_cloud_command(self, command: bytes):
478
+ self._listing_dev_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict)
479
+
480
+ def send_cloud_command(self, iot_id: str, command: bytes) -> str:
481
+ """Send a cloud command to the specified IoT device."""
509
482
  config = Config(
510
- app_key=self._app_key, # correct
483
+ app_key=self._app_key,
511
484
  app_secret=self._app_secret,
512
485
  domain=self._region.data.apiGatewayEndpoint,
513
486
  )
@@ -522,30 +495,36 @@ class CloudIOTGateway:
522
495
  )
523
496
 
524
497
  # TODO move to using InvokeThingServiceRequest()
498
+
499
+ message_id = str(uuid.uuid4())
500
+
525
501
  body = IoTApiRequest(
526
- id=str(uuid.uuid4()),
502
+ id=message_id,
527
503
  params={
528
504
  "args": {"content": self.converter.printBase64Binary(command)},
529
505
  "identifier": "device_protobuf_sync_service",
530
- "iotId": "MbXcDE2X63CENA0lPGIo000000", # TODO get iotId from listbybinding request
506
+ "iotId": f"{iot_id}",
531
507
  },
532
508
  request=request,
533
509
  version="1.0",
534
510
  )
535
511
 
536
512
  # send request
537
- # possibly need to do this ourselves
538
- response = client.do_request(
539
- "/thing/service/invoke", "https", "POST", None, body, RuntimeOptions()
540
- )
513
+ response = client.do_request("/thing/service/invoke", "https", "POST", None, body, RuntimeOptions())
541
514
  logger.debug(response.status_message)
542
515
  logger.debug(response.headers)
543
516
  logger.debug(response.status_code)
544
517
  logger.debug(response.body)
545
518
 
546
- # self._region = response.body.data
547
- # Decodifica il corpo della risposta
548
519
  response_body_str = response.body.decode("utf-8")
549
-
550
- # Carica la stringa JSON in un dizionario
551
520
  response_body_dict = json.loads(response_body_str)
521
+
522
+ if int(response_body_dict.get("code")) != 200:
523
+ logger.error(
524
+ "Error in sending cloud command: %s - %s",
525
+ str(response_body_dict.get("code")),
526
+ str(response_body_dict["msg"]),
527
+ )
528
+ return ""
529
+
530
+ return message_id
@@ -143,9 +143,7 @@ class tmp_constant:
143
143
  URI_ALINK_SERVICE = "alink/service" # deprecated
144
144
  URI_AUTH = "/auth" # deprecated
145
145
  URI_AUTHEN_REGISTER = "/sys/{productKey}/{deviceName}/thing/authen/sub/register"
146
- URI_AUTHEN_REGISTER_REPLY = (
147
- "/sys/{productKey}/{deviceName}/thing/authen/sub/register_reply"
148
- )
146
+ URI_AUTHEN_REGISTER_REPLY = "/sys/{productKey}/{deviceName}/thing/authen/sub/register_reply"
149
147
  URI_BLACKLIST_UPDATE_POST = "/thing/lan/blacklist/update"
150
148
  URI_BLACKLIST_UPDATE_REPLY_POST = "/thing/lan/blacklist/update_reply"
151
149
  URI_DEVICE = "/device/core/dev"
@@ -165,9 +163,7 @@ class tmp_constant:
165
163
  URI_TOPIC_LOCALDEVICE_STATECHANGE = "group/localstatechange"
166
164
  URI_TOPIC_REPLY_POST = "_reply"
167
165
  URI_UPDATE_DEVICE_INFO = "/sys/{productKey}/{deviceName}/thing/deviceinfo/update"
168
- URI_UPDATE_DEVICE_INFO_REPLY = (
169
- "/sys/{productKey}/{deviceName}/thing/deviceinfo/update_reply"
170
- )
166
+ URI_UPDATE_DEVICE_INFO_REPLY = "/sys/{productKey}/{deviceName}/thing/deviceinfo/update_reply"
171
167
  URI_USER = "/user"
172
168
  VALUE_SHA256 = "sha256"
173
169
  VERSION = "1.0"
@@ -46,15 +46,11 @@ class LubaBLE:
46
46
  if self.client is not None:
47
47
  return await self.client.disconnect()
48
48
 
49
- async def notification_handler(
50
- self, _characteristic: BleakGATTCharacteristic, data: bytearray
51
- ):
49
+ async def notification_handler(self, _characteristic: BleakGATTCharacteristic, data: bytearray):
52
50
  """Simple notification handler which prints the data received."""
53
51
  await self._bleEvt.BleNotification(data)
54
52
 
55
- def service_changed_handler(
56
- self, characteristic: BleakGATTCharacteristic, data: bytearray
57
- ):
53
+ def service_changed_handler(self, characteristic: BleakGATTCharacteristic, data: bytearray):
58
54
  """Simple notification handler which prints the data received."""
59
55
  print(f"Response 2 {characteristic.description}: {data}")
60
56
  print(data.decode("utf-8"))
@@ -63,12 +59,8 @@ class LubaBLE:
63
59
 
64
60
  async def notifications(self):
65
61
  if self.client.is_connected:
66
- await self.client.start_notify(
67
- UUID_NOTIFICATION_CHARACTERISTIC, self.notification_handler
68
- )
69
- await self.client.start_notify(
70
- SERVICE_CHANGED_CHARACTERISTIC, self.service_changed_handler
71
- )
62
+ await self.client.start_notify(UUID_NOTIFICATION_CHARACTERISTIC, self.notification_handler)
63
+ await self.client.start_notify(SERVICE_CHANGED_CHARACTERISTIC, self.service_changed_handler)
72
64
 
73
65
  def getClient(self):
74
66
  return self.client
@@ -79,15 +79,11 @@ class BleMessage:
79
79
 
80
80
  async def get_task(self):
81
81
  hash_map = {"pver": 1, "subCmd": 2, "result": 0}
82
- await self.messageNavigation.post_custom_data(
83
- self.get_json_string(bleOrderCmd.task, hash_map)
84
- )
82
+ await self.messageNavigation.post_custom_data(self.get_json_string(bleOrderCmd.task, hash_map))
85
83
 
86
84
  async def send_ble_alive(self):
87
85
  hash_map = {"ctrl": 1}
88
- await self.messageNavigation.post_custom_data(
89
- self.get_json_string(bleOrderCmd.bleAlive, hash_map)
90
- )
86
+ await self.messageNavigation.post_custom_data(self.get_json_string(bleOrderCmd.bleAlive, hash_map))
91
87
 
92
88
  def clearNotification(self):
93
89
  self.notification = None
@@ -106,9 +102,7 @@ class BleMessage:
106
102
  seqs=1,
107
103
  version=1,
108
104
  subtype=1,
109
- net=dev_net_pb2.DevNet(
110
- todev_ble_sync=1, todev_devinfo_req=dev_net_pb2.DrvDevInfoReq()
111
- ),
105
+ net=dev_net_pb2.DevNet(todev_ble_sync=1, todev_devinfo_req=dev_net_pb2.DrvDevInfoReq()),
112
106
  )
113
107
  byte_arr = luba_msg.SerializeToString()
114
108
  await self.post_custom_data_bytes(byte_arr)
@@ -117,9 +111,7 @@ class BleMessage:
117
111
  request = False
118
112
  type = self.messageNavigation.getTypeValue(0, 5)
119
113
  try:
120
- request = await self.messageNavigation.post(
121
- BleMessage.mEncrypted, BleMessage.mChecksum, False, type, None
122
- )
114
+ request = await self.messageNavigation.post(BleMessage.mEncrypted, BleMessage.mChecksum, False, type, None)
123
115
  # print(request)
124
116
  except Exception as err:
125
117
  # Log.w(TAG, "post requestDeviceStatus interrupted")
@@ -133,9 +125,7 @@ class BleMessage:
133
125
  request = False
134
126
  type = self.messageNavigation.getTypeValue(0, 7)
135
127
  try:
136
- request = await self.messageNavigation.post(
137
- BleMessage.mEncrypted, BleMessage.mChecksum, False, type, None
138
- )
128
+ request = await self.messageNavigation.post(BleMessage.mEncrypted, BleMessage.mChecksum, False, type, None)
139
129
  # print(request)
140
130
  except Exception as err:
141
131
  # Log.w(TAG, "post requestDeviceStatus interrupted")
@@ -318,9 +308,7 @@ class BleMessage:
318
308
  return
319
309
  type_val = self.getTypeValue(1, 19)
320
310
  try:
321
- suc = await self.post(
322
- self.mEncrypted, self.mChecksum, self.mRequireAck, type_val, data
323
- )
311
+ suc = await self.post(self.mEncrypted, self.mChecksum, self.mRequireAck, type_val, data)
324
312
  # int status = suc ? 0 : BlufiCallback.CODE_WRITE_DATA_FAILED
325
313
  # onPostCustomDataResult(status, data)
326
314
  # print(suc)
@@ -333,9 +321,7 @@ class BleMessage:
333
321
  return
334
322
  type_val = self.getTypeValue(1, 19)
335
323
  try:
336
- suc = await self.post(
337
- self.mEncrypted, self.mChecksum, self.mRequireAck, type_val, data
338
- )
324
+ suc = await self.post(self.mEncrypted, self.mChecksum, self.mRequireAck, type_val, data)
339
325
  # int status = suc ? 0 : BlufiCallback.CODE_WRITE_DATA_FAILED
340
326
  # onPostCustomDataResult(status, data)
341
327
  except Exception as err:
@@ -352,17 +338,11 @@ class BleMessage:
352
338
  if data is None:
353
339
  return await self.post_non_data(encrypt, checksum, require_ack, type_of)
354
340
 
355
- return await self.post_contains_data(
356
- encrypt, checksum, require_ack, type_of, data
357
- )
341
+ return await self.post_contains_data(encrypt, checksum, require_ack, type_of, data)
358
342
 
359
- async def post_non_data(
360
- self, encrypt: bool, checksum: bool, require_ack: bool, type_of: int
361
- ) -> bool:
343
+ async def post_non_data(self, encrypt: bool, checksum: bool, require_ack: bool, type_of: int) -> bool:
362
344
  sequence = self.generateSendSequence()
363
- postBytes = self.getPostBytes(
364
- type_of, encrypt, checksum, require_ack, False, sequence, None
365
- )
345
+ postBytes = self.getPostBytes(type_of, encrypt, checksum, require_ack, False, sequence, None)
366
346
  posted = await self.gatt_write(postBytes)
367
347
  return posted and (not require_ack or self.receiveAck(sequence))
368
348
 
@@ -385,9 +365,7 @@ class BleMessage:
385
365
  for index, chunk in enumerate(chunks):
386
366
  frag = index != len(chunks) - 1
387
367
  sequence = self.generateSendSequence()
388
- postBytes = self.getPostBytes(
389
- type_of, encrypt, checksum, require_ack, frag, sequence, chunk
390
- )
368
+ postBytes = self.getPostBytes(type_of, encrypt, checksum, require_ack, frag, sequence, chunk)
391
369
  # print("sequence")
392
370
  # print(sequence)
393
371
  posted = await self.gatt_write(postBytes)
@@ -415,9 +393,7 @@ class BleMessage:
415
393
  ) -> bytes:
416
394
  byteOS = BytesIO()
417
395
  dataLength = 0 if data == None else len(data)
418
- frameCtrl = FrameCtrlData.getFrameCTRLValue(
419
- encrypt, checksum, 0, require_ack, hasFrag
420
- )
396
+ frameCtrl = FrameCtrlData.getFrameCTRLValue(encrypt, checksum, 0, require_ack, hasFrag)
421
397
  byteOS.write(type.to_bytes(1, sys.byteorder))
422
398
  byteOS.write(frameCtrl.to_bytes(1, sys.byteorder))
423
399
  byteOS.write(sequence.to_bytes(1, sys.byteorder))
@@ -19,9 +19,7 @@ def store_sys_data(sys):
19
19
  tard_state_data_list = sys.systemTardStateTunnel.tard_state_data
20
20
  longValue8 = tard_state_data_list[0]
21
21
  longValue9 = tard_state_data_list[1]
22
- print(
23
- "Device status report,deviceState:", longValue8, ",deviceName:", "Luba..."
24
- )
22
+ print("Device status report,deviceState:", longValue8, ",deviceName:", "Luba...")
25
23
  chargeStateTemp = longValue9
26
24
  longValue10 = tard_state_data_list[6]
27
25
  longValue11 = tard_state_data_list[7]
@@ -59,5 +59,4 @@ class BlufiNotifyData:
59
59
  # JADX INFO: Access modifiers changed from: package-private
60
60
  def getDataArray(self):
61
61
  """Generated source for method getDataArray"""
62
- print("data Array")
63
62
  return self.mDataOS.getvalue()