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,31 @@
1
+ from dataclasses import dataclass
2
+
3
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
4
+
5
+
6
+ @dataclass
7
+ class Camera(DataClassORJSONMixin):
8
+ cameraId: int
9
+ token: str
10
+
11
+
12
+ @dataclass
13
+ class StreamSubscriptionResponse(DataClassORJSONMixin):
14
+ appid: str
15
+ cameras: list[Camera]
16
+ channelName: str
17
+ token: str
18
+ uid: int
19
+ license: str | None = None
20
+ availableTime: int | None = None
21
+
22
+
23
+ @dataclass
24
+ class VideoResourceResponse(DataClassORJSONMixin):
25
+ id: str
26
+ deviceId: str
27
+ deviceName: str
28
+ cycleType: int
29
+ usageYearMonth: str
30
+ totalTime: int
31
+ availableTime: int
@@ -0,0 +1,249 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Annotated, Generic, Literal, TypeVar
3
+
4
+ from mashumaro import DataClassDictMixin
5
+ from mashumaro.config import BaseConfig
6
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
7
+ from mashumaro.types import Alias
8
+
9
+
10
+ class UnauthorizedException(Exception):
11
+ pass
12
+
13
+
14
+ DataT = TypeVar("DataT")
15
+
16
+
17
+ @dataclass
18
+ class ErrorInfo(DataClassDictMixin):
19
+ code: str
20
+ platform: str
21
+ module: str
22
+ variant: str
23
+ level: str
24
+ description: str
25
+ en_implication: str
26
+ en_solution: str
27
+ zh_implication: str
28
+ zh_solution: str
29
+ de_implication: str
30
+ de_solution: str
31
+ fr_implication: str
32
+ fr_solution: str
33
+ it_implication: str
34
+ it_solution: str
35
+ ja_implication: str
36
+ ja_solution: str
37
+ es_implication: str
38
+ es_solution: str
39
+ cs_implication: str
40
+ cs_solution: str
41
+ ko_implication: str
42
+ ko_solution: str
43
+ sk_implication: str
44
+ sk_solution: str
45
+ pl_implication: str
46
+ pl_solution: str
47
+ nl_implication: str
48
+ nl_solution: str
49
+ da_implication: str
50
+ da_solution: str
51
+ sr_implication: str
52
+ sr_solution: str
53
+ sv_implication: str
54
+ sv_solution: str
55
+ sl_implication: str
56
+ sl_solution: str
57
+ pt_implication: str
58
+ pt_solution: str
59
+ hu_implication: str
60
+ hu_solution: str
61
+ hr_implication: str
62
+ hr_solution: str
63
+ no_implication: str
64
+ no_solution: str
65
+ fi_implication: str
66
+ fi_solution: str
67
+ ro_implication: str
68
+ ro_solution: str
69
+ bg_implication: str
70
+ bg_solution: str
71
+ et_implication: str
72
+ et_solution: str
73
+ lv_implication: str
74
+ lv_solution: str
75
+ lt_implication: str
76
+ lt_solution: str
77
+
78
+
79
+ @dataclass
80
+ class SettingVo(DataClassORJSONMixin):
81
+ """Device setting configuration."""
82
+
83
+ type: int = 0
84
+ is_switch: Annotated[int, Alias("isSwitch")] = 0
85
+
86
+
87
+ @dataclass
88
+ class LocationVo(DataClassORJSONMixin):
89
+ """Device location information."""
90
+
91
+ date_time: Annotated[str, Alias("dateTime")] = ""
92
+ date_timestamp: Annotated[int, Alias("dateTimestamp")] = 0
93
+ location: list[float] = field(default_factory=lambda: [0.0, 0.0])
94
+
95
+
96
+ @dataclass
97
+ class DeviceInfo:
98
+ """Complete device information."""
99
+
100
+ iot_id: Annotated[str, Alias("iotId")] = ""
101
+ device_id: Annotated[str, Alias("deviceId")] = ""
102
+ device_name: Annotated[str, Alias("deviceName")] = ""
103
+ device_type: Annotated[str, Alias("deviceType")] = ""
104
+ series: str = ""
105
+ product_series: Annotated[str, Alias("productSeries")] = ""
106
+ icon_code: Annotated[str, Alias("iconCode")] = ""
107
+ generation: int = 0
108
+ status: int = 0
109
+ is_subscribe: Annotated[int, Alias("isSubscribe")] = 0
110
+ setting_vos: Annotated[list[SettingVo], Alias("settingVos")] = field(default_factory=list)
111
+ active_time: Annotated[str, Alias("activeTime")] = ""
112
+ active_timestamp: Annotated[int, Alias("activeTimestamp")] = 0
113
+ location_vo: Annotated[LocationVo | None, Alias("locationVo")] = None
114
+
115
+ class Config(BaseConfig):
116
+ allow_deserialization_not_by_alias = True
117
+
118
+
119
+ @dataclass
120
+ class DeviceRecord(DataClassORJSONMixin):
121
+ identity_id: Annotated[str, Alias("identityId")]
122
+ iot_id: Annotated[str, Alias("iotId")]
123
+ product_key: Annotated[str, Alias("productKey")]
124
+ device_name: Annotated[str, Alias("deviceName")]
125
+ owned: int
126
+ status: int
127
+ bind_time: Annotated[int, Alias("bindTime")]
128
+ create_time: Annotated[str, Alias("createTime")]
129
+
130
+ class Config(BaseConfig):
131
+ allow_deserialization_not_by_alias = True
132
+
133
+
134
+ @dataclass
135
+ class DeviceRecords(DataClassORJSONMixin):
136
+ records: list[DeviceRecord]
137
+ total: int
138
+ size: int
139
+ current: int
140
+ pages: int
141
+
142
+
143
+ @dataclass
144
+ class MQTTConnection(DataClassORJSONMixin):
145
+ host: str
146
+ jwt: str
147
+ client_id: Annotated[str, Alias("clientId")]
148
+ username: str
149
+
150
+
151
+ @dataclass
152
+ class Response(DataClassORJSONMixin, Generic[DataT]):
153
+ code: int
154
+ msg: str
155
+ request_id: Annotated[str, Alias("requestId")] | None = None
156
+ data: DataT | None = None
157
+
158
+ class Config(BaseConfig):
159
+ omit_default = True
160
+
161
+
162
+ @dataclass
163
+ class LoginResponseUserInformation(DataClassORJSONMixin):
164
+ areaCode: str
165
+ domainAbbreviation: str
166
+ userId: str
167
+ userAccount: str
168
+ authType: str
169
+ email: str | None = None
170
+
171
+ class Config(BaseConfig):
172
+ omit_none = True
173
+
174
+
175
+ @dataclass
176
+ class JWTTokenInfo(DataClassORJSONMixin):
177
+ """specifically for newer devices and mqtt"""
178
+
179
+ iot: str # iot domain e.g api-iot-business-eu-dcdn.mammotion.com
180
+ robot: str # e.g api-robot-eu.mammotion.com
181
+
182
+
183
+ @dataclass
184
+ class LoginResponseData(DataClassORJSONMixin):
185
+ access_token: str
186
+ token_type: Literal["bearer", "Bearer"]
187
+ refresh_token: str
188
+ expires_in: int
189
+ authorization_code: str
190
+ userInformation: LoginResponseUserInformation
191
+ jti: str = None
192
+ grant_type: Literal["password", "Password"] = None
193
+ scope: Literal["read", "Read"] = None
194
+
195
+ class Config(BaseConfig):
196
+ omit_none = True
197
+
198
+
199
+ @dataclass
200
+ class FirmwareVersions(DataClassORJSONMixin):
201
+ firmware_version: Annotated[str, Alias("firmwareVersion")] = ""
202
+ firmware_code: Annotated[str, Alias("firmwareCode")] = ""
203
+ firmware_latest_version: Annotated[str, Alias("firmwareLatestVersion")] = ""
204
+ firmware_type: Annotated[str, Alias("firmwareType")] = ""
205
+
206
+ class Config(BaseConfig):
207
+ allow_deserialization_not_by_alias = True
208
+
209
+
210
+ @dataclass
211
+ class ProductVersionInfo(DataClassORJSONMixin):
212
+ release_note: Annotated[str, Alias("releaseNote")] = ""
213
+ release_version: Annotated[str, Alias("releaseVersion")] = ""
214
+ data_location: str | None = None
215
+
216
+ class Config(BaseConfig):
217
+ allow_deserialization_not_by_alias = True
218
+
219
+
220
+ @dataclass
221
+ class CheckDeviceVersion(DataClassORJSONMixin):
222
+ cause_code: Annotated[int, Alias("causeCode")] = 0
223
+ product_version_info_vo: Annotated[ProductVersionInfo | None, Alias("productVersionInfoVo")] = None
224
+ progress: int | None = 0
225
+ upgradeable: bool = False
226
+ device_id: Annotated[str, Alias("deviceId")] = ""
227
+ device_name: Annotated[str | None, Alias("deviceName")] = ""
228
+ current_version: Annotated[str, Alias("currentVersion")] = ""
229
+ isupgrading: bool | None = False
230
+ cause_msg: Annotated[str, Alias("causeMsg")] = ""
231
+
232
+ class Config(BaseConfig):
233
+ allow_deserialization_not_by_alias = True
234
+
235
+ def __eq__(self, other):
236
+ if not isinstance(other, CheckDeviceVersion):
237
+ return NotImplemented
238
+
239
+ if self.device_id != other.device_id or self.current_version != other.current_version:
240
+ return False
241
+
242
+ if self.product_version_info_vo and other.product_version_info_vo:
243
+ if self.product_version_info_vo.release_version != other.product_version_info_vo.release_version:
244
+ return False
245
+ return True
246
+ elif self.product_version_info_vo is None and other.product_version_info_vo is None:
247
+ return False
248
+ else:
249
+ return True
@@ -0,0 +1,61 @@
1
+ from typing import TypeVar, Union, get_args, get_origin
2
+
3
+ from pymammotion.http.model.http import Response
4
+
5
+ T = TypeVar("T")
6
+
7
+
8
+ def deserialize_data(value, target_type):
9
+ """Deserialize data into a specified target type.
10
+
11
+ The function handles deserialization of basic types, lists, and unions. It
12
+ recursively processes list elements and supports optional types by handling
13
+ Union[T, None]. For custom types with a `from_dict` method, it calls this
14
+ method for deserialization. If the target type is unknown or unsupported, it
15
+ returns the value unchanged.
16
+
17
+ Args:
18
+ value: The data to be deserialized.
19
+ target_type (type): The desired type into which the data should be deserialized.
20
+
21
+ Returns:
22
+ The deserialized data in the specified target type.
23
+
24
+ """
25
+ if value is None:
26
+ return None
27
+
28
+ origin = get_origin(target_type)
29
+ args = get_args(target_type)
30
+
31
+ if origin is list and args:
32
+ item_type = args[0]
33
+ return [deserialize_data(v, item_type) for v in value]
34
+
35
+ if origin is Union:
36
+ # Support Optional[T] = Union[T, None]
37
+ non_none_types = [t for t in args if t is not type(None)]
38
+ if len(non_none_types) == 1:
39
+ target = non_none_types[0]
40
+ # Handle Response[list[type]] case
41
+ if get_origin(target) is list and get_args(target):
42
+ item_type = get_args(target)[0]
43
+ return [deserialize_data(v, item_type) for v in value]
44
+ return deserialize_data(value, target)
45
+
46
+ if hasattr(target_type, "from_dict"):
47
+ return target_type.from_dict(value)
48
+
49
+ return value # fallback: unknown type, leave as-is
50
+
51
+
52
+ def response_factory(response_cls: type[Response[T]], raw_dict: dict) -> Response[T]:
53
+ # Extract the type of the generic `data` field
54
+ """Create a Response instance from a dictionary."""
55
+ data_type = get_args(response_cls)[0] if get_args(response_cls) else None
56
+
57
+ if data_type:
58
+ data_value = deserialize_data(raw_dict.get("data"), data_type)
59
+ return Response(code=raw_dict["code"], msg=raw_dict["msg"], data=data_value)
60
+ else:
61
+ return response_cls.from_dict(raw_dict)
@@ -0,0 +1,16 @@
1
+ """RTK device information."""
2
+ from dataclasses import dataclass, field
3
+
4
+ from mashumaro import field_options
5
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
6
+
7
+
8
+ @dataclass
9
+ class RTK(DataClassORJSONMixin):
10
+ """RTK device information."""
11
+
12
+ device_id: str = field(metadata=field_options(alias="deviceId"))
13
+ device_name: str = field(metadata=field_options(alias="deviceName"))
14
+ product_key: str = field(metadata=field_options(alias="productKey"))
15
+ status: int
16
+ lora: str
File without changes
File without changes
@@ -0,0 +1,24 @@
1
+ from abc import abstractmethod
2
+
3
+ from pymammotion.bluetooth.model.atomic_integer import AtomicInteger
4
+ from pymammotion.proto import MsgCmdType, MsgDevice
5
+ from pymammotion.utility.device_type import DeviceType
6
+
7
+
8
+ class AbstractMessage:
9
+ seqs = AtomicInteger(0)
10
+ user_account: int
11
+
12
+ @abstractmethod
13
+ def get_device_name(self) -> str:
14
+ """Get device name."""
15
+
16
+ @abstractmethod
17
+ def get_device_product_key(self) -> str:
18
+ """Get device name."""
19
+
20
+ def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice:
21
+ """Changes the rcver name if it's not a luba1."""
22
+ if DeviceType.is_luba_pro(self.get_device_name(), self.get_device_product_key()) and msg_type == MsgCmdType.NAV:
23
+ return MsgDevice.DEV_NAVIGATION
24
+ return msg_device
@@ -0,0 +1,81 @@
1
+ from pymammotion.mammotion.commands.messages.driver import MessageDriver
2
+ from pymammotion.mammotion.commands.messages.media import MessageMedia
3
+ from pymammotion.mammotion.commands.messages.navigation import MessageNavigation
4
+ from pymammotion.mammotion.commands.messages.network import MessageNetwork
5
+ from pymammotion.mammotion.commands.messages.ota import MessageOta
6
+ from pymammotion.mammotion.commands.messages.system import MessageSystem
7
+ from pymammotion.mammotion.commands.messages.video import MessageVideo
8
+ from pymammotion.utility.device_type import DeviceType
9
+ from pymammotion.utility.movement import get_percent, transform_both_speeds
10
+
11
+
12
+ class MammotionCommand(
13
+ MessageSystem, MessageNavigation, MessageNetwork, MessageOta, MessageVideo, MessageMedia, MessageDriver
14
+ ):
15
+ """MQTT commands for Luba."""
16
+
17
+ def __init__(self, device_name: str, user_account: int) -> None:
18
+ self._device_name = device_name
19
+ self._product_key = ""
20
+ self.user_account = user_account
21
+
22
+ def get_device_name(self) -> str:
23
+ """Get device name."""
24
+ return self._device_name
25
+
26
+ def read_write_device(self, rw_id: int, context: int, rw: int):
27
+ if (
28
+ rw_id == 6 or rw_id == 3 or rw_id == 7 or rw_id == 8 or rw_id == 10 or rw_id == 11
29
+ ) and DeviceType.is_luba_pro(self.get_device_name()):
30
+ return self.allpowerfull_rw_adapter_x3(rw_id, context, rw)
31
+ return self.allpowerfull_rw(rw_id, context, rw)
32
+
33
+ def traverse_mode(self, context: int) -> bytes:
34
+ """Sets the traversal mode back to charger."""
35
+ # setReChargeMode
36
+ # 0 direct
37
+ # 1 follow the perimeter
38
+ return self.read_write_device(7, context, 1)
39
+
40
+ def turning_mode(self, context: int) -> bytes:
41
+ """Sets the traversal mode back to charger."""
42
+ # setTurnAroundMode
43
+ # 0 zero turn
44
+ # 1 multipoint turn
45
+ return self.read_write_device(6, context, 1)
46
+
47
+ def get_error_code(self) -> bytes:
48
+ return self.read_write_device(5, 2, 1)
49
+
50
+ def get_error_timestamp(self) -> bytes:
51
+ return self.read_write_device(5, 3, 1)
52
+
53
+ def get_device_product_key(self) -> str:
54
+ return self._product_key
55
+
56
+ def set_device_product_key(self, product_key: str) -> None:
57
+ self._product_key = product_key
58
+
59
+ def move_forward(self, linear: float) -> bytes:
60
+ """Move forward. values 0.0 1.0."""
61
+ linear_percent = get_percent(abs(linear * 100))
62
+ (linear_speed, angular_speed) = transform_both_speeds(90.0, 0.0, linear_percent, 0.0)
63
+ return self.send_movement(linear_speed=linear_speed, angular_speed=angular_speed)
64
+
65
+ def move_back(self, linear: float) -> bytes:
66
+ """Move back. values 0.0 1.0."""
67
+ linear_percent = get_percent(abs(linear * 100))
68
+ (linear_speed, angular_speed) = transform_both_speeds(270.0, 0.0, linear_percent, 0.0)
69
+ return self.send_movement(linear_speed=linear_speed, angular_speed=angular_speed)
70
+
71
+ def move_left(self, angular: float) -> bytes:
72
+ """Move forward. values 0.0 1.0."""
73
+ angular_percent = get_percent(abs(angular * 100))
74
+ (linear_speed, angular_speed) = transform_both_speeds(0.0, 180.0, 0.0, angular_percent)
75
+ return self.send_movement(linear_speed=linear_speed, angular_speed=angular_speed)
76
+
77
+ def move_right(self, angular: float) -> bytes:
78
+ """Move back. values 0.0 1.0."""
79
+ angular_percent = get_percent(abs(angular * 100))
80
+ (linear_speed, angular_speed) = transform_both_speeds(0.0, 0.0, 0.0, angular_percent)
81
+ return self.send_movement(linear_speed=linear_speed, angular_speed=angular_speed)
File without changes
@@ -0,0 +1,43 @@
1
+ """RTK protobuf commands."""
2
+
3
+ from abc import ABC
4
+ from logging import getLogger
5
+ import time
6
+
7
+ from pymammotion.mammotion.commands.abstract_message import AbstractMessage
8
+ from pymammotion.proto import (
9
+ AppToBaseMqttRtkT,
10
+ BaseStation,
11
+ LubaMsg,
12
+ MsgAttr,
13
+ MsgCmdType,
14
+ MsgDevice,
15
+ RequestBasestationInfoT,
16
+ )
17
+
18
+ logger = getLogger(__name__)
19
+
20
+
21
+ class MessageBasestation(AbstractMessage, ABC):
22
+ def send_order_msg_basestation(self, driver) -> bytes:
23
+ return LubaMsg(
24
+ msgtype=MsgCmdType.BASESTATION,
25
+ sender=MsgDevice.DEV_MOBILEAPP,
26
+ rcver=self.get_msg_device(MsgCmdType.BASESTATION, MsgDevice.DEV_MAINCTL),
27
+ msgattr=MsgAttr.REQ,
28
+ timestamp=round(time.time() * 1000),
29
+ seqs=self.seqs.increment_and_get() & 255,
30
+ version=1,
31
+ subtype=self.user_account,
32
+ driver=driver,
33
+ ).SerializeToString()
34
+
35
+ def basestation_info(self) -> bytes:
36
+ """Build and send a request to get basestation info (request_type=1)."""
37
+ base = BaseStation(to_dev=RequestBasestationInfoT(request_type=1))
38
+ return self.send_order_msg_basestation(base)
39
+
40
+ def set_base_net_rtk_switch(self, rtk_switch: int) -> bytes:
41
+ """Set RTK switch via app_to_base_mqtt_rtk_t."""
42
+ base = BaseStation(app_to_base_mqtt_rtk_msg=AppToBaseMqttRtkT(rtk_switch=rtk_switch))
43
+ return self.send_order_msg_basestation(base)
@@ -0,0 +1,122 @@
1
+ # === sendOrderMsg_Driver ===
2
+ from abc import ABC
3
+ from logging import getLogger
4
+ import time
5
+
6
+ from pymammotion.mammotion.commands.abstract_message import AbstractMessage
7
+ from pymammotion.proto import (
8
+ AppGetCutterWorkMode,
9
+ AppSetCutterWorkMode,
10
+ DrvKnifeHeight,
11
+ DrvMotionCtrl,
12
+ DrvMowCtrlByHand,
13
+ DrvSrSpeed,
14
+ LubaMsg,
15
+ MctlDriver,
16
+ MsgAttr,
17
+ MsgCmdType,
18
+ MsgDevice,
19
+ RtkCfgReqT,
20
+ RtkSysMaskQueryT,
21
+ )
22
+
23
+ logger = getLogger(__name__)
24
+
25
+
26
+ class MessageDriver(AbstractMessage, ABC):
27
+ def send_order_msg_driver(self, driver) -> bytes:
28
+ """Build and serialize a driver command message."""
29
+ return LubaMsg(
30
+ msgtype=MsgCmdType.EMBED_DRIVER,
31
+ sender=MsgDevice.DEV_MOBILEAPP,
32
+ rcver=self.get_msg_device(MsgCmdType.EMBED_DRIVER, MsgDevice.DEV_MAINCTL),
33
+ msgattr=MsgAttr.REQ,
34
+ timestamp=round(time.time() * 1000),
35
+ seqs=self.seqs.increment_and_get() & 255,
36
+ version=1,
37
+ subtype=self.user_account,
38
+ driver=driver,
39
+ ).SerializeToString()
40
+
41
+ def set_blade_height(self, height: int) -> bytes:
42
+ """Set mower blade height."""
43
+ logger.debug(f"Send knife height height={height}")
44
+ build = MctlDriver(todev_knife_height_set=DrvKnifeHeight(knife_height=height))
45
+ logger.debug(f"Send command--Knife motor height setting height={height}")
46
+ return self.send_order_msg_driver(build)
47
+
48
+ def set_speed(self, speed: float) -> bytes:
49
+ """Set the device speed."""
50
+ logger.debug(f"{self.get_device_name()} set speed, {speed}")
51
+ build = MctlDriver(bidire_speed_read_set=DrvSrSpeed(speed=speed, rw=1))
52
+ logger.debug(f"Send command--Speed setting speed={speed}")
53
+ return self.send_order_msg_driver(build)
54
+
55
+ def get_cutter_mode(self) -> bytes:
56
+ """Request the current cutter mode."""
57
+ build = MctlDriver(current_cutter_mode=AppGetCutterWorkMode())
58
+ return self.send_order_msg_driver(build)
59
+
60
+ def set_cutter_mode(self, cutter_mode: int) -> bytes:
61
+ """Set blade speed."""
62
+ """
63
+ 1 slow
64
+ 0 normal
65
+ 2 fast
66
+ """
67
+ build = MctlDriver(cutter_mode_ctrl_by_hand=AppSetCutterWorkMode(cutter_mode=cutter_mode))
68
+ return self.send_order_msg_driver(build)
69
+
70
+ def syn_nav_star_point_data(self, sat_system: int) -> bytes:
71
+ """Synchronize navigation satellite frequency points."""
72
+ build = MctlDriver(rtk_sys_mask_query=RtkSysMaskQueryT(sat_system=sat_system))
73
+ logger.debug(f"Send command--Navigation satellite frequency point synchronization={sat_system}")
74
+ return self.send_order_msg_driver(build)
75
+
76
+ def set_nav_star_point(self, cmd_req: str) -> bytes:
77
+ """Configure navigation satellite frequency points."""
78
+ build = MctlDriver(rtk_cfg_req=RtkCfgReqT(cmd_req=cmd_req, cmd_length=len(cmd_req) - 1))
79
+ logger.debug(f"Send command--Navigation satellite frequency point setting={cmd_req}")
80
+ logger.debug(
81
+ f"Navigation satellite setting, Send command--Navigation satellite frequency point setting={cmd_req}"
82
+ )
83
+ return self.send_order_msg_driver(build)
84
+
85
+ def get_speed(self) -> bytes:
86
+ """Request the current speed value."""
87
+ build = MctlDriver(bidire_speed_read_set=DrvSrSpeed(rw=0))
88
+ logger.debug("Send command--Get speed value")
89
+ return self.send_order_msg_driver(build)
90
+
91
+ def operate_on_device(
92
+ self,
93
+ main_ctrl: int,
94
+ cut_knife_ctrl: int,
95
+ cut_knife_height: int,
96
+ max_run_speed: float,
97
+ ) -> bytes:
98
+ """Send manual mowing control command."""
99
+ build = MctlDriver(
100
+ mow_ctrl_by_hand=DrvMowCtrlByHand(
101
+ main_ctrl=main_ctrl,
102
+ cut_knife_ctrl=cut_knife_ctrl,
103
+ cut_knife_height=cut_knife_height,
104
+ max_run_speed=max_run_speed,
105
+ )
106
+ )
107
+ logger.debug(
108
+ f"Send command--Manual mowing command, main_ctrl:{main_ctrl}, cut_knife_ctrl:{cut_knife_ctrl}, "
109
+ f"cut_knife_height:{cut_knife_height}, max_run_speed:{max_run_speed}"
110
+ )
111
+
112
+ return self.send_order_msg_driver(build)
113
+
114
+ def send_movement(self, linear_speed: int, angular_speed: int) -> bytes:
115
+ """Send motion command with linear and angular speeds."""
116
+ logger.debug(f"Control command print, linearSpeed={
117
+ linear_speed} // angularSpeed={angular_speed}")
118
+ return self.send_order_msg_driver(
119
+ MctlDriver(
120
+ todev_devmotion_ctrl=DrvMotionCtrl(set_linear_speed=linear_speed, set_angular_speed=angular_speed)
121
+ )
122
+ )