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
File without changes
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import List
3
2
 
4
3
  from mashumaro.mixins.orjson import DataClassORJSONMixin
5
4
 
@@ -13,7 +12,20 @@ class Camera(DataClassORJSONMixin):
13
12
  @dataclass
14
13
  class StreamSubscriptionResponse(DataClassORJSONMixin):
15
14
  appid: str
16
- cameras: List[Camera]
15
+ cameras: list[Camera]
17
16
  channelName: str
18
17
  token: str
19
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
@@ -1,15 +1,21 @@
1
- from dataclasses import dataclass
2
- from typing import Generic, Literal, Optional, TypeVar
1
+ from dataclasses import dataclass, field
2
+ from typing import Annotated, Generic, Literal, TypeVar
3
3
 
4
4
  from mashumaro import DataClassDictMixin
5
5
  from mashumaro.config import BaseConfig
6
6
  from mashumaro.mixins.orjson import DataClassORJSONMixin
7
+ from mashumaro.types import Alias
8
+
9
+
10
+ class UnauthorizedException(Exception):
11
+ pass
12
+
7
13
 
8
14
  DataT = TypeVar("DataT")
9
15
 
10
16
 
11
17
  @dataclass
12
- class ErrorInfo:
18
+ class ErrorInfo(DataClassDictMixin):
13
19
  code: str
14
20
  platform: str
15
21
  module: str
@@ -38,18 +44,105 @@ class ErrorInfo:
38
44
  nl_solution: str
39
45
  da_implication: str
40
46
  da_solution: str
47
+ sr_implication: str
48
+ sr_solution: str
41
49
  sv_implication: str
42
50
  sv_solution: str
43
51
  sl_implication: str
44
52
  sl_solution: str
45
53
  pt_implication: str
46
54
  pt_solution: str
55
+ hu_implication: str
56
+ hu_solution: str
57
+ hr_implication: str
58
+ hr_solution: str
59
+ no_implication: str
60
+ no_solution: str
61
+ fi_implication: str
62
+ fi_solution: str
63
+ ro_implication: str
64
+ ro_solution: str
65
+ bg_implication: str
66
+ bg_solution: str
67
+ et_implication: str
68
+ et_solution: str
69
+ lv_implication: str
70
+ lv_solution: str
71
+ lt_implication: str
72
+ lt_solution: str
73
+
74
+
75
+ @dataclass
76
+ class SettingVo(DataClassORJSONMixin):
77
+ """Device setting configuration."""
78
+
79
+ type: int = 0
80
+ is_switch: Annotated[int, Alias("isSwitch")] = 0
81
+
82
+
83
+ @dataclass
84
+ class LocationVo(DataClassORJSONMixin):
85
+ """Device location information."""
86
+
87
+ date_time: Annotated[str, Alias("dateTime")] = ""
88
+ date_timestamp: Annotated[int, Alias("dateTimestamp")] = 0
89
+ location: list[float] = field(default_factory=lambda: [0.0, 0.0])
90
+
91
+
92
+ @dataclass
93
+ class DeviceInfo:
94
+ """Complete device information."""
95
+
96
+ iot_id: Annotated[str, Alias("iotId")] = ""
97
+ device_id: Annotated[str, Alias("deviceId")] = ""
98
+ device_name: Annotated[str, Alias("deviceName")] = ""
99
+ device_type: Annotated[str, Alias("deviceType")] = ""
100
+ series: str = ""
101
+ product_series: Annotated[str, Alias("productSeries")] = ""
102
+ icon_code: Annotated[str, Alias("iconCode")] = ""
103
+ generation: int = 0
104
+ status: int = 0
105
+ is_subscribe: Annotated[int, Alias("isSubscribe")] = 0
106
+ setting_vos: Annotated[list[SettingVo], Alias("settingVos")] = field(default_factory=list)
107
+ active_time: Annotated[str, Alias("activeTime")] = ""
108
+ active_timestamp: Annotated[int, Alias("activeTimestamp")] = 0
109
+ location_vo: Annotated[LocationVo | None, Alias("locationVo")] = None
110
+
111
+
112
+ @dataclass
113
+ class DeviceRecord(DataClassORJSONMixin):
114
+ identity_id: Annotated[str, Alias("identityId")]
115
+ iot_id: Annotated[str, Alias("iotId")]
116
+ product_key: Annotated[str, Alias("productKey")]
117
+ device_name: Annotated[str, Alias("deviceName")]
118
+ owned: int
119
+ status: int
120
+ bind_time: Annotated[int, Alias("bindTime")]
121
+ create_time: Annotated[str, Alias("createTime")]
122
+
123
+
124
+ @dataclass
125
+ class DeviceRecords(DataClassORJSONMixin):
126
+ records: list[DeviceRecord]
127
+ total: int
128
+ size: int
129
+ current: int
130
+ pages: int
131
+
132
+
133
+ @dataclass
134
+ class MQTTConnection(DataClassORJSONMixin):
135
+ host: str
136
+ jwt: str
137
+ client_id: Annotated[str, Alias("clientId")]
138
+ username: str
47
139
 
48
140
 
49
141
  @dataclass
50
- class Response(DataClassDictMixin, Generic[DataT]):
142
+ class Response(DataClassORJSONMixin, Generic[DataT]):
51
143
  code: int
52
144
  msg: str
145
+ request_id: Annotated[str, Alias("requestId")] | None = None
53
146
  data: DataT | None = None
54
147
 
55
148
  class Config(BaseConfig):
@@ -60,20 +153,78 @@ class Response(DataClassDictMixin, Generic[DataT]):
60
153
  class LoginResponseUserInformation(DataClassORJSONMixin):
61
154
  areaCode: str
62
155
  domainAbbreviation: str
63
- email: Optional[str]
64
156
  userId: str
65
157
  userAccount: str
66
158
  authType: str
159
+ email: str | None = None
160
+
161
+ class Config(BaseConfig):
162
+ omit_none = True
163
+
164
+
165
+ @dataclass
166
+ class JWTTokenInfo(DataClassORJSONMixin):
167
+ """specifically for newer devices and mqtt"""
168
+
169
+ iot: str # iot domain e.g api-iot-business-eu-dcdn.mammotion.com
170
+ robot: str # e.g api-robot-eu.mammotion.com
67
171
 
68
172
 
69
173
  @dataclass
70
174
  class LoginResponseData(DataClassORJSONMixin):
71
175
  access_token: str
72
- token_type: Literal["bearer"]
176
+ token_type: Literal["bearer", "Bearer"]
73
177
  refresh_token: str
74
178
  expires_in: int
75
- scope: Literal["read"]
76
- grant_type: Literal["password"]
77
179
  authorization_code: str
78
180
  userInformation: LoginResponseUserInformation
79
- jti: str
181
+ jti: str = None
182
+ grant_type: Literal["password", "Password"] = None
183
+ scope: Literal["read", "Read"] = None
184
+
185
+ class Config(BaseConfig):
186
+ omit_none = True
187
+
188
+
189
+ @dataclass
190
+ class FirmwareVersions(DataClassORJSONMixin):
191
+ firmware_version: Annotated[str, Alias("firmwareVersion")] = ""
192
+ firmware_code: Annotated[str, Alias("firmwareCode")] = ""
193
+ firmware_latest_version: Annotated[str, Alias("firmwareLatestVersion")] = ""
194
+ firmware_type: Annotated[str, Alias("firmwareType")] = ""
195
+
196
+
197
+ @dataclass
198
+ class ProductVersionInfo(DataClassORJSONMixin):
199
+ release_note: Annotated[str, Alias("releaseNote")] = ""
200
+ release_version: Annotated[str, Alias("releaseVersion")] = ""
201
+ data_location: str | None = None
202
+
203
+
204
+ @dataclass
205
+ class CheckDeviceVersion(DataClassORJSONMixin):
206
+ cause_code: Annotated[int, Alias("causeCode")] = 0
207
+ product_version_info_vo: Annotated[ProductVersionInfo | None, Alias("productVersionInfoVo")] = None
208
+ progress: int | None = 0
209
+ upgradeable: bool = False
210
+ device_id: Annotated[str, Alias("deviceId")] = ""
211
+ device_name: Annotated[str | None, Alias("deviceName")] = ""
212
+ current_version: Annotated[str, Alias("currentVersion")] = ""
213
+ isupgrading: bool | None = False
214
+ cause_msg: Annotated[str, Alias("causeMsg")] = ""
215
+
216
+ def __eq__(self, other):
217
+ if not isinstance(other, CheckDeviceVersion):
218
+ return NotImplemented
219
+
220
+ if self.device_id != other.device_id or self.current_version != other.current_version:
221
+ return False
222
+
223
+ if self.product_version_info_vo and other.product_version_info_vo:
224
+ if self.product_version_info_vo.release_version != other.product_version_info_vo.release_version:
225
+ return False
226
+ return True
227
+ elif self.product_version_info_vo is None and other.product_version_info_vo is None:
228
+ return False
229
+ else:
230
+ 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
@@ -1,22 +1,24 @@
1
1
  from abc import abstractmethod
2
2
 
3
- from pymammotion.proto.luba_msg import MsgCmdType, MsgDevice
3
+ from pymammotion.bluetooth.model.atomic_integer import AtomicInteger
4
+ from pymammotion.proto import MsgCmdType, MsgDevice
4
5
  from pymammotion.utility.device_type import DeviceType
5
6
 
6
7
 
7
8
  class AbstractMessage:
9
+ seqs = AtomicInteger(0)
10
+ user_account: int
11
+
8
12
  @abstractmethod
9
13
  def get_device_name(self) -> str:
10
14
  """Get device name."""
11
15
 
16
+ @abstractmethod
12
17
  def get_device_product_key(self) -> str:
13
18
  """Get device name."""
14
19
 
15
20
  def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice:
16
21
  """Changes the rcver name if it's not a luba1."""
17
- if (
18
- not DeviceType.is_luba1(self.get_device_name(), self.get_device_product_key())
19
- and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV
20
- ):
22
+ if DeviceType.is_luba_pro(self.get_device_name(), self.get_device_product_key()) and msg_type == MsgCmdType.NAV:
21
23
  return MsgDevice.DEV_NAVIGATION
22
24
  return msg_device
@@ -5,6 +5,7 @@ from pymammotion.mammotion.commands.messages.network import MessageNetwork
5
5
  from pymammotion.mammotion.commands.messages.ota import MessageOta
6
6
  from pymammotion.mammotion.commands.messages.system import MessageSystem
7
7
  from pymammotion.mammotion.commands.messages.video import MessageVideo
8
+ from pymammotion.utility.device_type import DeviceType
8
9
  from pymammotion.utility.movement import get_percent, transform_both_speeds
9
10
 
10
11
 
@@ -13,14 +14,42 @@ class MammotionCommand(
13
14
  ):
14
15
  """MQTT commands for Luba."""
15
16
 
16
- def __init__(self, device_name: str) -> None:
17
+ def __init__(self, device_name: str, user_account: int) -> None:
17
18
  self._device_name = device_name
18
19
  self._product_key = ""
20
+ self.user_account = user_account
19
21
 
20
22
  def get_device_name(self) -> str:
21
23
  """Get device name."""
22
24
  return self._device_name
23
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
+
24
53
  def get_device_product_key(self) -> str:
25
54
  return self._product_key
26
55
 
@@ -42,11 +71,11 @@ class MammotionCommand(
42
71
  def move_left(self, angular: float) -> bytes:
43
72
  """Move forward. values 0.0 1.0."""
44
73
  angular_percent = get_percent(abs(angular * 100))
45
- (linear_speed, angular_speed) = transform_both_speeds(0.0, 0.0, 0.0, angular_percent)
74
+ (linear_speed, angular_speed) = transform_both_speeds(0.0, 180.0, 0.0, angular_percent)
46
75
  return self.send_movement(linear_speed=linear_speed, angular_speed=angular_speed)
47
76
 
48
77
  def move_right(self, angular: float) -> bytes:
49
78
  """Move back. values 0.0 1.0."""
50
79
  angular_percent = get_percent(abs(angular * 100))
51
- (linear_speed, angular_speed) = transform_both_speeds(0.0, 180.0, 0.0, angular_percent)
80
+ (linear_speed, angular_speed) = transform_both_speeds(0.0, 0.0, 0.0, angular_percent)
52
81
  return self.send_movement(linear_speed=linear_speed, angular_speed=angular_speed)
@@ -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)
@@ -1,58 +1,90 @@
1
1
  # === sendOrderMsg_Driver ===
2
- import time
3
2
  from abc import ABC
4
3
  from logging import getLogger
4
+ import time
5
5
 
6
6
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
7
- from pymammotion.proto import mctrl_driver
8
- from pymammotion.proto.luba_msg import LubaMsg, MsgAttr, MsgCmdType, MsgDevice
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
+ )
9
22
 
10
23
  logger = getLogger(__name__)
11
24
 
12
25
 
13
26
  class MessageDriver(AbstractMessage, ABC):
14
27
  def send_order_msg_driver(self, driver) -> bytes:
28
+ """Build and serialize a driver command message."""
15
29
  return LubaMsg(
16
- msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_DRIVER,
30
+ msgtype=MsgCmdType.EMBED_DRIVER,
17
31
  sender=MsgDevice.DEV_MOBILEAPP,
18
- rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_EMBED_DRIVER, MsgDevice.DEV_MAINCTL),
19
- msgattr=MsgAttr.MSG_ATTR_REQ,
32
+ rcver=self.get_msg_device(MsgCmdType.EMBED_DRIVER, MsgDevice.DEV_MAINCTL),
33
+ msgattr=MsgAttr.REQ,
20
34
  timestamp=round(time.time() * 1000),
21
- seqs=1,
35
+ seqs=self.seqs.increment_and_get() & 255,
22
36
  version=1,
23
- subtype=1,
37
+ subtype=self.user_account,
24
38
  driver=driver,
25
39
  ).SerializeToString()
26
40
 
27
- def set_blade_height(self, height: int):
41
+ def set_blade_height(self, height: int) -> bytes:
42
+ """Set mower blade height."""
28
43
  logger.debug(f"Send knife height height={height}")
29
- build = mctrl_driver.MctlDriver(todev_knife_height_set=mctrl_driver.DrvKnifeHeight(knife_height=height))
44
+ build = MctlDriver(todev_knife_height_set=DrvKnifeHeight(knife_height=height))
30
45
  logger.debug(f"Send command--Knife motor height setting height={height}")
31
46
  return self.send_order_msg_driver(build)
32
47
 
33
- def set_speed(self, speed: float):
48
+ def set_speed(self, speed: float) -> bytes:
49
+ """Set the device speed."""
34
50
  logger.debug(f"{self.get_device_name()} set speed, {speed}")
35
- build = mctrl_driver.MctlDriver(bidire_speed_read_set=mctrl_driver.DrvSrSpeed(speed=speed, rw=1))
51
+ build = MctlDriver(bidire_speed_read_set=DrvSrSpeed(speed=speed, rw=1))
36
52
  logger.debug(f"Send command--Speed setting speed={speed}")
37
53
  return self.send_order_msg_driver(build)
38
54
 
39
- def syn_nav_star_point_data(self, sat_system: int):
40
- build = mctrl_driver.MctlDriver(rtk_sys_mask_query=mctrl_driver.RtkSysMaskQueryT(sat_system=sat_system))
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))
41
73
  logger.debug(f"Send command--Navigation satellite frequency point synchronization={sat_system}")
42
74
  return self.send_order_msg_driver(build)
43
75
 
44
- def set_nav_star_point(self, cmd_req: str):
45
- build = mctrl_driver.MctlDriver(
46
- rtk_cfg_req=mctrl_driver.RtkCfgReqT(cmd_req=cmd_req, cmd_length=len(cmd_req) - 1)
47
- )
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))
48
79
  logger.debug(f"Send command--Navigation satellite frequency point setting={cmd_req}")
49
80
  logger.debug(
50
81
  f"Navigation satellite setting, Send command--Navigation satellite frequency point setting={cmd_req}"
51
82
  )
52
83
  return self.send_order_msg_driver(build)
53
84
 
54
- def get_speed(self):
55
- build = mctrl_driver.MctlDriver(bidire_speed_read_set=mctrl_driver.DrvSrSpeed(rw=0))
85
+ def get_speed(self) -> bytes:
86
+ """Request the current speed value."""
87
+ build = MctlDriver(bidire_speed_read_set=DrvSrSpeed(rw=0))
56
88
  logger.debug("Send command--Get speed value")
57
89
  return self.send_order_msg_driver(build)
58
90
 
@@ -62,13 +94,14 @@ class MessageDriver(AbstractMessage, ABC):
62
94
  cut_knife_ctrl: int,
63
95
  cut_knife_height: int,
64
96
  max_run_speed: float,
65
- ):
66
- build = mctrl_driver.MctlDriver(
67
- mow_ctrl_by_hand=mctrl_driver.DrvMowCtrlByHand(
97
+ ) -> bytes:
98
+ """Send manual mowing control command."""
99
+ build = MctlDriver(
100
+ mow_ctrl_by_hand=DrvMowCtrlByHand(
68
101
  main_ctrl=main_ctrl,
69
102
  cut_knife_ctrl=cut_knife_ctrl,
70
103
  cut_knife_height=cut_knife_height,
71
- max_run__speed=max_run_speed,
104
+ max_run_speed=max_run_speed,
72
105
  )
73
106
  )
74
107
  logger.debug(
@@ -78,13 +111,12 @@ class MessageDriver(AbstractMessage, ABC):
78
111
 
79
112
  return self.send_order_msg_driver(build)
80
113
 
81
- def send_movement(self, linear_speed: int, angular_speed: int):
114
+ def send_movement(self, linear_speed: int, angular_speed: int) -> bytes:
115
+ """Send motion command with linear and angular speeds."""
82
116
  logger.debug(f"Control command print, linearSpeed={
83
117
  linear_speed} // angularSpeed={angular_speed}")
84
118
  return self.send_order_msg_driver(
85
- mctrl_driver.MctlDriver(
86
- todev_devmotion_ctrl=mctrl_driver.DrvMotionCtrl(
87
- set_linear_speed=linear_speed, set_angular_speed=angular_speed
88
- )
119
+ MctlDriver(
120
+ todev_devmotion_ctrl=DrvMotionCtrl(set_linear_speed=linear_speed, set_angular_speed=angular_speed)
89
121
  )
90
122
  )