pymammotion 0.2.51__py3-none-any.whl → 0.2.53__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.
@@ -62,6 +62,10 @@ class LoginException(Exception):
62
62
  """Raise exception when library cannot log in."""
63
63
 
64
64
 
65
+ class CheckSessionException(Exception):
66
+ """Raise exception when checking session results in a failure."""
67
+
68
+
65
69
  class CloudIOTGateway:
66
70
  """Class for interacting with Aliyun Cloud IoT Gateway."""
67
71
 
@@ -528,7 +532,7 @@ class CloudIOTGateway:
528
532
 
529
533
  if int(response_body_dict.get("code")) != 200:
530
534
  logger.error(response_body_dict)
531
- raise Exception("Error check or refresh token: " + response_body_dict.__str__())
535
+ raise CheckSessionException("Error check or refresh token: " + response_body_dict.__str__())
532
536
 
533
537
  session = SessionByAuthCodeResponse.from_dict(response_body_dict)
534
538
  session_data = session.data
@@ -725,3 +729,7 @@ class CloudIOTGateway:
725
729
  @property
726
730
  def login_by_oauth_response(self):
727
731
  return self._login_by_oauth_response
732
+
733
+ @property
734
+ def connect_response(self):
735
+ return self._connect_response
@@ -56,25 +56,6 @@ class BleMessage:
56
56
  self.mAck = queue.Queue()
57
57
  self.notification = BlufiNotifyData()
58
58
 
59
- async def get_device_version_main(self) -> None:
60
- commEsp = dev_net_pb2.DevNet(todev_devinfo_req=dev_net_pb2.DrvDevInfoReq())
61
-
62
- for i in range(1, 8):
63
- if i == 1:
64
- commEsp.todev_devinfo_req.req_ids.add(id=i, type=6)
65
- commEsp.todev_devinfo_req.req_ids.add(id=i, type=3)
66
-
67
- lubaMsg = luba_msg_pb2.LubaMsg()
68
- lubaMsg.msgtype = luba_msg_pb2.MSG_CMD_TYPE_ESP
69
- lubaMsg.sender = luba_msg_pb2.DEV_MOBILEAPP
70
- lubaMsg.msgattr = luba_msg_pb2.MSG_ATTR_REQ
71
- lubaMsg.seqs = 1
72
- lubaMsg.version = 1
73
- lubaMsg.subtype = 1
74
- lubaMsg.net.CopyFrom(commEsp)
75
- byte_arr = lubaMsg.SerializeToString()
76
- await self.post_custom_data_bytes(byte_arr)
77
-
78
59
  async def get_task(self) -> None:
79
60
  hash_map = {"pver": 1, "subCmd": 2, "result": 0}
80
61
  await self.post_custom_data(self.get_json_string(bleOrderCmd.task, hash_map))
@@ -10,6 +10,7 @@ from pymammotion.data.model import HashList, RapidState
10
10
  from pymammotion.data.model.device_config import DeviceLimits
11
11
  from pymammotion.data.model.location import Location
12
12
  from pymammotion.data.model.report_info import ReportData
13
+ from pymammotion.data.mqtt.properties import ThingPropertiesMessage
13
14
  from pymammotion.proto.dev_net import DevNet
14
15
  from pymammotion.proto.luba_msg import LubaMsg
15
16
  from pymammotion.proto.luba_mul import SocMul
@@ -33,6 +34,7 @@ from pymammotion.utility.map import CoordinateConverter
33
34
  class MowingDevice(DataClassORJSONMixin):
34
35
  """Wraps the betterproto dataclasses, so we can bypass the groups for keeping all data."""
35
36
 
37
+ mqtt_properties: ThingPropertiesMessage | None = None
36
38
  map: HashList = field(default_factory=HashList)
37
39
  location: Location = field(default_factory=Location)
38
40
  mowing_state: RapidState = field(default_factory=RapidState)
@@ -12,6 +12,7 @@ class PathType(IntEnum):
12
12
  AREA = 0
13
13
  OBSTACLE = 1
14
14
  PATH = 2
15
+ DUMP = 12
15
16
 
16
17
 
17
18
  @dataclass
@@ -20,10 +21,15 @@ class FrameList(DataClassORJSONMixin):
20
21
  data: list[NavGetCommDataAck]
21
22
 
22
23
 
24
+ @dataclass
25
+ class NavGetHashListData(DataClassORJSONMixin, NavGetHashListAck):
26
+ """Dataclass for NavGetHashListData."""
27
+
28
+
23
29
  @dataclass
24
30
  class RootHashList(DataClassORJSONMixin):
25
31
  total_frame: int = 0
26
- data: list[NavGetHashListAck] = list
32
+ data: list[NavGetHashListAck] = field(default_factory=list)
27
33
 
28
34
 
29
35
  @dataclass
@@ -45,15 +51,19 @@ class HashList(DataClassORJSONMixin):
45
51
  area: dict = field(default_factory=dict) # type 0
46
52
  path: dict = field(default_factory=dict) # type 2
47
53
  obstacle: dict = field(default_factory=dict) # type 1
54
+ dump: dict = field(default_factory=dict) # type 12?
48
55
  area_name: list[AreaHashNameList] = field(default_factory=list)
49
56
 
50
57
  def update_hash_lists(self, hashlist: list[int]) -> None:
51
58
  self.area = {hash_id: frames for hash_id, frames in self.area.items() if hash_id in hashlist}
52
59
  self.path = {hash_id: frames for hash_id, frames in self.path.items() if hash_id in hashlist}
53
60
  self.obstacle = {hash_id: frames for hash_id, frames in self.obstacle.items() if hash_id in hashlist}
61
+ self.dump = {hash_id: frames for hash_id, frames in self.dump.items() if hash_id in hashlist}
54
62
 
55
63
  @property
56
64
  def hashlist(self) -> list[int]:
65
+ if len(self.root_hash_list.data) == 0:
66
+ return []
57
67
  return [i for obj in self.root_hash_list.data for i in obj.data_couple]
58
68
 
59
69
  @property
@@ -62,7 +72,7 @@ class HashList(DataClassORJSONMixin):
62
72
  i
63
73
  for obj in self.root_hash_list.data
64
74
  for i in obj.data_couple
65
- if i not in set(self.area.keys()).union(self.path.keys(), self.obstacle.keys())
75
+ if i not in set(self.area.keys()).union(self.path.keys(), self.obstacle.keys(), self.dump.keys())
66
76
  ]
67
77
 
68
78
  def update_root_hash_list(self, hash_list: NavGetHashListAck) -> None:
@@ -72,12 +82,10 @@ class HashList(DataClassORJSONMixin):
72
82
  if obj.current_frame == hash_list.current_frame:
73
83
  # Replace the item if current_frame matches
74
84
  self.root_hash_list.data[index] = hash_list
75
- self.update_hash_lists(self.hashlist)
76
85
  return
77
86
 
78
87
  # If no match was found, append the new item
79
88
  self.root_hash_list.data.append(hash_list)
80
- self.update_hash_lists(self.hashlist)
81
89
 
82
90
  def missing_hash_frame(self):
83
91
  return self._find_missing_frames(self.root_hash_list)
@@ -92,6 +100,9 @@ class HashList(DataClassORJSONMixin):
92
100
  if hash_data.type == PathType.PATH:
93
101
  return self._find_missing_frames(self.path.get(hash_data.hash))
94
102
 
103
+ if hash_data.type == PathType.DUMP:
104
+ return self._find_missing_frames(self.dump.get(hash_data.hash))
105
+
95
106
  def update(self, hash_data: NavGetCommDataAck) -> bool:
96
107
  """Update the map data."""
97
108
  if hash_data.type == PathType.AREA:
@@ -106,6 +117,9 @@ class HashList(DataClassORJSONMixin):
106
117
  if hash_data.type == PathType.PATH:
107
118
  return self._add_hash_data(self.path, hash_data)
108
119
 
120
+ if hash_data.type == PathType.DUMP:
121
+ return self._add_hash_data(self.dump, hash_data)
122
+
109
123
  @staticmethod
110
124
  def _find_missing_frames(frame_list: FrameList | RootHashList) -> list[int]:
111
125
  if frame_list.total_frame == len(frame_list.data):
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import Generic, Literal, TypeVar, Union
2
+ from typing import Any, Generic, Literal, TypeVar, Union
3
3
 
4
4
  from mashumaro import DataClassDictMixin
5
5
  from mashumaro.mixins.orjson import DataClassORJSONMixin
@@ -113,18 +113,62 @@ Items = Union[
113
113
  ]
114
114
 
115
115
 
116
+ @dataclass
117
+ class Item:
118
+ time: int
119
+ value: Union[int, float, str, dict[str, Any]] # Depending on the type of value
120
+
121
+
122
+ @dataclass
123
+ class Items:
124
+ iotState: Item
125
+ extMod: Item
126
+ deviceVersionInfo: Item
127
+ leftMotorBootVersion: Item
128
+ knifeHeight: Item
129
+ rtMrMod: Item
130
+ iotMsgHz: Item
131
+ iotMsgTotal: Item
132
+ loraRawConfig: Item
133
+ loraGeneralConfig: Item
134
+ leftMotorVersion: Item
135
+ intMod: Item
136
+ coordinate: Item
137
+ bmsVersion: Item
138
+ rightMotorVersion: Item
139
+ stm32H7Version: Item
140
+ rightMotorBootVersion: Item
141
+ deviceVersion: Item
142
+ rtkVersion: Item
143
+ ltMrMod: Item
144
+ networkInfo: Item
145
+ bmsHardwareVersion: Item
146
+ batteryPercentage: Item
147
+ deviceState: Item
148
+ deviceOtherInfo: Item
149
+ mcBootVersion: Item
150
+
151
+
116
152
  @dataclass
117
153
  class Params(DataClassORJSONMixin):
154
+ deviceType: Literal["LawnMower"]
118
155
  checkFailedData: dict
119
156
  groupIdList: list[str]
157
+ _tenantId: str
120
158
  groupId: str
121
159
  categoryKey: Literal["LawnMower"]
122
160
  batchId: str
123
161
  gmtCreate: int
124
162
  productKey: str
163
+ generateTime: int
125
164
  deviceName: str
165
+ _traceId: str
126
166
  iotId: str
167
+ JMSXDeliveryCount: int
127
168
  checkLevel: int
169
+ qos: int
170
+ requestId: str
171
+ _categoryKey: str
128
172
  namespace: str
129
173
  tenantId: str
130
174
  thingType: Literal["DEVICE"]
@@ -7,6 +7,7 @@ import betterproto
7
7
 
8
8
  from pymammotion.data.model.device import MowingDevice
9
9
  from pymammotion.data.model.hash_list import AreaHashNameList
10
+ from pymammotion.data.mqtt.properties import ThingPropertiesMessage
10
11
  from pymammotion.proto.luba_msg import LubaMsg
11
12
  from pymammotion.proto.mctrl_nav import AppGetAllAreaHashName, NavGetCommDataAck, NavGetHashListAck
12
13
  from pymammotion.utility.constant import WorkMode
@@ -34,6 +35,10 @@ class StateManager:
34
35
  """Set device."""
35
36
  self._device = device
36
37
 
38
+ async def properties(self, properties: ThingPropertiesMessage) -> None:
39
+ params = properties.params
40
+ self._device.mqtt_properties = params
41
+
37
42
  async def notification(self, message: LubaMsg) -> None:
38
43
  """Handle protobuf notifications."""
39
44
  res = betterproto.which_one_of(message, "LubaSubMsg")
@@ -1,5 +1,8 @@
1
1
  from abc import abstractmethod
2
2
 
3
+ from pymammotion.proto.luba_msg import MsgCmdType, MsgDevice
4
+ from pymammotion.utility.device_type import DeviceType
5
+
3
6
 
4
7
  class AbstractMessage:
5
8
  @abstractmethod
@@ -8,3 +11,12 @@ class AbstractMessage:
8
11
 
9
12
  def get_device_product_key(self) -> str:
10
13
  """Get device name."""
14
+
15
+ def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice:
16
+ """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
+ ):
21
+ return MsgDevice.DEV_NAVIGATION
22
+ return msg_device
@@ -1,4 +1,5 @@
1
1
  from pymammotion.mammotion.commands.messages.driver import MessageDriver
2
+ from pymammotion.mammotion.commands.messages.media import MessageMedia
2
3
  from pymammotion.mammotion.commands.messages.navigation import MessageNavigation
3
4
  from pymammotion.mammotion.commands.messages.network import MessageNetwork
4
5
  from pymammotion.mammotion.commands.messages.ota import MessageOta
@@ -7,7 +8,9 @@ from pymammotion.mammotion.commands.messages.video import MessageVideo
7
8
  from pymammotion.utility.movement import get_percent, transform_both_speeds
8
9
 
9
10
 
10
- class MammotionCommand(MessageSystem, MessageNavigation, MessageNetwork, MessageOta, MessageVideo, MessageDriver):
11
+ class MammotionCommand(
12
+ MessageSystem, MessageNavigation, MessageNetwork, MessageOta, MessageVideo, MessageMedia, MessageDriver
13
+ ):
11
14
  """MQTT commands for Luba."""
12
15
 
13
16
  def __init__(self, device_name: str) -> None:
@@ -11,12 +11,11 @@ logger = getLogger(__name__)
11
11
 
12
12
 
13
13
  class MessageDriver(AbstractMessage, ABC):
14
- @staticmethod
15
- def send_order_msg_driver(driver):
14
+ def send_order_msg_driver(self, driver) -> bytes:
16
15
  return LubaMsg(
17
16
  msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_DRIVER,
18
17
  sender=MsgDevice.DEV_MOBILEAPP,
19
- rcver=MsgDevice.DEV_MAINCTL,
18
+ rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_EMBED_DRIVER, MsgDevice.DEV_MAINCTL),
20
19
  msgattr=MsgAttr.MSG_ATTR_REQ,
21
20
  timestamp=round(time.time() * 1000),
22
21
  seqs=1,
@@ -1,15 +1,18 @@
1
1
  # === sendOrderMsg_Media ===
2
+ from abc import ABC
3
+
4
+ from pymammotion.mammotion.commands.abstract_message import AbstractMessage
2
5
  from pymammotion.proto import luba_msg_pb2, luba_mul_pb2
6
+ from pymammotion.proto.luba_msg import MsgCmdType, MsgDevice
3
7
  from pymammotion.proto.luba_mul import MUL_LANGUAGE
4
8
 
5
9
 
6
- class MessageMedia:
7
- @staticmethod
10
+ class MessageMedia(AbstractMessage, ABC):
8
11
  def send_order_msg_media(self, mul):
9
12
  luba_msg = luba_msg_pb2.LubaMsg(
10
13
  msgtype=luba_msg_pb2.MSG_CMD_TYPE_MUL,
11
14
  sender=luba_msg_pb2.DEV_MOBILEAPP,
12
- rcver=luba_msg_pb2.SOC_MODULE_MULTIMEDIA,
15
+ rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_MUL, MsgDevice.SOC_MODULE_MULTIMEDIA),
13
16
  msgattr=luba_msg_pb2.MSG_ATTR_REQ,
14
17
  seqs=1,
15
18
  version=1,
@@ -25,21 +25,11 @@ from pymammotion.proto.mctrl_nav import (
25
25
  WorkReportCmdData,
26
26
  WorkReportUpdateCmd,
27
27
  )
28
- from pymammotion.utility.device_type import DeviceType
29
28
 
30
29
  logger = logging.getLogger(__name__)
31
30
 
32
31
 
33
32
  class MessageNavigation(AbstractMessage, ABC):
34
- def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice:
35
- """Changes the rcver name if it's not a luba1."""
36
- if (
37
- not DeviceType.is_luba1(self.get_device_name(), self.get_device_product_key())
38
- and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV
39
- ):
40
- return MsgDevice.DEV_NAVIGATION
41
- return msg_device
42
-
43
33
  def send_order_msg_nav(self, build) -> bytes:
44
34
  luba_msg = LubaMsg(
45
35
  msgtype=MsgCmdType.MSG_CMD_TYPE_NAV,
@@ -26,12 +26,22 @@ class MessageNetwork:
26
26
  comm_esp = dev_net_pb2.DevNet(todev_ble_sync=sync_type)
27
27
  return self.send_order_msg_net(comm_esp)
28
28
 
29
- def get_device_base_info(self):
29
+ def get_device_base_info(self) -> bytes:
30
30
  net = dev_net_pb2.DevNet(todev_devinfo_req=dev_net_pb2.DrvDevInfoReq())
31
31
  net.todev_devinfo_req.req_ids.add(id=1, type=6)
32
32
 
33
33
  return self.send_order_msg_net(net)
34
34
 
35
+ def get_device_version_main(self) -> bytes:
36
+ net = dev_net_pb2.DevNet(todev_devinfo_req=dev_net_pb2.DrvDevInfoReq())
37
+
38
+ for i in range(1, 8):
39
+ if i == 1:
40
+ net.todev_devinfo_req.req_ids.add(id=i, type=6)
41
+ net.todev_devinfo_req.req_ids.add(id=i, type=3)
42
+
43
+ return self.send_order_msg_net(net)
44
+
35
45
  def get_4g_module_info(self) -> bytes:
36
46
  build = dev_net_pb2.DevNet(todev_get_mnet_cfg_req=dev_net_pb2.DevNet().todev_get_mnet_cfg_req)
37
47
  logger.debug("Send command -- Get device 4G network module information")
@@ -1,13 +1,17 @@
1
1
  # === sendOrderMsg_Ota ===
2
+ from abc import ABC
3
+
4
+ from pymammotion.mammotion.commands.abstract_message import AbstractMessage
2
5
  from pymammotion.proto import luba_msg_pb2, mctrl_ota_pb2
6
+ from pymammotion.proto.luba_msg import MsgCmdType, MsgDevice
3
7
 
4
8
 
5
- class MessageOta:
9
+ class MessageOta(AbstractMessage, ABC):
6
10
  def send_order_msg_ota(self, ota):
7
11
  luba_msg = luba_msg_pb2.LubaMsg(
8
12
  msgtype=luba_msg_pb2.MSG_CMD_TYPE_EMBED_OTA,
9
13
  sender=luba_msg_pb2.DEV_MOBILEAPP,
10
- rcver=luba_msg_pb2.DEV_MAINCTL,
14
+ rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_EMBED_OTA, MsgDevice.DEV_MAINCTL),
11
15
  msgattr=luba_msg_pb2.MSG_ATTR_REQ,
12
16
  seqs=1,
13
17
  version=1,
@@ -26,6 +26,16 @@ class MessageSystem(AbstractMessage, ABC):
26
26
  messageNavigation: MessageNavigation = MessageNavigation()
27
27
 
28
28
  def send_order_msg_sys(self, sys):
29
+ luba_msg = LubaMsg(
30
+ msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
31
+ sender=MsgDevice.DEV_MOBILEAPP,
32
+ rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_EMBED_SYS, MsgDevice.DEV_MAINCTL),
33
+ sys=sys,
34
+ )
35
+
36
+ return luba_msg.SerializeToString()
37
+
38
+ def send_order_msg_sys_legacy(self, sys):
29
39
  luba_msg = LubaMsg(
30
40
  msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
31
41
  sender=MsgDevice.DEV_MOBILEAPP,
@@ -225,9 +235,10 @@ class MessageSystem(AbstractMessage, ABC):
225
235
  )
226
236
  logger.debug(f"Send command==== IOT slim data Act {
227
237
  build.todev_report_cfg.act} {build}")
228
- return self.send_order_msg_sys(build)
238
+ return self.send_order_msg_sys_legacy(build)
229
239
 
230
240
  def get_report_cfg_stop(self, timeout: int = 10000, period: int = 1000, no_change_period: int = 1000):
241
+ # TODO use send_order_msg_sys_legacy
231
242
  mctlsys = MctlSys(
232
243
  todev_report_cfg=ReportInfoCfg(
233
244
  act=RptAct.RPT_STOP,
@@ -260,6 +271,7 @@ class MessageSystem(AbstractMessage, ABC):
260
271
  return lubaMsg.SerializeToString()
261
272
 
262
273
  def get_report_cfg(self, timeout: int = 10000, period: int = 1000, no_change_period: int = 2000):
274
+ # TODO use send_order_msg_sys_legacy
263
275
  mctlsys = MctlSys(
264
276
  todev_report_cfg=ReportInfoCfg(
265
277
  act=RptAct.RPT_START,
@@ -1,15 +1,18 @@
1
1
  # === sendOrderMsg_Video ===
2
+ from abc import ABC
3
+
2
4
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
3
5
  from pymammotion.proto import luba_msg_pb2, luba_mul_pb2
6
+ from pymammotion.proto.luba_msg import MsgCmdType, MsgDevice
4
7
  from pymammotion.utility.device_type import DeviceType
5
8
 
6
9
 
7
- class MessageVideo(AbstractMessage):
10
+ class MessageVideo(AbstractMessage, ABC):
8
11
  async def send_order_msg_video(self, mul):
9
12
  luba_msg = luba_msg_pb2.LubaMsg(
10
13
  msgtype=luba_msg_pb2.MSG_CMD_TYPE_MUL,
11
14
  sender=luba_msg_pb2.DEV_MOBILEAPP,
12
- rcver=luba_msg_pb2.SOC_MODULE_MULTIMEDIA,
15
+ rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_MUL, MsgDevice.SOC_MODULE_MULTIMEDIA),
13
16
  mul=mul,
14
17
  )
15
18
 
@@ -74,7 +74,7 @@ class MammotionBaseDevice:
74
74
  if len(missing_frames) == 0:
75
75
  # get next in hash ack list
76
76
 
77
- data_hash = self.mower.map.missing_hashlist.pop()
77
+ data_hash = self.mower.map.missing_hashlist.pop() if len(self.mower.map.missing_hashlist) > 0 else None
78
78
  if data_hash is None:
79
79
  return
80
80
 
@@ -45,16 +45,15 @@ class MammotionMixedDeviceManager:
45
45
  mqtt: MammotionCloud | None = None,
46
46
  preference: ConnectionPreference = ConnectionPreference.BLUETOOTH,
47
47
  ) -> None:
48
- self._mower_state = None
49
48
  self.name = name
50
- self._mowing_state = MowingDevice()
49
+ self._mower_state = MowingDevice()
51
50
  self.add_ble(ble_device)
52
51
  self.add_cloud(cloud_device, mqtt)
53
52
  self.preference = preference
54
53
 
55
54
  @property
56
55
  def mower_state(self):
57
- return self._mowing_state
56
+ return self._mower_state
58
57
 
59
58
  @mower_state.setter
60
59
  def mower_state(self, value: MowingDevice) -> None:
@@ -62,7 +61,7 @@ class MammotionMixedDeviceManager:
62
61
  self._cloud_device.state_manager.set_device(value)
63
62
  if self._ble_device:
64
63
  self._ble_device.state_manager.set_device(value)
65
- self._mowing_state = value
64
+ self._mower_state = value
66
65
 
67
66
  def ble(self) -> MammotionBaseBLEDevice | None:
68
67
  return self._ble_device
@@ -70,6 +69,12 @@ class MammotionMixedDeviceManager:
70
69
  def cloud(self) -> MammotionBaseCloudDevice | None:
71
70
  return self._cloud_device
72
71
 
72
+ def has_queued_commands(self) -> bool:
73
+ if self.has_cloud() and self.preference == ConnectionPreference.WIFI:
74
+ return not self.cloud()._mqtt.command_queue.empty()
75
+ else:
76
+ return False
77
+
73
78
  def add_ble(self, ble_device: BLEDevice) -> None:
74
79
  if ble_device is not None:
75
80
  self._ble_device = MammotionBaseBLEDevice(self.mower_state, ble_device)
@@ -145,7 +150,6 @@ async def create_devices(
145
150
  return mammotion
146
151
 
147
152
 
148
- @cache
149
153
  class Mammotion:
150
154
  """Represents a Mammotion account and its devices."""
151
155
 
@@ -299,7 +303,7 @@ class Mammotion:
299
303
  _LOGGER.debug(_stream_response)
300
304
  return _stream_response
301
305
 
302
- def mower(self, name: str):
306
+ def mower(self, name: str) -> MowingDevice | None:
303
307
  device = self.get_device_by_name(name)
304
308
  if device:
305
309
  return device.mower_state
@@ -13,6 +13,7 @@ from pymammotion.aliyun.cloud_gateway import DeviceOfflineException, SetupExcept
13
13
  from pymammotion.aliyun.model.dev_by_account_response import Device
14
14
  from pymammotion.data.model.device import MowingDevice
15
15
  from pymammotion.data.mqtt.event import ThingEventMessage
16
+ from pymammotion.data.mqtt.properties import ThingPropertiesMessage
16
17
  from pymammotion.event.event import DataEvent
17
18
  from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
18
19
  from pymammotion.mammotion.devices.base import MammotionBaseDevice
@@ -33,6 +34,7 @@ class MammotionCloud:
33
34
  self.command_queue = asyncio.Queue()
34
35
  self._waiting_queue = deque()
35
36
  self.mqtt_message_event = DataEvent()
37
+ self.mqtt_properties_event = DataEvent()
36
38
  self.on_ready_event = DataEvent()
37
39
  self.on_disconnected_event = DataEvent()
38
40
  self._operation_lock = asyncio.Lock()
@@ -127,6 +129,7 @@ class MammotionCloud:
127
129
  # Call the callbacks for each cloudDevice
128
130
  await self.mqtt_message_event.data_event(event)
129
131
  if event.method == "thing.properties":
132
+ await self.mqtt_properties_event.data_event(event)
130
133
  _LOGGER.debug(event)
131
134
 
132
135
  async def _handle_mqtt_message(self, topic: str, payload: dict) -> None:
@@ -159,6 +162,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
159
162
  self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName)
160
163
  self.currentID = ""
161
164
  self._mqtt.mqtt_message_event.add_subscribers(self._parse_message_for_device)
165
+ self._mqtt.mqtt_properties_event.add_subscribers(self._parse_message_properties_for_device)
162
166
  self._mqtt.on_ready_event.add_subscribers(self.on_ready)
163
167
  self._mqtt.on_disconnected_event.add_subscribers(self.on_disconnect)
164
168
  self.set_queue_callback(self.queue_command)
@@ -247,6 +251,11 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
247
251
  return item
248
252
  return None
249
253
 
254
+ async def _parse_message_properties_for_device(self, event: ThingPropertiesMessage) -> None:
255
+ if event.params.iotId != self.iot_id:
256
+ return
257
+ self.state_manager.properties(event)
258
+
250
259
  async def _parse_message_for_device(self, event: ThingEventMessage) -> None:
251
260
  params = event.params
252
261
  if event.params.iotId != self.iot_id:
@@ -141,7 +141,7 @@ message NavGetCommData {
141
141
  int32 subCmd = 2;
142
142
  int32 action = 3;
143
143
  int32 type = 4;
144
- fixed64 Hash = 5;
144
+ int64 hash = 5;
145
145
  int64 paternalHashA = 6;
146
146
  int64 paternalHashB = 7;
147
147
  int32 totalFrame = 8;
@@ -434,7 +434,7 @@ message NavTaskCtrlAck {
434
434
 
435
435
  message NavMapNameMsg {
436
436
  int32 rw = 1; // Represents RW field
437
- fixed64 hash = 2; // Represents HASH field
437
+ int64 hash = 2; // Represents HASH field
438
438
  string name = 3; // Represents NAME field
439
439
  int32 result = 4; // Represents RESULT field
440
440
  string device_id = 5; // Represents DEVICEID field
@@ -470,7 +470,7 @@ message SvgMessageAckT {
470
470
 
471
471
  message AreaHashName {
472
472
  string name = 2;
473
- fixed64 hash = 1;
473
+ int64 hash = 1;
474
474
  }
475
475
 
476
476
  message AppGetAllAreaHashName {
@@ -131,7 +131,7 @@ message vision_point_msg {
131
131
  }
132
132
 
133
133
  message vision_point_info_msg {
134
- int32 lable = 1;
134
+ int32 label = 1;
135
135
  int32 num = 2;
136
136
  repeated vision_point_msg vision_point = 3;
137
137
  }
@@ -258,6 +258,12 @@ message rpt_lora {
258
258
  int32 lora_connection_status = 5;
259
259
  }
260
260
 
261
+ message mqtt_rtk_connect {
262
+ int32 rtk_switch = 1;
263
+ int32 rtk_channel = 2;
264
+ string rtk_base_num = 3;
265
+ }
266
+
261
267
  message rpt_rtk {
262
268
  int32 status = 1;
263
269
  int32 pos_level = 2;
@@ -271,6 +277,7 @@ message rpt_rtk {
271
277
  int32 co_view_stars = 10;
272
278
  int32 reset = 11;
273
279
  rpt_lora lora_info = 12;
280
+ mqtt_rtk_connect mqtt_rtk_info = 13;
274
281
  }
275
282
 
276
283
  message rpt_dev_location {
@@ -316,6 +323,12 @@ message rpt_connect_status {
316
323
  int32 mnet_rssi = 5;
317
324
  int32 mnet_inet = 6;
318
325
  net_used_type used_net = 7;
326
+ MnetCfg mnet_cfg = 8;
327
+ }
328
+
329
+
330
+ message nav_heading_state_t {
331
+ int32 heading_state = 1;
319
332
  }
320
333
 
321
334
  message rpt_work {
@@ -339,6 +352,7 @@ message rpt_work {
339
352
  int32 man_run_speed = 18;
340
353
  int32 nav_edit_status = 19;
341
354
  int32 knife_height = 20;
355
+ nav_heading_state_t nav_heading_state = 21;
342
356
  }
343
357
 
344
358
  message rpt_maintain {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pymammotion
3
- Version: 0.2.51
3
+ Version: 0.2.53
4
4
  Summary:
5
5
  License: GNU-3.0
6
6
  Author: Michael Arthur
@@ -1,6 +1,6 @@
1
1
  pymammotion/__init__.py,sha256=jHCQrpJaG1jAoID9T4RT3g4JsZc0JpJqIcqjnA7cXd0,1605
2
2
  pymammotion/aliyun/__init__.py,sha256=T1lkX7TRYiL4nqYanG4l4MImV-SlavSbuooC-W-uUGw,29
3
- pymammotion/aliyun/cloud_gateway.py,sha256=96xbl_QefMcN0x8Dhz0OJQr_IaAcgrrrqfxCQAUGxTc,25668
3
+ pymammotion/aliyun/cloud_gateway.py,sha256=bUZHA-u1O2KphQOWNTxoNXwTvVZ1JptQ-9D5mHm-Xnw,25877
4
4
  pymammotion/aliyun/cloud_service.py,sha256=px7dUKow5Z7VyebjYzuKkzkm77XbUXYiFiYO_2e-UQ0,2207
5
5
  pymammotion/aliyun/model/aep_response.py,sha256=8f6GIP58ve8gd6AL3HBoXxsy0n2q4ygWvjELGnoOnVc,452
6
6
  pymammotion/aliyun/model/connect_response.py,sha256=Yz-fEbDzgGPTo5Of2oAjmFkSv08T7ze80pQU4k-gKIU,824
@@ -12,7 +12,7 @@ pymammotion/aliyun/model/stream_subscription_response.py,sha256=po765WASQDboVCos
12
12
  pymammotion/aliyun/tmp_constant.py,sha256=M4Hq_lrGB3LZdX6R2XohRPFoK1NDnNV-pTJwJcJ9838,6650
13
13
  pymammotion/bluetooth/__init__.py,sha256=LAl8jqZ1fPh-3mLmViNQsP3s814C1vsocYUa6oSaXt0,36
14
14
  pymammotion/bluetooth/ble.py,sha256=YfkfEK3TLJ8BaidjAXfUVFv8reLCu6U_lYa3Bo0pddw,2449
15
- pymammotion/bluetooth/ble_message.py,sha256=jQ_vece7a5WH_SW3pC_sdk6_Ch84bgWrRw3BatEje0c,16188
15
+ pymammotion/bluetooth/ble_message.py,sha256=cnUJHfMj9Sf5LOSIgQ5XP4A-Jbc-BjorSS48YfOUCpM,15444
16
16
  pymammotion/bluetooth/const.py,sha256=CCqyHsYbB0BAYjwdhXt_n6eWWxmhlUrAFjvVv57mbvE,1749
17
17
  pymammotion/bluetooth/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  pymammotion/bluetooth/data/convert.py,sha256=6DMwvzVr9FWCoQFIKSI2poFXjISc_m6X59g8FlVO0-o,800
@@ -22,13 +22,13 @@ pymammotion/const.py,sha256=lWRxvTVdXnNHuxqvRkjO5ziK0Ic-fZMM6J2dbe5M6Nc,385
22
22
  pymammotion/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  pymammotion/data/model/__init__.py,sha256=aSyroxYQQS-WMRi6WmWm2js4wLa9nmsi160gx9tts4o,323
24
24
  pymammotion/data/model/account.py,sha256=vJM-KTf2q6eBfVC-UlNHBSmJvqHiCawZ40vnuhXhaz8,140
25
- pymammotion/data/model/device.py,sha256=s8k43ZZBTnLtvh93pcbCVRirzt8xo08Wx0xht46e-7Q,12070
25
+ pymammotion/data/model/device.py,sha256=hsN_NrrM_Kg1gbxcmSL3sVubGmeMxWHwnPiUN1-d8Eo,12196
26
26
  pymammotion/data/model/device_config.py,sha256=RkEbnkubJQ8nXPfZpuGPe_h9mGZGtxCxDSc8mY7UCT8,2824
27
27
  pymammotion/data/model/enums.py,sha256=EpKmO8yVUZyEnTY4yH0DMMVKYNQM42zpW1maUu0i3IE,1582
28
28
  pymammotion/data/model/excute_boarder_params.py,sha256=9CpUqrygcle1C_1hDW-riLmm4map4ZbE842NXjcomEI,1394
29
29
  pymammotion/data/model/execute_boarder.py,sha256=9rd_h4fbcsXxgnLOd2rO2hWyD1abnTGc47QTEpp8DD0,1103
30
30
  pymammotion/data/model/generate_route_information.py,sha256=MkUBoqGtCAKmiVQ4Q1pEoDVHZs5uLIo7vhfWT4nGbtY,801
31
- pymammotion/data/model/hash_list.py,sha256=dc15-HrvzQ6JLehNFIXnKzSWDCgb6_MAB6omXTphZXQ,4649
31
+ pymammotion/data/model/hash_list.py,sha256=v9zoOhrwr1TMTsyPL-BQSylbqfapmO4l5oIefaW9YHw,5183
32
32
  pymammotion/data/model/location.py,sha256=PwmITejfI4pm7PI4rzqSuuHetwle6IJr_CV95435s2M,871
33
33
  pymammotion/data/model/mowing_modes.py,sha256=5TrHSijUyPtIDWpNtgzx_vFQukRJWRz4gIrUaXggKPw,827
34
34
  pymammotion/data/model/plan.py,sha256=mcadkSL7fQXy0iJ0q786I3GEQY4i6kmQXfW6Ri69lcQ,2906
@@ -37,9 +37,9 @@ pymammotion/data/model/region_data.py,sha256=OTV15vRyn9JORXsQPjWMNF1ZujuNhsOKl25
37
37
  pymammotion/data/model/report_info.py,sha256=AE2u64uKgcJtVooldJcxXBsEHfDyCvH5DnQFUQ5qWmk,2226
38
38
  pymammotion/data/mqtt/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
39
39
  pymammotion/data/mqtt/event.py,sha256=plt53w3kHulB_MfbkxK9j7AfdQ5ahVU2s4kiQMoLio4,4612
40
- pymammotion/data/mqtt/properties.py,sha256=HkBPghr26L9_b4QaOi1DtPgb0UoPIOGSe9wb3kgnM6Y,2815
40
+ pymammotion/data/mqtt/properties.py,sha256=kvphcjrDuJHuX8Az98-wKeFv_rSmu2Fz9YKLGodGSj0,3759
41
41
  pymammotion/data/mqtt/status.py,sha256=zqnlo-MzejEQZszl0i0Wucoc3E76x6UtI9JLxoBnu54,1067
42
- pymammotion/data/state_manager.py,sha256=bGZXaiIosNTrN0L8DmddLqL2MRKbixKmyd2XFiZMCTE,4101
42
+ pymammotion/data/state_manager.py,sha256=tH1dI5uH8qiIeJjHXR8FhGiWPEcOz-f3D4TyAhCiz-I,4327
43
43
  pymammotion/event/__init__.py,sha256=mgATR6vPHACNQ-0zH5fi7NdzeTCDV1CZyaWPmtUusi8,115
44
44
  pymammotion/event/event.py,sha256=UzYnxV5DfvMDK3E06UvSzvzuBbaXOOUwO6xYt_zn9To,2034
45
45
  pymammotion/http/_init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -47,23 +47,23 @@ pymammotion/http/http.py,sha256=gJU8w3j8NL_m5YnxfbRxxm4y9SW0UfKr1iAaMvLsfuM,3562
47
47
  pymammotion/http/model/http.py,sha256=1NJP4hkBulcnpbD7Rcwltd8Ry9lnZzQVFJN8Oo7MKeM,1706
48
48
  pymammotion/mammotion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  pymammotion/mammotion/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
- pymammotion/mammotion/commands/abstract_message.py,sha256=nw6r7694yzl7iJKqRqhLmAPRjd_TL_Xo_-JXq2_a_ug,222
51
- pymammotion/mammotion/commands/mammotion_command.py,sha256=Cdv8sAt-W-Sihmhir1eN56OMA2e5b8y47NP2QKYqb-o,2419
50
+ pymammotion/mammotion/commands/abstract_message.py,sha256=l2Wcyg7tIEjRGyQAk9T2rQVOLwilxG1hXTNf7811jGA,727
51
+ pymammotion/mammotion/commands/mammotion_command.py,sha256=cjbetadbNWBBfhzxuT34jfmzfpYYa1CnsCeQZkANVG4,2510
52
52
  pymammotion/mammotion/commands/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- pymammotion/mammotion/commands/messages/driver.py,sha256=spkNa-sag_7xDuHy7mjsR_Fc8z9ztNKHCjtojWayJms,3710
54
- pymammotion/mammotion/commands/messages/media.py,sha256=ps0l06CXy5Ej--gTNCsyKttwo7yHLVrJUpn-wNJYecs,1150
55
- pymammotion/mammotion/commands/messages/navigation.py,sha256=4rXBL-mViWc38K6x1w5O-GjwV8UWS5xZXkf4aHYjs8A,23684
56
- pymammotion/mammotion/commands/messages/network.py,sha256=61msRJWyXdrO8FbI_rhrO2K8R1qkGVUj5BFzNwm7lwg,8155
57
- pymammotion/mammotion/commands/messages/ota.py,sha256=XkeuWBZtpYMMBze6r8UN7dJXbe2FxUNGNnjwBpXJKM0,1240
58
- pymammotion/mammotion/commands/messages/system.py,sha256=XoDvnERAMXIj6oK6l-P7kVBmloaIOAf9flCq0Q6vryg,12399
59
- pymammotion/mammotion/commands/messages/video.py,sha256=_8lJsU4sLm2CGnc7RDkueA0A51Ysui6x7SqFnhX8O2g,1007
53
+ pymammotion/mammotion/commands/messages/driver.py,sha256=DlOAC_yi1ycO5hKr5rfCpLmrqao6Mb6Fv1JWwMISnKE,3766
54
+ pymammotion/mammotion/commands/messages/media.py,sha256=l-m4l2Vp1ZOHPHyJTceuLaLvdgHOEfmykkbDncCDUI4,1359
55
+ pymammotion/mammotion/commands/messages/navigation.py,sha256=Z6RQK-pMh8o7_K_1yTENx3lkNBFQTU_ojunolSre0oM,23241
56
+ pymammotion/mammotion/commands/messages/network.py,sha256=ooBiXgJC71lrkMeS_DLc3_pp7S8VGgyq7ZAmna-jKXw,8516
57
+ pymammotion/mammotion/commands/messages/ota.py,sha256=g937HT_-OQXV6A3zUiZ53b45cOX6y-rzs5m-4b0IcTk,1473
58
+ pymammotion/mammotion/commands/messages/system.py,sha256=pUdV8z-pnoXvDM29DQbfAi2mG6uK7EHQzJL2sWZLHrc,12843
59
+ pymammotion/mammotion/commands/messages/video.py,sha256=ne1YSuQChaDFfmHgMO5Jc9_O_7vkt5yV1EXVEh3LTbg,1141
60
60
  pymammotion/mammotion/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
61
  pymammotion/mammotion/control/joystick.py,sha256=QfBVxM_gxpWsZAGO90whtgxCI2tIZ3TTad9wHIPsU9s,5640
62
62
  pymammotion/mammotion/devices/__init__.py,sha256=f2qQFPgLGmV85W2hSlMUh5BYuht9o_Ar_JEAAMD4fsE,102
63
- pymammotion/mammotion/devices/base.py,sha256=iqvGglud4GYKgivBjzge7WU9-pLY8Z6l4QjShxLT7Go,9746
64
- pymammotion/mammotion/devices/mammotion.py,sha256=ForzwnQu1IRxtwsFMHAudFCtOAUJC01BcX5RK7dYYcs,12295
63
+ pymammotion/mammotion/devices/base.py,sha256=MXAuJ9sGnf9QqBeNxGbOkdtqwVpPocVdxw0uT0DgSks,9800
64
+ pymammotion/mammotion/devices/mammotion.py,sha256=7H0SNO4GTCnrthJ6wL1pkVajqb74t006k4lzLr-_cZE,12500
65
65
  pymammotion/mammotion/devices/mammotion_bluetooth.py,sha256=nyO7pkKgAoRPs-28ESf6ee-y3G5JTYRO-MCp4wKPMlE,17476
66
- pymammotion/mammotion/devices/mammotion_cloud.py,sha256=Q1Il0nCxH-8utg_9AKAY3HxuuhB78uHZV4yVpZ4k1TI,11282
66
+ pymammotion/mammotion/devices/mammotion_cloud.py,sha256=VuXuYlvi-HRbfRlgIV3KraVtbu2EM3QxP-JWWAYj_E0,11774
67
67
  pymammotion/mqtt/__init__.py,sha256=Ocs5e-HLJvTuDpVXyECEsWIvwsUaxzj7lZ9mSYutNDY,105
68
68
  pymammotion/mqtt/mammotion_future.py,sha256=_OWqKOlUGl2yT1xOsXFQYpGd-1zQ63OxqXgy7KRQgYc,710
69
69
  pymammotion/mqtt/mammotion_mqtt.py,sha256=LaySave_hf0gU3crUTLqzpdQtxIwK8vu5DM8F8fbU2Y,8748
@@ -92,7 +92,7 @@ pymammotion/proto/mctrl_driver.proto,sha256=I0BncdAa3laeqT17Sn95r_1HuBD3dSc9IVu9
92
92
  pymammotion/proto/mctrl_driver.py,sha256=sseY2MxUtaQZvg7fvbA_gNvtqx9MVDW_rvUcfA2CWVs,2971
93
93
  pymammotion/proto/mctrl_driver_pb2.py,sha256=bfLwZb5Hehb6OIkgFrZMkQ0oTBXoOBxpruszKz-UM1U,3785
94
94
  pymammotion/proto/mctrl_driver_pb2.pyi,sha256=9_rcQELsSeOfeIQMTEFIpeXICpDe3arQeA4kAYWNSWw,5860
95
- pymammotion/proto/mctrl_nav.proto,sha256=TAJHfzgMRmT2IW0ciVJYAQDkfinRXoFEJGkk0VobSjU,12380
95
+ pymammotion/proto/mctrl_nav.proto,sha256=rti69pG6rKMhhX6dWGv0UwtUQkHwKc0r9tOQ78M-N7k,12374
96
96
  pymammotion/proto/mctrl_nav.py,sha256=xTsPmwhV_AwfoFWTGqpz2W4rVR7tHTHwNU4rS8QBLdc,24884
97
97
  pymammotion/proto/mctrl_nav_pb2.py,sha256=LAHfEmGfNVZCN6vuLSZF6s2wd1Qk-siaWe4mdPWPdxc,24213
98
98
  pymammotion/proto/mctrl_nav_pb2.pyi,sha256=qmGYfKh2o63e5ppl9QIWnwDbmGVUVOf7EqhC9ApE5fg,51336
@@ -104,7 +104,7 @@ pymammotion/proto/mctrl_pept.proto,sha256=HBTRiP1XJB5w9hT1V38aePPREpePBk5jkjupu_
104
104
  pymammotion/proto/mctrl_pept.py,sha256=utMtbXsCwGS14YggTqUdVIbTZsR0w49B6gKU8jHzbJg,1332
105
105
  pymammotion/proto/mctrl_pept_pb2.py,sha256=QqQ1BXo_EBs7cLmQGtRbnNy0rRxvaqtrGfKxXS8R5A8,2134
106
106
  pymammotion/proto/mctrl_pept_pb2.pyi,sha256=gr0lxUPqhyEnDdni9vpIQnAIhqAGtHlv1rFeb5EJnMY,2840
107
- pymammotion/proto/mctrl_sys.proto,sha256=_E9XeZyW1vhY8S0HxPcEjMKmuhULTonm8Q3Y0X3Qxaw,10222
107
+ pymammotion/proto/mctrl_sys.proto,sha256=FawssbrJkz2ZuwBGCCbFm-EO2qCYsXpeiFNHahCc9ug,10506
108
108
  pymammotion/proto/mctrl_sys.py,sha256=UJMQVXMjXVksp4O_eD_bUNPFM4ijvdSuma-sOIUsNUw,19138
109
109
  pymammotion/proto/mctrl_sys_pb2.py,sha256=DYemb514mlC7c27t-k1YqqBif0xxhLmnIWk8rXtSj1c,21497
110
110
  pymammotion/proto/mctrl_sys_pb2.pyi,sha256=Dj_1UM86kZ5MfcVyNC76Z0gKrfl5YFsVWP2b-bKoZvk,38912
@@ -118,7 +118,7 @@ pymammotion/utility/map.py,sha256=GYscVMg2cX3IPlNpCBNHDW0S55yS1WGRf1iHnNZ7TfQ,22
118
118
  pymammotion/utility/movement.py,sha256=N75oAoAgFydqoaOedYIxGUHmuTCtPzAOtb-d_29tpfI,615
119
119
  pymammotion/utility/periodic.py,sha256=MbeSb9cfhxzYmdT_RiE0dZe3H9IfbQW_zSqhmSX2RUc,3321
120
120
  pymammotion/utility/rocker_util.py,sha256=6tX7sS87qoQC_tsxbx3NLL-HgS08wtzXiZkhDiz7uo0,7179
121
- pymammotion-0.2.51.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
122
- pymammotion-0.2.51.dist-info/METADATA,sha256=iG3-bUgO8rYjPQBM_YcRPHOOPX7x7PdwmC8-aToZp6Y,4052
123
- pymammotion-0.2.51.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
124
- pymammotion-0.2.51.dist-info/RECORD,,
121
+ pymammotion-0.2.53.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
122
+ pymammotion-0.2.53.dist-info/METADATA,sha256=sDDhMB3l16nIsK86dpKvlH9eSq1X5E8cXMc0rhF2KtA,4052
123
+ pymammotion-0.2.53.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
124
+ pymammotion-0.2.53.dist-info/RECORD,,