pymammotion 0.4.32__py3-none-any.whl → 0.4.34__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.
@@ -399,7 +399,7 @@ class BleMessage:
399
399
  sequence = int(response[2]) # toInt
400
400
  current_sequence = self.mReadSequence.get() & 255
401
401
  if sequence == current_sequence:
402
- _LOGGER.debug(f"Received bluetooth data 1: {response.hex()}, object: {self}")
402
+ # _LOGGER.debug(f"Received bluetooth data 1: {response.hex()}, object: {self}")
403
403
  return 2
404
404
 
405
405
  # Compare with the second counter, mod 255
@@ -2,8 +2,7 @@
2
2
 
3
3
  from .generate_route_information import GenerateRouteInformation
4
4
  from .hash_list import HashList
5
- from .plan import Plan
6
5
  from .rapid_state import RapidState, RTKStatus
7
6
  from .region_data import RegionData
8
7
 
9
- __all__ = ["GenerateRouteInformation", "HashList", "Plan", "RapidState", "RTKStatus", "RegionData"]
8
+ __all__ = ["GenerateRouteInformation", "HashList", "RapidState", "RTKStatus", "RegionData"]
@@ -40,6 +40,7 @@ def create_path_order(operation_mode: OperationSettings, device_name: str) -> st
40
40
  bArr[1] = operation_mode.obstacle_laps
41
41
  bArr[3] = int(operation_mode.start_progress)
42
42
  bArr[2] = 0
43
+ bArr[5] = 0
43
44
  if not DeviceType.is_luba1(device_name):
44
45
  bArr[4] = 0
45
46
  if DeviceType.is_yuka(device_name) and not DeviceType.is_yuka_mini(device_name):
@@ -1,6 +1,14 @@
1
1
  from enum import Enum
2
2
 
3
3
 
4
+ class ConnectionPreference(Enum):
5
+ """Enum for connection preference."""
6
+
7
+ EITHER = 0
8
+ WIFI = 1
9
+ BLUETOOTH = 2
10
+
11
+
4
12
  class PositionMode(Enum):
5
13
  FIX = 0
6
14
  SINGLE = 1
@@ -170,7 +170,7 @@ class HashList(DataClassORJSONMixin):
170
170
  dump: dict[int, FrameList] = field(default_factory=dict) # type 12? / sub cmd 4
171
171
  svg: dict[int, FrameList] = field(default_factory=dict) # type 13
172
172
  line: dict[int, FrameList] = field(default_factory=dict) # type 10 possibly breakpoint? / sub cmd 3
173
- plan: dict[int, Plan] = field(default_factory=dict)
173
+ plan: dict[str, Plan] = field(default_factory=dict)
174
174
  area_name: list[AreaHashNameList] = field(default_factory=list)
175
175
 
176
176
  def update_hash_lists(self, hashlist: list[int]) -> None:
@@ -281,7 +281,7 @@ class HashList(DataClassORJSONMixin):
281
281
 
282
282
  def update_plan(self, plan: Plan) -> None:
283
283
  if plan.total_plan_num != 0:
284
- self.plan[plan.plan_index] = plan
284
+ self.plan[plan.plan_id] = plan
285
285
 
286
286
  def update(self, hash_data: NavGetCommData | SvgMessage) -> bool:
287
287
  """Update the map data."""
@@ -0,0 +1,27 @@
1
+ """bidire_reqconver_path as a model."""
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
6
+
7
+
8
+ @dataclass
9
+ class CurrentTaskSettings(DataClassORJSONMixin):
10
+ pver: int = 0
11
+ job_id: int = 0
12
+ job_ver: int = 0
13
+ job_mode: int = 0
14
+ sub_cmd: int = 0
15
+ edge_mode: int = 0
16
+ knife_height: int = 0
17
+ channel_width: int = 0
18
+ ultra_wave: int = 0
19
+ channel_mode: int = 0
20
+ toward: int = 0
21
+ speed: float = 0.0
22
+ zone_hashs: list[int] = field(default_factory=list)
23
+ path_hash: int = 0
24
+ reserved: str = ""
25
+ result: int = 0
26
+ toward_mode: int = 0
27
+ toward_included_angle: int = 0
@@ -9,6 +9,7 @@ import betterproto
9
9
 
10
10
  from pymammotion.data.model.device import MowingDevice
11
11
  from pymammotion.data.model.device_info import SideLight
12
+ from pymammotion.data.model.enums import ConnectionPreference
12
13
  from pymammotion.data.model.hash_list import AreaHashNameList, NavGetCommData, NavGetHashListData, Plan, SvgMessage
13
14
  from pymammotion.data.model.work import CurrentTaskSettings
14
15
  from pymammotion.data.mqtt.properties import ThingPropertiesMessage
@@ -35,27 +36,27 @@ logger = logging.getLogger(__name__)
35
36
  class StateManager:
36
37
  """Manage state."""
37
38
 
38
- _device: MowingDevice
39
- last_updated_at: datetime = datetime.now()
40
- cloud_gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
41
- cloud_get_commondata_ack_callback: Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None = None
42
- cloud_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
43
- cloud_on_notification_callback: Callable[[tuple[str, Any | None]], Awaitable[None]] | None = None
44
-
45
- # possibly don't need anymore
46
- cloud_queue_command_callback: Callable[[str, dict[str, Any]], Awaitable[bytes]] | None = None
47
-
48
- ble_gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
49
- ble_get_commondata_ack_callback: Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None = None
50
- ble_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
51
- ble_on_notification_callback: Callable[[tuple[str, Any | None]], Awaitable[None]] | None = None
52
-
53
- # possibly don't need anymore
54
- ble_queue_command_callback: Callable[[str, dict[str, Any]], Awaitable[bytes]] | None = None
55
-
56
39
  def __init__(self, device: MowingDevice) -> None:
57
- self._device = device
40
+ self._device: MowingDevice = device
58
41
  self.last_updated_at = datetime.now()
42
+ self.preference = ConnectionPreference.WIFI
43
+ self.cloud_gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
44
+ self.cloud_get_commondata_ack_callback: (
45
+ Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None
46
+ ) = None
47
+ self.cloud_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
48
+ self.cloud_on_notification_callback: Callable[[tuple[str, Any | None]], Awaitable[None]] | None = None
49
+
50
+ self.cloud_queue_command_callback: Callable[[str, dict[str, Any]], Awaitable[bytes]] | None = None
51
+
52
+ self.ble_gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
53
+ self.ble_get_commondata_ack_callback: Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None = (
54
+ None
55
+ )
56
+ self.ble_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
57
+ self.ble_on_notification_callback: Callable[[tuple[str, Any | None]], Awaitable[None]] | None = None
58
+
59
+ self.ble_queue_command_callback: Callable[[str, dict[str, Any]], Awaitable[bytes]] | None = None
59
60
 
60
61
  def get_device(self) -> MowingDevice:
61
62
  """Get device."""
@@ -34,7 +34,7 @@ def find_next_integer(lst: list[int], current_hash: int) -> int | None:
34
34
  class MammotionBaseDevice:
35
35
  """Base class for Mammotion devices."""
36
36
 
37
- def __init__(self, state_manager: StateManager, cloud_device: Device | None = None) -> None:
37
+ def __init__(self, state_manager: StateManager, cloud_device: Device) -> None:
38
38
  """Initialize MammotionBaseDevice."""
39
39
  self.loop = asyncio.get_event_loop()
40
40
  self._state_manager = state_manager
@@ -247,18 +247,8 @@ class MammotionBaseDevice:
247
247
  # if len(missing_frames) > 0:
248
248
  # del self.mower.map.svg[hash]
249
249
 
250
- if len(self.mower.map.root_hash_lists) == 0:
250
+ if len(self.mower.map.root_hash_lists) == 0 or len(self.mower.map.missing_hashlist()) > 0:
251
251
  await self.queue_command("get_all_boundary_hash_list", sub_cmd=0)
252
- # add a small delay to allow result to come through if it does.
253
- await asyncio.sleep(1)
254
-
255
- if len(self.mower.map.missing_hashlist()) > 0:
256
- data_hash = self.mower.map.missing_hashlist().pop()
257
- await self.queue_command("synchronize_hash_data", hash_num=data_hash)
258
-
259
- # if len(self.mower.map.missing_hashlist(3)) > 0:
260
- # data_hash = self.mower.map.missing_hashlist(3).pop()
261
- # await self.queue_command("synchronize_hash_data", hash_num=data_hash)
262
252
 
263
253
  # sub_cmd 3 is job hashes??
264
254
  # sub_cmd 4 is dump location (yuka)
@@ -11,8 +11,8 @@ from bleak.backends.device import BLEDevice
11
11
 
12
12
  from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
13
13
  from pymammotion.aliyun.model.dev_by_account_response import Device
14
- from pymammotion.data.model.account import Credentials
15
14
  from pymammotion.data.model.device import MowingDevice
15
+ from pymammotion.data.model.enums import ConnectionPreference
16
16
  from pymammotion.data.state_manager import StateManager
17
17
  from pymammotion.http.http import MammotionHTTP
18
18
  from pymammotion.mammotion.devices.mammotion_bluetooth import MammotionBaseBLEDevice
@@ -48,9 +48,10 @@ class MammotionMixedDeviceManager:
48
48
  self.name = name
49
49
  self._state_manager = StateManager(MowingDevice())
50
50
  self._state_manager.get_device().name = name
51
- self.add_ble(ble_device)
51
+ self.add_ble(cloud_device, ble_device)
52
52
  self.add_cloud(cloud_device, mqtt)
53
53
  self.preference = preference
54
+ self._state_manager.preference = preference
54
55
 
55
56
  @property
56
57
  def mower_state(self):
@@ -72,11 +73,13 @@ class MammotionMixedDeviceManager:
72
73
  else:
73
74
  return not self.ble().command_queue.empty()
74
75
 
75
- def add_ble(self, ble_device: BLEDevice) -> None:
76
+ def add_ble(self, cloud_device: Device, ble_device: BLEDevice) -> None:
76
77
  if ble_device is not None:
77
- self._ble_device = MammotionBaseBLEDevice(state_manager=self._state_manager, device=ble_device)
78
+ self._ble_device = MammotionBaseBLEDevice(
79
+ state_manager=self._state_manager, cloud_device=cloud_device, device=ble_device
80
+ )
78
81
 
79
- def add_cloud(self, cloud_device: Device | None = None, mqtt: MammotionCloud | None = None) -> None:
82
+ def add_cloud(self, cloud_device: Device, mqtt: MammotionCloud) -> None:
80
83
  if cloud_device is not None:
81
84
  self._cloud_device = MammotionBaseCloudDevice(
82
85
  mqtt, cloud_device=cloud_device, state_manager=self._state_manager
@@ -86,12 +89,22 @@ class MammotionMixedDeviceManager:
86
89
  self._cloud_device = cloud_device
87
90
 
88
91
  def remove_cloud(self) -> None:
92
+ self._state_manager.cloud_get_commondata_ack_callback = None
93
+ self._state_manager.cloud_get_hashlist_ack_callback = None
94
+ self._state_manager.cloud_get_plan_callback = None
95
+ self._state_manager.cloud_on_notification_callback = None
96
+ self._state_manager.cloud_gethash_ack_callback = None
89
97
  del self._cloud_device
90
98
 
91
99
  def replace_ble(self, ble_device: MammotionBaseBLEDevice) -> None:
92
100
  self._ble_device = ble_device
93
101
 
94
102
  def remove_ble(self) -> None:
103
+ self._state_manager.ble_get_commondata_ack_callback = None
104
+ self._state_manager.ble_get_hashlist_ack_callback = None
105
+ self._state_manager.ble_get_plan_callback = None
106
+ self._state_manager.ble_on_notification_callback = None
107
+ self._state_manager.ble_gethash_ack_callback = None
95
108
  del self._ble_device
96
109
 
97
110
  def replace_mqtt(self, mqtt: MammotionCloud) -> None:
@@ -139,22 +152,6 @@ class MammotionDeviceManager:
139
152
  del device_for_removal
140
153
 
141
154
 
142
- async def create_devices(
143
- ble_device: BLEDevice,
144
- cloud_credentials: Credentials | None = None,
145
- preference: ConnectionPreference = ConnectionPreference.BLUETOOTH,
146
- ):
147
- mammotion = Mammotion()
148
- mammotion.add_ble_device(ble_device, preference)
149
-
150
- if cloud_credentials and preference == ConnectionPreference.EITHER or preference == ConnectionPreference.WIFI:
151
- await mammotion.login_and_initiate_cloud(
152
- cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password
153
- )
154
-
155
- return mammotion
156
-
157
-
158
155
  class Mammotion:
159
156
  """Represents a Mammotion account and its devices."""
160
157
 
@@ -173,11 +170,16 @@ class Mammotion:
173
170
  self._login_lock = asyncio.Lock()
174
171
 
175
172
  def add_ble_device(
176
- self, ble_device: BLEDevice, preference: ConnectionPreference = ConnectionPreference.BLUETOOTH
173
+ self,
174
+ cloud_device: Device,
175
+ ble_device: BLEDevice,
176
+ preference: ConnectionPreference = ConnectionPreference.BLUETOOTH,
177
177
  ) -> None:
178
178
  if ble_device:
179
179
  self.device_manager.add_device(
180
- MammotionMixedDeviceManager(name=ble_device.name, ble_device=ble_device, preference=preference)
180
+ MammotionMixedDeviceManager(
181
+ name=ble_device.name, cloud_device=cloud_device, ble_device=ble_device, preference=preference
182
+ )
181
183
  )
182
184
 
183
185
  async def login_and_initiate_cloud(self, account, password, force: bool = False) -> None:
@@ -14,6 +14,7 @@ from bleak_retry_connector import (
14
14
  establish_connection,
15
15
  )
16
16
 
17
+ from pymammotion.aliyun.model.dev_by_account_response import Device
17
18
  from pymammotion.bluetooth import BleMessage
18
19
  from pymammotion.data.state_manager import StateManager
19
20
  from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
@@ -69,9 +70,11 @@ async def _handle_retry(fut: asyncio.Future[None], func, command: bytes) -> None
69
70
  class MammotionBaseBLEDevice(MammotionBaseDevice):
70
71
  """Base class for Mammotion BLE devices."""
71
72
 
72
- def __init__(self, state_manager: StateManager, device: BLEDevice, interface: int = 0, **kwargs: Any) -> None:
73
+ def __init__(
74
+ self, state_manager: StateManager, cloud_device: Device, device: BLEDevice, interface: int = 0, **kwargs: Any
75
+ ) -> None:
73
76
  """Initialize MammotionBaseBLEDevice."""
74
- super().__init__(state_manager)
77
+ super().__init__(state_manager, cloud_device)
75
78
  self._disconnect_strategy = True
76
79
  self._ble_sync_task = None
77
80
  self._prev_notification = None
@@ -88,6 +91,7 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
88
91
  self._connect_lock = asyncio.Lock()
89
92
  self._operation_lock = asyncio.Lock()
90
93
  self._key: str | None = None
94
+ self._cloud_device = cloud_device
91
95
  self.set_queue_callback(self.queue_command)
92
96
  self._state_manager.ble_gethash_ack_callback = self.datahash_response
93
97
  self._state_manager.ble_get_commondata_ack_callback = self.commdata_response
@@ -322,22 +326,23 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
322
326
 
323
327
  async def _notification_handler(self, _sender: BleakGATTCharacteristic, data: bytearray) -> None:
324
328
  """Handle notification responses."""
329
+
325
330
  if self._message is None:
326
331
  return
327
332
  result = self._message.parseNotification(data)
328
333
  if result == 0:
329
334
  data = await self._message.parseBlufiNotifyData(True)
335
+ self._message.clear_notification()
330
336
  try:
331
337
  self._update_raw_data(data)
332
338
  except (KeyError, ValueError, IndexError, UnicodeDecodeError):
333
339
  _LOGGER.exception("Error parsing message %s", data)
334
340
  data = b""
335
- finally:
336
- self._message.clear_notification()
337
341
 
338
342
  _LOGGER.debug("%s: Received notification: %s", self.name, data)
339
343
  else:
340
344
  return
345
+
341
346
  new_msg = LubaMsg().parse(data)
342
347
  if betterproto.serialized_on_wire(new_msg.net):
343
348
  if new_msg.net.todev_ble_sync != 0 or has_field(new_msg.net.toapp_wifi_iot_status):
@@ -181,6 +181,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
181
181
  self._mqtt.mqtt_message_event.remove_subscribers(self._parse_message_for_device)
182
182
  self._state_manager.cloud_gethash_ack_callback = None
183
183
  self._state_manager.cloud_get_commondata_ack_callback = None
184
+ self._state_manager.cloud_get_plan_callback = None
184
185
  if self._ble_sync_task:
185
186
  self._ble_sync_task.cancel()
186
187
 
@@ -229,7 +230,11 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
229
230
  # self.mqtt._mqtt_client.thing_on_thing_enable(None)
230
231
 
231
232
  async def _ble_sync(self) -> None:
232
- if self.stopped or self.mqtt.is_connected is False or self.state_manager.get_device().report_data.dev.sys_status in NO_REQUEST_MODES:
233
+ if (
234
+ self.stopped
235
+ or self.mqtt.is_connected is False
236
+ or self.state_manager.get_device().report_data.dev.sys_status in NO_REQUEST_MODES
237
+ ):
233
238
  return
234
239
 
235
240
  command_bytes = self._commands.send_todev_ble_sync(3)
@@ -0,0 +1,114 @@
1
+ import struct
2
+
3
+
4
+ class MurMurHashUtil:
5
+ @staticmethod
6
+ def get_unsigned_int(i):
7
+ return i & 0xFFFFFFFF
8
+
9
+ @staticmethod
10
+ def hash(byte_arr: bytes):
11
+ # Create a bytearray view with little endian order
12
+ position = 0
13
+ remaining_bytes = len(byte_arr)
14
+
15
+ # Initial values
16
+ remaining = remaining_bytes ^ 97
17
+ j = 0
18
+
19
+ # Process 8 bytes at a time
20
+ while remaining_bytes >= 8:
21
+ multiplier = 1540483477
22
+
23
+ # First 4 bytes
24
+ unsigned_int = struct.unpack_from("<I", byte_arr, position)[0]
25
+ position += 4
26
+ unsigned_int = (MurMurHashUtil.get_unsigned_int(unsigned_int) * multiplier) & 0xFFFFFFFF
27
+ remaining = (
28
+ ((remaining * multiplier) & 0xFFFFFFFF)
29
+ ^ (((unsigned_int ^ (unsigned_int >> 24)) & 0xFFFFFFFF) * multiplier) & 0xFFFFFFFF
30
+ ) & 0xFFFFFFFF
31
+
32
+ # Next 4 bytes
33
+ unsigned_int2 = struct.unpack_from("<I", byte_arr, position)[0]
34
+ position += 4
35
+ unsigned_int2 = (MurMurHashUtil.get_unsigned_int(unsigned_int2) * multiplier) & 0xFFFFFFFF
36
+ j = (
37
+ ((j * multiplier) & 0xFFFFFFFF)
38
+ ^ ((((unsigned_int2 ^ (unsigned_int2 >> 24)) & 0xFFFFFFFF) * multiplier) & 0xFFFFFFFF)
39
+ ) & 0xFFFFFFFF
40
+
41
+ remaining_bytes -= 8
42
+
43
+ # Process remaining 4 bytes if available
44
+ if remaining_bytes >= 4:
45
+ multiplier = 1540483477
46
+ unsigned_int3 = struct.unpack_from("<I", byte_arr, position)[0]
47
+ position += 4
48
+ unsigned_int3 = (MurMurHashUtil.get_unsigned_int(unsigned_int3) * multiplier) & 0xFFFFFFFF
49
+ remaining = (
50
+ ((remaining * multiplier) & 0xFFFFFFFF)
51
+ ^ ((((unsigned_int3 ^ (unsigned_int3 >> 24)) & 0xFFFFFFFF) * multiplier) & 0xFFFFFFFF)
52
+ ) & 0xFFFFFFFF
53
+ remaining_bytes -= 4
54
+
55
+ # Process final 1-3 bytes if available
56
+ if remaining_bytes == 1:
57
+ j = (
58
+ ((j ^ (MurMurHashUtil.get_unsigned_int(byte_arr[position]) & 0xFF)) & 0xFFFFFFFF) * 1540483477
59
+ ) & 0xFFFFFFFF
60
+ elif remaining_bytes == 2:
61
+ j = (j ^ ((MurMurHashUtil.get_unsigned_int(byte_arr[position + 1]) & 0xFF) << 8)) & 0xFFFFFFFF
62
+ j = (
63
+ ((j ^ (MurMurHashUtil.get_unsigned_int(byte_arr[position]) & 0xFF)) & 0xFFFFFFFF) * 1540483477
64
+ ) & 0xFFFFFFFF
65
+ elif remaining_bytes == 3:
66
+ j = (j ^ ((MurMurHashUtil.get_unsigned_int(byte_arr[position + 2]) & 0xFF) << 16)) & 0xFFFFFFFF
67
+ j = (j ^ ((MurMurHashUtil.get_unsigned_int(byte_arr[position + 1]) & 0xFF) << 8)) & 0xFFFFFFFF
68
+ j = (
69
+ ((j ^ (MurMurHashUtil.get_unsigned_int(byte_arr[position]) & 0xFF)) & 0xFFFFFFFF) * 1540483477
70
+ ) & 0xFFFFFFFF
71
+
72
+ # Final mixing
73
+ multiplier = 1540483477
74
+ j5 = (((remaining ^ (j >> 18)) & 0xFFFFFFFF) * multiplier) & 0xFFFFFFFF
75
+ j6 = (((j ^ (j5 >> 22)) & 0xFFFFFFFF) * multiplier) & 0xFFFFFFFF
76
+ j7 = (((j5 ^ (j6 >> 17)) & 0xFFFFFFFF) * multiplier) & 0xFFFFFFFF
77
+ j8 = j7 << 32
78
+
79
+ return j8 | ((((j6 ^ (j7 >> 19)) & 0xFFFFFFFF) * multiplier) & 0xFFFFFFFF)
80
+
81
+ @staticmethod
82
+ def hash_unsigned(s: str | bytes) -> None:
83
+ if isinstance(s, str):
84
+ return MurMurHashUtil.read_unsigned_long(MurMurHashUtil.hash(s.encode()))
85
+ elif isinstance(s, bytes) or isinstance(s, bytearray):
86
+ return MurMurHashUtil.read_unsigned_long(MurMurHashUtil.hash(s))
87
+ return None
88
+
89
+ @staticmethod
90
+ def long2bytes(value: int) -> bytes:
91
+ # Convert long to 8 bytes in little-endian order
92
+ result = bytearray(8)
93
+ for i in range(8):
94
+ result[7 - i] = (value >> (i * 8)) & 0xFF
95
+ return bytes(result)
96
+
97
+ @staticmethod
98
+ def read_unsigned_long(value: int) -> int:
99
+ return value & 0x7FFFFFFFFFFFFFFF
100
+
101
+ @staticmethod
102
+ def hash_unsigned_list(long_list: list[int]):
103
+ byte_arr = b""
104
+ for i in range(len(long_list)):
105
+ if i == 0:
106
+ byte_arr = MurMurHashUtil.long2bytes(long_list[i])
107
+ else:
108
+ byte_arr += MurMurHashUtil.long2bytes(long_list[i])
109
+
110
+ return MurMurHashUtil.read_unsigned_long(MurMurHashUtil.hash(byte_arr))
111
+
112
+ @staticmethod
113
+ def hash_string(s: str):
114
+ return MurMurHashUtil.hash(s.encode())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pymammotion
3
- Version: 0.4.32
3
+ Version: 0.4.34
4
4
  Summary:
5
5
  License: GPL-3.0
6
6
  Author: Michael Arthur
@@ -14,7 +14,7 @@ pymammotion/aliyun/tea/core.py,sha256=4SjhRkbPMbw-uI0lQnCN0SBNAHAgVFrpHeaauuu6nZ
14
14
  pymammotion/aliyun/tmp_constant.py,sha256=M4Hq_lrGB3LZdX6R2XohRPFoK1NDnNV-pTJwJcJ9838,6650
15
15
  pymammotion/bluetooth/__init__.py,sha256=LAl8jqZ1fPh-3mLmViNQsP3s814C1vsocYUa6oSaXt0,36
16
16
  pymammotion/bluetooth/ble.py,sha256=XQWJBpSzeIawCrLTsVrq9LI6jmM_ALVTttUkosK2BRM,2480
17
- pymammotion/bluetooth/ble_message.py,sha256=47xoj22Kw5XF9VT965J_BxpvTdOgSXy4opqkbsojvDo,18795
17
+ pymammotion/bluetooth/ble_message.py,sha256=qROCOsz68kfxWeEK3Hm4dfvsbA3SpxJRhX7sEmJAMZU,18797
18
18
  pymammotion/bluetooth/const.py,sha256=CCqyHsYbB0BAYjwdhXt_n6eWWxmhlUrAFjvVv57mbvE,1749
19
19
  pymammotion/bluetooth/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  pymammotion/bluetooth/data/convert.py,sha256=6DMwvzVr9FWCoQFIKSI2poFXjISc_m6X59g8FlVO0-o,800
@@ -24,29 +24,29 @@ pymammotion/bluetooth/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
24
24
  pymammotion/bluetooth/model/atomic_integer.py,sha256=jtSqeqd6It3TvzZN7TJyYHQNRuItuw0Bg-cL0AUEBhY,1666
25
25
  pymammotion/const.py,sha256=lWRxvTVdXnNHuxqvRkjO5ziK0Ic-fZMM6J2dbe5M6Nc,385
26
26
  pymammotion/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- pymammotion/data/model/__init__.py,sha256=aSyroxYQQS-WMRi6WmWm2js4wLa9nmsi160gx9tts4o,323
27
+ pymammotion/data/model/__init__.py,sha256=UVRbSXGOjYnWv30ZEvzT5QRpdVqAbyeToo-t0QBWyi4,292
28
28
  pymammotion/data/model/account.py,sha256=vJM-KTf2q6eBfVC-UlNHBSmJvqHiCawZ40vnuhXhaz8,140
29
29
  pymammotion/data/model/device.py,sha256=oWwvnqb45tqErPgDu2P9OX8HRuN-NV6bYNTotU5ofRY,7027
30
- pymammotion/data/model/device_config.py,sha256=1kFkztZk3_DXHaEUZN-j_4s0HT83m4qLVAg2uHCyzFg,2638
30
+ pymammotion/data/model/device_config.py,sha256=DTnoePsMwJrKVXqGeKvbZQjs8Zab61U5T3b-c-w0WlM,2654
31
31
  pymammotion/data/model/device_info.py,sha256=Q0qJ6BflQycH_VFugwaFFXcvM69q2v9JAe2wWOcCggE,904
32
32
  pymammotion/data/model/device_limits.py,sha256=m8HdxD-RaAkPm7jHYb9GLxMEH9IfzBPz0ZypmsLnId4,1946
33
- pymammotion/data/model/enums.py,sha256=EpKmO8yVUZyEnTY4yH0DMMVKYNQM42zpW1maUu0i3IE,1582
33
+ pymammotion/data/model/enums.py,sha256=NLImBbeqwbwBLbABC_NbBzk4M8OxhUTcto6QcGZVNOc,1707
34
34
  pymammotion/data/model/excute_boarder_params.py,sha256=9CpUqrygcle1C_1hDW-riLmm4map4ZbE842NXjcomEI,1394
35
35
  pymammotion/data/model/execute_boarder.py,sha256=9rd_h4fbcsXxgnLOd2rO2hWyD1abnTGc47QTEpp8DD0,1103
36
36
  pymammotion/data/model/generate_route_information.py,sha256=pgjqURwmEIzjCMbl4Z5JDDkfxyUAdry1KhPfyir3-mU,777
37
- pymammotion/data/model/hash_list.py,sha256=V1rHovMViWD9bUWcgKEyQ0xkdtgrEEvTjyOOCNIhRJw,12124
37
+ pymammotion/data/model/hash_list.py,sha256=t9pY5JkL8Ky5YE4y6FowsiBeEZJPFl8XrPvW21xavEE,12121
38
38
  pymammotion/data/model/location.py,sha256=PwmITejfI4pm7PI4rzqSuuHetwle6IJr_CV95435s2M,871
39
39
  pymammotion/data/model/mowing_modes.py,sha256=bBbRhDe-imZsXDR0TN0emQv6BiIkAlXJFb5isPEjgDk,1078
40
- pymammotion/data/model/plan.py,sha256=wGlcJT-w0EdbWK9jI838TCOm_MABFg7WoR664VB8RWg,2880
41
40
  pymammotion/data/model/rapid_state.py,sha256=mIdhAG_LZXpVcybxqTLgLXkNOmVmDTn04B9PGIDA8Ls,1251
42
41
  pymammotion/data/model/raw_data.py,sha256=x2xuqVC8CQuV3ui3QK4G5EqRET9EsNljHLHR11ByYgo,6471
43
42
  pymammotion/data/model/region_data.py,sha256=VokMRqB_o4OFL1TWAM90Fvm-1z4jcYrw3X2o760qpx4,2949
44
43
  pymammotion/data/model/report_info.py,sha256=3nXBFRfdKWnZj4YfBcrPwhBRpq58ICMQlrESfI1tTMg,3808
44
+ pymammotion/data/model/work.py,sha256=AfKMItFqnRtAlVHzKCfYY-BQy-WFDYZBzdj-9Yc03bo,655
45
45
  pymammotion/data/mqtt/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
46
46
  pymammotion/data/mqtt/event.py,sha256=r14gzZVxmlGVAwFdZQ1CUsMZFHHwRKnbt2VHnjugP28,5123
47
47
  pymammotion/data/mqtt/properties.py,sha256=pX5JRVmmpVO04CSPm5xAGcSWA_OeLd0JnBagLsfiSEc,3755
48
48
  pymammotion/data/mqtt/status.py,sha256=SgdrpE1Uldb01hybO6hYhgU1Sp1eILghC0UhMZMHrdQ,1091
49
- pymammotion/data/state_manager.py,sha256=eL92Ul2L-qfF1tfz5gx4jewMkMMfbIGoYzAQY3jCyxc,9704
49
+ pymammotion/data/state_manager.py,sha256=ClWwxwO88BLRkox0ljoZNzwdTWiPIBMyLBj-15V0GfI,9828
50
50
  pymammotion/event/__init__.py,sha256=mgATR6vPHACNQ-0zH5fi7NdzeTCDV1CZyaWPmtUusi8,115
51
51
  pymammotion/event/event.py,sha256=bj2RirSIRyBs0QvkcrOtwZWUX_8F3m1sySuHVyKmZLs,2143
52
52
  pymammotion/http/_init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -68,10 +68,10 @@ pymammotion/mammotion/commands/messages/video.py,sha256=Vn5F65ojr3chePBtfYjOg90E
68
68
  pymammotion/mammotion/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
69
  pymammotion/mammotion/control/joystick.py,sha256=QfBVxM_gxpWsZAGO90whtgxCI2tIZ3TTad9wHIPsU9s,5640
70
70
  pymammotion/mammotion/devices/__init__.py,sha256=f2qQFPgLGmV85W2hSlMUh5BYuht9o_Ar_JEAAMD4fsE,102
71
- pymammotion/mammotion/devices/base.py,sha256=LwwzRlf6vE8x6khIBDIlJU2y7_k1tTITLFU74uMP7mg,12306
72
- pymammotion/mammotion/devices/mammotion.py,sha256=ffzwsQqO9zDwMElWYMsxi-J5_rGaOOOHdAm5D3DXprc,13671
73
- pymammotion/mammotion/devices/mammotion_bluetooth.py,sha256=VnFlZMQhGrpsNe5TYhOyGnGrZSoLIMWTLJ8asPLy19c,18725
74
- pymammotion/mammotion/devices/mammotion_cloud.py,sha256=pQiwL9MT9q4guay2sY4-v2TtiQMAEpmsPUTnBJvMDvI,14053
71
+ pymammotion/mammotion/devices/base.py,sha256=bL6aPx5o8sv3BrdgSIkpqaBYtW6yQPJ4rKd9JEr-u6c,11815
72
+ pymammotion/mammotion/devices/mammotion.py,sha256=W8LtaZyYa6WhtSPpC7KHBPL3B9OHLlvZTg-Dr1oEFpo,13989
73
+ pymammotion/mammotion/devices/mammotion_bluetooth.py,sha256=r-IoqiBsgEUOxcvU0Ryz8RkW1oUJ6LcXLQa710cYmZI,18862
74
+ pymammotion/mammotion/devices/mammotion_cloud.py,sha256=sVw-McsTJ_8wlkToL5rkjAlN2d3ituvqLona5w5bd1o,14160
75
75
  pymammotion/mqtt/__init__.py,sha256=Ocs5e-HLJvTuDpVXyECEsWIvwsUaxzj7lZ9mSYutNDY,105
76
76
  pymammotion/mqtt/linkkit/__init__.py,sha256=ENgc3ynd2kd9gMQR3-kgmCu6Ed9Y6XCIzU0zFReUlkk,80
77
77
  pymammotion/mqtt/linkkit/h2client.py,sha256=w9Nvi_nY4CLD_fw-pHtYChwQf7e2TiAGeqkY_sF4cf0,19659
@@ -118,9 +118,10 @@ pymammotion/utility/device_config.py,sha256=65Jl73-dQDs4yMXwYXZW_bsgSvnwpFBZDu8O
118
118
  pymammotion/utility/device_type.py,sha256=Lhvi8CLY8qr1EghZFlFK85hhzsmyE0LHVZO0qAmYod4,12582
119
119
  pymammotion/utility/map.py,sha256=GYscVMg2cX3IPlNpCBNHDW0S55yS1WGRf1iHnNZ7TfQ,2227
120
120
  pymammotion/utility/movement.py,sha256=N75oAoAgFydqoaOedYIxGUHmuTCtPzAOtb-d_29tpfI,615
121
+ pymammotion/utility/mur_mur_hash.py,sha256=xEfOZVbqRawJj66eLgtnZ85OauDR47oIPr29OHelzPI,4468
121
122
  pymammotion/utility/periodic.py,sha256=MbeSb9cfhxzYmdT_RiE0dZe3H9IfbQW_zSqhmSX2RUc,3321
122
123
  pymammotion/utility/rocker_util.py,sha256=6tX7sS87qoQC_tsxbx3NLL-HgS08wtzXiZkhDiz7uo0,7179
123
- pymammotion-0.4.32.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
124
- pymammotion-0.4.32.dist-info/METADATA,sha256=4D-uF3c0qwmbXqu_JKITQsHo03e_NEba3OIjnytXFsg,3878
125
- pymammotion-0.4.32.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
126
- pymammotion-0.4.32.dist-info/RECORD,,
124
+ pymammotion-0.4.34.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
125
+ pymammotion-0.4.34.dist-info/METADATA,sha256=gIlSuwLIrxcSDNPZDFF-BpY9kNzJ8ZpSm8e9XcB_gOU,3878
126
+ pymammotion-0.4.34.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
127
+ pymammotion-0.4.34.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.2
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,55 +0,0 @@
1
- class Plan:
2
- def __init__(self) -> None:
3
- self.pver: int = 0
4
- self.sub_cmd: int = 0
5
- self.area: int = 0
6
- self.work_time: int = 0
7
- self.version: str = ""
8
- self.id: str = ""
9
- self.user_id: str = ""
10
- self.device_id: str = ""
11
- self.plan_id: str = ""
12
- self.task_id: str = ""
13
- self.job_id: str = ""
14
- self.start_time: str = ""
15
- self.end_time: str = ""
16
- self.week: int = 0
17
- self.knife_height: int = 0
18
- self.model: int = 0
19
- self.edge_mode: int = 0
20
- self.required_time: int = 0
21
- self.route_angle: int = 0
22
- self.route_model: int = 0
23
- self.route_spacing: int = 0
24
- self.ultrasonic_barrier: int = 0
25
- self.total_plan_num: int = 0
26
- self.plan_index: int = 0
27
- self.result: int = 0
28
- self.speed: float = 0.0
29
- self.task_name: str = ""
30
- self.job_name: str = ""
31
- self.zone_hashs: list[int] = []
32
- self.reserved: str = ""
33
- self.weeks: list[int] = []
34
- self.start_date: str = ""
35
- self.end_date: str = ""
36
- self.job_type: int = 0
37
- self.interval_days: int = 0
38
- self.count_down: int = 0
39
- self.is_enable: bool = True
40
- self.is_mow_work: bool = True
41
- self.is_sweeping_work: bool = True
42
- self.mowing_laps: int = 0
43
- self.path_order: int = 0
44
- self.demond_angle: int = 90
45
-
46
- def __str__(self) -> str:
47
- return f"Plan(pver={self.pver}, sub_cmd={self.sub_cmd}, area={self.area}, work_time={self.work_time}, version='{self.version}', id='{self.id}', user_id='{self.user_id}', device_id='{self.device_id}', plan_id='{self.plan_id}', task_id='{self.task_id}', job_id='{self.job_id}', start_time='{self.start_time}', end_time='{self.end_time}', week={self.week}, knife_height={self.knife_height}, model={self.model}, edge_mode={self.edge_mode}, required_time={self.required_time}, route_angle={self.route_angle}, route_model={self.route_model}, route_spacing={self.route_spacing}, ultrasonic_barrier={self.ultrasonic_barrier}, total_plan_num={self.total_plan_num}, plan_index={self.plan_index}, result={self.result}, speed={self.speed}, task_name='{self.task_name}', job_name='{self.job_name}', zone_hashs={self.zone_hashs}, reserved='{self.reserved}', weeks={self.weeks}, start_date='{self.start_date}', end_date='{self.end_date}', job_type={self.job_type}, interval_days={self.interval_days}, count_down={self.count_down}, is_enable={self.is_enable}, mowing_laps={self.mowing_laps}, path_order={self.path_order}, demond_angle={self.demond_angle}, is_mow_work={self.is_mow_work}, is_sweeping_work={self.is_sweeping_work})"
48
-
49
- def __eq__(self, other):
50
- if isinstance(other, Plan):
51
- return self.plan_id == other.plan_id
52
- return False
53
-
54
- def __hash__(self):
55
- return hash(self.plan_id)