pymammotion 0.4.33__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.
- pymammotion/bluetooth/ble_message.py +1 -1
- pymammotion/data/model/__init__.py +1 -2
- pymammotion/data/model/device_config.py +1 -0
- pymammotion/data/model/enums.py +8 -0
- pymammotion/data/model/hash_list.py +2 -2
- pymammotion/data/state_manager.py +20 -19
- pymammotion/mammotion/devices/base.py +2 -12
- pymammotion/mammotion/devices/mammotion.py +25 -23
- pymammotion/mammotion/devices/mammotion_bluetooth.py +9 -4
- pymammotion/mammotion/devices/mammotion_cloud.py +6 -1
- pymammotion/utility/mur_mur_hash.py +114 -0
- {pymammotion-0.4.33.dist-info → pymammotion-0.4.34.dist-info}/METADATA +1 -1
- {pymammotion-0.4.33.dist-info → pymammotion-0.4.34.dist-info}/RECORD +15 -15
- {pymammotion-0.4.33.dist-info → pymammotion-0.4.34.dist-info}/WHEEL +1 -1
- pymammotion/data/model/plan.py +0 -55
- {pymammotion-0.4.33.dist-info → pymammotion-0.4.34.dist-info}/LICENSE +0 -0
@@ -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", "
|
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):
|
pymammotion/data/model/enums.py
CHANGED
@@ -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[
|
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.
|
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."""
|
@@ -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
|
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(
|
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
|
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,
|
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(
|
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__(
|
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
|
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())
|
@@ -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=
|
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,20 +24,19 @@ 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=
|
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=
|
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=
|
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=
|
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
|
@@ -47,7 +46,7 @@ pymammotion/data/mqtt/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdr
|
|
47
46
|
pymammotion/data/mqtt/event.py,sha256=r14gzZVxmlGVAwFdZQ1CUsMZFHHwRKnbt2VHnjugP28,5123
|
48
47
|
pymammotion/data/mqtt/properties.py,sha256=pX5JRVmmpVO04CSPm5xAGcSWA_OeLd0JnBagLsfiSEc,3755
|
49
48
|
pymammotion/data/mqtt/status.py,sha256=SgdrpE1Uldb01hybO6hYhgU1Sp1eILghC0UhMZMHrdQ,1091
|
50
|
-
pymammotion/data/state_manager.py,sha256=
|
49
|
+
pymammotion/data/state_manager.py,sha256=ClWwxwO88BLRkox0ljoZNzwdTWiPIBMyLBj-15V0GfI,9828
|
51
50
|
pymammotion/event/__init__.py,sha256=mgATR6vPHACNQ-0zH5fi7NdzeTCDV1CZyaWPmtUusi8,115
|
52
51
|
pymammotion/event/event.py,sha256=bj2RirSIRyBs0QvkcrOtwZWUX_8F3m1sySuHVyKmZLs,2143
|
53
52
|
pymammotion/http/_init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -69,10 +68,10 @@ pymammotion/mammotion/commands/messages/video.py,sha256=Vn5F65ojr3chePBtfYjOg90E
|
|
69
68
|
pymammotion/mammotion/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
69
|
pymammotion/mammotion/control/joystick.py,sha256=QfBVxM_gxpWsZAGO90whtgxCI2tIZ3TTad9wHIPsU9s,5640
|
71
70
|
pymammotion/mammotion/devices/__init__.py,sha256=f2qQFPgLGmV85W2hSlMUh5BYuht9o_Ar_JEAAMD4fsE,102
|
72
|
-
pymammotion/mammotion/devices/base.py,sha256=
|
73
|
-
pymammotion/mammotion/devices/mammotion.py,sha256=
|
74
|
-
pymammotion/mammotion/devices/mammotion_bluetooth.py,sha256=
|
75
|
-
pymammotion/mammotion/devices/mammotion_cloud.py,sha256=
|
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
|
76
75
|
pymammotion/mqtt/__init__.py,sha256=Ocs5e-HLJvTuDpVXyECEsWIvwsUaxzj7lZ9mSYutNDY,105
|
77
76
|
pymammotion/mqtt/linkkit/__init__.py,sha256=ENgc3ynd2kd9gMQR3-kgmCu6Ed9Y6XCIzU0zFReUlkk,80
|
78
77
|
pymammotion/mqtt/linkkit/h2client.py,sha256=w9Nvi_nY4CLD_fw-pHtYChwQf7e2TiAGeqkY_sF4cf0,19659
|
@@ -119,9 +118,10 @@ pymammotion/utility/device_config.py,sha256=65Jl73-dQDs4yMXwYXZW_bsgSvnwpFBZDu8O
|
|
119
118
|
pymammotion/utility/device_type.py,sha256=Lhvi8CLY8qr1EghZFlFK85hhzsmyE0LHVZO0qAmYod4,12582
|
120
119
|
pymammotion/utility/map.py,sha256=GYscVMg2cX3IPlNpCBNHDW0S55yS1WGRf1iHnNZ7TfQ,2227
|
121
120
|
pymammotion/utility/movement.py,sha256=N75oAoAgFydqoaOedYIxGUHmuTCtPzAOtb-d_29tpfI,615
|
121
|
+
pymammotion/utility/mur_mur_hash.py,sha256=xEfOZVbqRawJj66eLgtnZ85OauDR47oIPr29OHelzPI,4468
|
122
122
|
pymammotion/utility/periodic.py,sha256=MbeSb9cfhxzYmdT_RiE0dZe3H9IfbQW_zSqhmSX2RUc,3321
|
123
123
|
pymammotion/utility/rocker_util.py,sha256=6tX7sS87qoQC_tsxbx3NLL-HgS08wtzXiZkhDiz7uo0,7179
|
124
|
-
pymammotion-0.4.
|
125
|
-
pymammotion-0.4.
|
126
|
-
pymammotion-0.4.
|
127
|
-
pymammotion-0.4.
|
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,,
|
pymammotion/data/model/plan.py
DELETED
@@ -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)
|
File without changes
|