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,237 @@
1
+ """MammotionMQTT."""
2
+
3
+ import asyncio
4
+ import base64
5
+ from collections.abc import Awaitable, Callable
6
+ import hashlib
7
+ import hmac
8
+ import json
9
+ import logging
10
+ from logging import getLogger
11
+
12
+ import betterproto2
13
+ from paho.mqtt.client import MQTTMessage
14
+
15
+ from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
16
+ from pymammotion.data.mqtt.event import ThingEventMessage
17
+ from pymammotion.data.mqtt.properties import ThingPropertiesMessage
18
+ from pymammotion.data.mqtt.status import ThingStatusMessage
19
+ from pymammotion.mqtt.linkkit.linkkit import LinkKit
20
+ from pymammotion.proto import LubaMsg
21
+
22
+ logger = getLogger(__name__)
23
+
24
+
25
+ class AliyunMQTT:
26
+ """MQTT client for pymammotion."""
27
+
28
+ def __init__(
29
+ self,
30
+ region_id: str,
31
+ product_key: str,
32
+ device_name: str,
33
+ device_secret: str,
34
+ iot_token: str,
35
+ cloud_client: CloudIOTGateway,
36
+ client_id: str | None = None,
37
+ ) -> None:
38
+ """Create instance of MammotionMQTT."""
39
+ super().__init__()
40
+ self._cloud_client = cloud_client
41
+ self.is_connected = False
42
+ self.is_ready = False
43
+ self.on_connected: Callable[[], Awaitable[None]] | None = None
44
+ self.on_ready: Callable[[], Awaitable[None]] | None = None
45
+ self.on_error: Callable[[str], Awaitable[None]] | None = None
46
+ self.on_disconnected: Callable[[], Awaitable[None]] | None = None
47
+ self.on_message: Callable[[str, str, str], Awaitable[None]] | None = None
48
+
49
+ self._product_key = product_key
50
+ self._device_name = device_name
51
+ self._device_secret = device_secret
52
+ self._mqtt_username = f"{device_name}&{product_key}"
53
+ # linkkit provides the correct MQTT service for all of this and uses paho under the hood
54
+ if client_id is None:
55
+ client_id = f"python-{device_name}"
56
+ self._mqtt_client_id = f"{client_id}|securemode=2,signmethod=hmacsha1|"
57
+ sign_content = f"clientId{client_id}deviceName{device_name}productKey{product_key}"
58
+ self._mqtt_password = hmac.new(
59
+ device_secret.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha1
60
+ ).hexdigest()
61
+
62
+ self._client_id = client_id
63
+ self.loop = asyncio.get_running_loop()
64
+
65
+ self._linkkit_client = LinkKit(
66
+ region_id,
67
+ product_key,
68
+ device_name,
69
+ device_secret,
70
+ auth_type="",
71
+ client_id=client_id,
72
+ password=self._mqtt_password,
73
+ username=self._mqtt_username,
74
+ )
75
+
76
+ self._linkkit_client.enable_logger(level=logging.ERROR)
77
+ self._linkkit_client.on_connect = self._thing_on_connect
78
+ self._linkkit_client.on_disconnect = self._on_disconnect
79
+ self._linkkit_client.on_thing_enable = self._thing_on_thing_enable
80
+ self._linkkit_client.on_topic_message = self._thing_on_topic_message
81
+ self._mqtt_host = f"{self._product_key}.iot-as-mqtt.{region_id}.aliyuncs.com"
82
+
83
+ @property
84
+ def iot_token(self) -> str:
85
+ if authcode_response := self._cloud_client.session_by_authcode_response.data:
86
+ return authcode_response.iotToken
87
+ return ""
88
+
89
+ def connect_async(self) -> None:
90
+ """Connect async to MQTT Server."""
91
+ logger.info("Connecting...")
92
+ if self._linkkit_client.check_state() is LinkKit.LinkKitState.INITIALIZED:
93
+ self._linkkit_client.thing_setup()
94
+ self._linkkit_client.connect_async()
95
+
96
+ def disconnect(self) -> None:
97
+ """Disconnect from MQTT Server."""
98
+ logger.info("Disconnecting...")
99
+
100
+ self._linkkit_client.disconnect()
101
+
102
+ def _thing_on_thing_enable(self, user_data) -> None:
103
+ """Is called when Thing is enabled."""
104
+ logger.debug("on_thing_enable")
105
+ self.is_connected = True
106
+ # logger.debug('subscribe_topic, topic:%s' % echo_topic)
107
+ # self._linkkit_client.subscribe_topic(echo_topic, 0)
108
+ self._linkkit_client.subscribe_topic(
109
+ f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
110
+ )
111
+ self._linkkit_client.subscribe_topic(
112
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/event/property/post_reply"
113
+ )
114
+ self._linkkit_client.subscribe_topic(
115
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/status/notify"
116
+ )
117
+ self._linkkit_client.subscribe_topic(
118
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/connect/event/notify"
119
+ )
120
+ self._linkkit_client.subscribe_topic(
121
+ f"/sys/{self._product_key}/{self._device_name}/app/down/_thing/event/notify"
122
+ )
123
+ self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events")
124
+ self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status")
125
+ self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties")
126
+ self._linkkit_client.subscribe_topic(
127
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/model/down_raw"
128
+ )
129
+
130
+ self._linkkit_client.publish_topic(
131
+ f"/sys/{self._product_key}/{self._device_name}/app/up/account/bind",
132
+ json.dumps(
133
+ {
134
+ "id": "msgid1",
135
+ "version": "1.0",
136
+ "request": {"clientId": self._mqtt_username},
137
+ "params": {"iotToken": self.iot_token},
138
+ }
139
+ ),
140
+ )
141
+
142
+ if self.on_ready:
143
+ self.is_ready = True
144
+ future = asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop)
145
+ asyncio.wrap_future(future, loop=self.loop)
146
+
147
+ def unsubscribe(self) -> None:
148
+ self._linkkit_client.unsubscribe_topic(
149
+ f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
150
+ )
151
+ self._linkkit_client.unsubscribe_topic(
152
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/event/property/post_reply"
153
+ )
154
+ self._linkkit_client.unsubscribe_topic(
155
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/status/notify"
156
+ )
157
+ self._linkkit_client.unsubscribe_topic(
158
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/connect/event/notify"
159
+ )
160
+ self._linkkit_client.unsubscribe_topic(
161
+ f"/sys/{self._product_key}/{self._device_name}/app/down/_thing/event/notify"
162
+ )
163
+ self._linkkit_client.unsubscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events")
164
+ self._linkkit_client.unsubscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status")
165
+ self._linkkit_client.unsubscribe_topic(
166
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties"
167
+ )
168
+ self._linkkit_client.unsubscribe_topic(
169
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/model/down_raw"
170
+ )
171
+
172
+ def _thing_on_topic_message(self, topic: str, payload: str, qos, user_data) -> None:
173
+ """Is called when thing topic comes in."""
174
+ logger.debug(
175
+ "on_topic_message, receive message, topic:%s, payload:%s, qos:%d",
176
+ topic,
177
+ payload,
178
+ qos,
179
+ )
180
+ json_payload = json.loads(payload)
181
+ iot_id = json_payload.get("params", {}).get("iotId", "")
182
+ if iot_id != "" and self.on_message is not None:
183
+ future = asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop)
184
+ asyncio.wrap_future(future, loop=self.loop)
185
+
186
+ def _thing_on_connect(self, session_flag, rc, user_data) -> None:
187
+ """Handle connection event and execute callback if set."""
188
+ self.is_connected = True
189
+ if self.on_connected is not None:
190
+ future = asyncio.run_coroutine_threadsafe(self.on_connected(), self.loop)
191
+ asyncio.wrap_future(future, loop=self.loop)
192
+
193
+ logger.debug("on_connect, session_flag:%d, rc:%d", session_flag, rc)
194
+
195
+ def _on_disconnect(self, _client, _userdata) -> None:
196
+ """Is called on disconnect."""
197
+ if self._linkkit_client.check_state() is LinkKit.LinkKitState.DISCONNECTED:
198
+ logger.info("Disconnected")
199
+ self.is_connected = False
200
+ self.is_ready = False
201
+ if self.on_disconnected:
202
+ future = asyncio.run_coroutine_threadsafe(self.on_disconnected(), self.loop)
203
+ asyncio.wrap_future(future, loop=self.loop)
204
+
205
+ def _on_message(self, _client, _userdata, message: MQTTMessage) -> None:
206
+ """Is called when message is received."""
207
+ logger.debug("Message on topic %s", message.topic)
208
+
209
+ payload = json.loads(message.payload)
210
+ if message.topic.endswith("/app/down/thing/events"):
211
+ event = ThingEventMessage(**payload)
212
+ params = event.params
213
+ if params.identifier == "device_protobuf_msg_event":
214
+ content = LubaMsg().parse(base64.b64decode(params.value.content))
215
+
216
+ logger.info("Unhandled protobuf event: %s", betterproto2.which_one_of(content, "LubaSubMsg"))
217
+ elif params.identifier == "device_warning_event":
218
+ logger.debug("identifier event: %s", params.identifier)
219
+ else:
220
+ logger.info("Unhandled event: %s", params.identifier)
221
+ elif message.topic.endswith("/app/down/thing/status"):
222
+ # the tell if a device has come back online
223
+ # lastStatus
224
+ # 1 online?
225
+ # 3 offline?
226
+ status = ThingStatusMessage(**payload)
227
+ logger.debug(status.params.status.value)
228
+ elif message.topic.endswith("/app/down/thing/properties"):
229
+ properties = ThingPropertiesMessage(**payload)
230
+ logger.debug("properties: %s", properties)
231
+ else:
232
+ logger.debug("Unhandled topic: %s", message.topic)
233
+ logger.debug(payload)
234
+
235
+ async def send_cloud_command(self, iot_id: str, command: bytes) -> str:
236
+ """Return internal cloud client."""
237
+ return await self._cloud_client.send_cloud_command(iot_id, command)
@@ -0,0 +1,5 @@
1
+ """Package for linkkit."""
2
+
3
+ from .linkkit import LinkKit
4
+
5
+ __all__ = ["LinkKit"]