pymammotion 0.4.0a2__py3-none-any.whl → 0.5.51__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymammotion might be problematic. Click here for more details.
- pymammotion/__init__.py +5 -4
- pymammotion/aliyun/client.py +235 -0
- pymammotion/aliyun/cloud_gateway.py +312 -64
- pymammotion/aliyun/model/aep_response.py +1 -2
- pymammotion/aliyun/model/dev_by_account_response.py +170 -23
- pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
- pymammotion/aliyun/model/regions_response.py +3 -3
- pymammotion/aliyun/model/session_by_authcode_response.py +2 -2
- pymammotion/aliyun/model/thing_response.py +12 -0
- pymammotion/aliyun/regions.py +62 -0
- pymammotion/aliyun/tea/core.py +297 -0
- pymammotion/bluetooth/ble.py +7 -9
- pymammotion/bluetooth/ble_message.py +10 -14
- pymammotion/const.py +3 -0
- pymammotion/data/model/__init__.py +1 -2
- pymammotion/data/model/device.py +95 -27
- pymammotion/data/model/device_config.py +4 -4
- pymammotion/data/model/device_info.py +35 -0
- pymammotion/data/model/device_limits.py +10 -10
- pymammotion/data/model/enums.py +12 -2
- pymammotion/data/model/errors.py +12 -0
- pymammotion/data/model/events.py +14 -0
- pymammotion/data/model/generate_geojson.py +521 -0
- pymammotion/data/model/generate_route_information.py +2 -2
- pymammotion/data/model/hash_list.py +370 -57
- pymammotion/data/model/location.py +4 -4
- pymammotion/data/model/mowing_modes.py +17 -1
- pymammotion/data/model/raw_data.py +2 -10
- pymammotion/data/model/region_data.py +10 -11
- pymammotion/data/model/report_info.py +31 -5
- pymammotion/data/model/work.py +27 -0
- pymammotion/data/mower_state_manager.py +316 -0
- pymammotion/data/mqtt/event.py +73 -28
- pymammotion/data/mqtt/mammotion_properties.py +257 -0
- pymammotion/data/mqtt/properties.py +93 -78
- pymammotion/data/mqtt/status.py +18 -17
- pymammotion/event/event.py +27 -6
- pymammotion/homeassistant/__init__.py +3 -0
- pymammotion/homeassistant/mower_api.py +484 -0
- pymammotion/homeassistant/rtk_api.py +54 -0
- pymammotion/http/encryption.py +5 -6
- pymammotion/http/http.py +574 -28
- pymammotion/http/model/__init__.py +0 -0
- pymammotion/{aliyun/model/stream_subscription_response.py → http/model/camera_stream.py} +14 -2
- pymammotion/http/model/http.py +129 -4
- pymammotion/http/model/response_factory.py +61 -0
- pymammotion/http/model/rtk.py +16 -0
- pymammotion/mammotion/commands/abstract_message.py +7 -5
- pymammotion/mammotion/commands/mammotion_command.py +30 -1
- pymammotion/mammotion/commands/messages/basestation.py +43 -0
- pymammotion/mammotion/commands/messages/driver.py +61 -29
- pymammotion/mammotion/commands/messages/media.py +68 -15
- pymammotion/mammotion/commands/messages/navigation.py +61 -25
- pymammotion/mammotion/commands/messages/network.py +17 -23
- pymammotion/mammotion/commands/messages/ota.py +18 -18
- pymammotion/mammotion/commands/messages/system.py +32 -49
- pymammotion/mammotion/commands/messages/video.py +15 -16
- pymammotion/mammotion/devices/__init__.py +27 -3
- pymammotion/mammotion/devices/base.py +40 -131
- pymammotion/mammotion/devices/mammotion.py +436 -201
- pymammotion/mammotion/devices/mammotion_bluetooth.py +57 -47
- pymammotion/mammotion/devices/mammotion_cloud.py +134 -105
- pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
- pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
- pymammotion/mammotion/devices/managers/managers.py +81 -0
- pymammotion/mammotion/devices/mower_device.py +124 -0
- pymammotion/mammotion/devices/mower_manager.py +107 -0
- pymammotion/mammotion/devices/rtk_ble.py +89 -0
- pymammotion/mammotion/devices/rtk_cloud.py +113 -0
- pymammotion/mammotion/devices/rtk_device.py +50 -0
- pymammotion/mammotion/devices/rtk_manager.py +122 -0
- pymammotion/mqtt/__init__.py +2 -1
- pymammotion/mqtt/aliyun_mqtt.py +232 -0
- pymammotion/mqtt/linkkit/__init__.py +5 -0
- pymammotion/mqtt/linkkit/h2client.py +585 -0
- pymammotion/mqtt/linkkit/linkkit.py +3023 -0
- pymammotion/mqtt/mammotion_mqtt.py +176 -169
- pymammotion/mqtt/mqtt_models.py +66 -0
- pymammotion/proto/__init__.py +4839 -4
- pymammotion/proto/basestation.proto +8 -0
- pymammotion/proto/basestation_pb2.py +11 -9
- pymammotion/proto/basestation_pb2.pyi +16 -2
- pymammotion/proto/dev_net.proto +79 -55
- pymammotion/proto/dev_net_pb2.py +60 -56
- pymammotion/proto/dev_net_pb2.pyi +49 -6
- pymammotion/proto/luba_msg.proto +2 -1
- pymammotion/proto/luba_msg_pb2.py +6 -6
- pymammotion/proto/luba_msg_pb2.pyi +1 -0
- pymammotion/proto/luba_mul.proto +62 -1
- pymammotion/proto/luba_mul_pb2.py +38 -22
- pymammotion/proto/luba_mul_pb2.pyi +94 -7
- pymammotion/proto/mctrl_driver.proto +44 -4
- pymammotion/proto/mctrl_driver_pb2.py +26 -14
- pymammotion/proto/mctrl_driver_pb2.pyi +66 -11
- pymammotion/proto/mctrl_nav.proto +93 -52
- pymammotion/proto/mctrl_nav_pb2.py +75 -67
- pymammotion/proto/mctrl_nav_pb2.pyi +142 -56
- pymammotion/proto/mctrl_ota.proto +40 -2
- pymammotion/proto/mctrl_ota_pb2.py +23 -13
- pymammotion/proto/mctrl_ota_pb2.pyi +67 -4
- pymammotion/proto/mctrl_pept.proto +8 -3
- pymammotion/proto/mctrl_pept_pb2.py +8 -6
- pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
- pymammotion/proto/mctrl_sys.proto +325 -86
- pymammotion/proto/mctrl_sys_pb2.py +162 -98
- pymammotion/proto/mctrl_sys_pb2.pyi +451 -25
- pymammotion/proto/message_pool.py +3 -0
- pymammotion/proto/py.typed +0 -0
- pymammotion/utility/constant/device_constant.py +29 -5
- pymammotion/utility/datatype_converter.py +13 -12
- pymammotion/utility/device_config.py +522 -130
- pymammotion/utility/device_type.py +218 -21
- pymammotion/utility/map.py +238 -51
- pymammotion/utility/mur_mur_hash.py +159 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/METADATA +26 -31
- pymammotion-0.5.51.dist-info/RECORD +152 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/WHEEL +1 -1
- pymammotion/aliyun/cloud_service.py +0 -65
- pymammotion/data/model/plan.py +0 -58
- pymammotion/data/state_manager.py +0 -129
- pymammotion/proto/basestation.py +0 -59
- pymammotion/proto/common.py +0 -12
- pymammotion/proto/dev_net.py +0 -381
- pymammotion/proto/luba_msg.py +0 -81
- pymammotion/proto/luba_mul.py +0 -76
- pymammotion/proto/mctrl_driver.py +0 -100
- pymammotion/proto/mctrl_nav.py +0 -664
- pymammotion/proto/mctrl_ota.py +0 -48
- pymammotion/proto/mctrl_pept.py +0 -41
- pymammotion/proto/mctrl_sys.py +0 -574
- pymammotion-0.4.0a2.dist-info/RECORD +0 -131
- /pymammotion/http/{_init_.py → __init__.py} +0 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
pymammotion/bluetooth/ble.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
from bleak import BleakClient, BleakScanner, BLEDevice
|
|
2
2
|
from bleak.backends.characteristic import BleakGATTCharacteristic
|
|
3
3
|
|
|
4
|
-
from pymammotion.bluetooth.const import
|
|
5
|
-
SERVICE_CHANGED_CHARACTERISTIC,
|
|
6
|
-
UUID_NOTIFICATION_CHARACTERISTIC,
|
|
7
|
-
)
|
|
4
|
+
from pymammotion.bluetooth.const import SERVICE_CHANGED_CHARACTERISTIC, UUID_NOTIFICATION_CHARACTERISTIC
|
|
8
5
|
from pymammotion.event.event import BleNotificationEvent
|
|
9
6
|
|
|
10
7
|
|
|
11
8
|
class MammotionBLE:
|
|
9
|
+
"""Class for basic ble connections to mowers."""
|
|
10
|
+
|
|
12
11
|
client: BleakClient
|
|
13
12
|
|
|
14
13
|
def __init__(self, bleEvt: BleNotificationEvent) -> None:
|
|
@@ -37,12 +36,10 @@ class MammotionBLE:
|
|
|
37
36
|
return await self.connect()
|
|
38
37
|
|
|
39
38
|
async def connect(self) -> bool:
|
|
40
|
-
if self.client is not None
|
|
41
|
-
return await self.client.connect()
|
|
39
|
+
return await self.client.connect() if self.client is not None else False
|
|
42
40
|
|
|
43
41
|
async def disconnect(self) -> bool:
|
|
44
|
-
if self.client is not None
|
|
45
|
-
return await self.client.disconnect()
|
|
42
|
+
return await self.client.disconnect() if self.client is not None else False
|
|
46
43
|
|
|
47
44
|
async def notification_handler(self, _characteristic: BleakGATTCharacteristic, data: bytearray) -> None:
|
|
48
45
|
"""Simple notification handler which prints the data received."""
|
|
@@ -60,5 +57,6 @@ class MammotionBLE:
|
|
|
60
57
|
await self.client.start_notify(UUID_NOTIFICATION_CHARACTERISTIC, self.notification_handler)
|
|
61
58
|
await self.client.start_notify(SERVICE_CHANGED_CHARACTERISTIC, self.service_changed_handler)
|
|
62
59
|
|
|
63
|
-
def
|
|
60
|
+
def get_client(self):
|
|
61
|
+
"""Returns the ble client."""
|
|
64
62
|
return self.client
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
from asyncio import sleep
|
|
2
|
+
from io import BytesIO
|
|
1
3
|
import itertools
|
|
2
4
|
import json
|
|
3
5
|
import logging
|
|
4
6
|
import queue
|
|
5
7
|
import sys
|
|
6
8
|
import time
|
|
7
|
-
from asyncio import sleep
|
|
8
|
-
from io import BytesIO
|
|
9
|
-
from typing import Union
|
|
10
9
|
|
|
11
10
|
from bleak import BleakClient
|
|
12
11
|
from jsonic.serializable import serialize
|
|
@@ -17,10 +16,7 @@ from pymammotion.bluetooth.data.framectrldata import FrameCtrlData
|
|
|
17
16
|
from pymammotion.bluetooth.data.notifydata import BlufiNotifyData
|
|
18
17
|
from pymammotion.bluetooth.model.atomic_integer import AtomicInteger
|
|
19
18
|
from pymammotion.data.model.execute_boarder import ExecuteBorder
|
|
20
|
-
from pymammotion.proto import
|
|
21
|
-
dev_net_pb2,
|
|
22
|
-
)
|
|
23
|
-
from pymammotion.proto.luba_msg import LubaMsg, MsgAttr, MsgCmdType, MsgDevice
|
|
19
|
+
from pymammotion.proto import DevNet, DrvDevInfoReq, LubaMsg, MsgAttr, MsgCmdType, MsgDevice
|
|
24
20
|
from pymammotion.utility.constant.device_constant import bleOrderCmd
|
|
25
21
|
|
|
26
22
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -320,7 +316,7 @@ class BleMessage:
|
|
|
320
316
|
hash_map = {"ctrl": 1}
|
|
321
317
|
await self.post_custom_data(self.get_json_string(bleOrderCmd.bleAlive, hash_map))
|
|
322
318
|
|
|
323
|
-
def get_json_string(self, cmd: int, hash_map: dict[str,
|
|
319
|
+
def get_json_string(self, cmd: int, hash_map: dict[str, int]) -> str:
|
|
324
320
|
jSONObject = {}
|
|
325
321
|
try:
|
|
326
322
|
jSONObject["cmd"] = cmd
|
|
@@ -334,7 +330,7 @@ class BleMessage:
|
|
|
334
330
|
print(e)
|
|
335
331
|
return ""
|
|
336
332
|
|
|
337
|
-
def
|
|
333
|
+
def clear_notification(self) -> None:
|
|
338
334
|
self.notification = None
|
|
339
335
|
self.notification = BlufiNotifyData()
|
|
340
336
|
|
|
@@ -344,14 +340,14 @@ class BleMessage:
|
|
|
344
340
|
async def send_device_info(self) -> None:
|
|
345
341
|
"""Currently not called"""
|
|
346
342
|
luba_msg = LubaMsg(
|
|
347
|
-
msgtype=MsgCmdType.
|
|
343
|
+
msgtype=MsgCmdType.ESP,
|
|
348
344
|
sender=MsgDevice.DEV_MOBILEAPP,
|
|
349
345
|
rcver=MsgDevice.DEV_COMM_ESP,
|
|
350
|
-
msgattr=MsgAttr.
|
|
346
|
+
msgattr=MsgAttr.REQ,
|
|
351
347
|
seqs=1,
|
|
352
348
|
version=1,
|
|
353
349
|
subtype=1,
|
|
354
|
-
net=
|
|
350
|
+
net=DevNet(todev_ble_sync=1, todev_devinfo_req=DrvDevInfoReq()),
|
|
355
351
|
)
|
|
356
352
|
byte_arr = luba_msg.SerializeToString()
|
|
357
353
|
await self.post_custom_data_bytes(byte_arr)
|
|
@@ -403,7 +399,7 @@ class BleMessage:
|
|
|
403
399
|
sequence = int(response[2]) # toInt
|
|
404
400
|
current_sequence = self.mReadSequence.get() & 255
|
|
405
401
|
if sequence == current_sequence:
|
|
406
|
-
_LOGGER.debug(f"Received bluetooth data 1: {response.hex()}, object: {self}")
|
|
402
|
+
# _LOGGER.debug(f"Received bluetooth data 1: {response.hex()}, object: {self}")
|
|
407
403
|
return 2
|
|
408
404
|
|
|
409
405
|
# Compare with the second counter, mod 255
|
|
@@ -656,7 +652,7 @@ class BleMessage:
|
|
|
656
652
|
return byteOS.getvalue()
|
|
657
653
|
|
|
658
654
|
@staticmethod
|
|
659
|
-
def calc_crc(initial: int, data:
|
|
655
|
+
def calc_crc(initial: int, data: bytes | bytearray) -> int:
|
|
660
656
|
"""Calculate CRC value for given initial value and byte array.
|
|
661
657
|
|
|
662
658
|
Args:
|
pymammotion/const.py
CHANGED
|
@@ -8,3 +8,6 @@ MAMMOTION_DOMAIN = "https://id.mammotion.com"
|
|
|
8
8
|
MAMMOTION_API_DOMAIN = "https://domestic.mammotion.com"
|
|
9
9
|
MAMMOTION_CLIENT_ID = "MADKALUBAS"
|
|
10
10
|
MAMMOTION_CLIENT_SECRET = "GshzGRZJjuMUgd2sYHM7"
|
|
11
|
+
|
|
12
|
+
MAMMOTION_OUATH2_CLIENT_ID = "GxebgSt8si6pKqR"
|
|
13
|
+
MAMMOTION_OUATH2_CLIENT_SECRET = "JP0508SRJFa0A90ADpzLINDBxMa4Vj"
|
|
@@ -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"]
|
pymammotion/data/model/device.py
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
"""MowingDevice class to wrap around the betterproto dataclasses."""
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
|
-
import
|
|
5
|
+
import betterproto2
|
|
7
6
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
8
7
|
|
|
9
8
|
from pymammotion.data.model import HashList, RapidState
|
|
10
|
-
from pymammotion.data.model.device_info import MowerInfo
|
|
9
|
+
from pymammotion.data.model.device_info import DeviceFirmwares, DeviceNonWorkingHours, MowerInfo
|
|
10
|
+
from pymammotion.data.model.errors import DeviceErrors
|
|
11
|
+
from pymammotion.data.model.events import Events
|
|
11
12
|
from pymammotion.data.model.location import Location
|
|
12
13
|
from pymammotion.data.model.report_info import ReportData
|
|
14
|
+
from pymammotion.data.model.work import CurrentTaskSettings
|
|
15
|
+
from pymammotion.data.mqtt.event import ThingEventMessage
|
|
13
16
|
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
|
14
|
-
from pymammotion.
|
|
15
|
-
from pymammotion.
|
|
16
|
-
|
|
17
|
-
ReportInfoData,
|
|
18
|
-
SystemRapidStateTunnelMsg,
|
|
19
|
-
SystemUpdateBufMsg,
|
|
20
|
-
)
|
|
17
|
+
from pymammotion.data.mqtt.status import ThingStatusMessage
|
|
18
|
+
from pymammotion.http.model.http import CheckDeviceVersion
|
|
19
|
+
from pymammotion.proto import DeviceFwInfo, MowToAppInfoT, ReportInfoData, SystemRapidStateTunnelMsg, SystemUpdateBufMsg
|
|
21
20
|
from pymammotion.utility.constant import WorkMode
|
|
22
21
|
from pymammotion.utility.conversions import parse_double
|
|
23
22
|
from pymammotion.utility.map import CoordinateConverter
|
|
@@ -27,30 +26,42 @@ from pymammotion.utility.map import CoordinateConverter
|
|
|
27
26
|
class MowingDevice(DataClassORJSONMixin):
|
|
28
27
|
"""Wraps the betterproto dataclasses, so we can bypass the groups for keeping all data."""
|
|
29
28
|
|
|
29
|
+
name: str = ""
|
|
30
|
+
online: bool = True
|
|
31
|
+
enabled: bool = True
|
|
32
|
+
update_check: CheckDeviceVersion = field(default_factory=CheckDeviceVersion)
|
|
30
33
|
mower_state: MowerInfo = field(default_factory=MowerInfo)
|
|
31
34
|
mqtt_properties: ThingPropertiesMessage | None = None
|
|
35
|
+
status_properties: ThingStatusMessage | None = None
|
|
36
|
+
device_event: ThingEventMessage | None = None
|
|
32
37
|
map: HashList = field(default_factory=HashList)
|
|
38
|
+
work: CurrentTaskSettings = field(default_factory=CurrentTaskSettings)
|
|
33
39
|
location: Location = field(default_factory=Location)
|
|
34
40
|
mowing_state: RapidState = field(default_factory=RapidState)
|
|
35
41
|
report_data: ReportData = field(default_factory=ReportData)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
device_firmwares: DeviceFirmwares = field(default_factory=DeviceFirmwares)
|
|
43
|
+
errors: DeviceErrors = field(default_factory=DeviceErrors)
|
|
44
|
+
non_work_hours: DeviceNonWorkingHours = field(default_factory=DeviceNonWorkingHours)
|
|
45
|
+
events: Events = field(default_factory=Events)
|
|
39
46
|
|
|
40
47
|
def buffer(self, buffer_list: SystemUpdateBufMsg) -> None:
|
|
41
48
|
"""Update the device based on which buffer we are reading from."""
|
|
42
49
|
match buffer_list.update_buf_data[0]:
|
|
43
50
|
case 1:
|
|
44
|
-
# 4 speed
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
# 4 speed?
|
|
52
|
+
if buffer_list.update_buf_data[5] != 0:
|
|
53
|
+
self.location.RTK.latitude = parse_double(buffer_list.update_buf_data[5], 8.0)
|
|
54
|
+
self.location.RTK.longitude = parse_double(buffer_list.update_buf_data[6], 8.0)
|
|
55
|
+
if buffer_list.update_buf_data[7] != 0:
|
|
56
|
+
# latitude Y longitude X
|
|
57
|
+
self.location.dock.longitude = parse_double(buffer_list.update_buf_data[7], 4.0)
|
|
58
|
+
self.location.dock.latitude = parse_double(buffer_list.update_buf_data[8], 4.0)
|
|
59
|
+
self.location.dock.rotation = buffer_list.update_buf_data[3]
|
|
60
|
+
|
|
50
61
|
case 2:
|
|
51
|
-
self.err_code_list.clear()
|
|
52
|
-
self.err_code_list_time.clear()
|
|
53
|
-
self.err_code_list.extend(
|
|
62
|
+
self.errors.err_code_list.clear()
|
|
63
|
+
self.errors.err_code_list_time.clear()
|
|
64
|
+
self.errors.err_code_list.extend(
|
|
54
65
|
[
|
|
55
66
|
buffer_list.update_buf_data[3],
|
|
56
67
|
buffer_list.update_buf_data[5],
|
|
@@ -64,7 +75,7 @@ class MowingDevice(DataClassORJSONMixin):
|
|
|
64
75
|
buffer_list.update_buf_data[21],
|
|
65
76
|
]
|
|
66
77
|
)
|
|
67
|
-
self.err_code_list_time.extend(
|
|
78
|
+
self.errors.err_code_list_time.extend(
|
|
68
79
|
[
|
|
69
80
|
buffer_list.update_buf_data[4],
|
|
70
81
|
buffer_list.update_buf_data[6],
|
|
@@ -78,28 +89,48 @@ class MowingDevice(DataClassORJSONMixin):
|
|
|
78
89
|
buffer_list.update_buf_data[22],
|
|
79
90
|
]
|
|
80
91
|
)
|
|
92
|
+
case 3:
|
|
93
|
+
# task state event
|
|
94
|
+
task_area_map: dict[int, int] = {}
|
|
95
|
+
task_area_ids = []
|
|
96
|
+
|
|
97
|
+
for i in range(3, len(buffer_list.update_buf_data), 2):
|
|
98
|
+
area_id = buffer_list.update_buf_data[i]
|
|
99
|
+
|
|
100
|
+
if area_id != 0:
|
|
101
|
+
area_value = int(buffer_list.update_buf_data[i + 1])
|
|
102
|
+
task_area_map[area_id] = area_value
|
|
103
|
+
task_area_ids.append(area_id)
|
|
104
|
+
self.events.work_tasks_event.hash_list = task_area_map
|
|
105
|
+
self.events.work_tasks_event.ids = task_area_ids
|
|
81
106
|
|
|
82
107
|
def update_report_data(self, toapp_report_data: ReportInfoData) -> None:
|
|
108
|
+
"""Set report data for the mower."""
|
|
83
109
|
coordinate_converter = CoordinateConverter(self.location.RTK.latitude, self.location.RTK.longitude)
|
|
84
110
|
for index, location in enumerate(toapp_report_data.locations):
|
|
85
111
|
if index == 0 and location.real_pos_y != 0:
|
|
86
112
|
self.location.position_type = location.pos_type
|
|
87
|
-
self.location.orientation = location.real_toward / 10000
|
|
113
|
+
self.location.orientation = int(location.real_toward / 10000)
|
|
88
114
|
self.location.device = coordinate_converter.enu_to_lla(
|
|
89
115
|
parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)
|
|
90
116
|
)
|
|
117
|
+
self.map.invalidate_maps(location.bol_hash)
|
|
91
118
|
if location.zone_hash:
|
|
92
119
|
self.location.work_zone = (
|
|
93
120
|
location.zone_hash if self.report_data.dev.sys_status == WorkMode.MODE_WORKING else 0
|
|
94
121
|
)
|
|
95
122
|
|
|
96
|
-
|
|
123
|
+
if toapp_report_data.fw_info:
|
|
124
|
+
self.update_device_firmwares(toapp_report_data.fw_info)
|
|
125
|
+
|
|
126
|
+
self.report_data.update(toapp_report_data.to_dict(casing=betterproto2.Casing.SNAKE))
|
|
97
127
|
|
|
98
128
|
def run_state_update(self, rapid_state: SystemRapidStateTunnelMsg) -> None:
|
|
129
|
+
"""Set lat long, work zone of RTK and robot."""
|
|
99
130
|
coordinate_converter = CoordinateConverter(self.location.RTK.latitude, self.location.RTK.longitude)
|
|
100
131
|
self.mowing_state = RapidState().from_raw(rapid_state.rapid_state_data)
|
|
101
132
|
self.location.position_type = self.mowing_state.pos_type
|
|
102
|
-
self.location.orientation = self.mowing_state.toward / 10000
|
|
133
|
+
self.location.orientation = int(self.mowing_state.toward / 10000)
|
|
103
134
|
self.location.device = coordinate_converter.enu_to_lla(
|
|
104
135
|
parse_double(self.mowing_state.pos_y, 4.0), parse_double(self.mowing_state.pos_x, 4.0)
|
|
105
136
|
)
|
|
@@ -109,7 +140,44 @@ class MowingDevice(DataClassORJSONMixin):
|
|
|
109
140
|
)
|
|
110
141
|
|
|
111
142
|
def mow_info(self, toapp_mow_info: MowToAppInfoT) -> None:
|
|
112
|
-
|
|
143
|
+
"""Set mow info."""
|
|
113
144
|
|
|
114
145
|
def report_missing_data(self) -> None:
|
|
115
146
|
"""Report missing data so we can refetch it."""
|
|
147
|
+
|
|
148
|
+
def update_device_firmwares(self, fw_info: DeviceFwInfo) -> None:
|
|
149
|
+
"""Set firmware versions on all parts of the robot or RTK."""
|
|
150
|
+
for mod in fw_info.mod:
|
|
151
|
+
match mod.type:
|
|
152
|
+
case 1:
|
|
153
|
+
self.device_firmwares.main_controller = mod.version
|
|
154
|
+
case 3:
|
|
155
|
+
self.device_firmwares.left_motor_driver = mod.version
|
|
156
|
+
case 4:
|
|
157
|
+
self.device_firmwares.right_motor_driver = mod.version
|
|
158
|
+
case 5:
|
|
159
|
+
self.device_firmwares.rtk_rover_station = mod.version
|
|
160
|
+
case 101:
|
|
161
|
+
# RTK main board
|
|
162
|
+
self.device_firmwares.main_controller = mod.version
|
|
163
|
+
case 102:
|
|
164
|
+
self.device_firmwares.rtk_version = mod.version
|
|
165
|
+
case 103:
|
|
166
|
+
self.device_firmwares.lora_version = mod.version
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class RTKDevice(DataClassORJSONMixin):
|
|
171
|
+
name: str
|
|
172
|
+
iot_id: str
|
|
173
|
+
product_key: str
|
|
174
|
+
online: bool = True
|
|
175
|
+
lat: float = 0.0
|
|
176
|
+
lon: float = 0.0
|
|
177
|
+
lora: str = ""
|
|
178
|
+
wifi_rssi: int = 0
|
|
179
|
+
device_version: str = ""
|
|
180
|
+
lora_version: str = ""
|
|
181
|
+
wifi_sta_mac: str = ""
|
|
182
|
+
bt_mac: str = ""
|
|
183
|
+
update_check: CheckDeviceVersion = field(default_factory=CheckDeviceVersion)
|
|
@@ -30,23 +30,23 @@ class OperationSettings(DataClassORJSONMixin):
|
|
|
30
30
|
obstacle_laps: int = 1
|
|
31
31
|
mowing_laps: int = 1 # border laps
|
|
32
32
|
start_progress: int = 0
|
|
33
|
-
areas:
|
|
33
|
+
areas: set[int] = field(default_factory=set)
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def create_path_order(operation_mode: OperationSettings, device_name: str) -> str:
|
|
37
37
|
# TODO add scheduling logic from getReserved() WorkSettingViewModel.java
|
|
38
|
-
i2 = 0
|
|
39
38
|
bArr = bytearray(8)
|
|
40
39
|
bArr[0] = operation_mode.border_mode
|
|
41
40
|
bArr[1] = operation_mode.obstacle_laps
|
|
42
41
|
bArr[3] = int(operation_mode.start_progress)
|
|
43
42
|
bArr[2] = 0
|
|
43
|
+
bArr[5] = 0
|
|
44
44
|
if not DeviceType.is_luba1(device_name):
|
|
45
45
|
bArr[4] = 0
|
|
46
|
-
if DeviceType.is_yuka(device_name):
|
|
46
|
+
if DeviceType.is_yuka(device_name) and not DeviceType.is_yuka_mini(device_name):
|
|
47
47
|
bArr[5] = calculate_yuka_mode(operation_mode)
|
|
48
48
|
else:
|
|
49
|
-
bArr[5] = 8 if DeviceType.
|
|
49
|
+
bArr[5] = 8 if DeviceType.is_luba_pro(device_name) else 0
|
|
50
50
|
|
|
51
51
|
bArr[6] = int(operation_mode.collect_grass_frequency) if operation_mode.is_dump else 10
|
|
52
52
|
if DeviceType.is_luba1(device_name):
|
|
@@ -14,12 +14,47 @@ class SideLight(DataClassORJSONMixin):
|
|
|
14
14
|
action: int = 0
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
@dataclass
|
|
18
|
+
class DeviceNonWorkingHours(DataClassORJSONMixin):
|
|
19
|
+
sub_cmd: int = 0
|
|
20
|
+
start_time: str = ""
|
|
21
|
+
end_time: str = ""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class LampInfo(DataClassORJSONMixin):
|
|
26
|
+
lamp_bright: int = 0
|
|
27
|
+
manual_light: bool = False
|
|
28
|
+
night_light: bool = False
|
|
29
|
+
|
|
30
|
+
|
|
17
31
|
@dataclass
|
|
18
32
|
class MowerInfo(DataClassORJSONMixin):
|
|
19
33
|
blade_status: bool = False
|
|
34
|
+
rain_detection: bool = False
|
|
35
|
+
traversal_mode: int = 0
|
|
36
|
+
turning_mode: int = 0
|
|
37
|
+
blade_mode: int = 0
|
|
38
|
+
blade_rpm: int = 0
|
|
20
39
|
side_led: SideLight = field(default_factory=SideLight)
|
|
21
40
|
collector_installation_status: bool = False
|
|
22
41
|
model: str = ""
|
|
23
42
|
swversion: str = ""
|
|
24
43
|
product_key: str = ""
|
|
25
44
|
model_id: str = ""
|
|
45
|
+
sub_model_id: str = ""
|
|
46
|
+
ble_mac: str = ""
|
|
47
|
+
wifi_mac: str = ""
|
|
48
|
+
lamp_info: LampInfo = field(default_factory=LampInfo)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class DeviceFirmwares(DataClassORJSONMixin):
|
|
53
|
+
device_version: str = ""
|
|
54
|
+
left_motor_driver: str = ""
|
|
55
|
+
lora_version: str = ""
|
|
56
|
+
main_controller: str = ""
|
|
57
|
+
model_name: str = ""
|
|
58
|
+
right_motor_driver: str = ""
|
|
59
|
+
rtk_rover_station: str = ""
|
|
60
|
+
rtk_version: str = ""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
@dataclass
|
|
@@ -9,18 +9,18 @@ class RangeLimit:
|
|
|
9
9
|
|
|
10
10
|
@dataclass
|
|
11
11
|
class DeviceLimits:
|
|
12
|
-
|
|
13
|
-
working_speed: RangeLimit =
|
|
14
|
-
|
|
12
|
+
blade_height: RangeLimit = field(default_factory=RangeLimit)
|
|
13
|
+
working_speed: RangeLimit = field(default_factory=RangeLimit)
|
|
14
|
+
path_spacing: RangeLimit = field(default_factory=RangeLimit)
|
|
15
15
|
work_area_num_max: int = 60
|
|
16
16
|
display_image_type: int = 0
|
|
17
17
|
|
|
18
18
|
def to_dict(self) -> dict:
|
|
19
19
|
"""Convert the device limits to a dictionary format."""
|
|
20
20
|
return {
|
|
21
|
-
"
|
|
21
|
+
"blade_height": {"min": self.blade_height.min, "max": self.blade_height.max},
|
|
22
22
|
"working_speed": {"min": self.working_speed.min, "max": self.working_speed.max},
|
|
23
|
-
"
|
|
23
|
+
"path_spacing": {"min": self.path_spacing.min, "max": self.path_spacing.max},
|
|
24
24
|
"work_area_num_max": self.work_area_num_max,
|
|
25
25
|
"display_image_type": self.display_image_type,
|
|
26
26
|
}
|
|
@@ -29,9 +29,9 @@ class DeviceLimits:
|
|
|
29
29
|
def from_dict(cls, data: dict) -> "DeviceLimits":
|
|
30
30
|
"""Create a DeviceLimits instance from a dictionary."""
|
|
31
31
|
return cls(
|
|
32
|
-
|
|
32
|
+
blade_height=RangeLimit(min=data["blade_height"]["min"], max=data["blade_height"]["max"]),
|
|
33
33
|
working_speed=RangeLimit(min=data["working_speed"]["min"], max=data["working_speed"]["max"]),
|
|
34
|
-
|
|
34
|
+
path_spacing=RangeLimit(min=data["path_spacing"]["min"], max=data["path_spacing"]["max"]),
|
|
35
35
|
work_area_num_max=data["work_area_num_max"],
|
|
36
36
|
display_image_type=data["display_image_type"],
|
|
37
37
|
)
|
|
@@ -40,9 +40,9 @@ class DeviceLimits:
|
|
|
40
40
|
"""Validate that all ranges are logical (min <= max)."""
|
|
41
41
|
return all(
|
|
42
42
|
[
|
|
43
|
-
self.
|
|
43
|
+
self.blade_height.min <= self.blade_height.max,
|
|
44
44
|
self.working_speed.min <= self.working_speed.max,
|
|
45
|
-
self.
|
|
45
|
+
self.path_spacing.min <= self.path_spacing.max,
|
|
46
46
|
self.work_area_num_max > 0,
|
|
47
47
|
self.display_image_type in (0, 1),
|
|
48
48
|
]
|
pymammotion/data/model/enums.py
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
class ConnectionPreference(Enum):
|
|
5
|
+
"""Enum for connection preference."""
|
|
6
|
+
|
|
7
|
+
ANY = 0
|
|
8
|
+
WIFI = 1
|
|
9
|
+
BLUETOOTH = 2
|
|
10
|
+
PREFER_WIFI = 3
|
|
11
|
+
PREFER_BLUETOOTH = 4
|
|
12
|
+
|
|
13
|
+
|
|
4
14
|
class PositionMode(Enum):
|
|
5
15
|
FIX = 0
|
|
6
16
|
SINGLE = 1
|
|
@@ -9,7 +19,7 @@ class PositionMode(Enum):
|
|
|
9
19
|
UNKNOWN = 4
|
|
10
20
|
|
|
11
21
|
@staticmethod
|
|
12
|
-
def from_value(value: int):
|
|
22
|
+
def from_value(value: int) -> "PositionMode":
|
|
13
23
|
if value == 0:
|
|
14
24
|
return PositionMode.FIX
|
|
15
25
|
elif value == 1:
|
|
@@ -42,7 +52,7 @@ class RTKStatus(Enum):
|
|
|
42
52
|
UNKNOWN = 6
|
|
43
53
|
|
|
44
54
|
@staticmethod
|
|
45
|
-
def from_value(value: int):
|
|
55
|
+
def from_value(value: int) -> "RTKStatus":
|
|
46
56
|
if value == 0:
|
|
47
57
|
return RTKStatus.NONE
|
|
48
58
|
elif value == 1 or value == 2:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
4
|
+
|
|
5
|
+
from pymammotion.http.model.http import ErrorInfo
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class DeviceErrors(DataClassORJSONMixin):
|
|
10
|
+
err_code_list: list[int] = field(default_factory=list)
|
|
11
|
+
err_code_list_time: list[int] = field(default_factory=list)
|
|
12
|
+
error_codes: dict[str, ErrorInfo] = field(default_factory=dict)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class WorkTaskEvent(DataClassORJSONMixin):
|
|
8
|
+
hash_area_map: dict[int, int] = field(default_factory=dict)
|
|
9
|
+
ids: list[int] = field(default_factory=list)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class Events(DataClassORJSONMixin):
|
|
14
|
+
work_tasks_event: WorkTaskEvent = field(default_factory=WorkTaskEvent)
|