pymammotion 0.2.62__py3-none-any.whl → 0.5.51__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 (135) hide show
  1. pymammotion/__init__.py +9 -6
  2. pymammotion/aliyun/client.py +235 -0
  3. pymammotion/aliyun/cloud_gateway.py +320 -69
  4. pymammotion/aliyun/model/aep_response.py +1 -2
  5. pymammotion/aliyun/model/dev_by_account_response.py +170 -23
  6. pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
  7. pymammotion/aliyun/model/regions_response.py +3 -3
  8. pymammotion/aliyun/model/session_by_authcode_response.py +2 -2
  9. pymammotion/aliyun/model/thing_response.py +12 -0
  10. pymammotion/aliyun/regions.py +62 -0
  11. pymammotion/aliyun/tea/core.py +297 -0
  12. pymammotion/bluetooth/ble.py +11 -15
  13. pymammotion/bluetooth/ble_message.py +389 -106
  14. pymammotion/bluetooth/model/atomic_integer.py +54 -0
  15. pymammotion/const.py +3 -0
  16. pymammotion/data/model/__init__.py +1 -2
  17. pymammotion/data/model/device.py +92 -240
  18. pymammotion/data/model/device_config.py +10 -24
  19. pymammotion/data/model/device_info.py +35 -0
  20. pymammotion/data/model/device_limits.py +49 -0
  21. pymammotion/data/model/enums.py +12 -2
  22. pymammotion/data/model/errors.py +12 -0
  23. pymammotion/data/model/events.py +14 -0
  24. pymammotion/data/model/generate_geojson.py +521 -0
  25. pymammotion/data/model/generate_route_information.py +3 -4
  26. pymammotion/data/model/hash_list.py +384 -48
  27. pymammotion/data/model/location.py +4 -4
  28. pymammotion/data/model/mowing_modes.py +24 -1
  29. pymammotion/data/model/raw_data.py +215 -0
  30. pymammotion/data/model/region_data.py +10 -11
  31. pymammotion/data/model/report_info.py +62 -6
  32. pymammotion/data/model/work.py +27 -0
  33. pymammotion/data/mower_state_manager.py +316 -0
  34. pymammotion/data/mqtt/event.py +73 -28
  35. pymammotion/data/mqtt/mammotion_properties.py +257 -0
  36. pymammotion/data/mqtt/properties.py +93 -78
  37. pymammotion/data/mqtt/status.py +18 -17
  38. pymammotion/event/event.py +32 -8
  39. pymammotion/homeassistant/__init__.py +3 -0
  40. pymammotion/homeassistant/mower_api.py +484 -0
  41. pymammotion/homeassistant/rtk_api.py +54 -0
  42. pymammotion/http/__init__.py +0 -0
  43. pymammotion/http/encryption.py +220 -0
  44. pymammotion/http/http.py +652 -44
  45. pymammotion/http/model/__init__.py +0 -0
  46. pymammotion/{aliyun/model/stream_subscription_response.py → http/model/camera_stream.py} +14 -2
  47. pymammotion/http/model/http.py +160 -9
  48. pymammotion/http/model/response_factory.py +61 -0
  49. pymammotion/http/model/rtk.py +16 -0
  50. pymammotion/mammotion/commands/abstract_message.py +7 -5
  51. pymammotion/mammotion/commands/mammotion_command.py +32 -3
  52. pymammotion/mammotion/commands/messages/basestation.py +43 -0
  53. pymammotion/mammotion/commands/messages/driver.py +61 -29
  54. pymammotion/mammotion/commands/messages/media.py +68 -15
  55. pymammotion/mammotion/commands/messages/navigation.py +61 -25
  56. pymammotion/mammotion/commands/messages/network.py +93 -100
  57. pymammotion/mammotion/commands/messages/ota.py +18 -18
  58. pymammotion/mammotion/commands/messages/system.py +97 -72
  59. pymammotion/mammotion/commands/messages/video.py +17 -12
  60. pymammotion/mammotion/devices/__init__.py +27 -3
  61. pymammotion/mammotion/devices/base.py +50 -127
  62. pymammotion/mammotion/devices/mammotion.py +447 -212
  63. pymammotion/mammotion/devices/mammotion_bluetooth.py +105 -60
  64. pymammotion/mammotion/devices/mammotion_cloud.py +157 -105
  65. pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
  66. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  67. pymammotion/mammotion/devices/managers/managers.py +81 -0
  68. pymammotion/mammotion/devices/mower_device.py +124 -0
  69. pymammotion/mammotion/devices/mower_manager.py +107 -0
  70. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  71. pymammotion/mammotion/devices/rtk_cloud.py +113 -0
  72. pymammotion/mammotion/devices/rtk_device.py +50 -0
  73. pymammotion/mammotion/devices/rtk_manager.py +122 -0
  74. pymammotion/mqtt/__init__.py +2 -1
  75. pymammotion/mqtt/aliyun_mqtt.py +232 -0
  76. pymammotion/mqtt/linkkit/__init__.py +5 -0
  77. pymammotion/mqtt/linkkit/h2client.py +585 -0
  78. pymammotion/mqtt/linkkit/linkkit.py +3023 -0
  79. pymammotion/mqtt/mammotion_mqtt.py +176 -169
  80. pymammotion/mqtt/mqtt_models.py +66 -0
  81. pymammotion/proto/__init__.py +4839 -4
  82. pymammotion/proto/basestation.proto +8 -0
  83. pymammotion/proto/basestation_pb2.py +11 -9
  84. pymammotion/proto/basestation_pb2.pyi +16 -2
  85. pymammotion/proto/dev_net.proto +79 -55
  86. pymammotion/proto/dev_net_pb2.py +60 -56
  87. pymammotion/proto/dev_net_pb2.pyi +49 -6
  88. pymammotion/proto/luba_msg.proto +2 -1
  89. pymammotion/proto/luba_msg_pb2.py +6 -6
  90. pymammotion/proto/luba_msg_pb2.pyi +1 -0
  91. pymammotion/proto/luba_mul.proto +62 -1
  92. pymammotion/proto/luba_mul_pb2.py +38 -22
  93. pymammotion/proto/luba_mul_pb2.pyi +94 -7
  94. pymammotion/proto/mctrl_driver.proto +44 -4
  95. pymammotion/proto/mctrl_driver_pb2.py +26 -14
  96. pymammotion/proto/mctrl_driver_pb2.pyi +66 -11
  97. pymammotion/proto/mctrl_nav.proto +97 -51
  98. pymammotion/proto/mctrl_nav_pb2.py +75 -67
  99. pymammotion/proto/mctrl_nav_pb2.pyi +142 -56
  100. pymammotion/proto/mctrl_ota.proto +40 -2
  101. pymammotion/proto/mctrl_ota_pb2.py +23 -13
  102. pymammotion/proto/mctrl_ota_pb2.pyi +67 -4
  103. pymammotion/proto/mctrl_pept.proto +8 -3
  104. pymammotion/proto/mctrl_pept_pb2.py +8 -6
  105. pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
  106. pymammotion/proto/mctrl_sys.proto +325 -86
  107. pymammotion/proto/mctrl_sys_pb2.py +162 -98
  108. pymammotion/proto/mctrl_sys_pb2.pyi +451 -25
  109. pymammotion/proto/message_pool.py +3 -0
  110. pymammotion/proto/py.typed +0 -0
  111. pymammotion/utility/constant/device_constant.py +65 -21
  112. pymammotion/utility/datatype_converter.py +13 -12
  113. pymammotion/utility/device_config.py +755 -0
  114. pymammotion/utility/device_type.py +218 -21
  115. pymammotion/utility/map.py +238 -51
  116. pymammotion/utility/mur_mur_hash.py +159 -0
  117. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info}/METADATA +27 -31
  118. pymammotion-0.5.51.dist-info/RECORD +152 -0
  119. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info}/WHEEL +1 -1
  120. pymammotion/aliyun/cloud_service.py +0 -65
  121. pymammotion/data/model/plan.py +0 -58
  122. pymammotion/data/state_manager.py +0 -130
  123. pymammotion/proto/basestation.py +0 -59
  124. pymammotion/proto/common.py +0 -12
  125. pymammotion/proto/dev_net.py +0 -381
  126. pymammotion/proto/luba_msg.py +0 -81
  127. pymammotion/proto/luba_mul.py +0 -76
  128. pymammotion/proto/mctrl_driver.py +0 -100
  129. pymammotion/proto/mctrl_nav.py +0 -660
  130. pymammotion/proto/mctrl_ota.py +0 -48
  131. pymammotion/proto/mctrl_pept.py +0 -41
  132. pymammotion/proto/mctrl_sys.py +0 -574
  133. pymammotion-0.2.62.dist-info/RECORD +0 -125
  134. /pymammotion/{http/_init_.py → bluetooth/model/__init__.py} +0 -0
  135. {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,232 @@
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._iot_token = iot_token
53
+ self._mqtt_username = f"{device_name}&{product_key}"
54
+ # linkkit provides the correct MQTT service for all of this and uses paho under the hood
55
+ if client_id is None:
56
+ client_id = f"python-{device_name}"
57
+ self._mqtt_client_id = f"{client_id}|securemode=2,signmethod=hmacsha1|"
58
+ sign_content = f"clientId{client_id}deviceName{device_name}productKey{product_key}"
59
+ self._mqtt_password = hmac.new(
60
+ device_secret.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha1
61
+ ).hexdigest()
62
+
63
+ self._client_id = client_id
64
+ self.loop = asyncio.get_running_loop()
65
+
66
+ self._linkkit_client = LinkKit(
67
+ region_id,
68
+ product_key,
69
+ device_name,
70
+ device_secret,
71
+ auth_type="",
72
+ client_id=client_id,
73
+ password=self._mqtt_password,
74
+ username=self._mqtt_username,
75
+ )
76
+
77
+ self._linkkit_client.enable_logger(level=logging.ERROR)
78
+ self._linkkit_client.on_connect = self._thing_on_connect
79
+ self._linkkit_client.on_disconnect = self._on_disconnect
80
+ self._linkkit_client.on_thing_enable = self._thing_on_thing_enable
81
+ self._linkkit_client.on_topic_message = self._thing_on_topic_message
82
+ self._mqtt_host = f"{self._product_key}.iot-as-mqtt.{region_id}.aliyuncs.com"
83
+
84
+ def connect_async(self) -> None:
85
+ """Connect async to MQTT Server."""
86
+ logger.info("Connecting...")
87
+ if self._linkkit_client.check_state() is LinkKit.LinkKitState.INITIALIZED:
88
+ self._linkkit_client.thing_setup()
89
+ self._linkkit_client.connect_async()
90
+
91
+ def disconnect(self) -> None:
92
+ """Disconnect from MQTT Server."""
93
+ logger.info("Disconnecting...")
94
+
95
+ self._linkkit_client.disconnect()
96
+
97
+ def _thing_on_thing_enable(self, user_data) -> None:
98
+ """Is called when Thing is enabled."""
99
+ logger.debug("on_thing_enable")
100
+ self.is_connected = True
101
+ # logger.debug('subscribe_topic, topic:%s' % echo_topic)
102
+ # self._linkkit_client.subscribe_topic(echo_topic, 0)
103
+ self._linkkit_client.subscribe_topic(
104
+ f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
105
+ )
106
+ self._linkkit_client.subscribe_topic(
107
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/event/property/post_reply"
108
+ )
109
+ self._linkkit_client.subscribe_topic(
110
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/status/notify"
111
+ )
112
+ self._linkkit_client.subscribe_topic(
113
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/connect/event/notify"
114
+ )
115
+ self._linkkit_client.subscribe_topic(
116
+ f"/sys/{self._product_key}/{self._device_name}/app/down/_thing/event/notify"
117
+ )
118
+ self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events")
119
+ self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status")
120
+ self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties")
121
+ self._linkkit_client.subscribe_topic(
122
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/model/down_raw"
123
+ )
124
+
125
+ self._linkkit_client.publish_topic(
126
+ f"/sys/{self._product_key}/{self._device_name}/app/up/account/bind",
127
+ json.dumps(
128
+ {
129
+ "id": "msgid1",
130
+ "version": "1.0",
131
+ "request": {"clientId": self._mqtt_username},
132
+ "params": {"iotToken": self._iot_token},
133
+ }
134
+ ),
135
+ )
136
+
137
+ if self.on_ready:
138
+ self.is_ready = True
139
+ future = asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop)
140
+ asyncio.wrap_future(future, loop=self.loop)
141
+
142
+ def unsubscribe(self) -> None:
143
+ self._linkkit_client.unsubscribe_topic(
144
+ f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
145
+ )
146
+ self._linkkit_client.unsubscribe_topic(
147
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/event/property/post_reply"
148
+ )
149
+ self._linkkit_client.unsubscribe_topic(
150
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/status/notify"
151
+ )
152
+ self._linkkit_client.unsubscribe_topic(
153
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/connect/event/notify"
154
+ )
155
+ self._linkkit_client.unsubscribe_topic(
156
+ f"/sys/{self._product_key}/{self._device_name}/app/down/_thing/event/notify"
157
+ )
158
+ self._linkkit_client.unsubscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events")
159
+ self._linkkit_client.unsubscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status")
160
+ self._linkkit_client.unsubscribe_topic(
161
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties"
162
+ )
163
+ self._linkkit_client.unsubscribe_topic(
164
+ f"/sys/{self._product_key}/{self._device_name}/app/down/thing/model/down_raw"
165
+ )
166
+
167
+ def _thing_on_topic_message(self, topic: str, payload: str, qos, user_data) -> None:
168
+ """Is called when thing topic comes in."""
169
+ logger.debug(
170
+ "on_topic_message, receive message, topic:%s, payload:%s, qos:%d",
171
+ topic,
172
+ payload,
173
+ qos,
174
+ )
175
+ json_payload = json.loads(payload)
176
+ iot_id = json_payload.get("params", {}).get("iotId", "")
177
+ if iot_id != "" and self.on_message is not None:
178
+ future = asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop)
179
+ asyncio.wrap_future(future, loop=self.loop)
180
+
181
+ def _thing_on_connect(self, session_flag, rc, user_data) -> None:
182
+ """Handle connection event and execute callback if set."""
183
+ self.is_connected = True
184
+ if self.on_connected is not None:
185
+ future = asyncio.run_coroutine_threadsafe(self.on_connected(), self.loop)
186
+ asyncio.wrap_future(future, loop=self.loop)
187
+
188
+ logger.debug("on_connect, session_flag:%d, rc:%d", session_flag, rc)
189
+
190
+ def _on_disconnect(self, _client, _userdata) -> None:
191
+ """Is called on disconnect."""
192
+ if self._linkkit_client.check_state() is LinkKit.LinkKitState.DISCONNECTED:
193
+ logger.info("Disconnected")
194
+ self.is_connected = False
195
+ self.is_ready = False
196
+ if self.on_disconnected:
197
+ future = asyncio.run_coroutine_threadsafe(self.on_disconnected(), self.loop)
198
+ asyncio.wrap_future(future, loop=self.loop)
199
+
200
+ def _on_message(self, _client, _userdata, message: MQTTMessage) -> None:
201
+ """Is called when message is received."""
202
+ logger.debug("Message on topic %s", message.topic)
203
+
204
+ payload = json.loads(message.payload)
205
+ if message.topic.endswith("/app/down/thing/events"):
206
+ event = ThingEventMessage(**payload)
207
+ params = event.params
208
+ if params.identifier == "device_protobuf_msg_event":
209
+ content = LubaMsg().parse(base64.b64decode(params.value.content))
210
+
211
+ logger.info("Unhandled protobuf event: %s", betterproto2.which_one_of(content, "LubaSubMsg"))
212
+ elif params.identifier == "device_warning_event":
213
+ logger.debug("identifier event: %s", params.identifier)
214
+ else:
215
+ logger.info("Unhandled event: %s", params.identifier)
216
+ elif message.topic.endswith("/app/down/thing/status"):
217
+ # the tell if a device has come back online
218
+ # lastStatus
219
+ # 1 online?
220
+ # 3 offline?
221
+ status = ThingStatusMessage(**payload)
222
+ logger.debug(status.params.status.value)
223
+ elif message.topic.endswith("/app/down/thing/properties"):
224
+ properties = ThingPropertiesMessage(**payload)
225
+ logger.debug("properties: %s", properties)
226
+ else:
227
+ logger.debug("Unhandled topic: %s", message.topic)
228
+ logger.debug(payload)
229
+
230
+ async def send_cloud_command(self, iot_id: str, command: bytes) -> str:
231
+ """Return internal cloud client."""
232
+ 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"]