From 9c0f6a5efcd1d722975b2e63f5862f0b6503cb98 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Sun, 11 Aug 2024 11:42:28 +1200 Subject: [PATCH 01/35] start work on mowing --- pymammotion/data/model/device.py | 7 +++--- pymammotion/data/state_manager.py | 4 +++- .../mammotion/commands/messages/navigation.py | 4 ++-- pymammotion/mammotion/devices/mammotion.py | 22 +++++++++++++++++-- pyproject.toml | 4 ++-- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/pymammotion/data/model/device.py b/pymammotion/data/model/device.py index 3187f71..841881c 100644 --- a/pymammotion/data/model/device.py +++ b/pymammotion/data/model/device.py @@ -2,8 +2,6 @@ from dataclasses import dataclass -import betterproto - from pymammotion.data.model import HashList from pymammotion.data.model.location import Location from pymammotion.proto.dev_net import DevNet @@ -13,7 +11,7 @@ from pymammotion.proto.mctrl_nav import MctlNav from pymammotion.proto.mctrl_ota import MctlOta from pymammotion.proto.mctrl_pept import MctlPept -from pymammotion.proto.mctrl_sys import MctlSys, SystemUpdateBufMsg +from pymammotion.proto.mctrl_sys import MctlSys, MowToAppInfoT, SystemUpdateBufMsg @dataclass @@ -84,6 +82,9 @@ def buffer(self, buffer_list: SystemUpdateBufMsg): ] ) + def mow_info(self, toapp_mow_info: MowToAppInfoT): + pass + @property def net(self): """Will return a wrapped betterproto of net.""" diff --git a/pymammotion/data/state_manager.py b/pymammotion/data/state_manager.py index db23b87..3546e06 100644 --- a/pymammotion/data/state_manager.py +++ b/pymammotion/data/state_manager.py @@ -69,6 +69,8 @@ def _update_sys_data(self, message): self._device.buffer(sys_msg[1]) case "toapp_report_data": pass + case "mow_to_app_info": + self._device.mow_info(sys_msg[1]) def _update_driver_data(self, message): pass @@ -80,4 +82,4 @@ def _update_mul_data(self, message): pass def _update_ota_data(self, message): - pass \ No newline at end of file + pass diff --git a/pymammotion/mammotion/commands/messages/navigation.py b/pymammotion/mammotion/commands/messages/navigation.py index c0c353f..0859e31 100644 --- a/pymammotion/mammotion/commands/messages/navigation.py +++ b/pymammotion/mammotion/commands/messages/navigation.py @@ -353,8 +353,8 @@ def generate_route_information(self, generate_route_information: GenerateRouteIn channel_width=generate_route_information.channel_width, channel_mode=generate_route_information.channel_mode, toward=generate_route_information.toward, - toward_included_angle=generate_route_information.toward_included_angle, - toward_mode=generate_route_information.toward_mode, + toward_included_angle=generate_route_information.toward_included_angle, # luba 2 yuka only + toward_mode=generate_route_information.toward_mode, # luba 2 yuka only reserved=generate_route_information.path_order, ) logger.debug(f"{self.get_device_name()}Generate route====={build}") diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index dec27f2..e260ec5 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -192,8 +192,6 @@ async def commdata_response(self, common_data: NavGetCommDataAck): region_data.current_frame = current_frame await self._send_command_with_args("get_regional_data", regional_data=region_data) - - def _update_raw_data(self, data: bytes) -> None: """Update raw and model data from notifications.""" tmp_msg = LubaMsg().parse(data) @@ -217,6 +215,9 @@ def _update_raw_data(self, data: bytes) -> None: def _update_nav_data(self, tmp_msg): """Update navigation data.""" nav_sub_msg = betterproto.which_one_of(tmp_msg.nav, "SubNavMsg") + if nav_sub_msg[1] is None: + _LOGGER.debug("Sub message was NoneType %s", nav_sub_msg[0]) + return nav = self._raw_data.get("nav", {}) if isinstance(nav_sub_msg[1], int): nav[nav_sub_msg[0]] = nav_sub_msg[1] @@ -227,6 +228,9 @@ def _update_nav_data(self, tmp_msg): def _update_sys_data(self, tmp_msg): """Update system data.""" sys_sub_msg = betterproto.which_one_of(tmp_msg.sys, "SubSysMsg") + if sys_sub_msg[1] is None: + _LOGGER.debug("Sub message was NoneType %s", sys_sub_msg[0]) + return sys = self._raw_data.get("sys", {}) sys[sys_sub_msg[0]] = sys_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE) self._raw_data["sys"] = sys @@ -234,6 +238,9 @@ def _update_sys_data(self, tmp_msg): def _update_driver_data(self, tmp_msg): """Update driver data.""" drv_sub_msg = betterproto.which_one_of(tmp_msg.driver, "SubDrvMsg") + if drv_sub_msg[1] is None: + _LOGGER.debug("Sub message was NoneType %s", drv_sub_msg[0]) + return drv = self._raw_data.get("driver", {}) drv[drv_sub_msg[0]] = drv_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE) self._raw_data["driver"] = drv @@ -241,6 +248,9 @@ def _update_driver_data(self, tmp_msg): def _update_net_data(self, tmp_msg): """Update network data.""" net_sub_msg = betterproto.which_one_of(tmp_msg.net, "NetSubType") + if net_sub_msg[1] is None: + _LOGGER.debug("Sub message was NoneType %s", net_sub_msg[0]) + return net = self._raw_data.get("net", {}) if isinstance(net_sub_msg[1], int): net[net_sub_msg[0]] = net_sub_msg[1] @@ -251,6 +261,9 @@ def _update_net_data(self, tmp_msg): def _update_mul_data(self, tmp_msg): """Update mul data.""" mul_sub_msg = betterproto.which_one_of(tmp_msg.mul, "SubMul") + if mul_sub_msg[1] is None: + _LOGGER.debug("Sub message was NoneType %s", mul_sub_msg[0]) + return mul = self._raw_data.get("mul", {}) mul[mul_sub_msg[0]] = mul_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE) self._raw_data["mul"] = mul @@ -258,6 +271,9 @@ def _update_mul_data(self, tmp_msg): def _update_ota_data(self, tmp_msg): """Update OTA data.""" ota_sub_msg = betterproto.which_one_of(tmp_msg.ota, "SubOtaMsg") + if ota_sub_msg[1] is None: + _LOGGER.debug("Sub message was NoneType %s", ota_sub_msg[0]) + return ota = self._raw_data.get("ota", {}) ota[ota_sub_msg[0]] = ota_sub_msg[1].to_dict(casing=betterproto.Casing.SNAKE) self._raw_data["ota"] = ota @@ -487,6 +503,8 @@ async def _ensure_connected(self): ) self._reset_disconnect_timer() await self._start_notify() + command_bytes = self._commands.send_todev_ble_sync(2) + await self._message.post_custom_data_bytes(command_bytes) self.schedule_ble_sync() async def _send_command_locked(self, key: str, command: bytes) -> bytes: diff --git a/pyproject.toml b/pyproject.toml index c77ff2c..9deddd8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.0.52" +version = "0.0.54" license = "GNU-3.0" description = "" readme = "README.md" @@ -47,7 +47,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.0.52" +current_version = "0.0.54" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 9ca6997d5e7ffc5bdfca498c436cd39faf00c9b3 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 12 Aug 2024 11:16:44 +1200 Subject: [PATCH 02/35] update mtrl nav --- pymammotion/data/model/device.py | 2 + pymammotion/data/model/device_config.py | 9 ++ pymammotion/data/model/mowing_modes.py | 8 ++ .../mammotion/commands/messages/navigation.py | 35 +++++- pymammotion/mammotion/devices/mammotion.py | 22 ++-- pymammotion/proto/mctrl_nav.proto | 62 ++++++++++ pymammotion/proto/mctrl_nav.py | 76 +++++++++++- pymammotion/proto/mctrl_nav_pb2.py | 18 ++- pymammotion/proto/mctrl_nav_pb2.pyi | 110 +++++++++++++++++- scripts.txt | 6 +- tests/test2_instance.py | 27 +++-- 11 files changed, 342 insertions(+), 33 deletions(-) create mode 100644 pymammotion/data/model/device_config.py diff --git a/pymammotion/data/model/device.py b/pymammotion/data/model/device.py index 841881c..63edf16 100644 --- a/pymammotion/data/model/device.py +++ b/pymammotion/data/model/device.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from pymammotion.data.model import HashList +from pymammotion.data.model.device_config import DeviceLimits from pymammotion.data.model.location import Location from pymammotion.proto.dev_net import DevNet from pymammotion.proto.luba_msg import LubaMsg @@ -28,6 +29,7 @@ def __init__(self): self.location = Location() self.err_code_list = [] self.err_code_list_time = [] + self.limits = DeviceLimits(30, 70, 0.2, 0.6) @classmethod def from_raw(cls, raw: dict) -> "MowingDevice": diff --git a/pymammotion/data/model/device_config.py b/pymammotion/data/model/device_config.py new file mode 100644 index 0000000..46749c5 --- /dev/null +++ b/pymammotion/data/model/device_config.py @@ -0,0 +1,9 @@ +from dataclasses import dataclass + + +@dataclass +class DeviceLimits: + cutter_height_min: int + cutter_height_max: int + working_speed_min: float + working_speed_max: float diff --git a/pymammotion/data/model/mowing_modes.py b/pymammotion/data/model/mowing_modes.py index ffcdfe3..7a9b7ab 100644 --- a/pymammotion/data/model/mowing_modes.py +++ b/pymammotion/data/model/mowing_modes.py @@ -35,3 +35,11 @@ class MowOrder(IntEnum): border_first = 0 grid_first = 1 + + +class BypassStrategy(IntEnum): + """Matches up with ultra_wave.""" + direct_touch = 0 + slow_touch = 1 + less_touch = 2 + no_touch = 3 # luba 2 yuka only or possibly value of 10 diff --git a/pymammotion/mammotion/commands/messages/navigation.py b/pymammotion/mammotion/commands/messages/navigation.py index 0859e31..642193d 100644 --- a/pymammotion/mammotion/commands/messages/navigation.py +++ b/pymammotion/mammotion/commands/messages/navigation.py @@ -23,7 +23,7 @@ NavUploadZigZagResultAck, SimulationCmdData, WorkReportCmdData, - WorkReportUpdateCmd, + WorkReportUpdateCmd, NavMapNameMsg, ) logger = logging.getLogger(__name__) @@ -254,6 +254,37 @@ def leave_dock(self): logger.debug("Send command--One-click automatic undocking") return self.send_order_msg_nav(build) + def get_area_name_list(self, device_id: str) -> bytes: + # Build the NavMapNameMsg with the specified parameters + mctl_nav = MctlNav( + toapp_map_name_msg=NavMapNameMsg( + hash=0, + result=0, + device_id=device_id, # iotId or ??? + rw=0 + ) + ) + + # Send the message with the specified ID and acknowledge flag + logger.debug("Send command--Get area name list") + return self.send_order_msg_nav(mctl_nav) + + def set_area_name(self, device_id, hash_id: int, name: str) -> bytes: + # Build the NavMapNameMsg with the specified parameters + mctl_nav = MctlNav( + toapp_map_name_msg=NavMapNameMsg( + hash=hash_id, + name=name, + result=0, + device_id=device_id, + rw=1 + ) + ) + + # Send the message with the specified ID and acknowledge flag + logger.debug("Send command--Get area name list") + return self.send_order_msg_nav(mctl_nav) + def get_all_boundary_hash_list(self, sub_cmd: int): build = MctlNav(todev_gethash=NavGetHashList(pver=1, sub_cmd=sub_cmd)) logger.debug(f"Area loading=====================:Get area hash list++Bluetooth:{sub_cmd}") @@ -354,7 +385,7 @@ def generate_route_information(self, generate_route_information: GenerateRouteIn channel_mode=generate_route_information.channel_mode, toward=generate_route_information.toward, toward_included_angle=generate_route_information.toward_included_angle, # luba 2 yuka only - toward_mode=generate_route_information.toward_mode, # luba 2 yuka only + toward_mode=generate_route_information.toward_mode, # luba 2 yuka only reserved=generate_route_information.path_order, ) logger.debug(f"{self.get_device_name()}Generate route====={build}") diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index e260ec5..87c8f71 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -71,7 +71,7 @@ def _sb_uuid(comms_type: str = "service") -> UUID | str: def slashescape(err): """Escape a slash character.""" # print err, dir(err), err.start, err.end, err.object[:err.start] - thebyte = err.object[err.start : err.end] + thebyte = err.object[err.start: err.end] repl = "\\x" + hex(ord(thebyte))[2:] return (repl, err.end) @@ -120,9 +120,9 @@ class MammotionDevice: _ble_device: MammotionBaseBLEDevice | None = None def __init__( - self, - ble_device: BLEDevice, - preference: ConnectionPreference = ConnectionPreference.EITHER, + self, + ble_device: BLEDevice, + preference: ConnectionPreference = ConnectionPreference.EITHER, ) -> None: """Initialize MammotionDevice.""" if ble_device: @@ -318,6 +318,8 @@ async def start_map_sync(self): await self._send_command_with_args("get_hash_response", total_frame=1, current_frame=1) + await self._send_command_with_args("get_area_name_list", + device_id=self.luba_msg.device.net.toapp_wifi_iot_status.devicename) # sub_cmd 3 is job hashes?? # sub_cmd 4 is dump location (yuka) # jobs list @@ -707,12 +709,12 @@ class MammotionBaseCloudDevice(MammotionBaseDevice): """Base class for Mammotion Cloud devices.""" def __init__( - self, - mqtt_client: MammotionMQTT, - iot_id: str, - device_name: str, - nick_name: str, - **kwargs: Any, + self, + mqtt_client: MammotionMQTT, + iot_id: str, + device_name: str, + nick_name: str, + **kwargs: Any, ) -> None: """Initialize MammotionBaseCloudDevice.""" super().__init__() diff --git a/pymammotion/proto/mctrl_nav.proto b/pymammotion/proto/mctrl_nav.proto index c979712..63c7202 100644 --- a/pymammotion/proto/mctrl_nav.proto +++ b/pymammotion/proto/mctrl_nav.proto @@ -423,6 +423,63 @@ message nav_get_all_plan_task { repeated plan_task_name_id_t tasks = 1; } +// Define the NavTaskCtrlAck message +message NavTaskCtrlAck { + int32 type = 1; + int32 action = 2; + int32 result = 3; + int32 nav_state = 4; + string reserved = 5; +} + +message NavMapNameMsg { + int32 rw = 1; // Represents RW field + int64 hash = 2; // Represents HASH field + string name = 3; // Represents NAME field + int32 result = 4; // Represents RESULT field + string device_id = 5; // Represents DEVICEID field +} + +message svg_message_t { + double x_move = 1; + double y_move = 2; + double scale = 3; + double rotate = 4; + double base_width_m = 5; + int32 base_width_pix = 7; + double base_height_m = 6; + int32 base_height_pix = 8; + int32 data_count = 12; + bool hide_svg = 13; + int32 name_count = 11; + string svg_file_name = 9; + string svg_file_data = 10; +} + +message SvgMessageAckT { + int32 pver = 1; // Represents PVER field + int32 sub_cmd = 2; // Represents SUBCMD field + int32 total_frame = 3; // Represents TOTALFRAME field + int32 current_frame = 4; // Represents CURRENTFRAME field + int64 data_hash = 5; // Represents DATAHASH field + int64 paternal_hash_a = 6; // Represents PATERNALHASHA field + int32 type = 7; // Represents TYPE field + int32 result = 8; // Represents RESULT field + svg_message_t svg_message = 9; // Represents SVG_MESSAGE field +} + +message AreaHashName { + // Define fields for AreaHashName message here + // For example: + string name = 1; // Replace with actual field definitions + int64 hash = 2; // Replace with actual field definitions +} + +message AppGetAllAreaHashName { + string device_id = 1; // Represents DEVICEID field + repeated AreaHashName hashnames = 2; // Represents HASHNAMES field +} + message MctlNav { oneof SubNavMsg { NavLatLonUp toapp_lat_up = 1; @@ -481,5 +538,10 @@ message MctlNav { costmap_t toapp_costmap = 54; plan_task_name_id_t plan_task_name_id = 55; nav_get_all_plan_task all_plan_task = 56; + NavTaskCtrlAck todev_taskctrl_ack = 57; + NavMapNameMsg toapp_map_name_msg = 58; + SvgMessageAckT todev_svg_msg = 59; + SvgMessageAckT toapp_svg_msg = 60; + AppGetAllAreaHashName toapp_all_hash_name = 61; } } \ No newline at end of file diff --git a/pymammotion/proto/mctrl_nav.py b/pymammotion/proto/mctrl_nav.py index 38e2af0..b95f99c 100644 --- a/pymammotion/proto/mctrl_nav.py +++ b/pymammotion/proto/mctrl_nav.py @@ -2,8 +2,6 @@ # sources: pymammotion/proto/mctrl_nav.proto # plugin: python-betterproto from dataclasses import dataclass - - import betterproto from .common import * @@ -475,6 +473,69 @@ class NavGetAllPlanTask(betterproto.Message): tasks: list["PlanTaskNameIdT"] = betterproto.message_field(1) +@dataclass +class NavTaskCtrlAck(betterproto.Message): + """Define the NavTaskCtrlAck message""" + + type: int = betterproto.int32_field(1) + action: int = betterproto.int32_field(2) + result: int = betterproto.int32_field(3) + nav_state: int = betterproto.int32_field(4) + reserved: str = betterproto.string_field(5) + + +@dataclass +class NavMapNameMsg(betterproto.Message): + rw: int = betterproto.int32_field(1) + hash: int = betterproto.int64_field(2) + name: str = betterproto.string_field(3) + result: int = betterproto.int32_field(4) + device_id: str = betterproto.string_field(5) + + +@dataclass +class SvgMessageT(betterproto.Message): + x_move: float = betterproto.double_field(1) + y_move: float = betterproto.double_field(2) + scale: float = betterproto.double_field(3) + rotate: float = betterproto.double_field(4) + base_width_m: float = betterproto.double_field(5) + base_width_pix: int = betterproto.int32_field(7) + base_height_m: float = betterproto.double_field(6) + base_height_pix: int = betterproto.int32_field(8) + data_count: int = betterproto.int32_field(12) + hide_svg: bool = betterproto.bool_field(13) + name_count: int = betterproto.int32_field(11) + svg_file_name: str = betterproto.string_field(9) + svg_file_data: str = betterproto.string_field(10) + + +@dataclass +class SvgMessageAckT(betterproto.Message): + pver: int = betterproto.int32_field(1) + sub_cmd: int = betterproto.int32_field(2) + total_frame: int = betterproto.int32_field(3) + current_frame: int = betterproto.int32_field(4) + data_hash: int = betterproto.int64_field(5) + paternal_hash_a: int = betterproto.int64_field(6) + type: int = betterproto.int32_field(7) + result: int = betterproto.int32_field(8) + svg_message: "SvgMessageT" = betterproto.message_field(9) + + +@dataclass +class AreaHashName(betterproto.Message): + # Define fields for AreaHashName message here For example: + name: str = betterproto.string_field(1) + hash: int = betterproto.int64_field(2) + + +@dataclass +class AppGetAllAreaHashName(betterproto.Message): + device_id: str = betterproto.string_field(1) + hashnames: list["AreaHashName"] = betterproto.message_field(2) + + @dataclass class MctlNav(betterproto.Message): toapp_lat_up: "NavLatLonUp" = betterproto.message_field(1, group="SubNavMsg") @@ -587,3 +648,14 @@ class MctlNav(betterproto.Message): all_plan_task: "NavGetAllPlanTask" = betterproto.message_field( 56, group="SubNavMsg" ) + todev_taskctrl_ack: "NavTaskCtrlAck" = betterproto.message_field( + 57, group="SubNavMsg" + ) + toapp_map_name_msg: "NavMapNameMsg" = betterproto.message_field( + 58, group="SubNavMsg" + ) + todev_svg_msg: "SvgMessageAckT" = betterproto.message_field(59, group="SubNavMsg") + toapp_svg_msg: "SvgMessageAckT" = betterproto.message_field(60, group="SubNavMsg") + toapp_all_hash_name: "AppGetAllAreaHashName" = betterproto.message_field( + 61, group="SubNavMsg" + ) diff --git a/pymammotion/proto/mctrl_nav_pb2.py b/pymammotion/proto/mctrl_nav_pb2.py index f8160b5..34d52ec 100644 --- a/pymammotion/proto/mctrl_nav_pb2.py +++ b/pymammotion/proto/mctrl_nav_pb2.py @@ -14,7 +14,7 @@ from pymammotion.proto import common_pb2 as pymammotion_dot_proto_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!pymammotion/proto/mctrl_nav.proto\x1a\x1epymammotion/proto/common.proto\"\'\n\x0bNavLatLonUp\x12\x0b\n\x03lat\x18\x01 \x01(\x01\x12\x0b\n\x03lon\x18\x02 \x01(\x01\"!\n\x0eNavBorderState\x12\x0f\n\x07\x62\x64state\x18\x01 \x01(\x05\"\xc9\x01\n\x08NavPosUp\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\x0e\n\x06status\x18\x03 \x01(\x05\x12\x0e\n\x06toward\x18\x04 \x01(\x05\x12\r\n\x05stars\x18\x05 \x01(\x05\x12\x0b\n\x03\x61ge\x18\x06 \x01(\x02\x12\x11\n\tlatStddev\x18\x07 \x01(\x02\x12\x11\n\tlonStddev\x18\x08 \x01(\x02\x12\x11\n\tl2dfStars\x18\t \x01(\x05\x12\x0f\n\x07posType\x18\n \x01(\x05\x12\x0f\n\x07\x63HashId\x18\x0b \x01(\x03\x12\x10\n\x08posLevel\x18\x0c \x01(\x05\":\n\x13NavBorderDataGetAck\x12\r\n\x05jobId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\"Z\n\x15NavObstiBorderDataGet\x12\x15\n\robstacleIndex\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\x12\x14\n\x0cobstaclesLen\x18\x03 \x01(\x05\"G\n\x18NavObstiBorderDataGetAck\x12\x15\n\robstacleIndex\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\"d\n\x0eNavCHlLineData\x12\x12\n\nstartJobRI\x18\x01 \x01(\x05\x12\x10\n\x08\x65ndJobRI\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\x12\x16\n\x0e\x63hannelLineLen\x18\x04 \x01(\x05\"O\n\x11NavCHlLineDataAck\x12\x12\n\nstartJobRI\x18\x01 \x01(\x05\x12\x10\n\x08\x65ndJobRI\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\"\x7f\n\x0bNavTaskInfo\x12\x0c\n\x04\x61rea\x18\x01 \x01(\x05\x12\x0c\n\x04time\x18\x02 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x0f\n\x07pathlen\x18\x05 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x06 \x03(\x0b\x32\x0f.CommDataCouple\"J\n\x10NavBorderDataGet\x12\r\n\x05jobId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\x12\x11\n\tborderLen\x18\x03 \x01(\x05\"\x91\x01\n\x0cNavOptLineUp\x12\x12\n\nstartJobRI\x18\x01 \x01(\x05\x12\x10\n\x08\x65ndJobRI\x18\x02 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x16\n\x0e\x63hannelDataLen\x18\x05 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x06 \x03(\x0b\x32\x0f.CommDataCouple\"~\n\x11NavOptiBorderInfo\x12\r\n\x05jobId\x18\x01 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\x12\x15\n\rborderDataLen\x18\x04 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x05 \x03(\x0b\x32\x0f.CommDataCouple\"\x81\x01\n\rNavOptObsInfo\x12\x12\n\nobstacleId\x18\x01 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\x12\x17\n\x0fobstacleDataLen\x18\x04 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x05 \x03(\x0b\x32\x0f.CommDataCouple\"\xb4\x01\n\x0bNavStartJob\x12\r\n\x05jobId\x18\x01 \x01(\x03\x12\x0e\n\x06jobVer\x18\x02 \x01(\x05\x12\x0f\n\x07jobMode\x18\x03 \x01(\x05\x12\x13\n\x0brainTactics\x18\x04 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x05 \x01(\x05\x12\r\n\x05speed\x18\x06 \x01(\x02\x12\x14\n\x0c\x63hannelWidth\x18\x07 \x01(\x05\x12\x11\n\tUltraWave\x18\x08 \x01(\x05\x12\x13\n\x0b\x63hannelMode\x18\t \x01(\x05\"\'\n\x0fNavTaskProgress\x12\x14\n\x0ctaskProgress\x18\x01 \x01(\x05\"\x1e\n\x0bNavResFrame\x12\x0f\n\x07\x66rameid\x18\x01 \x01(\x05\"|\n\x0eNavGetHashList\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x12\n\ntotalFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x05 \x01(\x06\x12\x10\n\x08reserved\x18\x06 \x01(\t\"\xb4\x01\n\x11NavGetHashListAck\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x12\n\ntotalFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x05 \x01(\x06\x12\x0f\n\x07hashLen\x18\x06 \x01(\x05\x12\x10\n\x08reserved\x18\x07 \x01(\t\x12\x0e\n\x06result\x18\x08 \x01(\x05\x12\x12\n\ndataCouple\x18\r \x03(\x03\"\xd6\x01\n\x0eNavGetCommData\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x03 \x01(\x05\x12\x0c\n\x04type\x18\x04 \x01(\x05\x12\x0c\n\x04Hash\x18\x05 \x01(\x03\x12\x15\n\rpaternalHashA\x18\x06 \x01(\x03\x12\x15\n\rpaternalHashB\x18\x07 \x01(\x03\x12\x12\n\ntotalFrame\x18\x08 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\t \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\n \x01(\x06\x12\x10\n\x08reserved\x18\x0b \x01(\t\"\x9f\x02\n\x11NavGetCommDataAck\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x04 \x01(\x05\x12\x0c\n\x04type\x18\x05 \x01(\x05\x12\x0c\n\x04Hash\x18\x06 \x01(\x06\x12\x15\n\rpaternalHashA\x18\x07 \x01(\x06\x12\x15\n\rpaternalHashB\x18\x08 \x01(\x06\x12\x12\n\ntotalFrame\x18\t \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\n \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x0b \x01(\x06\x12\x0f\n\x07\x64\x61taLen\x18\x0c \x01(\x05\x12#\n\ndataCouple\x18\r \x03(\x0b\x32\x0f.CommDataCouple\x12\x10\n\x08reserved\x18\x0e \x01(\t\"\xde\x02\n\x0fNavReqCoverPath\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\r\n\x05jobId\x18\x02 \x01(\x03\x12\x0e\n\x06jobVer\x18\x03 \x01(\x05\x12\x0f\n\x07jobMode\x18\x04 \x01(\x05\x12\x0e\n\x06subCmd\x18\x05 \x01(\x05\x12\x10\n\x08\x65\x64geMode\x18\x06 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x07 \x01(\x05\x12\x14\n\x0c\x63hannelWidth\x18\x08 \x01(\x05\x12\x11\n\tUltraWave\x18\t \x01(\x05\x12\x13\n\x0b\x63hannelMode\x18\n \x01(\x05\x12\x0e\n\x06toward\x18\x0b \x01(\x05\x12\r\n\x05speed\x18\x0c \x01(\x02\x12\x11\n\tzoneHashs\x18\r \x03(\x06\x12\x10\n\x08pathHash\x18\x0e \x01(\x06\x12\x10\n\x08reserved\x18\x0f \x01(\t\x12\x0e\n\x06result\x18\x10 \x01(\x05\x12\x13\n\x0btoward_mode\x18\x11 \x01(\x05\x12\x1d\n\x15toward_included_angle\x18\x12 \x01(\x05\"\xa7\x03\n\x15NavUploadZigZagResult\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\r\n\x05jobId\x18\x02 \x01(\x03\x12\x0e\n\x06jobVer\x18\x03 \x01(\x05\x12\x0e\n\x06result\x18\x04 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x05 \x01(\x05\x12\x0c\n\x04time\x18\x06 \x01(\x05\x12\x14\n\x0ctotalZoneNum\x18\x07 \x01(\x05\x12\x1a\n\x12\x63urrentZonePathNum\x18\x08 \x01(\x05\x12\x19\n\x11\x63urrentZonePathId\x18\t \x01(\x05\x12\x13\n\x0b\x63urrentZone\x18\n \x01(\x05\x12\x13\n\x0b\x63urrentHash\x18\x0b \x01(\x06\x12\x12\n\ntotalFrame\x18\x0c \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\r \x01(\x05\x12\x13\n\x0b\x63hannelMode\x18\x0e \x01(\x05\x12\x15\n\rchannelModeId\x18\x0f \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x10 \x01(\x06\x12\x0f\n\x07\x64\x61taLen\x18\x11 \x01(\x05\x12\x10\n\x08reserved\x18\x12 \x01(\t\x12#\n\ndataCouple\x18\x13 \x03(\x0b\x32\x0f.CommDataCouple\x12\x0e\n\x06subCmd\x18\x14 \x01(\x05\"\xb0\x01\n\x18NavUploadZigZagResultAck\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x13\n\x0b\x63urrentZone\x18\x02 \x01(\x05\x12\x13\n\x0b\x63urrentHash\x18\x03 \x01(\x06\x12\x12\n\ntotalFrame\x18\x04 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x05 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x06 \x01(\x06\x12\x10\n\x08reserved\x18\x07 \x01(\t\x12\x0e\n\x06subCmd\x18\x08 \x01(\x05\"M\n\x0bNavTaskCtrl\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\x12\x10\n\x08reserved\x18\x04 \x01(\t\"o\n\x0bNavTaskIdRw\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x10\n\x08taskName\x18\x03 \x01(\t\x12\x0e\n\x06taskId\x18\x04 \x01(\t\x12\x0e\n\x06result\x18\x05 \x01(\x05\x12\x10\n\x08reserved\x18\x06 \x01(\t\"J\n\x12NavSysHashOverview\x12\x1a\n\x12\x63ommonhashOverview\x18\x01 \x01(\x06\x12\x18\n\x10pathHashOverview\x18\x02 \x01(\x06\"i\n\x11NavTaskBreakPoint\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\x0e\n\x06toward\x18\x03 \x01(\x05\x12\x0c\n\x04\x66lag\x18\x04 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x05 \x01(\x05\x12\x10\n\x08zoneHash\x18\x06 \x01(\x06\"\xc2\x05\n\rNavPlanJobSet\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x03 \x01(\x05\x12\x10\n\x08workTime\x18\x04 \x01(\x05\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\n\n\x02id\x18\x06 \x01(\t\x12\x0e\n\x06userId\x18\x07 \x01(\t\x12\x10\n\x08\x64\x65viceId\x18\x08 \x01(\t\x12\x0e\n\x06planId\x18\t \x01(\t\x12\x0e\n\x06taskId\x18\n \x01(\t\x12\r\n\x05jobId\x18\x0b \x01(\t\x12\x11\n\tstartTime\x18\x0c \x01(\t\x12\x0f\n\x07\x65ndTime\x18\r \x01(\t\x12\x0c\n\x04week\x18\x0e \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x0f \x01(\x05\x12\r\n\x05model\x18\x10 \x01(\x05\x12\x10\n\x08\x65\x64geMode\x18\x11 \x01(\x05\x12\x14\n\x0crequiredTime\x18\x12 \x01(\x05\x12\x12\n\nrouteAngle\x18\x13 \x01(\x05\x12\x12\n\nrouteModel\x18\x14 \x01(\x05\x12\x14\n\x0crouteSpacing\x18\x15 \x01(\x05\x12\x19\n\x11ultrasonicBarrier\x18\x16 \x01(\x05\x12\x14\n\x0ctotalPlanNum\x18\x17 \x01(\x05\x12\x11\n\tPlanIndex\x18\x18 \x01(\x05\x12\x0e\n\x06result\x18\x19 \x01(\x05\x12\r\n\x05speed\x18\x1a \x01(\x02\x12\x10\n\x08taskName\x18\x1b \x01(\t\x12\x0f\n\x07jobName\x18\x1c \x01(\t\x12\x11\n\tzoneHashs\x18\x1d \x03(\x06\x12\x10\n\x08reserved\x18\x1e \x01(\t\x12\x11\n\tstartDate\x18\x1f \x01(\t\x12\x0f\n\x07\x65ndDate\x18 \x01(\t\x12\x13\n\x0btriggerType\x18! \x01(\x05\x12\x0b\n\x03\x64\x61y\x18\" \x01(\x05\x12\r\n\x05weeks\x18# \x03(\x07\x12\x18\n\x10remained_seconds\x18$ \x01(\x03\x12\x12\n\ntowardMode\x18% \x01(\x05\x12\x1b\n\x13towardIncludedAngle\x18& \x01(\x05\"\x86\x01\n\x10NavUnableTimeSet\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x10\n\x08\x64\x65viceId\x18\x02 \x01(\t\x12\x17\n\x0funableStartTime\x18\x03 \x01(\t\x12\x15\n\runableEndTime\x18\x04 \x01(\t\x12\x0e\n\x06result\x18\x05 \x01(\x05\x12\x10\n\x08reserved\x18\x06 \x01(\t\"6\n\x0e\x63hargePileType\x12\x0e\n\x06toward\x18\x01 \x01(\x05\x12\t\n\x01x\x18\x02 \x01(\x02\x12\t\n\x01y\x18\x03 \x01(\x02\"J\n\x11SimulationCmdData\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x10\n\x08param_id\x18\x02 \x01(\x05\x12\x13\n\x0bparam_value\x18\x03 \x03(\x05\"%\n\x13WorkReportUpdateCmd\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\"<\n\x13WorkReportUpdateAck\x12\x13\n\x0bupdate_flag\x18\x01 \x01(\x08\x12\x10\n\x08info_num\x18\x02 \x01(\x05\"7\n\x11WorkReportCmdData\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x12\n\ngetInfoNum\x18\x02 \x01(\x05\"\x8e\x02\n\x11WorkReportInfoAck\x12\x16\n\x0einterrupt_flag\x18\x01 \x01(\x08\x12\x17\n\x0fstart_work_time\x18\x02 \x01(\x03\x12\x15\n\rend_work_time\x18\x03 \x01(\x03\x12\x16\n\x0ework_time_used\x18\x04 \x01(\x05\x12\x11\n\twork_ares\x18\x05 \x01(\x01\x12\x15\n\rwork_progress\x18\x06 \x01(\x05\x12\x17\n\x0fheight_of_knife\x18\x07 \x01(\x05\x12\x11\n\twork_type\x18\x08 \x01(\x05\x12\x13\n\x0bwork_result\x18\t \x01(\x05\x12\x15\n\rtotal_ack_num\x18\n \x01(\x05\x12\x17\n\x0f\x63urrent_ack_num\x18\x0b \x01(\x05\"\xb2\x01\n\x19\x61pp_request_cover_paths_t\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x12\n\ntotalFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x05 \x01(\x06\x12\x16\n\x0etransaction_id\x18\x06 \x01(\x03\x12\x10\n\x08reserved\x18\x07 \x03(\x03\x12\x11\n\thash_list\x18\x08 \x03(\x06\"\x99\x01\n\x13\x63over_path_packet_t\x12\x11\n\tpath_hash\x18\x01 \x01(\x06\x12\x11\n\tpath_type\x18\x02 \x01(\x05\x12\x12\n\npath_total\x18\x03 \x01(\x05\x12\x10\n\x08path_cur\x18\x04 \x01(\x05\x12\x11\n\tzone_hash\x18\x05 \x01(\x06\x12#\n\ndataCouple\x18\x06 \x03(\x0b\x32\x0f.CommDataCouple\"\xb2\x02\n\x13\x63over_path_upload_t\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x0e\n\x06subCmd\x18\x03 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x04 \x01(\x05\x12\x0c\n\x04time\x18\x05 \x01(\x05\x12\x12\n\ntotalFrame\x18\x06 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x07 \x01(\x05\x12\x16\n\x0etotal_path_num\x18\x08 \x01(\x05\x12\x16\n\x0evaild_path_num\x18\t \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\n \x01(\x06\x12\x16\n\x0etransaction_id\x18\x0b \x01(\x03\x12\x10\n\x08reserved\x18\x0c \x03(\x03\x12\x0f\n\x07\x64\x61taLen\x18\r \x01(\x05\x12*\n\x0cpath_packets\x18\x0e \x03(\x0b\x32\x14.cover_path_packet_t\"M\n\x14zone_start_precent_t\x12\x10\n\x08\x64\x61taHash\x18\x01 \x01(\x06\x12\t\n\x01x\x18\x02 \x01(\x02\x12\t\n\x01y\x18\x03 \x01(\x02\x12\r\n\x05index\x18\x04 \x01(\x05\",\n\x0fvision_ctrl_msg\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0b\n\x03\x63md\x18\x02 \x01(\x05\"<\n\x11nav_sys_param_msg\x12\n\n\x02rw\x18\x01 \x01(\x05\x12\n\n\x02id\x18\x02 \x01(\x05\x12\x0f\n\x07\x63ontext\x18\x03 \x01(\x05\"Q\n\x15nav_plan_task_execute\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\n\n\x02id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06result\x18\x04 \x01(\x05\"y\n\tcostmap_t\x12\r\n\x05width\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\x10\n\x08\x63\x65nter_x\x18\x03 \x01(\x02\x12\x10\n\x08\x63\x65nter_y\x18\x04 \x01(\x02\x12\x0b\n\x03yaw\x18\x05 \x01(\x02\x12\x0b\n\x03res\x18\x06 \x01(\x02\x12\x0f\n\x07\x63ostmap\x18\x07 \x03(\x05\"/\n\x13plan_task_name_id_t\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"<\n\x15nav_get_all_plan_task\x12#\n\x05tasks\x18\x01 \x03(\x0b\x32\x14.plan_task_name_id_t\"\xfb\x12\n\x07MctlNav\x12$\n\x0ctoapp_lat_up\x18\x01 \x01(\x0b\x32\x0c.NavLatLonUpH\x00\x12!\n\x0ctoapp_pos_up\x18\x02 \x01(\x0b\x32\t.NavPosUpH\x00\x12.\n\x13todev_chl_line_data\x18\x03 \x01(\x0b\x32\x0f.NavCHlLineDataH\x00\x12\'\n\x0ftoapp_task_info\x18\x04 \x01(\x0b\x32\x0c.NavTaskInfoH\x00\x12*\n\x11toapp_opt_line_up\x18\x05 \x01(\x0b\x32\r.NavOptLineUpH\x00\x12\x33\n\x15toapp_opt_border_info\x18\x06 \x01(\x0b\x32\x12.NavOptiBorderInfoH\x00\x12,\n\x12toapp_opt_obs_info\x18\x07 \x01(\x0b\x32\x0e.NavOptObsInfoH\x00\x12+\n\x13todev_task_info_ack\x18\x08 \x01(\x0b\x32\x0c.NavResFrameH\x00\x12\x31\n\x19todev_opt_border_info_ack\x18\t \x01(\x0b\x32\x0c.NavResFrameH\x00\x12.\n\x16todev_opt_obs_info_ack\x18\n \x01(\x0b\x32\x0c.NavResFrameH\x00\x12-\n\x15todev_opt_line_up_ack\x18\x0b \x01(\x0b\x32\x0c.NavResFrameH\x00\x12*\n\x0ftoapp_chgpileto\x18\x0c \x01(\x0b\x32\x0f.chargePileTypeH\x00\x12\x17\n\rtodev_sustask\x18\r \x01(\x05H\x00\x12\x18\n\x0etodev_rechgcmd\x18\x0e \x01(\x05H\x00\x12\x17\n\rtodev_edgecmd\x18\x0f \x01(\x05H\x00\x12\x1b\n\x11todev_draw_border\x18\x10 \x01(\x05H\x00\x12\x1f\n\x15todev_draw_border_end\x18\x11 \x01(\x05H\x00\x12\x18\n\x0etodev_draw_obs\x18\x12 \x01(\x05H\x00\x12\x1c\n\x12todev_draw_obs_end\x18\x13 \x01(\x05H\x00\x12\x18\n\x0etodev_chl_line\x18\x14 \x01(\x05H\x00\x12\x1c\n\x12todev_chl_line_end\x18\x15 \x01(\x05H\x00\x12\x19\n\x0ftodev_save_task\x18\x16 \x01(\x05H\x00\x12\x1d\n\x13todev_cancel_suscmd\x18\x17 \x01(\x05H\x00\x12\x1e\n\x14todev_reset_chg_pile\x18\x18 \x01(\x05H\x00\x12\x1f\n\x15todev_cancel_draw_cmd\x18\x19 \x01(\x05H\x00\x12$\n\x1atodev_one_touch_leave_pile\x18\x1a \x01(\x05H\x00\x12&\n\x0etodev_mow_task\x18\x1b \x01(\x0b\x32\x0c.NavStartJobH\x00\x12\'\n\x0ctoapp_bstate\x18\x1c \x01(\x0b\x32\x0f.NavBorderStateH\x00\x12\x1a\n\x10todev_lat_up_ack\x18\x1d \x01(\x05H\x00\x12(\n\rtodev_gethash\x18\x1e \x01(\x0b\x32\x0f.NavGetHashListH\x00\x12/\n\x11toapp_gethash_ack\x18\x1f \x01(\x0b\x32\x12.NavGetHashListAckH\x00\x12/\n\x14todev_get_commondata\x18 \x01(\x0b\x32\x0f.NavGetCommDataH\x00\x12\x36\n\x18toapp_get_commondata_ack\x18! \x01(\x0b\x32\x12.NavGetCommDataAckH\x00\x12\x31\n\x15\x62idire_reqconver_path\x18\" \x01(\x0b\x32\x10.NavReqCoverPathH\x00\x12.\n\x0ctoapp_zigzag\x18# \x01(\x0b\x32\x16.NavUploadZigZagResultH\x00\x12\x35\n\x10todev_zigzag_ack\x18$ \x01(\x0b\x32\x19.NavUploadZigZagResultAckH\x00\x12&\n\x0etodev_taskctrl\x18% \x01(\x0b\x32\x0c.NavTaskCtrlH\x00\x12%\n\rbidire_taskid\x18& \x01(\x0b\x32\x0c.NavTaskIdRwH\x00\x12&\n\x08toapp_bp\x18\' \x01(\x0b\x32\x12.NavTaskBreakPointH\x00\x12+\n\x11todev_planjob_set\x18( \x01(\x0b\x32\x0e.NavPlanJobSetH\x00\x12\x32\n\x15todev_unable_time_set\x18) \x01(\x0b\x32\x11.NavUnableTimeSetH\x00\x12,\n\x0esimulation_cmd\x18* \x01(\x0b\x32\x12.SimulationCmdDataH\x00\x12<\n\x1ctodev_work_report_update_cmd\x18+ \x01(\x0b\x32\x14.WorkReportUpdateCmdH\x00\x12<\n\x1ctoapp_work_report_update_ack\x18, \x01(\x0b\x32\x14.WorkReportUpdateAckH\x00\x12\x33\n\x15todev_work_report_cmd\x18- \x01(\x0b\x32\x12.WorkReportCmdDataH\x00\x12\x33\n\x15toapp_work_report_ack\x18. \x01(\x0b\x32\x12.WorkReportInfoAckH\x00\x12\x36\n\x18toapp_work_report_upload\x18/ \x01(\x0b\x32\x12.WorkReportInfoAckH\x00\x12=\n\x17\x61pp_request_cover_paths\x18\x30 \x01(\x0b\x32\x1a.app_request_cover_paths_tH\x00\x12\x31\n\x11\x63over_path_upload\x18\x31 \x01(\x0b\x32\x14.cover_path_upload_tH\x00\x12\x33\n\x12zone_start_precent\x18\x32 \x01(\x0b\x32\x15.zone_start_precent_tH\x00\x12\'\n\x0bvision_ctrl\x18\x33 \x01(\x0b\x32\x10.vision_ctrl_msgH\x00\x12/\n\x11nav_sys_param_cmd\x18\x34 \x01(\x0b\x32\x12.nav_sys_param_msgH\x00\x12\x33\n\x11plan_task_execute\x18\x35 \x01(\x0b\x32\x16.nav_plan_task_executeH\x00\x12#\n\rtoapp_costmap\x18\x36 \x01(\x0b\x32\n.costmap_tH\x00\x12\x31\n\x11plan_task_name_id\x18\x37 \x01(\x0b\x32\x14.plan_task_name_id_tH\x00\x12/\n\rall_plan_task\x18\x38 \x01(\x0b\x32\x16.nav_get_all_plan_taskH\x00\x42\x0b\n\tSubNavMsgb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!pymammotion/proto/mctrl_nav.proto\x1a\x1epymammotion/proto/common.proto\"\'\n\x0bNavLatLonUp\x12\x0b\n\x03lat\x18\x01 \x01(\x01\x12\x0b\n\x03lon\x18\x02 \x01(\x01\"!\n\x0eNavBorderState\x12\x0f\n\x07\x62\x64state\x18\x01 \x01(\x05\"\xc9\x01\n\x08NavPosUp\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\x0e\n\x06status\x18\x03 \x01(\x05\x12\x0e\n\x06toward\x18\x04 \x01(\x05\x12\r\n\x05stars\x18\x05 \x01(\x05\x12\x0b\n\x03\x61ge\x18\x06 \x01(\x02\x12\x11\n\tlatStddev\x18\x07 \x01(\x02\x12\x11\n\tlonStddev\x18\x08 \x01(\x02\x12\x11\n\tl2dfStars\x18\t \x01(\x05\x12\x0f\n\x07posType\x18\n \x01(\x05\x12\x0f\n\x07\x63HashId\x18\x0b \x01(\x03\x12\x10\n\x08posLevel\x18\x0c \x01(\x05\":\n\x13NavBorderDataGetAck\x12\r\n\x05jobId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\"Z\n\x15NavObstiBorderDataGet\x12\x15\n\robstacleIndex\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\x12\x14\n\x0cobstaclesLen\x18\x03 \x01(\x05\"G\n\x18NavObstiBorderDataGetAck\x12\x15\n\robstacleIndex\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\"d\n\x0eNavCHlLineData\x12\x12\n\nstartJobRI\x18\x01 \x01(\x05\x12\x10\n\x08\x65ndJobRI\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\x12\x16\n\x0e\x63hannelLineLen\x18\x04 \x01(\x05\"O\n\x11NavCHlLineDataAck\x12\x12\n\nstartJobRI\x18\x01 \x01(\x05\x12\x10\n\x08\x65ndJobRI\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\"\x7f\n\x0bNavTaskInfo\x12\x0c\n\x04\x61rea\x18\x01 \x01(\x05\x12\x0c\n\x04time\x18\x02 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x0f\n\x07pathlen\x18\x05 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x06 \x03(\x0b\x32\x0f.CommDataCouple\"J\n\x10NavBorderDataGet\x12\r\n\x05jobId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\x12\x11\n\tborderLen\x18\x03 \x01(\x05\"\x91\x01\n\x0cNavOptLineUp\x12\x12\n\nstartJobRI\x18\x01 \x01(\x05\x12\x10\n\x08\x65ndJobRI\x18\x02 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x16\n\x0e\x63hannelDataLen\x18\x05 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x06 \x03(\x0b\x32\x0f.CommDataCouple\"~\n\x11NavOptiBorderInfo\x12\r\n\x05jobId\x18\x01 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\x12\x15\n\rborderDataLen\x18\x04 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x05 \x03(\x0b\x32\x0f.CommDataCouple\"\x81\x01\n\rNavOptObsInfo\x12\x12\n\nobstacleId\x18\x01 \x01(\x05\x12\x10\n\x08\x61llFrame\x18\x02 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x03 \x01(\x05\x12\x17\n\x0fobstacleDataLen\x18\x04 \x01(\x05\x12\x1b\n\x02\x64\x63\x18\x05 \x03(\x0b\x32\x0f.CommDataCouple\"\xb4\x01\n\x0bNavStartJob\x12\r\n\x05jobId\x18\x01 \x01(\x03\x12\x0e\n\x06jobVer\x18\x02 \x01(\x05\x12\x0f\n\x07jobMode\x18\x03 \x01(\x05\x12\x13\n\x0brainTactics\x18\x04 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x05 \x01(\x05\x12\r\n\x05speed\x18\x06 \x01(\x02\x12\x14\n\x0c\x63hannelWidth\x18\x07 \x01(\x05\x12\x11\n\tUltraWave\x18\x08 \x01(\x05\x12\x13\n\x0b\x63hannelMode\x18\t \x01(\x05\"\'\n\x0fNavTaskProgress\x12\x14\n\x0ctaskProgress\x18\x01 \x01(\x05\"\x1e\n\x0bNavResFrame\x12\x0f\n\x07\x66rameid\x18\x01 \x01(\x05\"|\n\x0eNavGetHashList\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x12\n\ntotalFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x05 \x01(\x06\x12\x10\n\x08reserved\x18\x06 \x01(\t\"\xb4\x01\n\x11NavGetHashListAck\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x12\n\ntotalFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x05 \x01(\x06\x12\x0f\n\x07hashLen\x18\x06 \x01(\x05\x12\x10\n\x08reserved\x18\x07 \x01(\t\x12\x0e\n\x06result\x18\x08 \x01(\x05\x12\x12\n\ndataCouple\x18\r \x03(\x03\"\xd6\x01\n\x0eNavGetCommData\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x03 \x01(\x05\x12\x0c\n\x04type\x18\x04 \x01(\x05\x12\x0c\n\x04Hash\x18\x05 \x01(\x03\x12\x15\n\rpaternalHashA\x18\x06 \x01(\x03\x12\x15\n\rpaternalHashB\x18\x07 \x01(\x03\x12\x12\n\ntotalFrame\x18\x08 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\t \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\n \x01(\x06\x12\x10\n\x08reserved\x18\x0b \x01(\t\"\x9f\x02\n\x11NavGetCommDataAck\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x04 \x01(\x05\x12\x0c\n\x04type\x18\x05 \x01(\x05\x12\x0c\n\x04Hash\x18\x06 \x01(\x06\x12\x15\n\rpaternalHashA\x18\x07 \x01(\x06\x12\x15\n\rpaternalHashB\x18\x08 \x01(\x06\x12\x12\n\ntotalFrame\x18\t \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\n \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x0b \x01(\x06\x12\x0f\n\x07\x64\x61taLen\x18\x0c \x01(\x05\x12#\n\ndataCouple\x18\r \x03(\x0b\x32\x0f.CommDataCouple\x12\x10\n\x08reserved\x18\x0e \x01(\t\"\xde\x02\n\x0fNavReqCoverPath\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\r\n\x05jobId\x18\x02 \x01(\x03\x12\x0e\n\x06jobVer\x18\x03 \x01(\x05\x12\x0f\n\x07jobMode\x18\x04 \x01(\x05\x12\x0e\n\x06subCmd\x18\x05 \x01(\x05\x12\x10\n\x08\x65\x64geMode\x18\x06 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x07 \x01(\x05\x12\x14\n\x0c\x63hannelWidth\x18\x08 \x01(\x05\x12\x11\n\tUltraWave\x18\t \x01(\x05\x12\x13\n\x0b\x63hannelMode\x18\n \x01(\x05\x12\x0e\n\x06toward\x18\x0b \x01(\x05\x12\r\n\x05speed\x18\x0c \x01(\x02\x12\x11\n\tzoneHashs\x18\r \x03(\x06\x12\x10\n\x08pathHash\x18\x0e \x01(\x06\x12\x10\n\x08reserved\x18\x0f \x01(\t\x12\x0e\n\x06result\x18\x10 \x01(\x05\x12\x13\n\x0btoward_mode\x18\x11 \x01(\x05\x12\x1d\n\x15toward_included_angle\x18\x12 \x01(\x05\"\xa7\x03\n\x15NavUploadZigZagResult\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\r\n\x05jobId\x18\x02 \x01(\x03\x12\x0e\n\x06jobVer\x18\x03 \x01(\x05\x12\x0e\n\x06result\x18\x04 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x05 \x01(\x05\x12\x0c\n\x04time\x18\x06 \x01(\x05\x12\x14\n\x0ctotalZoneNum\x18\x07 \x01(\x05\x12\x1a\n\x12\x63urrentZonePathNum\x18\x08 \x01(\x05\x12\x19\n\x11\x63urrentZonePathId\x18\t \x01(\x05\x12\x13\n\x0b\x63urrentZone\x18\n \x01(\x05\x12\x13\n\x0b\x63urrentHash\x18\x0b \x01(\x06\x12\x12\n\ntotalFrame\x18\x0c \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\r \x01(\x05\x12\x13\n\x0b\x63hannelMode\x18\x0e \x01(\x05\x12\x15\n\rchannelModeId\x18\x0f \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x10 \x01(\x06\x12\x0f\n\x07\x64\x61taLen\x18\x11 \x01(\x05\x12\x10\n\x08reserved\x18\x12 \x01(\t\x12#\n\ndataCouple\x18\x13 \x03(\x0b\x32\x0f.CommDataCouple\x12\x0e\n\x06subCmd\x18\x14 \x01(\x05\"\xb0\x01\n\x18NavUploadZigZagResultAck\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x13\n\x0b\x63urrentZone\x18\x02 \x01(\x05\x12\x13\n\x0b\x63urrentHash\x18\x03 \x01(\x06\x12\x12\n\ntotalFrame\x18\x04 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x05 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x06 \x01(\x06\x12\x10\n\x08reserved\x18\x07 \x01(\t\x12\x0e\n\x06subCmd\x18\x08 \x01(\x05\"M\n\x0bNavTaskCtrl\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\x12\x10\n\x08reserved\x18\x04 \x01(\t\"o\n\x0bNavTaskIdRw\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x10\n\x08taskName\x18\x03 \x01(\t\x12\x0e\n\x06taskId\x18\x04 \x01(\t\x12\x0e\n\x06result\x18\x05 \x01(\x05\x12\x10\n\x08reserved\x18\x06 \x01(\t\"J\n\x12NavSysHashOverview\x12\x1a\n\x12\x63ommonhashOverview\x18\x01 \x01(\x06\x12\x18\n\x10pathHashOverview\x18\x02 \x01(\x06\"i\n\x11NavTaskBreakPoint\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\x0e\n\x06toward\x18\x03 \x01(\x05\x12\x0c\n\x04\x66lag\x18\x04 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x05 \x01(\x05\x12\x10\n\x08zoneHash\x18\x06 \x01(\x06\"\xc2\x05\n\rNavPlanJobSet\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x03 \x01(\x05\x12\x10\n\x08workTime\x18\x04 \x01(\x05\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\n\n\x02id\x18\x06 \x01(\t\x12\x0e\n\x06userId\x18\x07 \x01(\t\x12\x10\n\x08\x64\x65viceId\x18\x08 \x01(\t\x12\x0e\n\x06planId\x18\t \x01(\t\x12\x0e\n\x06taskId\x18\n \x01(\t\x12\r\n\x05jobId\x18\x0b \x01(\t\x12\x11\n\tstartTime\x18\x0c \x01(\t\x12\x0f\n\x07\x65ndTime\x18\r \x01(\t\x12\x0c\n\x04week\x18\x0e \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x0f \x01(\x05\x12\r\n\x05model\x18\x10 \x01(\x05\x12\x10\n\x08\x65\x64geMode\x18\x11 \x01(\x05\x12\x14\n\x0crequiredTime\x18\x12 \x01(\x05\x12\x12\n\nrouteAngle\x18\x13 \x01(\x05\x12\x12\n\nrouteModel\x18\x14 \x01(\x05\x12\x14\n\x0crouteSpacing\x18\x15 \x01(\x05\x12\x19\n\x11ultrasonicBarrier\x18\x16 \x01(\x05\x12\x14\n\x0ctotalPlanNum\x18\x17 \x01(\x05\x12\x11\n\tPlanIndex\x18\x18 \x01(\x05\x12\x0e\n\x06result\x18\x19 \x01(\x05\x12\r\n\x05speed\x18\x1a \x01(\x02\x12\x10\n\x08taskName\x18\x1b \x01(\t\x12\x0f\n\x07jobName\x18\x1c \x01(\t\x12\x11\n\tzoneHashs\x18\x1d \x03(\x06\x12\x10\n\x08reserved\x18\x1e \x01(\t\x12\x11\n\tstartDate\x18\x1f \x01(\t\x12\x0f\n\x07\x65ndDate\x18 \x01(\t\x12\x13\n\x0btriggerType\x18! \x01(\x05\x12\x0b\n\x03\x64\x61y\x18\" \x01(\x05\x12\r\n\x05weeks\x18# \x03(\x07\x12\x18\n\x10remained_seconds\x18$ \x01(\x03\x12\x12\n\ntowardMode\x18% \x01(\x05\x12\x1b\n\x13towardIncludedAngle\x18& \x01(\x05\"\x86\x01\n\x10NavUnableTimeSet\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x10\n\x08\x64\x65viceId\x18\x02 \x01(\t\x12\x17\n\x0funableStartTime\x18\x03 \x01(\t\x12\x15\n\runableEndTime\x18\x04 \x01(\t\x12\x0e\n\x06result\x18\x05 \x01(\x05\x12\x10\n\x08reserved\x18\x06 \x01(\t\"6\n\x0e\x63hargePileType\x12\x0e\n\x06toward\x18\x01 \x01(\x05\x12\t\n\x01x\x18\x02 \x01(\x02\x12\t\n\x01y\x18\x03 \x01(\x02\"J\n\x11SimulationCmdData\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x10\n\x08param_id\x18\x02 \x01(\x05\x12\x13\n\x0bparam_value\x18\x03 \x03(\x05\"%\n\x13WorkReportUpdateCmd\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\"<\n\x13WorkReportUpdateAck\x12\x13\n\x0bupdate_flag\x18\x01 \x01(\x08\x12\x10\n\x08info_num\x18\x02 \x01(\x05\"7\n\x11WorkReportCmdData\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x12\n\ngetInfoNum\x18\x02 \x01(\x05\"\x8e\x02\n\x11WorkReportInfoAck\x12\x16\n\x0einterrupt_flag\x18\x01 \x01(\x08\x12\x17\n\x0fstart_work_time\x18\x02 \x01(\x03\x12\x15\n\rend_work_time\x18\x03 \x01(\x03\x12\x16\n\x0ework_time_used\x18\x04 \x01(\x05\x12\x11\n\twork_ares\x18\x05 \x01(\x01\x12\x15\n\rwork_progress\x18\x06 \x01(\x05\x12\x17\n\x0fheight_of_knife\x18\x07 \x01(\x05\x12\x11\n\twork_type\x18\x08 \x01(\x05\x12\x13\n\x0bwork_result\x18\t \x01(\x05\x12\x15\n\rtotal_ack_num\x18\n \x01(\x05\x12\x17\n\x0f\x63urrent_ack_num\x18\x0b \x01(\x05\"\xb2\x01\n\x19\x61pp_request_cover_paths_t\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06subCmd\x18\x02 \x01(\x05\x12\x12\n\ntotalFrame\x18\x03 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x04 \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\x05 \x01(\x06\x12\x16\n\x0etransaction_id\x18\x06 \x01(\x03\x12\x10\n\x08reserved\x18\x07 \x03(\x03\x12\x11\n\thash_list\x18\x08 \x03(\x06\"\x99\x01\n\x13\x63over_path_packet_t\x12\x11\n\tpath_hash\x18\x01 \x01(\x06\x12\x11\n\tpath_type\x18\x02 \x01(\x05\x12\x12\n\npath_total\x18\x03 \x01(\x05\x12\x10\n\x08path_cur\x18\x04 \x01(\x05\x12\x11\n\tzone_hash\x18\x05 \x01(\x06\x12#\n\ndataCouple\x18\x06 \x03(\x0b\x32\x0f.CommDataCouple\"\xb2\x02\n\x13\x63over_path_upload_t\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x0e\n\x06subCmd\x18\x03 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x04 \x01(\x05\x12\x0c\n\x04time\x18\x05 \x01(\x05\x12\x12\n\ntotalFrame\x18\x06 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x07 \x01(\x05\x12\x16\n\x0etotal_path_num\x18\x08 \x01(\x05\x12\x16\n\x0evaild_path_num\x18\t \x01(\x05\x12\x10\n\x08\x64\x61taHash\x18\n \x01(\x06\x12\x16\n\x0etransaction_id\x18\x0b \x01(\x03\x12\x10\n\x08reserved\x18\x0c \x03(\x03\x12\x0f\n\x07\x64\x61taLen\x18\r \x01(\x05\x12*\n\x0cpath_packets\x18\x0e \x03(\x0b\x32\x14.cover_path_packet_t\"M\n\x14zone_start_precent_t\x12\x10\n\x08\x64\x61taHash\x18\x01 \x01(\x06\x12\t\n\x01x\x18\x02 \x01(\x02\x12\t\n\x01y\x18\x03 \x01(\x02\x12\r\n\x05index\x18\x04 \x01(\x05\",\n\x0fvision_ctrl_msg\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0b\n\x03\x63md\x18\x02 \x01(\x05\"<\n\x11nav_sys_param_msg\x12\n\n\x02rw\x18\x01 \x01(\x05\x12\n\n\x02id\x18\x02 \x01(\x05\x12\x0f\n\x07\x63ontext\x18\x03 \x01(\x05\"Q\n\x15nav_plan_task_execute\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\n\n\x02id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06result\x18\x04 \x01(\x05\"y\n\tcostmap_t\x12\r\n\x05width\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\x10\n\x08\x63\x65nter_x\x18\x03 \x01(\x02\x12\x10\n\x08\x63\x65nter_y\x18\x04 \x01(\x02\x12\x0b\n\x03yaw\x18\x05 \x01(\x02\x12\x0b\n\x03res\x18\x06 \x01(\x02\x12\x0f\n\x07\x63ostmap\x18\x07 \x03(\x05\"/\n\x13plan_task_name_id_t\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"<\n\x15nav_get_all_plan_task\x12#\n\x05tasks\x18\x01 \x03(\x0b\x32\x14.plan_task_name_id_t\"c\n\x0eNavTaskCtrlAck\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\x12\x11\n\tnav_state\x18\x04 \x01(\x05\x12\x10\n\x08reserved\x18\x05 \x01(\t\"Z\n\rNavMapNameMsg\x12\n\n\x02rw\x18\x01 \x01(\x05\x12\x0c\n\x04hash\x18\x02 \x01(\x03\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06result\x18\x04 \x01(\x05\x12\x11\n\tdevice_id\x18\x05 \x01(\t\"\x94\x02\n\rsvg_message_t\x12\x0e\n\x06x_move\x18\x01 \x01(\x01\x12\x0e\n\x06y_move\x18\x02 \x01(\x01\x12\r\n\x05scale\x18\x03 \x01(\x01\x12\x0e\n\x06rotate\x18\x04 \x01(\x01\x12\x14\n\x0c\x62\x61se_width_m\x18\x05 \x01(\x01\x12\x16\n\x0e\x62\x61se_width_pix\x18\x07 \x01(\x05\x12\x15\n\rbase_height_m\x18\x06 \x01(\x01\x12\x17\n\x0f\x62\x61se_height_pix\x18\x08 \x01(\x05\x12\x12\n\ndata_count\x18\x0c \x01(\x05\x12\x10\n\x08hide_svg\x18\r \x01(\x08\x12\x12\n\nname_count\x18\x0b \x01(\x05\x12\x15\n\rsvg_file_name\x18\t \x01(\t\x12\x15\n\rsvg_file_data\x18\n \x01(\t\"\xca\x01\n\x0eSvgMessageAckT\x12\x0c\n\x04pver\x18\x01 \x01(\x05\x12\x0f\n\x07sub_cmd\x18\x02 \x01(\x05\x12\x13\n\x0btotal_frame\x18\x03 \x01(\x05\x12\x15\n\rcurrent_frame\x18\x04 \x01(\x05\x12\x11\n\tdata_hash\x18\x05 \x01(\x03\x12\x17\n\x0fpaternal_hash_a\x18\x06 \x01(\x03\x12\x0c\n\x04type\x18\x07 \x01(\x05\x12\x0e\n\x06result\x18\x08 \x01(\x05\x12#\n\x0bsvg_message\x18\t \x01(\x0b\x32\x0e.svg_message_t\"*\n\x0c\x41reaHashName\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04hash\x18\x02 \x01(\x03\"L\n\x15\x41ppGetAllAreaHashName\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12 \n\thashnames\x18\x02 \x03(\x0b\x32\r.AreaHashName\"\xe3\x14\n\x07MctlNav\x12$\n\x0ctoapp_lat_up\x18\x01 \x01(\x0b\x32\x0c.NavLatLonUpH\x00\x12!\n\x0ctoapp_pos_up\x18\x02 \x01(\x0b\x32\t.NavPosUpH\x00\x12.\n\x13todev_chl_line_data\x18\x03 \x01(\x0b\x32\x0f.NavCHlLineDataH\x00\x12\'\n\x0ftoapp_task_info\x18\x04 \x01(\x0b\x32\x0c.NavTaskInfoH\x00\x12*\n\x11toapp_opt_line_up\x18\x05 \x01(\x0b\x32\r.NavOptLineUpH\x00\x12\x33\n\x15toapp_opt_border_info\x18\x06 \x01(\x0b\x32\x12.NavOptiBorderInfoH\x00\x12,\n\x12toapp_opt_obs_info\x18\x07 \x01(\x0b\x32\x0e.NavOptObsInfoH\x00\x12+\n\x13todev_task_info_ack\x18\x08 \x01(\x0b\x32\x0c.NavResFrameH\x00\x12\x31\n\x19todev_opt_border_info_ack\x18\t \x01(\x0b\x32\x0c.NavResFrameH\x00\x12.\n\x16todev_opt_obs_info_ack\x18\n \x01(\x0b\x32\x0c.NavResFrameH\x00\x12-\n\x15todev_opt_line_up_ack\x18\x0b \x01(\x0b\x32\x0c.NavResFrameH\x00\x12*\n\x0ftoapp_chgpileto\x18\x0c \x01(\x0b\x32\x0f.chargePileTypeH\x00\x12\x17\n\rtodev_sustask\x18\r \x01(\x05H\x00\x12\x18\n\x0etodev_rechgcmd\x18\x0e \x01(\x05H\x00\x12\x17\n\rtodev_edgecmd\x18\x0f \x01(\x05H\x00\x12\x1b\n\x11todev_draw_border\x18\x10 \x01(\x05H\x00\x12\x1f\n\x15todev_draw_border_end\x18\x11 \x01(\x05H\x00\x12\x18\n\x0etodev_draw_obs\x18\x12 \x01(\x05H\x00\x12\x1c\n\x12todev_draw_obs_end\x18\x13 \x01(\x05H\x00\x12\x18\n\x0etodev_chl_line\x18\x14 \x01(\x05H\x00\x12\x1c\n\x12todev_chl_line_end\x18\x15 \x01(\x05H\x00\x12\x19\n\x0ftodev_save_task\x18\x16 \x01(\x05H\x00\x12\x1d\n\x13todev_cancel_suscmd\x18\x17 \x01(\x05H\x00\x12\x1e\n\x14todev_reset_chg_pile\x18\x18 \x01(\x05H\x00\x12\x1f\n\x15todev_cancel_draw_cmd\x18\x19 \x01(\x05H\x00\x12$\n\x1atodev_one_touch_leave_pile\x18\x1a \x01(\x05H\x00\x12&\n\x0etodev_mow_task\x18\x1b \x01(\x0b\x32\x0c.NavStartJobH\x00\x12\'\n\x0ctoapp_bstate\x18\x1c \x01(\x0b\x32\x0f.NavBorderStateH\x00\x12\x1a\n\x10todev_lat_up_ack\x18\x1d \x01(\x05H\x00\x12(\n\rtodev_gethash\x18\x1e \x01(\x0b\x32\x0f.NavGetHashListH\x00\x12/\n\x11toapp_gethash_ack\x18\x1f \x01(\x0b\x32\x12.NavGetHashListAckH\x00\x12/\n\x14todev_get_commondata\x18 \x01(\x0b\x32\x0f.NavGetCommDataH\x00\x12\x36\n\x18toapp_get_commondata_ack\x18! \x01(\x0b\x32\x12.NavGetCommDataAckH\x00\x12\x31\n\x15\x62idire_reqconver_path\x18\" \x01(\x0b\x32\x10.NavReqCoverPathH\x00\x12.\n\x0ctoapp_zigzag\x18# \x01(\x0b\x32\x16.NavUploadZigZagResultH\x00\x12\x35\n\x10todev_zigzag_ack\x18$ \x01(\x0b\x32\x19.NavUploadZigZagResultAckH\x00\x12&\n\x0etodev_taskctrl\x18% \x01(\x0b\x32\x0c.NavTaskCtrlH\x00\x12%\n\rbidire_taskid\x18& \x01(\x0b\x32\x0c.NavTaskIdRwH\x00\x12&\n\x08toapp_bp\x18\' \x01(\x0b\x32\x12.NavTaskBreakPointH\x00\x12+\n\x11todev_planjob_set\x18( \x01(\x0b\x32\x0e.NavPlanJobSetH\x00\x12\x32\n\x15todev_unable_time_set\x18) \x01(\x0b\x32\x11.NavUnableTimeSetH\x00\x12,\n\x0esimulation_cmd\x18* \x01(\x0b\x32\x12.SimulationCmdDataH\x00\x12<\n\x1ctodev_work_report_update_cmd\x18+ \x01(\x0b\x32\x14.WorkReportUpdateCmdH\x00\x12<\n\x1ctoapp_work_report_update_ack\x18, \x01(\x0b\x32\x14.WorkReportUpdateAckH\x00\x12\x33\n\x15todev_work_report_cmd\x18- \x01(\x0b\x32\x12.WorkReportCmdDataH\x00\x12\x33\n\x15toapp_work_report_ack\x18. \x01(\x0b\x32\x12.WorkReportInfoAckH\x00\x12\x36\n\x18toapp_work_report_upload\x18/ \x01(\x0b\x32\x12.WorkReportInfoAckH\x00\x12=\n\x17\x61pp_request_cover_paths\x18\x30 \x01(\x0b\x32\x1a.app_request_cover_paths_tH\x00\x12\x31\n\x11\x63over_path_upload\x18\x31 \x01(\x0b\x32\x14.cover_path_upload_tH\x00\x12\x33\n\x12zone_start_precent\x18\x32 \x01(\x0b\x32\x15.zone_start_precent_tH\x00\x12\'\n\x0bvision_ctrl\x18\x33 \x01(\x0b\x32\x10.vision_ctrl_msgH\x00\x12/\n\x11nav_sys_param_cmd\x18\x34 \x01(\x0b\x32\x12.nav_sys_param_msgH\x00\x12\x33\n\x11plan_task_execute\x18\x35 \x01(\x0b\x32\x16.nav_plan_task_executeH\x00\x12#\n\rtoapp_costmap\x18\x36 \x01(\x0b\x32\n.costmap_tH\x00\x12\x31\n\x11plan_task_name_id\x18\x37 \x01(\x0b\x32\x14.plan_task_name_id_tH\x00\x12/\n\rall_plan_task\x18\x38 \x01(\x0b\x32\x16.nav_get_all_plan_taskH\x00\x12-\n\x12todev_taskctrl_ack\x18\x39 \x01(\x0b\x32\x0f.NavTaskCtrlAckH\x00\x12,\n\x12toapp_map_name_msg\x18: \x01(\x0b\x32\x0e.NavMapNameMsgH\x00\x12(\n\rtodev_svg_msg\x18; \x01(\x0b\x32\x0f.SvgMessageAckTH\x00\x12(\n\rtoapp_svg_msg\x18< \x01(\x0b\x32\x0f.SvgMessageAckTH\x00\x12\x35\n\x13toapp_all_hash_name\x18= \x01(\x0b\x32\x16.AppGetAllAreaHashNameH\x00\x42\x0b\n\tSubNavMsgb\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pymammotion.proto.mctrl_nav_pb2', globals()) @@ -111,6 +111,18 @@ _PLAN_TASK_NAME_ID_T._serialized_end=6270 _NAV_GET_ALL_PLAN_TASK._serialized_start=6272 _NAV_GET_ALL_PLAN_TASK._serialized_end=6332 - _MCTLNAV._serialized_start=6335 - _MCTLNAV._serialized_end=8762 + _NAVTASKCTRLACK._serialized_start=6334 + _NAVTASKCTRLACK._serialized_end=6433 + _NAVMAPNAMEMSG._serialized_start=6435 + _NAVMAPNAMEMSG._serialized_end=6525 + _SVG_MESSAGE_T._serialized_start=6528 + _SVG_MESSAGE_T._serialized_end=6804 + _SVGMESSAGEACKT._serialized_start=6807 + _SVGMESSAGEACKT._serialized_end=7009 + _AREAHASHNAME._serialized_start=7011 + _AREAHASHNAME._serialized_end=7053 + _APPGETALLAREAHASHNAME._serialized_start=7055 + _APPGETALLAREAHASHNAME._serialized_end=7131 + _MCTLNAV._serialized_start=7134 + _MCTLNAV._serialized_end=9793 # @@protoc_insertion_point(module_scope) diff --git a/pymammotion/proto/mctrl_nav_pb2.pyi b/pymammotion/proto/mctrl_nav_pb2.pyi index d2cdd93..c671633 100644 --- a/pymammotion/proto/mctrl_nav_pb2.pyi +++ b/pymammotion/proto/mctrl_nav_pb2.pyi @@ -6,8 +6,24 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor +class AppGetAllAreaHashName(_message.Message): + __slots__ = ["device_id", "hashnames"] + DEVICE_ID_FIELD_NUMBER: _ClassVar[int] + HASHNAMES_FIELD_NUMBER: _ClassVar[int] + device_id: str + hashnames: _containers.RepeatedCompositeFieldContainer[AreaHashName] + def __init__(self, device_id: _Optional[str] = ..., hashnames: _Optional[_Iterable[_Union[AreaHashName, _Mapping]]] = ...) -> None: ... + +class AreaHashName(_message.Message): + __slots__ = ["hash", "name"] + HASH_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + hash: int + name: str + def __init__(self, name: _Optional[str] = ..., hash: _Optional[int] = ...) -> None: ... + class MctlNav(_message.Message): - __slots__ = ["all_plan_task", "app_request_cover_paths", "bidire_reqconver_path", "bidire_taskid", "cover_path_upload", "nav_sys_param_cmd", "plan_task_execute", "plan_task_name_id", "simulation_cmd", "toapp_bp", "toapp_bstate", "toapp_chgpileto", "toapp_costmap", "toapp_get_commondata_ack", "toapp_gethash_ack", "toapp_lat_up", "toapp_opt_border_info", "toapp_opt_line_up", "toapp_opt_obs_info", "toapp_pos_up", "toapp_task_info", "toapp_work_report_ack", "toapp_work_report_update_ack", "toapp_work_report_upload", "toapp_zigzag", "todev_cancel_draw_cmd", "todev_cancel_suscmd", "todev_chl_line", "todev_chl_line_data", "todev_chl_line_end", "todev_draw_border", "todev_draw_border_end", "todev_draw_obs", "todev_draw_obs_end", "todev_edgecmd", "todev_get_commondata", "todev_gethash", "todev_lat_up_ack", "todev_mow_task", "todev_one_touch_leave_pile", "todev_opt_border_info_ack", "todev_opt_line_up_ack", "todev_opt_obs_info_ack", "todev_planjob_set", "todev_rechgcmd", "todev_reset_chg_pile", "todev_save_task", "todev_sustask", "todev_task_info_ack", "todev_taskctrl", "todev_unable_time_set", "todev_work_report_cmd", "todev_work_report_update_cmd", "todev_zigzag_ack", "vision_ctrl", "zone_start_precent"] + __slots__ = ["all_plan_task", "app_request_cover_paths", "bidire_reqconver_path", "bidire_taskid", "cover_path_upload", "nav_sys_param_cmd", "plan_task_execute", "plan_task_name_id", "simulation_cmd", "toapp_all_hash_name", "toapp_bp", "toapp_bstate", "toapp_chgpileto", "toapp_costmap", "toapp_get_commondata_ack", "toapp_gethash_ack", "toapp_lat_up", "toapp_map_name_msg", "toapp_opt_border_info", "toapp_opt_line_up", "toapp_opt_obs_info", "toapp_pos_up", "toapp_svg_msg", "toapp_task_info", "toapp_work_report_ack", "toapp_work_report_update_ack", "toapp_work_report_upload", "toapp_zigzag", "todev_cancel_draw_cmd", "todev_cancel_suscmd", "todev_chl_line", "todev_chl_line_data", "todev_chl_line_end", "todev_draw_border", "todev_draw_border_end", "todev_draw_obs", "todev_draw_obs_end", "todev_edgecmd", "todev_get_commondata", "todev_gethash", "todev_lat_up_ack", "todev_mow_task", "todev_one_touch_leave_pile", "todev_opt_border_info_ack", "todev_opt_line_up_ack", "todev_opt_obs_info_ack", "todev_planjob_set", "todev_rechgcmd", "todev_reset_chg_pile", "todev_save_task", "todev_sustask", "todev_svg_msg", "todev_task_info_ack", "todev_taskctrl", "todev_taskctrl_ack", "todev_unable_time_set", "todev_work_report_cmd", "todev_work_report_update_cmd", "todev_zigzag_ack", "vision_ctrl", "zone_start_precent"] ALL_PLAN_TASK_FIELD_NUMBER: _ClassVar[int] APP_REQUEST_COVER_PATHS_FIELD_NUMBER: _ClassVar[int] BIDIRE_REQCONVER_PATH_FIELD_NUMBER: _ClassVar[int] @@ -17,6 +33,7 @@ class MctlNav(_message.Message): PLAN_TASK_EXECUTE_FIELD_NUMBER: _ClassVar[int] PLAN_TASK_NAME_ID_FIELD_NUMBER: _ClassVar[int] SIMULATION_CMD_FIELD_NUMBER: _ClassVar[int] + TOAPP_ALL_HASH_NAME_FIELD_NUMBER: _ClassVar[int] TOAPP_BP_FIELD_NUMBER: _ClassVar[int] TOAPP_BSTATE_FIELD_NUMBER: _ClassVar[int] TOAPP_CHGPILETO_FIELD_NUMBER: _ClassVar[int] @@ -24,10 +41,12 @@ class MctlNav(_message.Message): TOAPP_GETHASH_ACK_FIELD_NUMBER: _ClassVar[int] TOAPP_GET_COMMONDATA_ACK_FIELD_NUMBER: _ClassVar[int] TOAPP_LAT_UP_FIELD_NUMBER: _ClassVar[int] + TOAPP_MAP_NAME_MSG_FIELD_NUMBER: _ClassVar[int] TOAPP_OPT_BORDER_INFO_FIELD_NUMBER: _ClassVar[int] TOAPP_OPT_LINE_UP_FIELD_NUMBER: _ClassVar[int] TOAPP_OPT_OBS_INFO_FIELD_NUMBER: _ClassVar[int] TOAPP_POS_UP_FIELD_NUMBER: _ClassVar[int] + TOAPP_SVG_MSG_FIELD_NUMBER: _ClassVar[int] TOAPP_TASK_INFO_FIELD_NUMBER: _ClassVar[int] TOAPP_WORK_REPORT_ACK_FIELD_NUMBER: _ClassVar[int] TOAPP_WORK_REPORT_UPDATE_ACK_FIELD_NUMBER: _ClassVar[int] @@ -56,6 +75,8 @@ class MctlNav(_message.Message): TODEV_RESET_CHG_PILE_FIELD_NUMBER: _ClassVar[int] TODEV_SAVE_TASK_FIELD_NUMBER: _ClassVar[int] TODEV_SUSTASK_FIELD_NUMBER: _ClassVar[int] + TODEV_SVG_MSG_FIELD_NUMBER: _ClassVar[int] + TODEV_TASKCTRL_ACK_FIELD_NUMBER: _ClassVar[int] TODEV_TASKCTRL_FIELD_NUMBER: _ClassVar[int] TODEV_TASK_INFO_ACK_FIELD_NUMBER: _ClassVar[int] TODEV_UNABLE_TIME_SET_FIELD_NUMBER: _ClassVar[int] @@ -73,6 +94,7 @@ class MctlNav(_message.Message): plan_task_execute: nav_plan_task_execute plan_task_name_id: plan_task_name_id_t simulation_cmd: SimulationCmdData + toapp_all_hash_name: AppGetAllAreaHashName toapp_bp: NavTaskBreakPoint toapp_bstate: NavBorderState toapp_chgpileto: chargePileType @@ -80,10 +102,12 @@ class MctlNav(_message.Message): toapp_get_commondata_ack: NavGetCommDataAck toapp_gethash_ack: NavGetHashListAck toapp_lat_up: NavLatLonUp + toapp_map_name_msg: NavMapNameMsg toapp_opt_border_info: NavOptiBorderInfo toapp_opt_line_up: NavOptLineUp toapp_opt_obs_info: NavOptObsInfo toapp_pos_up: NavPosUp + toapp_svg_msg: SvgMessageAckT toapp_task_info: NavTaskInfo toapp_work_report_ack: WorkReportInfoAck toapp_work_report_update_ack: WorkReportUpdateAck @@ -112,15 +136,17 @@ class MctlNav(_message.Message): todev_reset_chg_pile: int todev_save_task: int todev_sustask: int + todev_svg_msg: SvgMessageAckT todev_task_info_ack: NavResFrame todev_taskctrl: NavTaskCtrl + todev_taskctrl_ack: NavTaskCtrlAck todev_unable_time_set: NavUnableTimeSet todev_work_report_cmd: WorkReportCmdData todev_work_report_update_cmd: WorkReportUpdateCmd todev_zigzag_ack: NavUploadZigZagResultAck vision_ctrl: vision_ctrl_msg zone_start_precent: zone_start_precent_t - def __init__(self, toapp_lat_up: _Optional[_Union[NavLatLonUp, _Mapping]] = ..., toapp_pos_up: _Optional[_Union[NavPosUp, _Mapping]] = ..., todev_chl_line_data: _Optional[_Union[NavCHlLineData, _Mapping]] = ..., toapp_task_info: _Optional[_Union[NavTaskInfo, _Mapping]] = ..., toapp_opt_line_up: _Optional[_Union[NavOptLineUp, _Mapping]] = ..., toapp_opt_border_info: _Optional[_Union[NavOptiBorderInfo, _Mapping]] = ..., toapp_opt_obs_info: _Optional[_Union[NavOptObsInfo, _Mapping]] = ..., todev_task_info_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., todev_opt_border_info_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., todev_opt_obs_info_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., todev_opt_line_up_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., toapp_chgpileto: _Optional[_Union[chargePileType, _Mapping]] = ..., todev_sustask: _Optional[int] = ..., todev_rechgcmd: _Optional[int] = ..., todev_edgecmd: _Optional[int] = ..., todev_draw_border: _Optional[int] = ..., todev_draw_border_end: _Optional[int] = ..., todev_draw_obs: _Optional[int] = ..., todev_draw_obs_end: _Optional[int] = ..., todev_chl_line: _Optional[int] = ..., todev_chl_line_end: _Optional[int] = ..., todev_save_task: _Optional[int] = ..., todev_cancel_suscmd: _Optional[int] = ..., todev_reset_chg_pile: _Optional[int] = ..., todev_cancel_draw_cmd: _Optional[int] = ..., todev_one_touch_leave_pile: _Optional[int] = ..., todev_mow_task: _Optional[_Union[NavStartJob, _Mapping]] = ..., toapp_bstate: _Optional[_Union[NavBorderState, _Mapping]] = ..., todev_lat_up_ack: _Optional[int] = ..., todev_gethash: _Optional[_Union[NavGetHashList, _Mapping]] = ..., toapp_gethash_ack: _Optional[_Union[NavGetHashListAck, _Mapping]] = ..., todev_get_commondata: _Optional[_Union[NavGetCommData, _Mapping]] = ..., toapp_get_commondata_ack: _Optional[_Union[NavGetCommDataAck, _Mapping]] = ..., bidire_reqconver_path: _Optional[_Union[NavReqCoverPath, _Mapping]] = ..., toapp_zigzag: _Optional[_Union[NavUploadZigZagResult, _Mapping]] = ..., todev_zigzag_ack: _Optional[_Union[NavUploadZigZagResultAck, _Mapping]] = ..., todev_taskctrl: _Optional[_Union[NavTaskCtrl, _Mapping]] = ..., bidire_taskid: _Optional[_Union[NavTaskIdRw, _Mapping]] = ..., toapp_bp: _Optional[_Union[NavTaskBreakPoint, _Mapping]] = ..., todev_planjob_set: _Optional[_Union[NavPlanJobSet, _Mapping]] = ..., todev_unable_time_set: _Optional[_Union[NavUnableTimeSet, _Mapping]] = ..., simulation_cmd: _Optional[_Union[SimulationCmdData, _Mapping]] = ..., todev_work_report_update_cmd: _Optional[_Union[WorkReportUpdateCmd, _Mapping]] = ..., toapp_work_report_update_ack: _Optional[_Union[WorkReportUpdateAck, _Mapping]] = ..., todev_work_report_cmd: _Optional[_Union[WorkReportCmdData, _Mapping]] = ..., toapp_work_report_ack: _Optional[_Union[WorkReportInfoAck, _Mapping]] = ..., toapp_work_report_upload: _Optional[_Union[WorkReportInfoAck, _Mapping]] = ..., app_request_cover_paths: _Optional[_Union[app_request_cover_paths_t, _Mapping]] = ..., cover_path_upload: _Optional[_Union[cover_path_upload_t, _Mapping]] = ..., zone_start_precent: _Optional[_Union[zone_start_precent_t, _Mapping]] = ..., vision_ctrl: _Optional[_Union[vision_ctrl_msg, _Mapping]] = ..., nav_sys_param_cmd: _Optional[_Union[nav_sys_param_msg, _Mapping]] = ..., plan_task_execute: _Optional[_Union[nav_plan_task_execute, _Mapping]] = ..., toapp_costmap: _Optional[_Union[costmap_t, _Mapping]] = ..., plan_task_name_id: _Optional[_Union[plan_task_name_id_t, _Mapping]] = ..., all_plan_task: _Optional[_Union[nav_get_all_plan_task, _Mapping]] = ...) -> None: ... + def __init__(self, toapp_lat_up: _Optional[_Union[NavLatLonUp, _Mapping]] = ..., toapp_pos_up: _Optional[_Union[NavPosUp, _Mapping]] = ..., todev_chl_line_data: _Optional[_Union[NavCHlLineData, _Mapping]] = ..., toapp_task_info: _Optional[_Union[NavTaskInfo, _Mapping]] = ..., toapp_opt_line_up: _Optional[_Union[NavOptLineUp, _Mapping]] = ..., toapp_opt_border_info: _Optional[_Union[NavOptiBorderInfo, _Mapping]] = ..., toapp_opt_obs_info: _Optional[_Union[NavOptObsInfo, _Mapping]] = ..., todev_task_info_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., todev_opt_border_info_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., todev_opt_obs_info_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., todev_opt_line_up_ack: _Optional[_Union[NavResFrame, _Mapping]] = ..., toapp_chgpileto: _Optional[_Union[chargePileType, _Mapping]] = ..., todev_sustask: _Optional[int] = ..., todev_rechgcmd: _Optional[int] = ..., todev_edgecmd: _Optional[int] = ..., todev_draw_border: _Optional[int] = ..., todev_draw_border_end: _Optional[int] = ..., todev_draw_obs: _Optional[int] = ..., todev_draw_obs_end: _Optional[int] = ..., todev_chl_line: _Optional[int] = ..., todev_chl_line_end: _Optional[int] = ..., todev_save_task: _Optional[int] = ..., todev_cancel_suscmd: _Optional[int] = ..., todev_reset_chg_pile: _Optional[int] = ..., todev_cancel_draw_cmd: _Optional[int] = ..., todev_one_touch_leave_pile: _Optional[int] = ..., todev_mow_task: _Optional[_Union[NavStartJob, _Mapping]] = ..., toapp_bstate: _Optional[_Union[NavBorderState, _Mapping]] = ..., todev_lat_up_ack: _Optional[int] = ..., todev_gethash: _Optional[_Union[NavGetHashList, _Mapping]] = ..., toapp_gethash_ack: _Optional[_Union[NavGetHashListAck, _Mapping]] = ..., todev_get_commondata: _Optional[_Union[NavGetCommData, _Mapping]] = ..., toapp_get_commondata_ack: _Optional[_Union[NavGetCommDataAck, _Mapping]] = ..., bidire_reqconver_path: _Optional[_Union[NavReqCoverPath, _Mapping]] = ..., toapp_zigzag: _Optional[_Union[NavUploadZigZagResult, _Mapping]] = ..., todev_zigzag_ack: _Optional[_Union[NavUploadZigZagResultAck, _Mapping]] = ..., todev_taskctrl: _Optional[_Union[NavTaskCtrl, _Mapping]] = ..., bidire_taskid: _Optional[_Union[NavTaskIdRw, _Mapping]] = ..., toapp_bp: _Optional[_Union[NavTaskBreakPoint, _Mapping]] = ..., todev_planjob_set: _Optional[_Union[NavPlanJobSet, _Mapping]] = ..., todev_unable_time_set: _Optional[_Union[NavUnableTimeSet, _Mapping]] = ..., simulation_cmd: _Optional[_Union[SimulationCmdData, _Mapping]] = ..., todev_work_report_update_cmd: _Optional[_Union[WorkReportUpdateCmd, _Mapping]] = ..., toapp_work_report_update_ack: _Optional[_Union[WorkReportUpdateAck, _Mapping]] = ..., todev_work_report_cmd: _Optional[_Union[WorkReportCmdData, _Mapping]] = ..., toapp_work_report_ack: _Optional[_Union[WorkReportInfoAck, _Mapping]] = ..., toapp_work_report_upload: _Optional[_Union[WorkReportInfoAck, _Mapping]] = ..., app_request_cover_paths: _Optional[_Union[app_request_cover_paths_t, _Mapping]] = ..., cover_path_upload: _Optional[_Union[cover_path_upload_t, _Mapping]] = ..., zone_start_precent: _Optional[_Union[zone_start_precent_t, _Mapping]] = ..., vision_ctrl: _Optional[_Union[vision_ctrl_msg, _Mapping]] = ..., nav_sys_param_cmd: _Optional[_Union[nav_sys_param_msg, _Mapping]] = ..., plan_task_execute: _Optional[_Union[nav_plan_task_execute, _Mapping]] = ..., toapp_costmap: _Optional[_Union[costmap_t, _Mapping]] = ..., plan_task_name_id: _Optional[_Union[plan_task_name_id_t, _Mapping]] = ..., all_plan_task: _Optional[_Union[nav_get_all_plan_task, _Mapping]] = ..., todev_taskctrl_ack: _Optional[_Union[NavTaskCtrlAck, _Mapping]] = ..., toapp_map_name_msg: _Optional[_Union[NavMapNameMsg, _Mapping]] = ..., todev_svg_msg: _Optional[_Union[SvgMessageAckT, _Mapping]] = ..., toapp_svg_msg: _Optional[_Union[SvgMessageAckT, _Mapping]] = ..., toapp_all_hash_name: _Optional[_Union[AppGetAllAreaHashName, _Mapping]] = ...) -> None: ... class NavBorderDataGet(_message.Message): __slots__ = ["borderLen", "currentFrame", "jobId"] @@ -272,6 +298,20 @@ class NavLatLonUp(_message.Message): lon: float def __init__(self, lat: _Optional[float] = ..., lon: _Optional[float] = ...) -> None: ... +class NavMapNameMsg(_message.Message): + __slots__ = ["device_id", "hash", "name", "result", "rw"] + DEVICE_ID_FIELD_NUMBER: _ClassVar[int] + HASH_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + RESULT_FIELD_NUMBER: _ClassVar[int] + RW_FIELD_NUMBER: _ClassVar[int] + device_id: str + hash: int + name: str + result: int + rw: int + def __init__(self, rw: _Optional[int] = ..., hash: _Optional[int] = ..., name: _Optional[str] = ..., result: _Optional[int] = ..., device_id: _Optional[str] = ...) -> None: ... + class NavObstiBorderDataGet(_message.Message): __slots__ = ["currentFrame", "obstacleIndex", "obstaclesLen"] CURRENTFRAME_FIELD_NUMBER: _ClassVar[int] @@ -546,6 +586,20 @@ class NavTaskCtrl(_message.Message): type: int def __init__(self, type: _Optional[int] = ..., action: _Optional[int] = ..., result: _Optional[int] = ..., reserved: _Optional[str] = ...) -> None: ... +class NavTaskCtrlAck(_message.Message): + __slots__ = ["action", "nav_state", "reserved", "result", "type"] + ACTION_FIELD_NUMBER: _ClassVar[int] + NAV_STATE_FIELD_NUMBER: _ClassVar[int] + RESERVED_FIELD_NUMBER: _ClassVar[int] + RESULT_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + action: int + nav_state: int + reserved: str + result: int + type: int + def __init__(self, type: _Optional[int] = ..., action: _Optional[int] = ..., result: _Optional[int] = ..., nav_state: _Optional[int] = ..., reserved: _Optional[str] = ...) -> None: ... + class NavTaskIdRw(_message.Message): __slots__ = ["pver", "reserved", "result", "subCmd", "taskId", "taskName"] PVER_FIELD_NUMBER: _ClassVar[int] @@ -674,6 +728,28 @@ class SimulationCmdData(_message.Message): subCmd: int def __init__(self, subCmd: _Optional[int] = ..., param_id: _Optional[int] = ..., param_value: _Optional[_Iterable[int]] = ...) -> None: ... +class SvgMessageAckT(_message.Message): + __slots__ = ["current_frame", "data_hash", "paternal_hash_a", "pver", "result", "sub_cmd", "svg_message", "total_frame", "type"] + CURRENT_FRAME_FIELD_NUMBER: _ClassVar[int] + DATA_HASH_FIELD_NUMBER: _ClassVar[int] + PATERNAL_HASH_A_FIELD_NUMBER: _ClassVar[int] + PVER_FIELD_NUMBER: _ClassVar[int] + RESULT_FIELD_NUMBER: _ClassVar[int] + SUB_CMD_FIELD_NUMBER: _ClassVar[int] + SVG_MESSAGE_FIELD_NUMBER: _ClassVar[int] + TOTAL_FRAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + current_frame: int + data_hash: int + paternal_hash_a: int + pver: int + result: int + sub_cmd: int + svg_message: svg_message_t + total_frame: int + type: int + def __init__(self, pver: _Optional[int] = ..., sub_cmd: _Optional[int] = ..., total_frame: _Optional[int] = ..., current_frame: _Optional[int] = ..., data_hash: _Optional[int] = ..., paternal_hash_a: _Optional[int] = ..., type: _Optional[int] = ..., result: _Optional[int] = ..., svg_message: _Optional[_Union[svg_message_t, _Mapping]] = ...) -> None: ... + class WorkReportCmdData(_message.Message): __slots__ = ["getInfoNum", "subCmd"] GETINFONUM_FIELD_NUMBER: _ClassVar[int] @@ -854,6 +930,36 @@ class plan_task_name_id_t(_message.Message): name: str def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ...) -> None: ... +class svg_message_t(_message.Message): + __slots__ = ["base_height_m", "base_height_pix", "base_width_m", "base_width_pix", "data_count", "hide_svg", "name_count", "rotate", "scale", "svg_file_data", "svg_file_name", "x_move", "y_move"] + BASE_HEIGHT_M_FIELD_NUMBER: _ClassVar[int] + BASE_HEIGHT_PIX_FIELD_NUMBER: _ClassVar[int] + BASE_WIDTH_M_FIELD_NUMBER: _ClassVar[int] + BASE_WIDTH_PIX_FIELD_NUMBER: _ClassVar[int] + DATA_COUNT_FIELD_NUMBER: _ClassVar[int] + HIDE_SVG_FIELD_NUMBER: _ClassVar[int] + NAME_COUNT_FIELD_NUMBER: _ClassVar[int] + ROTATE_FIELD_NUMBER: _ClassVar[int] + SCALE_FIELD_NUMBER: _ClassVar[int] + SVG_FILE_DATA_FIELD_NUMBER: _ClassVar[int] + SVG_FILE_NAME_FIELD_NUMBER: _ClassVar[int] + X_MOVE_FIELD_NUMBER: _ClassVar[int] + Y_MOVE_FIELD_NUMBER: _ClassVar[int] + base_height_m: float + base_height_pix: int + base_width_m: float + base_width_pix: int + data_count: int + hide_svg: bool + name_count: int + rotate: float + scale: float + svg_file_data: str + svg_file_name: str + x_move: float + y_move: float + def __init__(self, x_move: _Optional[float] = ..., y_move: _Optional[float] = ..., scale: _Optional[float] = ..., rotate: _Optional[float] = ..., base_width_m: _Optional[float] = ..., base_width_pix: _Optional[int] = ..., base_height_m: _Optional[float] = ..., base_height_pix: _Optional[int] = ..., data_count: _Optional[int] = ..., hide_svg: bool = ..., name_count: _Optional[int] = ..., svg_file_name: _Optional[str] = ..., svg_file_data: _Optional[str] = ...) -> None: ... + class vision_ctrl_msg(_message.Message): __slots__ = ["cmd", "type"] CMD_FIELD_NUMBER: _ClassVar[int] diff --git a/scripts.txt b/scripts.txt index 5d162ba..e26f03d 100644 --- a/scripts.txt +++ b/scripts.txt @@ -11,13 +11,13 @@ poetry install poetry run python tests/test2_instance.py # generate models from protobuf -poetry run protoc -I=. --python_out=. --python_betterproto_out=. ./pyluba/proto/*.proto +poetry run protoc -I=. --python_out=. --python_betterproto_out=. ./pymammotion/proto/*.proto # generate python proto from protobuf -poetry run protoc -I=. --python_out=. --python_out=. ./pyluba/proto/*.proto +poetry run protoc -I=. --python_out=. --python_out=. ./pymammotion/proto/*.proto # generate typehints from protobuf -poetry run protoc -I=. --python_out=. --pyi_out=. ./pyluba/proto/*.proto +poetry run protoc -I=. --python_out=. --pyi_out=. ./pymammotion/proto/*.proto poetry run ruff check --fix poetry run mypy {filename} diff --git a/tests/test2_instance.py b/tests/test2_instance.py index 88f2ab6..7bd3b1b 100644 --- a/tests/test2_instance.py +++ b/tests/test2_instance.py @@ -62,7 +62,8 @@ async def run(loop): await asyncio.sleep(2) await luba_ble.start_sync(0) - await asyncio.sleep(2) + # await luba_ble.start_map_sync() + #await asyncio.sleep(2) # print(luba_ble.luba_msg.sys.toapp_report_data.dev) # if has_field(luba_ble.luba_msg.sys.toapp_report_data.dev): # dev = luba_ble.luba_msg.sys.toapp_report_data.dev @@ -85,21 +86,25 @@ async def run(loop): # await luba_ble.command("return_to_dock") # await luba_ble.command("get_hash_response", total_frame=1, current_frame=1) counter = 30 - while (counter > 0): - luba_device = await scan_for_luba() - if luba_device is not None: - luba_ble.update_device(luba_device) - await luba_ble.start_sync(0) - await asyncio.sleep(10) - # await luba_ble._execute_disconnect_with_lock() - await asyncio.sleep(60) - - counter -= 1 + # while (counter > 0): + # luba_device = await scan_for_luba() + # if luba_device is not None: + # luba_ble.update_device(luba_device) + # await luba_ble.start_sync(0) + # await asyncio.sleep(10) + # # await luba_ble._execute_disconnect_with_lock() + # await asyncio.sleep(60) + # + # counter -= 1 # app_request_cover_paths_t use hashlist from ?? # asyncio.run(await ble_heartbeat(luba_ble)) + # await luba_ble.command("synchronize_hash_data", hash_num=25410289481558284) + await asyncio.sleep(20) print("end run?") + print(luba_ble.luba_msg.map) + if __name__ == '__main__': From a3e6718ac780f79903d4f060c4dfe7f77b60d7be Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 13 Aug 2024 06:20:50 +1200 Subject: [PATCH 03/35] fix nav commands for none luba 1s --- .../mammotion/commands/messages/navigation.py | 13 ++++++++++--- .../utility/constant/device_constant.py | 19 +++++++++++++++++++ pymammotion/utility/device_type.py | 3 +++ pyproject.toml | 4 ++-- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/pymammotion/mammotion/commands/messages/navigation.py b/pymammotion/mammotion/commands/messages/navigation.py index 642193d..e54eb0b 100644 --- a/pymammotion/mammotion/commands/messages/navigation.py +++ b/pymammotion/mammotion/commands/messages/navigation.py @@ -25,17 +25,24 @@ WorkReportCmdData, WorkReportUpdateCmd, NavMapNameMsg, ) +from pymammotion.utility.device_type import DeviceType logger = logging.getLogger(__name__) class MessageNavigation(AbstractMessage, ABC): - @staticmethod - def send_order_msg_nav(build) -> bytes: + + def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice: + """Changes the rcver name if it's not a luba1.""" + if DeviceType.is_luba1(self.get_device_name()) and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV: + return MsgDevice.DEV_NAVIGATION + return msg_device + + def send_order_msg_nav(self, build) -> bytes: luba_msg = LubaMsg( msgtype=MsgCmdType.MSG_CMD_TYPE_NAV, sender=MsgDevice.DEV_MOBILEAPP, - rcver=MsgDevice.DEV_MAINCTL, + rcver=self.get_msg_device(MsgCmdType.MSG_CMD_TYPE_NAV, MsgDevice.DEV_MAINCTL), msgattr=MsgAttr.MSG_ATTR_REQ, seqs=1, version=1, diff --git a/pymammotion/utility/constant/device_constant.py b/pymammotion/utility/constant/device_constant.py index 5dea52c..8530398 100644 --- a/pymammotion/utility/constant/device_constant.py +++ b/pymammotion/utility/constant/device_constant.py @@ -1,3 +1,6 @@ +from enum import IntEnum + + class bleOrderCmd: allpowerfullRW = 67 alongBorder = 9 @@ -250,3 +253,19 @@ def device_mode(value) -> str: 38: "MODE_BOUNDARY_JUMP", } return modes.get(value, "Invalid mode") + + +class PosType(IntEnum): + """Position of the robot.""" + AREA_BORDER_ON = 7 + AREA_INSIDE = 1 + AREA_OUT = 0 + CHANNAL_AREA_OVERLAP = 9 + CHANNEL_ON = 3 + CHARGE_ON = 5 + DUMPING_AREA_INSIDE = 8 + DUMPING_OUTSIDE = 10 + OBS_ON = 2 + TURN_AREA_INSIDE = 4 + VIRTUAL_INSIDE = 6 + diff --git a/pymammotion/utility/device_type.py b/pymammotion/utility/device_type.py index 676bbf9..b401467 100644 --- a/pymammotion/utility/device_type.py +++ b/pymammotion/utility/device_type.py @@ -7,6 +7,9 @@ class DeviceType(Enum): LUBA = (1, "Luba", "Luba 1") LUBA_2 = (2, "Luba-VS", "Luba 2") LUBA_YUKA = (3, "Yuka-", "Yuka") + YUKA_MINI = (4, "Yuka-MN", "Yuka Mini") + YUKA_MINI2 = (5, "Yuka-YM", "Yuka Mini 2") + LUBA_VP = (6, "Luba-VP", "Luba VP") def __init__(self, value: int, name: str, model: str): self._value = value diff --git a/pyproject.toml b/pyproject.toml index 9deddd8..1edf2e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.0.54" +version = "0.0.55" license = "GNU-3.0" description = "" readme = "README.md" @@ -47,7 +47,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.0.54" +current_version = "0.0.55" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 2ec41cf900ebe7346082df1e93c10419d476c1f0 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 13 Aug 2024 06:28:51 +1200 Subject: [PATCH 04/35] ruff format and so on --- .../mammotion/commands/messages/navigation.py | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/pymammotion/mammotion/commands/messages/navigation.py b/pymammotion/mammotion/commands/messages/navigation.py index e54eb0b..6534540 100644 --- a/pymammotion/mammotion/commands/messages/navigation.py +++ b/pymammotion/mammotion/commands/messages/navigation.py @@ -14,6 +14,7 @@ MctlNav, NavGetCommData, NavGetHashList, + NavMapNameMsg, NavPlanJobSet, NavPlanTaskExecute, NavReqCoverPath, @@ -23,7 +24,7 @@ NavUploadZigZagResultAck, SimulationCmdData, WorkReportCmdData, - WorkReportUpdateCmd, NavMapNameMsg, + WorkReportUpdateCmd, ) from pymammotion.utility.device_type import DeviceType @@ -31,7 +32,6 @@ class MessageNavigation(AbstractMessage, ABC): - def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice: """Changes the rcver name if it's not a luba1.""" if DeviceType.is_luba1(self.get_device_name()) and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV: @@ -265,10 +265,10 @@ def get_area_name_list(self, device_id: str) -> bytes: # Build the NavMapNameMsg with the specified parameters mctl_nav = MctlNav( toapp_map_name_msg=NavMapNameMsg( - hash=0, - result=0, - device_id=device_id, # iotId or ??? - rw=0 + hash=0, + result=0, + device_id=device_id, # iotId or ??? + rw=0, ) ) @@ -279,13 +279,7 @@ def get_area_name_list(self, device_id: str) -> bytes: def set_area_name(self, device_id, hash_id: int, name: str) -> bytes: # Build the NavMapNameMsg with the specified parameters mctl_nav = MctlNav( - toapp_map_name_msg=NavMapNameMsg( - hash=hash_id, - name=name, - result=0, - device_id=device_id, - rw=1 - ) + toapp_map_name_msg=NavMapNameMsg(hash=hash_id, name=name, result=0, device_id=device_id, rw=1) ) # Send the message with the specified ID and acknowledge flag @@ -392,7 +386,7 @@ def generate_route_information(self, generate_route_information: GenerateRouteIn channel_mode=generate_route_information.channel_mode, toward=generate_route_information.toward, toward_included_angle=generate_route_information.toward_included_angle, # luba 2 yuka only - toward_mode=generate_route_information.toward_mode, # luba 2 yuka only + toward_mode=generate_route_information.toward_mode, # luba 2 yuka only reserved=generate_route_information.path_order, ) logger.debug(f"{self.get_device_name()}Generate route====={build}") From 01941801fbaa73abd6f6f0a3e3349bc5e1ce6772 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 13 Aug 2024 07:45:00 +1200 Subject: [PATCH 05/35] add product key to commands for checking device --- pymammotion/mammotion/commands/abstract_message.py | 3 +++ pymammotion/mammotion/commands/mammotion_command.py | 11 +++++++++++ pymammotion/mammotion/commands/messages/navigation.py | 2 +- pymammotion/mammotion/devices/mammotion.py | 4 ++++ pyproject.toml | 4 ++-- 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/pymammotion/mammotion/commands/abstract_message.py b/pymammotion/mammotion/commands/abstract_message.py index 163adf1..91916e0 100644 --- a/pymammotion/mammotion/commands/abstract_message.py +++ b/pymammotion/mammotion/commands/abstract_message.py @@ -5,3 +5,6 @@ class AbstractMessage: @abstractmethod def get_device_name(self) -> str: """Get device name.""" + + def get_device_product_key(self) -> str: + """Get device name.""" diff --git a/pymammotion/mammotion/commands/mammotion_command.py b/pymammotion/mammotion/commands/mammotion_command.py index 534746e..3fb2e4c 100644 --- a/pymammotion/mammotion/commands/mammotion_command.py +++ b/pymammotion/mammotion/commands/mammotion_command.py @@ -4,18 +4,29 @@ from pymammotion.mammotion.commands.messages.system import MessageSystem from pymammotion.mammotion.commands.messages.video import MessageVideo from pymammotion.proto import dev_net_pb2, luba_msg_pb2 +from pymammotion.utility.device_type import DeviceType class MammotionCommand(MessageSystem, MessageNavigation, MessageNetwork, MessageOta, MessageVideo): """MQTT commands for Luba.""" + + def __init__(self, device_name: str) -> None: self._device_name = device_name + self._product_key = "" def get_device_name(self) -> str: """Get device name.""" return self._device_name + def get_device_product_key(self) -> str: + return self._product_key + + def set_device_product_key(self, product_key: str) -> None: + self._product_key = product_key + + """BLE commands for Luba.""" def send_todev_ble_sync(self, sync_type: int) -> bytes: diff --git a/pymammotion/mammotion/commands/messages/navigation.py b/pymammotion/mammotion/commands/messages/navigation.py index 6534540..e63f3ba 100644 --- a/pymammotion/mammotion/commands/messages/navigation.py +++ b/pymammotion/mammotion/commands/messages/navigation.py @@ -34,7 +34,7 @@ class MessageNavigation(AbstractMessage, ABC): def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice: """Changes the rcver name if it's not a luba1.""" - if DeviceType.is_luba1(self.get_device_name()) and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV: + if not DeviceType.is_luba1(self.get_device_name(), self.get_device_product_key()) and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV: return MsgDevice.DEV_NAVIGATION return msg_device diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 87c8f71..ec49f56 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -545,6 +545,10 @@ async def _notification_handler(self, _sender: BleakGATTCharacteristic, data: by new_msg = LubaMsg().parse(data) if betterproto.serialized_on_wire(new_msg.net): if new_msg.net.todev_ble_sync != 0 or has_field(new_msg.net.toapp_wifi_iot_status): + + if has_field(new_msg.net.toapp_wifi_iot_status) and self._commands.get_device_product_key() == "": + self._commands.set_device_product_key(new_msg.net.toapp_wifi_iot_status.productkey) + return # may or may not be correct, some work could be done here to correctly match responses diff --git a/pyproject.toml b/pyproject.toml index 1edf2e2..57f281d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.0.55" +version = "0.0.56" license = "GNU-3.0" description = "" readme = "README.md" @@ -47,7 +47,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.0.55" +current_version = "0.0.56" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 7e13f96a29e5e7c46d4400bebed387a2fd049ce6 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 13 Aug 2024 09:54:35 +1200 Subject: [PATCH 06/35] fix blade on off --- pymammotion/mammotion/commands/messages/system.py | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pymammotion/mammotion/commands/messages/system.py b/pymammotion/mammotion/commands/messages/system.py index e6e3554..0cb8fae 100644 --- a/pymammotion/mammotion/commands/messages/system.py +++ b/pymammotion/mammotion/commands/messages/system.py @@ -28,7 +28,7 @@ def reset_system(self): print("Send command - send factory reset") return self.send_order_msg_sys(build) - async def set_blade_control(self, on_off: int): + def set_blade_control(self, on_off: int): mctlsys = mctrl_sys_pb2.MctlSys() sysKnifeControl = mctrl_sys_pb2.SysKnifeControl() sysKnifeControl.knife_status = on_off diff --git a/pyproject.toml b/pyproject.toml index 57f281d..9f8485e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.0.56" +version = "0.1.1" license = "GNU-3.0" description = "" readme = "README.md" @@ -47,7 +47,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.0.56" +current_version = "0.1.1" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 6f0b5789f3e85fdb3efa186d640662795405ff54 Mon Sep 17 00:00:00 2001 From: Andrea Cagnola Date: Tue, 13 Aug 2024 15:21:22 +0200 Subject: [PATCH 07/35] Try to implement MQTT response - Need helo on Lock --- pymammotion/data/mqtt/event.py | 39 +++++- pymammotion/mammotion/devices/mammotion.py | 156 ++++++++++++++++----- tests/login_and_mqtt_test.py | 12 +- 3 files changed, 169 insertions(+), 38 deletions(-) diff --git a/pymammotion/data/mqtt/event.py b/pymammotion/data/mqtt/event.py index 015aa61..949c3c7 100644 --- a/pymammotion/data/mqtt/event.py +++ b/pymammotion/data/mqtt/event.py @@ -1,6 +1,7 @@ from base64 import b64decode +import base64 from dataclasses import dataclass -from typing import Any, Literal, Union +from typing import Any, Literal, Union, Optional from google.protobuf import json_format from mashumaro.mixins.orjson import DataClassORJSONMixin @@ -33,7 +34,6 @@ def validate(cls, v: str): data.ParseFromString(binary) return json_format.MessageToDict(data) - @dataclass class DeviceProtobufMsgEventValue(DataClassORJSONMixin): content: Base64EncodedProtobuf @@ -67,6 +67,16 @@ class GeneralParams(DataClassORJSONMixin): tenantInstanceId: str value: Any + # Campi opzionali + checkFailedData: Optional[dict] = None + _tenantId: Optional[str] = None + generateTime: Optional[int] = None + JMSXDeliveryCount: Optional[int] = None + qos: Optional[int] = None + requestId: Optional[str] = None + _categoryKey: Optional[str] = None + deviceType: Optional[str] = None + _traceId: Optional[str] = None @dataclass class DeviceProtobufMsgEventParams(GeneralParams): @@ -88,3 +98,28 @@ class ThingEventMessage(DataClassORJSONMixin): id: str params: Union[DeviceProtobufMsgEventParams, DeviceWarningEventParams] version: Literal["1.0"] + + @classmethod + def from_dicts(cls, payload: dict) -> 'ThingEventMessage': + """Deserializza il payload JSON in un'istanza di ThingEventMessage.""" + method = payload.get('method') + event_id = payload.get('id') + params_dict = payload.get('params', {}) + version = payload.get('version') + + # Determina quale classe usare per i parametri + identifier = params_dict.get('identifier') + if identifier == 'device_protobuf_msg_event': + params_obj = DeviceProtobufMsgEventParams(**params_dict) + elif identifier == 'device_warning_event': + params_obj = DeviceWarningEventParams(**params_dict) + else: + raise ValueError(f"Unknown identifier: {identifier}") + + # Crea e restituisce l'istanza di ThingEventMessage + return cls( + method=method, + id=event_id, + params=params_obj, + version=version + ) \ No newline at end of file diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index ec49f56..e2c6ab9 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -10,6 +10,7 @@ from enum import Enum from typing import Any, cast from uuid import UUID +import base64 import betterproto from bleak import BleakClient @@ -105,6 +106,11 @@ async def _handle_retry(fut: asyncio.Future[None], func, command: bytes) -> None if not fut.done(): await func(command) +async def _handle_retry_cloud(fut: asyncio.Future[None], func, iotId: str, command: bytes) -> None: + """Handle a retry.""" + if not fut.done(): + func(iotId, command) + class ConnectionPreference(Enum): """Enum for connection preference.""" @@ -320,6 +326,8 @@ async def start_map_sync(self): await self._send_command_with_args("get_area_name_list", device_id=self.luba_msg.device.net.toapp_wifi_iot_status.devicename) + + # sub_cmd 3 is job hashes?? # sub_cmd 4 is dump location (yuka) # jobs list @@ -713,12 +721,12 @@ class MammotionBaseCloudDevice(MammotionBaseDevice): """Base class for Mammotion Cloud devices.""" def __init__( - self, - mqtt_client: MammotionMQTT, - iot_id: str, - device_name: str, - nick_name: str, - **kwargs: Any, + self, + mqtt_client: MammotionMQTT, + iot_id: str, + device_name: str, + nick_name: str, + **kwargs: Any, ) -> None: """Initialize MammotionBaseCloudDevice.""" super().__init__() @@ -727,44 +735,105 @@ def __init__( self.nick_name = nick_name self._command_futures = {} self._commands: MammotionCommand = MammotionCommand(device_name) - self.loop = asyncio.get_event_loop() + self.currentID = "" + self._operation_lock = asyncio.Lock() def _on_mqtt_message(self, topic: str, payload: str) -> None: """Handle incoming MQTT messages.""" _LOGGER.debug("MQTT message received on topic %s: %s", topic, payload) - payload = json.loads(payload) - message_id = self._extract_message_id(payload) - if message_id and message_id in self._command_futures: - self._parse_mqtt_response(topic=topic, payload=payload) - future = self._command_futures.pop(message_id) - if not future.done(): - future.set_result(payload) + + json_str = json.dumps(payload) + payload = json.loads(json_str) + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self._handle_mqtt_message(topic, payload)) + loop.close() async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: """Send command to device via MQTT and read response.""" - future = self.loop.create_future() - command_bytes = getattr(self._commands, key)() - message_id = self._mqtt_client.get_cloud_client().send_cloud_command(self.iot_id, command_bytes) - if message_id != "": - self._command_futures[message_id] = future + if self._operation_lock.locked(): + _LOGGER.debug( + "%s: Operation already in progress, waiting for it to complete;", + self.nick_name + ) + + async with self._operation_lock: try: - return await asyncio.wait_for(future, timeout=TIMEOUT_CLOUD_RESPONSE) - except asyncio.TimeoutError: - _LOGGER.error("Command '%s' timed out", key) - return None + command_bytes = getattr(self._commands, key)() + return await self._send_command_locked(key, command_bytes) + except Exception as ex: + _LOGGER.exception( + "%s: error in sending command - %s", + self.nick_name, + ex + ) + raise + + async def _send_command_locked(self, key: str, command: bytes) -> bytes: + """Send command to device and read response.""" + try: + return await self._execute_command_locked(key, command) + except Exception as ex: + # Disconnect so we can reset state and try again + await asyncio.sleep(DBUS_ERROR_BACKOFF_TIME) + _LOGGER.debug( + "%s: error in _send_command_locked: %s", + self.nick_name, + ex, + ) + raise + + async def _execute_command_locked(self, key: str, command: bytes) -> bytes: + """Execute command and read response.""" + assert self._mqtt_client is not None + self._notify_future = self.loop.create_future() + self._key = key + _LOGGER.debug("%s: Sending command: %s", self.nick_name, key) + self._mqtt_client.get_cloud_client().send_cloud_command(self.iot_id, command) + + retry_handle = self.loop.call_at( + self.loop.time() + 20, + lambda: asyncio.ensure_future( + _handle_retry_cloud(self._notify_future, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) + ), + ) + timeout = 20 + timeout_handle = self.loop.call_at(self.loop.time() + timeout, _handle_timeout, self._notify_future) + timeout_expired = False + try: + notify_msg = await self._notify_future + except asyncio.TimeoutError: + timeout_expired = True + raise + finally: + if not timeout_expired: + timeout_handle.cancel() + retry_handle.cancel() + self._notify_future = None + + _LOGGER.debug("%s: Message received", self.nick_name) + + return notify_msg async def _send_command_with_args(self, key: str, **kwargs: any) -> bytes | None: """Send command with arguments to device via MQTT and read response.""" - future = self.loop.create_future() - command_bytes = getattr(self._commands, key)(**kwargs) - message_id = self._mqtt_client.get_cloud_client().send_cloud_command(self.iot_id, command_bytes) - if message_id != "": - self._command_futures[message_id] = future + if self._operation_lock.locked(): + _LOGGER.debug( + "%s: Operation already in progress, waiting for it to complete;", + self.nick_name + ) + async with self._operation_lock: try: - return await asyncio.wait_for(future, timeout=TIMEOUT_CLOUD_RESPONSE) - except asyncio.TimeoutError: - _LOGGER.error("Command '%s' timed out", key) - return None + command_bytes = getattr(self._commands, key)(**kwargs) + return await self._send_command_locked(key, command_bytes) + except Exception as ex: + _LOGGER.exception( + "%s: error in sending command - %s", + self.nick_name, + ex + ) + raise def _extract_message_id(self, payload: dict) -> str: """Extract the message ID from the payload.""" @@ -779,13 +848,30 @@ def _extract_encoded_message(self, payload: dict) -> str: _LOGGER.error("Error extracting encoded message. Payload: %s", payload) return "" - def _parse_mqtt_response(self, topic: str, payload: dict) -> None: + async def _parse_mqtt_response(self, topic: str, payload: dict) -> None: """Parse the MQTT response.""" if topic.endswith("/app/down/thing/events"): - event = ThingEventMessage(**payload) + print (f"Thing event received") + event = ThingEventMessage.from_dicts(payload) params = event.params if params.identifier == "device_protobuf_msg_event": - self._update_raw_data(cast(bytes, params.value.content)) + print (f"Protobuf reveice") + binary_data = base64.b64decode(params.value.get('content', '')) + self._update_raw_data(cast(bytes, binary_data)) + new_msg = LubaMsg().parse(cast(bytes, binary_data)) + if self._notify_future and not self._notify_future.done(): + self._notify_future.set_result(new_msg) + await self._state_manager.notification(new_msg) + + async def _handle_mqtt_message(self, topic: str, payload: dict) -> None: + """Async handler for incoming MQTT messages.""" + await self._parse_mqtt_response(topic=topic, payload=payload) + # message_id = self._extract_message_id(payload) + # print (f"Received message id: {self.currentID}") + # message_id = self.currentID + # if message_id and message_id in self._command_futures: + # print (f"Start parsing response") + async def _disconnect(self): """Disconnect the MQTT client.""" diff --git a/tests/login_and_mqtt_test.py b/tests/login_and_mqtt_test.py index b324c5b..f2a61d4 100644 --- a/tests/login_and_mqtt_test.py +++ b/tests/login_and_mqtt_test.py @@ -34,6 +34,16 @@ async def run(): cloud_client.list_binding_by_account() return cloud_client + +async def sync_status_and_map(cloud_device: MammotionBaseCloudDevice): + await asyncio.sleep(1) + await cloud_device.start_sync(0) + await asyncio.sleep(2) + await cloud_device.start_map_sync() + + while(True): + print(cloud_device.luba_msg) + await asyncio.sleep(5) if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) @@ -69,6 +79,6 @@ async def run(): device._on_mqtt_message(topic, payload) for device in _devices_list if device.iot_id == iot_id ] - logger.debug(_devices_list) + sync = event_loop.run_until_complete(sync_status_and_map(_devices_list[0])) event_loop.run_forever() \ No newline at end of file From 0de6ccdf32b58b5ecf6384b892057220e9134e39 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Wed, 14 Aug 2024 07:08:39 +1200 Subject: [PATCH 08/35] update protobufs with basestation --- pymammotion/proto/basestation.proto | 43 ++++++ pymammotion/proto/basestation.py | 59 ++++++++ pymammotion/proto/basestation_pb2.py | 33 +++++ pymammotion/proto/basestation_pb2.pyi | 75 ++++++++++ pymammotion/proto/dev_net.proto | 8 +- pymammotion/proto/dev_net.py | 4 +- pymammotion/proto/dev_net_pb2.py | 2 +- pymammotion/proto/dev_net_pb2.pyi | 20 +-- pymammotion/proto/luba_msg.proto | 2 + pymammotion/proto/luba_msg.py | 3 +- pymammotion/proto/luba_msg_pb2.py | 23 +-- pymammotion/proto/luba_msg_pb2.pyi | 7 +- pymammotion/proto/mctrl_nav.py | 4 +- pymammotion/proto/mctrl_sys.proto | 4 +- pymammotion/proto/mctrl_sys.py | 36 +++-- pymammotion/proto/mctrl_sys_pb2.py | 196 +++++++++++++------------- pymammotion/proto/mctrl_sys_pb2.pyi | 12 +- 17 files changed, 373 insertions(+), 158 deletions(-) create mode 100644 pymammotion/proto/basestation.proto create mode 100644 pymammotion/proto/basestation.py create mode 100644 pymammotion/proto/basestation_pb2.py create mode 100644 pymammotion/proto/basestation_pb2.pyi diff --git a/pymammotion/proto/basestation.proto b/pymammotion/proto/basestation.proto new file mode 100644 index 0000000..15a222c --- /dev/null +++ b/pymammotion/proto/basestation.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +message request_basestation_info_t { + uint32 request_type = 1; +} + +message response_basestation_info_t { + uint64 system_status = 1; + uint32 app_connect_type = 2; + int32 ble_rssi = 3; + int32 wifi_rssi = 4; + uint64 sats_num = 5; + int64 lora_scan = 6; + int64 lora_channel = 7; + int64 lora_locid = 8; + int64 lora_netid = 9; + uint64 rtk_status = 10; + int32 lowpower_status = 11; + int32 mqtt_rtk_status = 12; + int32 rtk_channel = 13; + int32 rtk_switch = 14; +} + +message app_to_base_mqtt_rtk_t { + int32 rtk_switch = 1; + string rtk_url = 2; + int32 rtk_port = 3; + string rtk_username = 4; + string rtk_password = 5; +} + +message base_to_app_mqtt_rtk_t { + int32 rtk_switch_status = 1; +} + +message BaseStation { + oneof BaseStationSubType { + request_basestation_info_t to_dev = 1; + response_basestation_info_t to_app = 2; + app_to_base_mqtt_rtk_t app_to_base_mqtt_rtk_msg = 3; + base_to_app_mqtt_rtk_t base_to_app_mqtt_rtk_msg = 4; + } +} diff --git a/pymammotion/proto/basestation.py b/pymammotion/proto/basestation.py new file mode 100644 index 0000000..a442a40 --- /dev/null +++ b/pymammotion/proto/basestation.py @@ -0,0 +1,59 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: pymammotion/proto/basestation.proto +# plugin: python-betterproto +from dataclasses import dataclass + +import betterproto + + +@dataclass +class RequestBasestationInfoT(betterproto.Message): + request_type: int = betterproto.uint32_field(1) + + +@dataclass +class ResponseBasestationInfoT(betterproto.Message): + system_status: int = betterproto.uint64_field(1) + app_connect_type: int = betterproto.uint32_field(2) + ble_rssi: int = betterproto.int32_field(3) + wifi_rssi: int = betterproto.int32_field(4) + sats_num: int = betterproto.uint64_field(5) + lora_scan: int = betterproto.int64_field(6) + lora_channel: int = betterproto.int64_field(7) + lora_locid: int = betterproto.int64_field(8) + lora_netid: int = betterproto.int64_field(9) + rtk_status: int = betterproto.uint64_field(10) + lowpower_status: int = betterproto.int32_field(11) + mqtt_rtk_status: int = betterproto.int32_field(12) + rtk_channel: int = betterproto.int32_field(13) + rtk_switch: int = betterproto.int32_field(14) + + +@dataclass +class AppToBaseMqttRtkT(betterproto.Message): + rtk_switch: int = betterproto.int32_field(1) + rtk_url: str = betterproto.string_field(2) + rtk_port: int = betterproto.int32_field(3) + rtk_username: str = betterproto.string_field(4) + rtk_password: str = betterproto.string_field(5) + + +@dataclass +class BaseToAppMqttRtkT(betterproto.Message): + rtk_switch_status: int = betterproto.int32_field(1) + + +@dataclass +class BaseStation(betterproto.Message): + to_dev: "RequestBasestationInfoT" = betterproto.message_field( + 1, group="BaseStationSubType" + ) + to_app: "ResponseBasestationInfoT" = betterproto.message_field( + 2, group="BaseStationSubType" + ) + app_to_base_mqtt_rtk_msg: "AppToBaseMqttRtkT" = betterproto.message_field( + 3, group="BaseStationSubType" + ) + base_to_app_mqtt_rtk_msg: "BaseToAppMqttRtkT" = betterproto.message_field( + 4, group="BaseStationSubType" + ) diff --git a/pymammotion/proto/basestation_pb2.py b/pymammotion/proto/basestation_pb2.py new file mode 100644 index 0000000..a4a0131 --- /dev/null +++ b/pymammotion/proto/basestation_pb2.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: pymammotion/proto/basestation.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#pymammotion/proto/basestation.proto\"2\n\x1arequest_basestation_info_t\x12\x14\n\x0crequest_type\x18\x01 \x01(\r\"\xc5\x02\n\x1bresponse_basestation_info_t\x12\x15\n\rsystem_status\x18\x01 \x01(\x04\x12\x18\n\x10\x61pp_connect_type\x18\x02 \x01(\r\x12\x10\n\x08\x62le_rssi\x18\x03 \x01(\x05\x12\x11\n\twifi_rssi\x18\x04 \x01(\x05\x12\x10\n\x08sats_num\x18\x05 \x01(\x04\x12\x11\n\tlora_scan\x18\x06 \x01(\x03\x12\x14\n\x0clora_channel\x18\x07 \x01(\x03\x12\x12\n\nlora_locid\x18\x08 \x01(\x03\x12\x12\n\nlora_netid\x18\t \x01(\x03\x12\x12\n\nrtk_status\x18\n \x01(\x04\x12\x17\n\x0flowpower_status\x18\x0b \x01(\x05\x12\x17\n\x0fmqtt_rtk_status\x18\x0c \x01(\x05\x12\x13\n\x0brtk_channel\x18\r \x01(\x05\x12\x12\n\nrtk_switch\x18\x0e \x01(\x05\"{\n\x16\x61pp_to_base_mqtt_rtk_t\x12\x12\n\nrtk_switch\x18\x01 \x01(\x05\x12\x0f\n\x07rtk_url\x18\x02 \x01(\t\x12\x10\n\x08rtk_port\x18\x03 \x01(\x05\x12\x14\n\x0crtk_username\x18\x04 \x01(\t\x12\x14\n\x0crtk_password\x18\x05 \x01(\t\"3\n\x16\x62\x61se_to_app_mqtt_rtk_t\x12\x19\n\x11rtk_switch_status\x18\x01 \x01(\x05\"\xfc\x01\n\x0b\x42\x61seStation\x12-\n\x06to_dev\x18\x01 \x01(\x0b\x32\x1b.request_basestation_info_tH\x00\x12.\n\x06to_app\x18\x02 \x01(\x0b\x32\x1c.response_basestation_info_tH\x00\x12;\n\x18\x61pp_to_base_mqtt_rtk_msg\x18\x03 \x01(\x0b\x32\x17.app_to_base_mqtt_rtk_tH\x00\x12;\n\x18\x62\x61se_to_app_mqtt_rtk_msg\x18\x04 \x01(\x0b\x32\x17.base_to_app_mqtt_rtk_tH\x00\x42\x14\n\x12\x42\x61seStationSubTypeb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pymammotion.proto.basestation_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _REQUEST_BASESTATION_INFO_T._serialized_start=39 + _REQUEST_BASESTATION_INFO_T._serialized_end=89 + _RESPONSE_BASESTATION_INFO_T._serialized_start=92 + _RESPONSE_BASESTATION_INFO_T._serialized_end=417 + _APP_TO_BASE_MQTT_RTK_T._serialized_start=419 + _APP_TO_BASE_MQTT_RTK_T._serialized_end=542 + _BASE_TO_APP_MQTT_RTK_T._serialized_start=544 + _BASE_TO_APP_MQTT_RTK_T._serialized_end=595 + _BASESTATION._serialized_start=598 + _BASESTATION._serialized_end=850 +# @@protoc_insertion_point(module_scope) diff --git a/pymammotion/proto/basestation_pb2.pyi b/pymammotion/proto/basestation_pb2.pyi new file mode 100644 index 0000000..4b17441 --- /dev/null +++ b/pymammotion/proto/basestation_pb2.pyi @@ -0,0 +1,75 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class BaseStation(_message.Message): + __slots__ = ["app_to_base_mqtt_rtk_msg", "base_to_app_mqtt_rtk_msg", "to_app", "to_dev"] + APP_TO_BASE_MQTT_RTK_MSG_FIELD_NUMBER: _ClassVar[int] + BASE_TO_APP_MQTT_RTK_MSG_FIELD_NUMBER: _ClassVar[int] + TO_APP_FIELD_NUMBER: _ClassVar[int] + TO_DEV_FIELD_NUMBER: _ClassVar[int] + app_to_base_mqtt_rtk_msg: app_to_base_mqtt_rtk_t + base_to_app_mqtt_rtk_msg: base_to_app_mqtt_rtk_t + to_app: response_basestation_info_t + to_dev: request_basestation_info_t + def __init__(self, to_dev: _Optional[_Union[request_basestation_info_t, _Mapping]] = ..., to_app: _Optional[_Union[response_basestation_info_t, _Mapping]] = ..., app_to_base_mqtt_rtk_msg: _Optional[_Union[app_to_base_mqtt_rtk_t, _Mapping]] = ..., base_to_app_mqtt_rtk_msg: _Optional[_Union[base_to_app_mqtt_rtk_t, _Mapping]] = ...) -> None: ... + +class app_to_base_mqtt_rtk_t(_message.Message): + __slots__ = ["rtk_password", "rtk_port", "rtk_switch", "rtk_url", "rtk_username"] + RTK_PASSWORD_FIELD_NUMBER: _ClassVar[int] + RTK_PORT_FIELD_NUMBER: _ClassVar[int] + RTK_SWITCH_FIELD_NUMBER: _ClassVar[int] + RTK_URL_FIELD_NUMBER: _ClassVar[int] + RTK_USERNAME_FIELD_NUMBER: _ClassVar[int] + rtk_password: str + rtk_port: int + rtk_switch: int + rtk_url: str + rtk_username: str + def __init__(self, rtk_switch: _Optional[int] = ..., rtk_url: _Optional[str] = ..., rtk_port: _Optional[int] = ..., rtk_username: _Optional[str] = ..., rtk_password: _Optional[str] = ...) -> None: ... + +class base_to_app_mqtt_rtk_t(_message.Message): + __slots__ = ["rtk_switch_status"] + RTK_SWITCH_STATUS_FIELD_NUMBER: _ClassVar[int] + rtk_switch_status: int + def __init__(self, rtk_switch_status: _Optional[int] = ...) -> None: ... + +class request_basestation_info_t(_message.Message): + __slots__ = ["request_type"] + REQUEST_TYPE_FIELD_NUMBER: _ClassVar[int] + request_type: int + def __init__(self, request_type: _Optional[int] = ...) -> None: ... + +class response_basestation_info_t(_message.Message): + __slots__ = ["app_connect_type", "ble_rssi", "lora_channel", "lora_locid", "lora_netid", "lora_scan", "lowpower_status", "mqtt_rtk_status", "rtk_channel", "rtk_status", "rtk_switch", "sats_num", "system_status", "wifi_rssi"] + APP_CONNECT_TYPE_FIELD_NUMBER: _ClassVar[int] + BLE_RSSI_FIELD_NUMBER: _ClassVar[int] + LORA_CHANNEL_FIELD_NUMBER: _ClassVar[int] + LORA_LOCID_FIELD_NUMBER: _ClassVar[int] + LORA_NETID_FIELD_NUMBER: _ClassVar[int] + LORA_SCAN_FIELD_NUMBER: _ClassVar[int] + LOWPOWER_STATUS_FIELD_NUMBER: _ClassVar[int] + MQTT_RTK_STATUS_FIELD_NUMBER: _ClassVar[int] + RTK_CHANNEL_FIELD_NUMBER: _ClassVar[int] + RTK_STATUS_FIELD_NUMBER: _ClassVar[int] + RTK_SWITCH_FIELD_NUMBER: _ClassVar[int] + SATS_NUM_FIELD_NUMBER: _ClassVar[int] + SYSTEM_STATUS_FIELD_NUMBER: _ClassVar[int] + WIFI_RSSI_FIELD_NUMBER: _ClassVar[int] + app_connect_type: int + ble_rssi: int + lora_channel: int + lora_locid: int + lora_netid: int + lora_scan: int + lowpower_status: int + mqtt_rtk_status: int + rtk_channel: int + rtk_status: int + rtk_switch: int + sats_num: int + system_status: int + wifi_rssi: int + def __init__(self, system_status: _Optional[int] = ..., app_connect_type: _Optional[int] = ..., ble_rssi: _Optional[int] = ..., wifi_rssi: _Optional[int] = ..., sats_num: _Optional[int] = ..., lora_scan: _Optional[int] = ..., lora_channel: _Optional[int] = ..., lora_locid: _Optional[int] = ..., lora_netid: _Optional[int] = ..., rtk_status: _Optional[int] = ..., lowpower_status: _Optional[int] = ..., mqtt_rtk_status: _Optional[int] = ..., rtk_channel: _Optional[int] = ..., rtk_switch: _Optional[int] = ...) -> None: ... diff --git a/pymammotion/proto/dev_net.proto b/pymammotion/proto/dev_net.proto index 5f83596..dc1da03 100644 --- a/pymammotion/proto/dev_net.proto +++ b/pymammotion/proto/dev_net.proto @@ -1,11 +1,11 @@ syntax = "proto3"; message DrvWifiUpload { - int32 Wifi_Msg_Upload = 1; + int32 wifi_msg_upload = 1; } message DrvWifiList { - int32 NVS_Wifi_Upload = 1; + int32 nvs_wifi_upload = 1; } message DrvWifiSet { @@ -17,8 +17,8 @@ message DrvWifiSet { message DrvWifiMsg { bool status1 = 1; bool status2 = 2; - string IP = 3; - string Msgssid = 4; + string ip = 3; + string msgssid = 4; string password = 5; int32 rssi = 6; string productkey = 7; diff --git a/pymammotion/proto/dev_net.py b/pymammotion/proto/dev_net.py index 263a2c5..0cee55f 100644 --- a/pymammotion/proto/dev_net.py +++ b/pymammotion/proto/dev_net.py @@ -149,7 +149,7 @@ class DrvDevInfoReqId(betterproto.Message): class DrvDevInfoRespId(betterproto.Message): id: int = betterproto.int32_field(1) type: int = betterproto.int32_field(2) - res: DrvDevInfoResult = betterproto.enum_field(3) + res: "DrvDevInfoResult" = betterproto.enum_field(3) info: str = betterproto.string_field(4) @@ -233,7 +233,7 @@ class GetMnetInfoReq(betterproto.Message): class GetMnetInfoRsp(betterproto.Message): req_ids: int = betterproto.int32_field(1) result: int = betterproto.int32_field(2) - mnet: MnetInfo = betterproto.message_field(3) + mnet: "MnetInfo" = betterproto.message_field(3) @dataclass diff --git a/pymammotion/proto/dev_net_pb2.py b/pymammotion/proto/dev_net_pb2.py index a171ebe..504b744 100644 --- a/pymammotion/proto/dev_net_pb2.py +++ b/pymammotion/proto/dev_net_pb2.py @@ -13,7 +13,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fpymammotion/proto/dev_net.proto\"(\n\rDrvWifiUpload\x12\x17\n\x0fWifi_Msg_Upload\x18\x01 \x01(\x05\"&\n\x0b\x44rvWifiList\x12\x17\n\x0fNVS_Wifi_Upload\x18\x01 \x01(\x05\"H\n\nDrvWifiSet\x12\x13\n\x0b\x63onfigParam\x18\x01 \x01(\x05\x12\x10\n\x08\x43onfssid\x18\x02 \x01(\t\x12\x13\n\x0bwifi_enable\x18\x03 \x01(\x08\"\xa8\x01\n\nDrvWifiMsg\x12\x0f\n\x07status1\x18\x01 \x01(\x08\x12\x0f\n\x07status2\x18\x02 \x01(\x08\x12\n\n\x02IP\x18\x03 \x01(\t\x12\x0f\n\x07Msgssid\x18\x04 \x01(\t\x12\x10\n\x08password\x18\x05 \x01(\t\x12\x0c\n\x04rssi\x18\x06 \x01(\x05\x12\x12\n\nproductkey\x18\x07 \x01(\t\x12\x12\n\ndevicename\x18\x08 \x01(\t\x12\x13\n\x0bwifi_enable\x18\t \x01(\x08\"?\n\x0b\x44rvWifiConf\x12\x10\n\x08succFlag\x18\x01 \x01(\x08\x12\x0c\n\x04\x63ode\x18\x02 \x01(\x05\x12\x10\n\x08\x43onfssid\x18\x03 \x01(\t\"\\\n\rDrvListUpload\x12\x0b\n\x03sum\x18\x01 \x01(\x05\x12\x0f\n\x07\x63urrent\x18\x02 \x01(\x05\x12\x0e\n\x06status\x18\x03 \x01(\x05\x12\x0f\n\x07Memssid\x18\x04 \x01(\t\x12\x0c\n\x04rssi\x18\x05 \x01(\x05\"Y\n\x10\x44rvUploadFileReq\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0e\n\x06userId\x18\x03 \x01(\t\x12\x0b\n\x03num\x18\x04 \x01(\x05\x12\x0c\n\x04type\x18\x05 \x01(\x05\"$\n\x13\x44rvUploadFileCancel\x12\r\n\x05\x62izId\x18\x01 \x01(\t\"z\n\x15\x44rvUploadFileToAppReq\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x11\n\toperation\x18\x02 \x01(\x05\x12\x10\n\x08serverIp\x18\x03 \x01(\x07\x12\x12\n\nserverPort\x18\x04 \x01(\x05\x12\x0b\n\x03num\x18\x05 \x01(\x05\x12\x0c\n\x04type\x18\x06 \x01(\x05\"I\n\x15\x44rvUploadFileToAppRsp\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x11\n\toperation\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\"+\n\x0f\x44rvDevInfoReqId\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04type\x18\x02 \x01(\x05\"Z\n\x10\x44rvDevInfoRespId\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04type\x18\x02 \x01(\x05\x12\x1e\n\x03res\x18\x03 \x01(\x0e\x32\x11.DrvDevInfoResult\x12\x0c\n\x04info\x18\x04 \x01(\t\"2\n\rDrvDevInfoReq\x12!\n\x07req_ids\x18\x01 \x03(\x0b\x32\x10.DrvDevInfoReqId\"5\n\x0e\x44rvDevInfoResp\x12#\n\x08resp_ids\x18\x01 \x03(\x0b\x32\x11.DrvDevInfoRespId\"\x8a\x01\n\x10\x44rvUpgradeReport\x12\x0f\n\x07\x64\x65vname\x18\x01 \x01(\t\x12\r\n\x05otaid\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x10\n\x08progress\x18\x04 \x01(\x05\x12\x0e\n\x06result\x18\x05 \x01(\x05\x12\x0f\n\x07message\x18\x06 \x01(\t\x12\x12\n\nproperties\x18\x07 \x01(\t\"l\n\x13WifiIotStatusReport\x12\x16\n\x0ewifi_connected\x18\x01 \x01(\x08\x12\x15\n\riot_connected\x18\x02 \x01(\x08\x12\x12\n\nproductkey\x18\x03 \x01(\t\x12\x12\n\ndevicename\x18\x04 \x01(\t\"*\n\x0c\x42leTestBytes\x12\x0c\n\x04seqs\x18\x01 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x07\"$\n\x11GetNetworkInfoReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\"\x87\x01\n\x11GetNetworkInfoRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x11\n\twifi_ssid\x18\x02 \x01(\t\x12\x10\n\x08wifi_mac\x18\x03 \x01(\t\x12\x11\n\twifi_rssi\x18\x04 \x01(\x05\x12\n\n\x02ip\x18\x05 \x01(\x07\x12\x0c\n\x04mask\x18\x06 \x01(\x07\x12\x0f\n\x07gateway\x18\x07 \x01(\x07\"N\n\x10mnet_inet_status\x12\x0f\n\x07\x63onnect\x18\x01 \x01(\x08\x12\n\n\x02ip\x18\x02 \x01(\x07\x12\x0c\n\x04mask\x18\x03 \x01(\x07\x12\x0f\n\x07gateway\x18\x04 \x01(\x07\"\xb6\x01\n\x08MnetInfo\x12\r\n\x05model\x18\x01 \x01(\t\x12\x10\n\x08revision\x18\x02 \x01(\t\x12\x0c\n\x04imei\x18\x03 \x01(\t\x12\x1a\n\x03sim\x18\x04 \x01(\x0e\x32\r.sim_card_sta\x12\x0c\n\x04imsi\x18\x05 \x01(\t\x12\"\n\tlink_type\x18\x06 \x01(\x0e\x32\x0f.mnet_link_type\x12\x0c\n\x04rssi\x18\x07 \x01(\x05\x12\x1f\n\x04inet\x18\x08 \x01(\x0b\x32\x11.mnet_inet_status\"!\n\x0eGetMnetInfoReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\"J\n\x0eGetMnetInfoRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x17\n\x04mnet\x18\x03 \x01(\x0b\x32\t.MnetInfo\"}\n\x07MnetApn\x12\x0b\n\x03\x63id\x18\x01 \x01(\x05\x12\x11\n\tapn_alias\x18\x02 \x01(\t\x12\x10\n\x08\x61pn_name\x18\x03 \x01(\t\x12\x1c\n\x04\x61uth\x18\x04 \x01(\x0e\x32\x0e.apn_auth_type\x12\x10\n\x08username\x18\x05 \x01(\t\x12\x10\n\x08password\x18\x06 \x01(\t\"9\n\nMnetApnCfg\x12\x14\n\x0c\x61pn_used_idx\x18\x01 \x01(\x05\x12\x15\n\x03\x61pn\x18\x02 \x03(\x0b\x32\x08.MnetApn\">\n\rMnetApnSetCfg\x12\x13\n\x0buse_default\x18\x01 \x01(\x08\x12\x18\n\x03\x63\x66g\x18\x02 \x01(\x0b\x32\x0b.MnetApnCfg\"~\n\x07MnetCfg\x12\x13\n\x0bmnet_enable\x18\x01 \x01(\x08\x12\x13\n\x0binet_enable\x18\x02 \x01(\x08\x12\x17\n\x04type\x18\x03 \x01(\x0e\x32\t.net_type\x12\x1b\n\x03\x61pn\x18\x04 \x01(\x0b\x32\x0e.MnetApnSetCfg\x12\x13\n\x0b\x61uto_select\x18\x05 \x01(\x08\" \n\rGetMnetCfgReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\"G\n\rGetMnetCfgRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x15\n\x03\x63\x66g\x18\x03 \x01(\x0b\x32\x08.MnetCfg\"7\n\rSetMnetCfgReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x15\n\x03\x63\x66g\x18\x02 \x01(\x0b\x32\x08.MnetCfg\"0\n\rSetMnetCfgRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\"N\n\x0e\x44rvDebugDdsZmq\x12\x11\n\tis_enable\x18\x01 \x01(\x08\x12\x15\n\rrx_topic_name\x18\x02 \x01(\t\x12\x12\n\ntx_zmq_url\x18\x03 \x01(\t\"!\n\x0cSetDrvBleMTU\x12\x11\n\tmtu_count\x18\x01 \x01(\x05\"\xca\n\n\x06\x44\x65vNet\x12\x18\n\x0etodev_ble_sync\x18\x01 \x01(\x05H\x00\x12\'\n\x0etodev_ConfType\x18\x02 \x01(\x0e\x32\r.WifiConfTypeH\x00\x12-\n\x13todev_WifiMsgUpload\x18\x03 \x01(\x0b\x32\x0e.DrvWifiUploadH\x00\x12,\n\x14todev_WifiListUpload\x18\x04 \x01(\x0b\x32\x0c.DrvWifiListH\x00\x12/\n\x18todev_Wifi_Configuration\x18\x05 \x01(\x0b\x32\x0b.DrvWifiSetH\x00\x12$\n\rtoapp_WifiMsg\x18\x06 \x01(\x0b\x32\x0b.DrvWifiMsgH\x00\x12&\n\x0etoapp_WifiConf\x18\x07 \x01(\x0b\x32\x0c.DrvWifiConfH\x00\x12*\n\x10toapp_ListUpload\x18\x08 \x01(\x0b\x32\x0e.DrvListUploadH\x00\x12/\n\x12todev_req_log_info\x18\t \x01(\x0b\x32\x11.DrvUploadFileReqH\x00\x12\x35\n\x15todev_log_data_cancel\x18\n \x01(\x0b\x32\x14.DrvUploadFileCancelH\x00\x12+\n\x11todev_devinfo_req\x18\x0b \x01(\x0b\x32\x0e.DrvDevInfoReqH\x00\x12-\n\x12toapp_devinfo_resp\x18\x0c \x01(\x0b\x32\x0f.DrvDevInfoRespH\x00\x12\x31\n\x14toapp_upgrade_report\x18\r \x01(\x0b\x32\x11.DrvUpgradeReportH\x00\x12\x35\n\x15toapp_wifi_iot_status\x18\x0e \x01(\x0b\x32\x14.WifiIotStatusReportH\x00\x12\x36\n\x14todev_uploadfile_req\x18\x0f \x01(\x0b\x32\x16.DrvUploadFileToAppReqH\x00\x12\x36\n\x14toapp_uploadfile_rsp\x18\x10 \x01(\x0b\x32\x16.DrvUploadFileToAppRspH\x00\x12\x33\n\x15todev_networkinfo_req\x18\x11 \x01(\x0b\x32\x12.GetNetworkInfoReqH\x00\x12\x33\n\x15toapp_networkinfo_rsp\x18\x12 \x01(\x0b\x32\x12.GetNetworkInfoRspH\x00\x12%\n\x0c\x62ir_testdata\x18\x13 \x01(\x0b\x32\r.BleTestBytesH\x00\x12.\n\x13todev_mnet_info_req\x18\x14 \x01(\x0b\x32\x0f.GetMnetInfoReqH\x00\x12.\n\x13toapp_mnet_info_rsp\x18\x15 \x01(\x0b\x32\x0f.GetMnetInfoRspH\x00\x12\x30\n\x16todev_get_mnet_cfg_req\x18\x16 \x01(\x0b\x32\x0e.GetMnetCfgReqH\x00\x12\x30\n\x16toapp_get_mnet_cfg_rsp\x18\x17 \x01(\x0b\x32\x0e.GetMnetCfgRspH\x00\x12\x30\n\x16todev_set_mnet_cfg_req\x18\x18 \x01(\x0b\x32\x0e.SetMnetCfgReqH\x00\x12\x30\n\x16toapp_set_mnet_cfg_rsp\x18\x19 \x01(\x0b\x32\x0e.SetMnetCfgRspH\x00\x12,\n\x11todev_set_dds2zmq\x18\x1a \x01(\x0b\x32\x0f.DrvDebugDdsZmqH\x00\x12*\n\x11todev_set_ble_mtu\x18\x1b \x01(\x0b\x32\r.SetDrvBleMTUH\x00\x12\x36\n\x19todev_set_iot_offline_req\x18\x1c \x01(\x0e\x32\x11.iot_conctrl_typeH\x00\x42\x0c\n\nNetSubType*l\n\x0cWifiConfType\x12\x12\n\x0e\x44isconnectWifi\x10\x00\x12\x0e\n\nForgetWifi\x10\x01\x12\x15\n\x11\x44irectConnectWifi\x10\x02\x12\x11\n\rReconnectWifi\x10\x03\x12\x0e\n\nset_enable\x10\x04*l\n\x15\x44rvUploadFileFileType\x12\x11\n\rFILE_TYPE_ALL\x10\x00\x12\x14\n\x10\x46ILE_TYPE_SYSLOG\x10\x01\x12\x14\n\x10\x46ILE_TYPE_NAVLOG\x10\x02\x12\x14\n\x10\x46ILE_TYPE_RTKLOG\x10\x03*R\n\x10\x44rvDevInfoResult\x12\x13\n\x0f\x44RV_RESULT_FAIL\x10\x00\x12\x12\n\x0e\x44RV_RESULT_SUC\x10\x01\x12\x15\n\x11\x44RV_RESULT_NOTSUP\x10\x02*p\n\x0csim_card_sta\x12\x0c\n\x08SIM_NONE\x10\x00\x12\x0f\n\x0bSIM_NO_CARD\x10\x01\x12\x0f\n\x0bSIM_INVALID\x10\x02\x12\x11\n\rSIM_INPUT_PIN\x10\x03\x12\x11\n\rSIM_INPUT_PUK\x10\x04\x12\n\n\x06SIM_OK\x10\x05*Z\n\x0emnet_link_type\x12\x12\n\x0eMNET_LINK_NONE\x10\x00\x12\x10\n\x0cMNET_LINK_2G\x10\x01\x12\x10\n\x0cMNET_LINK_3G\x10\x02\x12\x10\n\x0cMNET_LINK_4G\x10\x03*^\n\rapn_auth_type\x12\x11\n\rAPN_AUTH_NONE\x10\x00\x12\x10\n\x0c\x41PN_AUTH_PAP\x10\x01\x12\x11\n\rAPN_AUTH_CHAP\x10\x02\x12\x15\n\x11\x41PN_AUTH_PAP_CHAP\x10\x03*0\n\x08net_type\x12\x11\n\rNET_TYPE_WIFI\x10\x00\x12\x11\n\rNET_TYPE_MNET\x10\x01*Q\n\x10iot_conctrl_type\x12\x14\n\x10IOT_TYPE_OFFLINE\x10\x00\x12\x13\n\x0fIOT_TYPE_ONLINE\x10\x01\x12\x12\n\x0eIOT_TYPE_RESET\x10\x02\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fpymammotion/proto/dev_net.proto\"(\n\rDrvWifiUpload\x12\x17\n\x0fwifi_msg_upload\x18\x01 \x01(\x05\"&\n\x0b\x44rvWifiList\x12\x17\n\x0fnvs_wifi_upload\x18\x01 \x01(\x05\"H\n\nDrvWifiSet\x12\x13\n\x0b\x63onfigParam\x18\x01 \x01(\x05\x12\x10\n\x08\x43onfssid\x18\x02 \x01(\t\x12\x13\n\x0bwifi_enable\x18\x03 \x01(\x08\"\xa8\x01\n\nDrvWifiMsg\x12\x0f\n\x07status1\x18\x01 \x01(\x08\x12\x0f\n\x07status2\x18\x02 \x01(\x08\x12\n\n\x02ip\x18\x03 \x01(\t\x12\x0f\n\x07msgssid\x18\x04 \x01(\t\x12\x10\n\x08password\x18\x05 \x01(\t\x12\x0c\n\x04rssi\x18\x06 \x01(\x05\x12\x12\n\nproductkey\x18\x07 \x01(\t\x12\x12\n\ndevicename\x18\x08 \x01(\t\x12\x13\n\x0bwifi_enable\x18\t \x01(\x08\"?\n\x0b\x44rvWifiConf\x12\x10\n\x08succFlag\x18\x01 \x01(\x08\x12\x0c\n\x04\x63ode\x18\x02 \x01(\x05\x12\x10\n\x08\x43onfssid\x18\x03 \x01(\t\"\\\n\rDrvListUpload\x12\x0b\n\x03sum\x18\x01 \x01(\x05\x12\x0f\n\x07\x63urrent\x18\x02 \x01(\x05\x12\x0e\n\x06status\x18\x03 \x01(\x05\x12\x0f\n\x07Memssid\x18\x04 \x01(\t\x12\x0c\n\x04rssi\x18\x05 \x01(\x05\"Y\n\x10\x44rvUploadFileReq\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0e\n\x06userId\x18\x03 \x01(\t\x12\x0b\n\x03num\x18\x04 \x01(\x05\x12\x0c\n\x04type\x18\x05 \x01(\x05\"$\n\x13\x44rvUploadFileCancel\x12\r\n\x05\x62izId\x18\x01 \x01(\t\"z\n\x15\x44rvUploadFileToAppReq\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x11\n\toperation\x18\x02 \x01(\x05\x12\x10\n\x08serverIp\x18\x03 \x01(\x07\x12\x12\n\nserverPort\x18\x04 \x01(\x05\x12\x0b\n\x03num\x18\x05 \x01(\x05\x12\x0c\n\x04type\x18\x06 \x01(\x05\"I\n\x15\x44rvUploadFileToAppRsp\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x11\n\toperation\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\"+\n\x0f\x44rvDevInfoReqId\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04type\x18\x02 \x01(\x05\"Z\n\x10\x44rvDevInfoRespId\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04type\x18\x02 \x01(\x05\x12\x1e\n\x03res\x18\x03 \x01(\x0e\x32\x11.DrvDevInfoResult\x12\x0c\n\x04info\x18\x04 \x01(\t\"2\n\rDrvDevInfoReq\x12!\n\x07req_ids\x18\x01 \x03(\x0b\x32\x10.DrvDevInfoReqId\"5\n\x0e\x44rvDevInfoResp\x12#\n\x08resp_ids\x18\x01 \x03(\x0b\x32\x11.DrvDevInfoRespId\"\x8a\x01\n\x10\x44rvUpgradeReport\x12\x0f\n\x07\x64\x65vname\x18\x01 \x01(\t\x12\r\n\x05otaid\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x10\n\x08progress\x18\x04 \x01(\x05\x12\x0e\n\x06result\x18\x05 \x01(\x05\x12\x0f\n\x07message\x18\x06 \x01(\t\x12\x12\n\nproperties\x18\x07 \x01(\t\"l\n\x13WifiIotStatusReport\x12\x16\n\x0ewifi_connected\x18\x01 \x01(\x08\x12\x15\n\riot_connected\x18\x02 \x01(\x08\x12\x12\n\nproductkey\x18\x03 \x01(\t\x12\x12\n\ndevicename\x18\x04 \x01(\t\"*\n\x0c\x42leTestBytes\x12\x0c\n\x04seqs\x18\x01 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x07\"$\n\x11GetNetworkInfoReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\"\x87\x01\n\x11GetNetworkInfoRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x11\n\twifi_ssid\x18\x02 \x01(\t\x12\x10\n\x08wifi_mac\x18\x03 \x01(\t\x12\x11\n\twifi_rssi\x18\x04 \x01(\x05\x12\n\n\x02ip\x18\x05 \x01(\x07\x12\x0c\n\x04mask\x18\x06 \x01(\x07\x12\x0f\n\x07gateway\x18\x07 \x01(\x07\"N\n\x10mnet_inet_status\x12\x0f\n\x07\x63onnect\x18\x01 \x01(\x08\x12\n\n\x02ip\x18\x02 \x01(\x07\x12\x0c\n\x04mask\x18\x03 \x01(\x07\x12\x0f\n\x07gateway\x18\x04 \x01(\x07\"\xb6\x01\n\x08MnetInfo\x12\r\n\x05model\x18\x01 \x01(\t\x12\x10\n\x08revision\x18\x02 \x01(\t\x12\x0c\n\x04imei\x18\x03 \x01(\t\x12\x1a\n\x03sim\x18\x04 \x01(\x0e\x32\r.sim_card_sta\x12\x0c\n\x04imsi\x18\x05 \x01(\t\x12\"\n\tlink_type\x18\x06 \x01(\x0e\x32\x0f.mnet_link_type\x12\x0c\n\x04rssi\x18\x07 \x01(\x05\x12\x1f\n\x04inet\x18\x08 \x01(\x0b\x32\x11.mnet_inet_status\"!\n\x0eGetMnetInfoReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\"J\n\x0eGetMnetInfoRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x17\n\x04mnet\x18\x03 \x01(\x0b\x32\t.MnetInfo\"}\n\x07MnetApn\x12\x0b\n\x03\x63id\x18\x01 \x01(\x05\x12\x11\n\tapn_alias\x18\x02 \x01(\t\x12\x10\n\x08\x61pn_name\x18\x03 \x01(\t\x12\x1c\n\x04\x61uth\x18\x04 \x01(\x0e\x32\x0e.apn_auth_type\x12\x10\n\x08username\x18\x05 \x01(\t\x12\x10\n\x08password\x18\x06 \x01(\t\"9\n\nMnetApnCfg\x12\x14\n\x0c\x61pn_used_idx\x18\x01 \x01(\x05\x12\x15\n\x03\x61pn\x18\x02 \x03(\x0b\x32\x08.MnetApn\">\n\rMnetApnSetCfg\x12\x13\n\x0buse_default\x18\x01 \x01(\x08\x12\x18\n\x03\x63\x66g\x18\x02 \x01(\x0b\x32\x0b.MnetApnCfg\"~\n\x07MnetCfg\x12\x13\n\x0bmnet_enable\x18\x01 \x01(\x08\x12\x13\n\x0binet_enable\x18\x02 \x01(\x08\x12\x17\n\x04type\x18\x03 \x01(\x0e\x32\t.net_type\x12\x1b\n\x03\x61pn\x18\x04 \x01(\x0b\x32\x0e.MnetApnSetCfg\x12\x13\n\x0b\x61uto_select\x18\x05 \x01(\x08\" \n\rGetMnetCfgReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\"G\n\rGetMnetCfgRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x15\n\x03\x63\x66g\x18\x03 \x01(\x0b\x32\x08.MnetCfg\"7\n\rSetMnetCfgReq\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x15\n\x03\x63\x66g\x18\x02 \x01(\x0b\x32\x08.MnetCfg\"0\n\rSetMnetCfgRsp\x12\x0f\n\x07req_ids\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\x05\"N\n\x0e\x44rvDebugDdsZmq\x12\x11\n\tis_enable\x18\x01 \x01(\x08\x12\x15\n\rrx_topic_name\x18\x02 \x01(\t\x12\x12\n\ntx_zmq_url\x18\x03 \x01(\t\"!\n\x0cSetDrvBleMTU\x12\x11\n\tmtu_count\x18\x01 \x01(\x05\"\xca\n\n\x06\x44\x65vNet\x12\x18\n\x0etodev_ble_sync\x18\x01 \x01(\x05H\x00\x12\'\n\x0etodev_ConfType\x18\x02 \x01(\x0e\x32\r.WifiConfTypeH\x00\x12-\n\x13todev_WifiMsgUpload\x18\x03 \x01(\x0b\x32\x0e.DrvWifiUploadH\x00\x12,\n\x14todev_WifiListUpload\x18\x04 \x01(\x0b\x32\x0c.DrvWifiListH\x00\x12/\n\x18todev_Wifi_Configuration\x18\x05 \x01(\x0b\x32\x0b.DrvWifiSetH\x00\x12$\n\rtoapp_WifiMsg\x18\x06 \x01(\x0b\x32\x0b.DrvWifiMsgH\x00\x12&\n\x0etoapp_WifiConf\x18\x07 \x01(\x0b\x32\x0c.DrvWifiConfH\x00\x12*\n\x10toapp_ListUpload\x18\x08 \x01(\x0b\x32\x0e.DrvListUploadH\x00\x12/\n\x12todev_req_log_info\x18\t \x01(\x0b\x32\x11.DrvUploadFileReqH\x00\x12\x35\n\x15todev_log_data_cancel\x18\n \x01(\x0b\x32\x14.DrvUploadFileCancelH\x00\x12+\n\x11todev_devinfo_req\x18\x0b \x01(\x0b\x32\x0e.DrvDevInfoReqH\x00\x12-\n\x12toapp_devinfo_resp\x18\x0c \x01(\x0b\x32\x0f.DrvDevInfoRespH\x00\x12\x31\n\x14toapp_upgrade_report\x18\r \x01(\x0b\x32\x11.DrvUpgradeReportH\x00\x12\x35\n\x15toapp_wifi_iot_status\x18\x0e \x01(\x0b\x32\x14.WifiIotStatusReportH\x00\x12\x36\n\x14todev_uploadfile_req\x18\x0f \x01(\x0b\x32\x16.DrvUploadFileToAppReqH\x00\x12\x36\n\x14toapp_uploadfile_rsp\x18\x10 \x01(\x0b\x32\x16.DrvUploadFileToAppRspH\x00\x12\x33\n\x15todev_networkinfo_req\x18\x11 \x01(\x0b\x32\x12.GetNetworkInfoReqH\x00\x12\x33\n\x15toapp_networkinfo_rsp\x18\x12 \x01(\x0b\x32\x12.GetNetworkInfoRspH\x00\x12%\n\x0c\x62ir_testdata\x18\x13 \x01(\x0b\x32\r.BleTestBytesH\x00\x12.\n\x13todev_mnet_info_req\x18\x14 \x01(\x0b\x32\x0f.GetMnetInfoReqH\x00\x12.\n\x13toapp_mnet_info_rsp\x18\x15 \x01(\x0b\x32\x0f.GetMnetInfoRspH\x00\x12\x30\n\x16todev_get_mnet_cfg_req\x18\x16 \x01(\x0b\x32\x0e.GetMnetCfgReqH\x00\x12\x30\n\x16toapp_get_mnet_cfg_rsp\x18\x17 \x01(\x0b\x32\x0e.GetMnetCfgRspH\x00\x12\x30\n\x16todev_set_mnet_cfg_req\x18\x18 \x01(\x0b\x32\x0e.SetMnetCfgReqH\x00\x12\x30\n\x16toapp_set_mnet_cfg_rsp\x18\x19 \x01(\x0b\x32\x0e.SetMnetCfgRspH\x00\x12,\n\x11todev_set_dds2zmq\x18\x1a \x01(\x0b\x32\x0f.DrvDebugDdsZmqH\x00\x12*\n\x11todev_set_ble_mtu\x18\x1b \x01(\x0b\x32\r.SetDrvBleMTUH\x00\x12\x36\n\x19todev_set_iot_offline_req\x18\x1c \x01(\x0e\x32\x11.iot_conctrl_typeH\x00\x42\x0c\n\nNetSubType*l\n\x0cWifiConfType\x12\x12\n\x0e\x44isconnectWifi\x10\x00\x12\x0e\n\nForgetWifi\x10\x01\x12\x15\n\x11\x44irectConnectWifi\x10\x02\x12\x11\n\rReconnectWifi\x10\x03\x12\x0e\n\nset_enable\x10\x04*l\n\x15\x44rvUploadFileFileType\x12\x11\n\rFILE_TYPE_ALL\x10\x00\x12\x14\n\x10\x46ILE_TYPE_SYSLOG\x10\x01\x12\x14\n\x10\x46ILE_TYPE_NAVLOG\x10\x02\x12\x14\n\x10\x46ILE_TYPE_RTKLOG\x10\x03*R\n\x10\x44rvDevInfoResult\x12\x13\n\x0f\x44RV_RESULT_FAIL\x10\x00\x12\x12\n\x0e\x44RV_RESULT_SUC\x10\x01\x12\x15\n\x11\x44RV_RESULT_NOTSUP\x10\x02*p\n\x0csim_card_sta\x12\x0c\n\x08SIM_NONE\x10\x00\x12\x0f\n\x0bSIM_NO_CARD\x10\x01\x12\x0f\n\x0bSIM_INVALID\x10\x02\x12\x11\n\rSIM_INPUT_PIN\x10\x03\x12\x11\n\rSIM_INPUT_PUK\x10\x04\x12\n\n\x06SIM_OK\x10\x05*Z\n\x0emnet_link_type\x12\x12\n\x0eMNET_LINK_NONE\x10\x00\x12\x10\n\x0cMNET_LINK_2G\x10\x01\x12\x10\n\x0cMNET_LINK_3G\x10\x02\x12\x10\n\x0cMNET_LINK_4G\x10\x03*^\n\rapn_auth_type\x12\x11\n\rAPN_AUTH_NONE\x10\x00\x12\x10\n\x0c\x41PN_AUTH_PAP\x10\x01\x12\x11\n\rAPN_AUTH_CHAP\x10\x02\x12\x15\n\x11\x41PN_AUTH_PAP_CHAP\x10\x03*0\n\x08net_type\x12\x11\n\rNET_TYPE_WIFI\x10\x00\x12\x11\n\rNET_TYPE_MNET\x10\x01*Q\n\x10iot_conctrl_type\x12\x14\n\x10IOT_TYPE_OFFLINE\x10\x00\x12\x13\n\x0fIOT_TYPE_ONLINE\x10\x01\x12\x12\n\x0eIOT_TYPE_RESET\x10\x02\x62\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pymammotion.proto.dev_net_pb2', globals()) diff --git a/pymammotion/proto/dev_net_pb2.pyi b/pymammotion/proto/dev_net_pb2.pyi index ffda255..ce92434 100644 --- a/pymammotion/proto/dev_net_pb2.pyi +++ b/pymammotion/proto/dev_net_pb2.pyi @@ -236,18 +236,16 @@ class DrvWifiConf(_message.Message): def __init__(self, succFlag: bool = ..., code: _Optional[int] = ..., Confssid: _Optional[str] = ...) -> None: ... class DrvWifiList(_message.Message): - __slots__ = ["NVS_Wifi_Upload"] + __slots__ = ["nvs_wifi_upload"] NVS_WIFI_UPLOAD_FIELD_NUMBER: _ClassVar[int] - NVS_Wifi_Upload: int - def __init__(self, NVS_Wifi_Upload: _Optional[int] = ...) -> None: ... + nvs_wifi_upload: int + def __init__(self, nvs_wifi_upload: _Optional[int] = ...) -> None: ... class DrvWifiMsg(_message.Message): - __slots__ = ["IP", "Msgssid", "devicename", "password", "productkey", "rssi", "status1", "status2", "wifi_enable"] + __slots__ = ["devicename", "ip", "msgssid", "password", "productkey", "rssi", "status1", "status2", "wifi_enable"] DEVICENAME_FIELD_NUMBER: _ClassVar[int] - IP: str IP_FIELD_NUMBER: _ClassVar[int] MSGSSID_FIELD_NUMBER: _ClassVar[int] - Msgssid: str PASSWORD_FIELD_NUMBER: _ClassVar[int] PRODUCTKEY_FIELD_NUMBER: _ClassVar[int] RSSI_FIELD_NUMBER: _ClassVar[int] @@ -255,13 +253,15 @@ class DrvWifiMsg(_message.Message): STATUS2_FIELD_NUMBER: _ClassVar[int] WIFI_ENABLE_FIELD_NUMBER: _ClassVar[int] devicename: str + ip: str + msgssid: str password: str productkey: str rssi: int status1: bool status2: bool wifi_enable: bool - def __init__(self, status1: bool = ..., status2: bool = ..., IP: _Optional[str] = ..., Msgssid: _Optional[str] = ..., password: _Optional[str] = ..., rssi: _Optional[int] = ..., productkey: _Optional[str] = ..., devicename: _Optional[str] = ..., wifi_enable: bool = ...) -> None: ... + def __init__(self, status1: bool = ..., status2: bool = ..., ip: _Optional[str] = ..., msgssid: _Optional[str] = ..., password: _Optional[str] = ..., rssi: _Optional[int] = ..., productkey: _Optional[str] = ..., devicename: _Optional[str] = ..., wifi_enable: bool = ...) -> None: ... class DrvWifiSet(_message.Message): __slots__ = ["Confssid", "configParam", "wifi_enable"] @@ -274,10 +274,10 @@ class DrvWifiSet(_message.Message): def __init__(self, configParam: _Optional[int] = ..., Confssid: _Optional[str] = ..., wifi_enable: bool = ...) -> None: ... class DrvWifiUpload(_message.Message): - __slots__ = ["Wifi_Msg_Upload"] + __slots__ = ["wifi_msg_upload"] WIFI_MSG_UPLOAD_FIELD_NUMBER: _ClassVar[int] - Wifi_Msg_Upload: int - def __init__(self, Wifi_Msg_Upload: _Optional[int] = ...) -> None: ... + wifi_msg_upload: int + def __init__(self, wifi_msg_upload: _Optional[int] = ...) -> None: ... class GetMnetCfgReq(_message.Message): __slots__ = ["req_ids"] diff --git a/pymammotion/proto/luba_msg.proto b/pymammotion/proto/luba_msg.proto index 39da798..eef9c8d 100644 --- a/pymammotion/proto/luba_msg.proto +++ b/pymammotion/proto/luba_msg.proto @@ -1,5 +1,6 @@ syntax = "proto3"; +import "pymammotion/proto/basestation.proto"; import "pymammotion/proto/mctrl_driver.proto"; import "pymammotion/proto/mctrl_nav.proto"; import "pymammotion/proto/mctrl_sys.proto"; @@ -28,6 +29,7 @@ message LubaMsg { SocMul mul = 14; MsgNull null = 16; MctlPept pept = 17; + BaseStation base = 18; } uint64 timestamp = 15; } diff --git a/pymammotion/proto/luba_msg.py b/pymammotion/proto/luba_msg.py index 26a7c41..1d07234 100644 --- a/pymammotion/proto/luba_msg.py +++ b/pymammotion/proto/luba_msg.py @@ -4,8 +4,8 @@ from dataclasses import dataclass import betterproto - from .common import * +from .basestation import * from .dev_net import * from .luba_mul import * from .mctrl_driver import * @@ -77,4 +77,5 @@ class LubaMsg(betterproto.Message): mul: "SocMul" = betterproto.message_field(14, group="LubaSubMsg") null: "MsgNull" = betterproto.message_field(16, group="LubaSubMsg") pept: "MctlPept" = betterproto.message_field(17, group="LubaSubMsg") + base: "BaseStation" = betterproto.message_field(18, group="LubaSubMsg") timestamp: int = betterproto.uint64_field(15) diff --git a/pymammotion/proto/luba_msg_pb2.py b/pymammotion/proto/luba_msg_pb2.py index 490c8fc..43acd45 100644 --- a/pymammotion/proto/luba_msg_pb2.py +++ b/pymammotion/proto/luba_msg_pb2.py @@ -11,6 +11,7 @@ _sym_db = _symbol_database.Default() +from pymammotion.proto import basestation_pb2 as pymammotion_dot_proto_dot_basestation__pb2 from pymammotion.proto import mctrl_driver_pb2 as pymammotion_dot_proto_dot_mctrl__driver__pb2 from pymammotion.proto import mctrl_nav_pb2 as pymammotion_dot_proto_dot_mctrl__nav__pb2 from pymammotion.proto import mctrl_sys_pb2 as pymammotion_dot_proto_dot_mctrl__sys__pb2 @@ -20,21 +21,21 @@ from pymammotion.proto import mctrl_pept_pb2 as pymammotion_dot_proto_dot_mctrl__pept__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n pymammotion/proto/luba_msg.proto\x1a$pymammotion/proto/mctrl_driver.proto\x1a!pymammotion/proto/mctrl_nav.proto\x1a!pymammotion/proto/mctrl_sys.proto\x1a\x1fpymammotion/proto/dev_net.proto\x1a!pymammotion/proto/mctrl_ota.proto\x1a pymammotion/proto/luba_mul.proto\x1a\"pymammotion/proto/mctrl_pept.proto\"\t\n\x07MsgNull\"\x99\x03\n\x07LubaMsg\x12\x1c\n\x07msgtype\x18\x01 \x01(\x0e\x32\x0b.MsgCmdType\x12\x1a\n\x06sender\x18\x02 \x01(\x0e\x32\n.MsgDevice\x12\x19\n\x05rcver\x18\x03 \x01(\x0e\x32\n.MsgDevice\x12\x19\n\x07msgattr\x18\x04 \x01(\x0e\x32\x08.MsgAttr\x12\x0c\n\x04seqs\x18\x05 \x01(\x05\x12\x0f\n\x07version\x18\x06 \x01(\x05\x12\x0f\n\x07subtype\x18\x07 \x01(\x05\x12\x16\n\x03net\x18\x08 \x01(\x0b\x32\x07.DevNetH\x00\x12\x17\n\x03sys\x18\n \x01(\x0b\x32\x08.MctlSysH\x00\x12\x17\n\x03nav\x18\x0b \x01(\x0b\x32\x08.MctlNavH\x00\x12\x1d\n\x06\x64river\x18\x0c \x01(\x0b\x32\x0b.MctlDriverH\x00\x12\x17\n\x03ota\x18\r \x01(\x0b\x32\x08.MctlOtaH\x00\x12\x16\n\x03mul\x18\x0e \x01(\x0b\x32\x07.SocMulH\x00\x12\x18\n\x04null\x18\x10 \x01(\x0b\x32\x08.MsgNullH\x00\x12\x19\n\x04pept\x18\x11 \x01(\x0b\x32\t.MctlPeptH\x00\x12\x11\n\ttimestamp\x18\x0f \x01(\x04\x42\x0c\n\nLubaSubMsg*\xd7\x02\n\nMsgCmdType\x12\x16\n\x12MSG_CMD_TYPE_START\x10\x00\x12\x15\n\x10MSG_CMD_TYPE_NAV\x10\xf0\x01\x12\x1e\n\x19MSG_CMD_TYPE_LOCALIZATION\x10\xf1\x01\x12\x1a\n\x15MSG_CMD_TYPE_PLANNING\x10\xf2\x01\x12\x1e\n\x19MSG_CMD_TYPE_EMBED_DRIVER\x10\xf3\x01\x12\x1b\n\x16MSG_CMD_TYPE_EMBED_SYS\x10\xf4\x01\x12\x1f\n\x1aMSG_CMD_TYPE_EMBED_MIDWARE\x10\xf5\x01\x12\x1b\n\x16MSG_CMD_TYPE_EMBED_OTA\x10\xf6\x01\x12\x1d\n\x18MSG_CMD_TYPE_APPLICATION\x10\xf7\x01\x12\x15\n\x10MSG_CMD_TYPE_ESP\x10\xf8\x01\x12\x15\n\x10MSG_CMD_TYPE_MUL\x10\xf9\x01\x12\x16\n\x11MSG_CMD_TYPE_PEPT\x10\xfa\x01*V\n\x07MsgAttr\x12\x11\n\rMSG_ATTR_NONE\x10\x00\x12\x10\n\x0cMSG_ATTR_REQ\x10\x01\x12\x11\n\rMSG_ATTR_RESP\x10\x02\x12\x13\n\x0fMSG_ATTR_REPORT\x10\x03*\xa8\x02\n\tMsgDevice\x12\x10\n\x0c\x44\x45V_COMM_ESP\x10\x00\x12\x0f\n\x0b\x44\x45V_MAINCTL\x10\x01\x12\x11\n\rDEV_LEFTMOTOR\x10\x02\x12\x12\n\x0e\x44\x45V_RIGHTMOTOR\x10\x03\x12\x13\n\x0f\x44\x45V_BASESTATION\x10\x04\x12\x0e\n\nDEV_RTKCLI\x10\x05\x12\x0f\n\x0b\x44\x45V_USBHOST\x10\x06\x12\x11\n\rDEV_MOBILEAPP\x10\x07\x12\x11\n\rDEV_IOTSERVER\x10\x08\x12\x0b\n\x07\x44\x45V_BMS\x10\t\x12\x12\n\x0e\x44\x45V_NAVIGATION\x10\x11\x12\x14\n\x10\x44\x45V_LOCALIZATION\x10\x12\x12\x12\n\x0e\x44\x45V_PERCEPTION\x10\x13\x12\x19\n\x15SOC_MODULE_MULTIMEDIA\x10\x15\x12\x0f\n\x0b\x44\x45V_IOTCTRL\x10\x19\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n pymammotion/proto/luba_msg.proto\x1a#pymammotion/proto/basestation.proto\x1a$pymammotion/proto/mctrl_driver.proto\x1a!pymammotion/proto/mctrl_nav.proto\x1a!pymammotion/proto/mctrl_sys.proto\x1a\x1fpymammotion/proto/dev_net.proto\x1a!pymammotion/proto/mctrl_ota.proto\x1a pymammotion/proto/luba_mul.proto\x1a\"pymammotion/proto/mctrl_pept.proto\"\t\n\x07MsgNull\"\xb7\x03\n\x07LubaMsg\x12\x1c\n\x07msgtype\x18\x01 \x01(\x0e\x32\x0b.MsgCmdType\x12\x1a\n\x06sender\x18\x02 \x01(\x0e\x32\n.MsgDevice\x12\x19\n\x05rcver\x18\x03 \x01(\x0e\x32\n.MsgDevice\x12\x19\n\x07msgattr\x18\x04 \x01(\x0e\x32\x08.MsgAttr\x12\x0c\n\x04seqs\x18\x05 \x01(\x05\x12\x0f\n\x07version\x18\x06 \x01(\x05\x12\x0f\n\x07subtype\x18\x07 \x01(\x05\x12\x16\n\x03net\x18\x08 \x01(\x0b\x32\x07.DevNetH\x00\x12\x17\n\x03sys\x18\n \x01(\x0b\x32\x08.MctlSysH\x00\x12\x17\n\x03nav\x18\x0b \x01(\x0b\x32\x08.MctlNavH\x00\x12\x1d\n\x06\x64river\x18\x0c \x01(\x0b\x32\x0b.MctlDriverH\x00\x12\x17\n\x03ota\x18\r \x01(\x0b\x32\x08.MctlOtaH\x00\x12\x16\n\x03mul\x18\x0e \x01(\x0b\x32\x07.SocMulH\x00\x12\x18\n\x04null\x18\x10 \x01(\x0b\x32\x08.MsgNullH\x00\x12\x19\n\x04pept\x18\x11 \x01(\x0b\x32\t.MctlPeptH\x00\x12\x1c\n\x04\x62\x61se\x18\x12 \x01(\x0b\x32\x0c.BaseStationH\x00\x12\x11\n\ttimestamp\x18\x0f \x01(\x04\x42\x0c\n\nLubaSubMsg*\xd7\x02\n\nMsgCmdType\x12\x16\n\x12MSG_CMD_TYPE_START\x10\x00\x12\x15\n\x10MSG_CMD_TYPE_NAV\x10\xf0\x01\x12\x1e\n\x19MSG_CMD_TYPE_LOCALIZATION\x10\xf1\x01\x12\x1a\n\x15MSG_CMD_TYPE_PLANNING\x10\xf2\x01\x12\x1e\n\x19MSG_CMD_TYPE_EMBED_DRIVER\x10\xf3\x01\x12\x1b\n\x16MSG_CMD_TYPE_EMBED_SYS\x10\xf4\x01\x12\x1f\n\x1aMSG_CMD_TYPE_EMBED_MIDWARE\x10\xf5\x01\x12\x1b\n\x16MSG_CMD_TYPE_EMBED_OTA\x10\xf6\x01\x12\x1d\n\x18MSG_CMD_TYPE_APPLICATION\x10\xf7\x01\x12\x15\n\x10MSG_CMD_TYPE_ESP\x10\xf8\x01\x12\x15\n\x10MSG_CMD_TYPE_MUL\x10\xf9\x01\x12\x16\n\x11MSG_CMD_TYPE_PEPT\x10\xfa\x01*V\n\x07MsgAttr\x12\x11\n\rMSG_ATTR_NONE\x10\x00\x12\x10\n\x0cMSG_ATTR_REQ\x10\x01\x12\x11\n\rMSG_ATTR_RESP\x10\x02\x12\x13\n\x0fMSG_ATTR_REPORT\x10\x03*\xa8\x02\n\tMsgDevice\x12\x10\n\x0c\x44\x45V_COMM_ESP\x10\x00\x12\x0f\n\x0b\x44\x45V_MAINCTL\x10\x01\x12\x11\n\rDEV_LEFTMOTOR\x10\x02\x12\x12\n\x0e\x44\x45V_RIGHTMOTOR\x10\x03\x12\x13\n\x0f\x44\x45V_BASESTATION\x10\x04\x12\x0e\n\nDEV_RTKCLI\x10\x05\x12\x0f\n\x0b\x44\x45V_USBHOST\x10\x06\x12\x11\n\rDEV_MOBILEAPP\x10\x07\x12\x11\n\rDEV_IOTSERVER\x10\x08\x12\x0b\n\x07\x44\x45V_BMS\x10\t\x12\x12\n\x0e\x44\x45V_NAVIGATION\x10\x11\x12\x14\n\x10\x44\x45V_LOCALIZATION\x10\x12\x12\x12\n\x0e\x44\x45V_PERCEPTION\x10\x13\x12\x19\n\x15SOC_MODULE_MULTIMEDIA\x10\x15\x12\x0f\n\x0b\x44\x45V_IOTCTRL\x10\x19\x62\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pymammotion.proto.luba_msg_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _MSGCMDTYPE._serialized_start=706 - _MSGCMDTYPE._serialized_end=1049 - _MSGATTR._serialized_start=1051 - _MSGATTR._serialized_end=1137 - _MSGDEVICE._serialized_start=1140 - _MSGDEVICE._serialized_end=1436 - _MSGNULL._serialized_start=282 - _MSGNULL._serialized_end=291 - _LUBAMSG._serialized_start=294 - _LUBAMSG._serialized_end=703 + _MSGCMDTYPE._serialized_start=773 + _MSGCMDTYPE._serialized_end=1116 + _MSGATTR._serialized_start=1118 + _MSGATTR._serialized_end=1204 + _MSGDEVICE._serialized_start=1207 + _MSGDEVICE._serialized_end=1503 + _MSGNULL._serialized_start=319 + _MSGNULL._serialized_end=328 + _LUBAMSG._serialized_start=331 + _LUBAMSG._serialized_end=770 # @@protoc_insertion_point(module_scope) diff --git a/pymammotion/proto/luba_msg_pb2.pyi b/pymammotion/proto/luba_msg_pb2.pyi index a22bed4..79c3eca 100644 --- a/pymammotion/proto/luba_msg_pb2.pyi +++ b/pymammotion/proto/luba_msg_pb2.pyi @@ -1,3 +1,4 @@ +from pymammotion.proto import basestation_pb2 as _basestation_pb2 from pymammotion.proto import mctrl_driver_pb2 as _mctrl_driver_pb2 from pymammotion.proto import mctrl_nav_pb2 as _mctrl_nav_pb2 from pymammotion.proto import mctrl_sys_pb2 as _mctrl_sys_pb2 @@ -44,7 +45,8 @@ MSG_CMD_TYPE_START: MsgCmdType SOC_MODULE_MULTIMEDIA: MsgDevice class LubaMsg(_message.Message): - __slots__ = ["driver", "msgattr", "msgtype", "mul", "nav", "net", "null", "ota", "pept", "rcver", "sender", "seqs", "subtype", "sys", "timestamp", "version"] + __slots__ = ["base", "driver", "msgattr", "msgtype", "mul", "nav", "net", "null", "ota", "pept", "rcver", "sender", "seqs", "subtype", "sys", "timestamp", "version"] + BASE_FIELD_NUMBER: _ClassVar[int] DRIVER_FIELD_NUMBER: _ClassVar[int] MSGATTR_FIELD_NUMBER: _ClassVar[int] MSGTYPE_FIELD_NUMBER: _ClassVar[int] @@ -61,6 +63,7 @@ class LubaMsg(_message.Message): SYS_FIELD_NUMBER: _ClassVar[int] TIMESTAMP_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] + base: _basestation_pb2.BaseStation driver: _mctrl_driver_pb2.MctlDriver msgattr: MsgAttr msgtype: MsgCmdType @@ -77,7 +80,7 @@ class LubaMsg(_message.Message): sys: _mctrl_sys_pb2.MctlSys timestamp: int version: int - def __init__(self, msgtype: _Optional[_Union[MsgCmdType, str]] = ..., sender: _Optional[_Union[MsgDevice, str]] = ..., rcver: _Optional[_Union[MsgDevice, str]] = ..., msgattr: _Optional[_Union[MsgAttr, str]] = ..., seqs: _Optional[int] = ..., version: _Optional[int] = ..., subtype: _Optional[int] = ..., net: _Optional[_Union[_dev_net_pb2.DevNet, _Mapping]] = ..., sys: _Optional[_Union[_mctrl_sys_pb2.MctlSys, _Mapping]] = ..., nav: _Optional[_Union[_mctrl_nav_pb2.MctlNav, _Mapping]] = ..., driver: _Optional[_Union[_mctrl_driver_pb2.MctlDriver, _Mapping]] = ..., ota: _Optional[_Union[_mctrl_ota_pb2.MctlOta, _Mapping]] = ..., mul: _Optional[_Union[_luba_mul_pb2.SocMul, _Mapping]] = ..., null: _Optional[_Union[MsgNull, _Mapping]] = ..., pept: _Optional[_Union[_mctrl_pept_pb2.MctlPept, _Mapping]] = ..., timestamp: _Optional[int] = ...) -> None: ... + def __init__(self, msgtype: _Optional[_Union[MsgCmdType, str]] = ..., sender: _Optional[_Union[MsgDevice, str]] = ..., rcver: _Optional[_Union[MsgDevice, str]] = ..., msgattr: _Optional[_Union[MsgAttr, str]] = ..., seqs: _Optional[int] = ..., version: _Optional[int] = ..., subtype: _Optional[int] = ..., net: _Optional[_Union[_dev_net_pb2.DevNet, _Mapping]] = ..., sys: _Optional[_Union[_mctrl_sys_pb2.MctlSys, _Mapping]] = ..., nav: _Optional[_Union[_mctrl_nav_pb2.MctlNav, _Mapping]] = ..., driver: _Optional[_Union[_mctrl_driver_pb2.MctlDriver, _Mapping]] = ..., ota: _Optional[_Union[_mctrl_ota_pb2.MctlOta, _Mapping]] = ..., mul: _Optional[_Union[_luba_mul_pb2.SocMul, _Mapping]] = ..., null: _Optional[_Union[MsgNull, _Mapping]] = ..., pept: _Optional[_Union[_mctrl_pept_pb2.MctlPept, _Mapping]] = ..., base: _Optional[_Union[_basestation_pb2.BaseStation, _Mapping]] = ..., timestamp: _Optional[int] = ...) -> None: ... class MsgNull(_message.Message): __slots__ = [] diff --git a/pymammotion/proto/mctrl_nav.py b/pymammotion/proto/mctrl_nav.py index b95f99c..3553849 100644 --- a/pymammotion/proto/mctrl_nav.py +++ b/pymammotion/proto/mctrl_nav.py @@ -2,9 +2,9 @@ # sources: pymammotion/proto/mctrl_nav.proto # plugin: python-betterproto from dataclasses import dataclass -import betterproto -from .common import * + +import betterproto @dataclass diff --git a/pymammotion/proto/mctrl_sys.proto b/pymammotion/proto/mctrl_sys.proto index d038344..f1284bd 100644 --- a/pymammotion/proto/mctrl_sys.proto +++ b/pymammotion/proto/mctrl_sys.proto @@ -72,8 +72,8 @@ message SysMowInfo { int32 deviceState = 1; int32 batVal = 2; int32 knifeHeight = 3; - int32 RTKstatus = 4; - int32 RTKstars = 5; + int32 rtk_status = 4; + int32 rtk_stars = 5; } message SysOptiLineAck { diff --git a/pymammotion/proto/mctrl_sys.py b/pymammotion/proto/mctrl_sys.py index b425993..21ba9c3 100644 --- a/pymammotion/proto/mctrl_sys.py +++ b/pymammotion/proto/mctrl_sys.py @@ -6,8 +6,6 @@ import betterproto -from .dev_net import * - class Operation(betterproto.Enum): WRITE = 0 @@ -287,8 +285,8 @@ class SystemUpdateBufMsg(betterproto.Message): @dataclass class SysOffChipFlash(betterproto.Message): - op: Operation = betterproto.enum_field(1) - id: OffPartId = betterproto.enum_field(2) + op: "Operation" = betterproto.enum_field(1) + id: "OffPartId" = betterproto.enum_field(2) start_addr: int = betterproto.uint32_field(3) offset: int = betterproto.uint32_field(4) length: int = betterproto.int32_field(5) @@ -434,10 +432,10 @@ class RptDevStatus(betterproto.Message): last_status: int = betterproto.int32_field(5) sys_time_stamp: int = betterproto.int64_field(6) vslam_status: int = betterproto.int32_field(7) - mnet_info: MnetInfo = betterproto.message_field(8) - vio_survival_info: VioSurvivalInfoT = betterproto.message_field(9) - collector_status: CollectorStatusT = betterproto.message_field(10) - lock_state: LockStateT = betterproto.message_field(11) + mnet_info: "MnetInfo" = betterproto.message_field(8) + vio_survival_info: "VioSurvivalInfoT" = betterproto.message_field(9) + collector_status: "CollectorStatusT" = betterproto.message_field(10) + lock_state: "LockStateT" = betterproto.message_field(11) @dataclass @@ -494,16 +492,16 @@ class ReportInfoCfg(betterproto.Message): @dataclass class ReportInfoData(betterproto.Message): - connect: RptConnectStatus = betterproto.message_field(1) - dev: RptDevStatus = betterproto.message_field(2) - rtk: RptRtk = betterproto.message_field(3) - locations: list[RptDevLocation] = betterproto.message_field(4) - work: RptWork = betterproto.message_field(5) - fw_info: DeviceFwInfo = betterproto.message_field(6) - maintain: RptMaintain = betterproto.message_field(7) - vision_point_info: list[VisionPointInfoMsg] = betterproto.message_field(8) - vio_to_app_info: VioToAppInfoMsg = betterproto.message_field(9) - vision_statistic_info: VisionStatisticInfoMsg = betterproto.message_field(10) + connect: "RptConnectStatus" = betterproto.message_field(1) + dev: "RptDevStatus" = betterproto.message_field(2) + rtk: "RptRtk" = betterproto.message_field(3) + locations: list["RptDevLocation"] = betterproto.message_field(4) + work: "RptWork" = betterproto.message_field(5) + fw_info: "DeviceFwInfo" = betterproto.message_field(6) + maintain: "RptMaintain" = betterproto.message_field(7) + vision_point_info: list["VisionPointInfoMsg"] = betterproto.message_field(8) + vio_to_app_info: "VioToAppInfoMsg" = betterproto.message_field(9) + vision_statistic_info: "VisionStatisticInfoMsg" = betterproto.message_field(10) @dataclass @@ -566,7 +564,7 @@ class MctlSys(betterproto.Message): 37, group="SubSysMsg" ) todev_report_cfg: "ReportInfoCfg" = betterproto.message_field(38, group="SubSysMsg") - toapp_report_data: ReportInfoData = betterproto.message_field( + toapp_report_data: "ReportInfoData" = betterproto.message_field( 39, group="SubSysMsg" ) simulation_cmd: "MCtrlSimulationCmdData" = betterproto.message_field( diff --git a/pymammotion/proto/mctrl_sys_pb2.py b/pymammotion/proto/mctrl_sys_pb2.py index d1438e8..df6ea84 100644 --- a/pymammotion/proto/mctrl_sys_pb2.py +++ b/pymammotion/proto/mctrl_sys_pb2.py @@ -14,25 +14,25 @@ from pymammotion.proto import dev_net_pb2 as pymammotion_dot_proto_dot_dev__net__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!pymammotion/proto/mctrl_sys.proto\x1a\x1fpymammotion/proto/dev_net.proto\"\x1a\n\x08SysBatUp\x12\x0e\n\x06\x62\x61tVal\x18\x01 \x01(\x05\"Z\n\x0cSysWorkState\x12\x13\n\x0b\x64\x65viceState\x18\x01 \x01(\x05\x12\x13\n\x0b\x63hargeState\x18\x02 \x01(\x05\x12\x0e\n\x06\x63mHash\x18\x03 \x01(\x03\x12\x10\n\x08pathHash\x18\x04 \x01(\x03\"5\n\x0eSysSetTimeZone\x12\x11\n\ttimeStamp\x18\x01 \x01(\x05\x12\x10\n\x08timeArea\x18\x02 \x01(\x05\"\x9e\x01\n\x0eSysSetDateTime\x12\x0c\n\x04Year\x18\x01 \x01(\x05\x12\r\n\x05Month\x18\x02 \x01(\x05\x12\x0c\n\x04\x44\x61te\x18\x03 \x01(\x05\x12\x0c\n\x04Week\x18\x04 \x01(\x05\x12\r\n\x05Hours\x18\x05 \x01(\x05\x12\x0f\n\x07Minutes\x18\x06 \x01(\x05\x12\x0f\n\x07Seconds\x18\x07 \x01(\x05\x12\x10\n\x08timeZone\x18\x08 \x01(\x05\x12\x10\n\x08\x64\x61ylight\x18\t \x01(\x05\"V\n\nSysJobPlan\x12\r\n\x05jobId\x18\x01 \x01(\x03\x12\x0f\n\x07jobMode\x18\x02 \x01(\x05\x12\x13\n\x0brainTactics\x18\x03 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x04 \x01(\x05\"\"\n\rSysDevErrCode\x12\x11\n\terrorCode\x18\x01 \x01(\x05\"!\n\x0cSysBoardType\x12\x11\n\tboardType\x18\x01 \x01(\x05\"5\n\x0cSysSwVersion\x12\x11\n\tboardType\x18\x01 \x01(\x05\x12\x12\n\nversionLen\x18\x02 \x01(\x05\"1\n\rSysDelJobPlan\x12\x10\n\x08\x64\x65viceId\x18\x01 \x01(\t\x12\x0e\n\x06planId\x18\x02 \x01(\t\"\xec\x01\n\x0eSysJobPlanTime\x12\x0e\n\x06planId\x18\x01 \x01(\x03\x12\x16\n\x0estart_job_time\x18\x02 \x01(\x05\x12\x14\n\x0c\x65nd_job_time\x18\x03 \x01(\x05\x12\x13\n\x0btime_in_day\x18\x04 \x01(\x05\x12\x15\n\rjob_plan_mode\x18\x05 \x01(\x05\x12\x17\n\x0fjob_plan_enable\x18\x06 \x01(\x05\x12\x0f\n\x07weekDay\x18\x07 \x03(\x05\x12\x15\n\rtimeInWeekDay\x18\x08 \x03(\x05\x12\x10\n\x08\x65veryDay\x18\t \x01(\x05\x12\x1d\n\x08job_plan\x18\n \x01(\x0b\x32\x0b.SysJobPlan\"k\n\nSysMowInfo\x12\x13\n\x0b\x64\x65viceState\x18\x01 \x01(\x05\x12\x0e\n\x06\x62\x61tVal\x18\x02 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x03 \x01(\x05\x12\x11\n\tRTKstatus\x18\x04 \x01(\x05\x12\x10\n\x08RTKstars\x18\x05 \x01(\x05\";\n\x0eSysOptiLineAck\x12\x13\n\x0bresponesCmd\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\"5\n\nSysCommCmd\x12\n\n\x02rw\x18\x01 \x01(\x05\x12\n\n\x02id\x18\x02 \x01(\x05\x12\x0f\n\x07\x63ontext\x18\x03 \x01(\x05\"H\n\x15SysUploadFileProgress\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x10\n\x08progress\x18\x03 \x01(\x05\"\x1f\n\x0cSysErrorCode\x12\x0f\n\x07\x63ode_no\x18\x01 \x01(\x05\"\x1e\n\tSysBorder\x12\x11\n\tborderval\x18\x01 \x01(\x05\"*\n\x10SysPlanJobStatus\x12\x16\n\x0eplanjob_status\x18\x01 \x01(\x05\"=\n\x0fSysKnifeControl\x12\x14\n\x0cknife_status\x18\x01 \x01(\x05\x12\x14\n\x0cknife_height\x18\x02 \x01(\x05\"+\n\x14SysResetSystemStatus\x12\x13\n\x0breset_staus\x18\x01 \x01(\x05\"\x8a\x01\n\rTimeCtrlLight\x12\x0f\n\x07operate\x18\x01 \x01(\x05\x12\x0e\n\x06\x65nable\x18\x02 \x01(\x05\x12\x12\n\nstart_hour\x18\x03 \x01(\x05\x12\x11\n\tstart_min\x18\x04 \x01(\x05\x12\x10\n\x08\x65nd_hour\x18\x05 \x01(\x05\x12\x0f\n\x07\x65nd_min\x18\x06 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\x05\"3\n\x10vision_point_msg\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"\\\n\x15vision_point_info_msg\x12\r\n\x05lable\x18\x01 \x01(\x05\x12\x0b\n\x03num\x18\x02 \x01(\x05\x12\'\n\x0cvision_point\x18\x03 \x03(\x0b\x32\x11.vision_point_msg\"\x9a\x01\n\x13vio_to_app_info_msg\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\x12\x0f\n\x07heading\x18\x03 \x01(\x01\x12\x11\n\tvio_state\x18\x04 \x01(\x05\x12\x12\n\nbrightness\x18\x05 \x01(\x05\x12\x1a\n\x12\x64\x65tect_feature_num\x18\x06 \x01(\x05\x12\x19\n\x11track_feature_num\x18\x07 \x01(\x05\"1\n\x14vision_statistic_msg\x12\x0c\n\x04mean\x18\x01 \x01(\x02\x12\x0b\n\x03var\x18\x02 \x01(\x02\"m\n\x19vision_statistic_info_msg\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12\x0b\n\x03num\x18\x02 \x01(\x05\x12\x30\n\x11vision_statistics\x18\x03 \x03(\x0b\x32\x15.vision_statistic_msg\"\xd3\x01\n\x1asystemRapidStateTunnel_msg\x12\x18\n\x10rapid_state_data\x18\x01 \x03(\x03\x12\x31\n\x11vision_point_info\x18\x02 \x03(\x0b\x32\x16.vision_point_info_msg\x12-\n\x0fvio_to_app_info\x18\x03 \x01(\x0b\x32\x14.vio_to_app_info_msg\x12\x39\n\x15vision_statistic_info\x18\x04 \x01(\x0b\x32\x1a.vision_statistic_info_msg\"4\n\x19systemTardStateTunnel_msg\x12\x17\n\x0ftard_state_data\x18\x01 \x03(\x03\".\n\x13systemUpdateBuf_msg\x12\x17\n\x0fupdate_buf_data\x18\x01 \x03(\x03\"\x9e\x01\n\x0fSysOffChipFlash\x12\x16\n\x02op\x18\x01 \x01(\x0e\x32\n.Operation\x12\x16\n\x02id\x18\x02 \x01(\x0e\x32\n.OffPartId\x12\x12\n\nstart_addr\x18\x03 \x01(\r\x12\x0e\n\x06offset\x18\x04 \x01(\r\x12\x0e\n\x06length\x18\x05 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\x0c\n\x04\x63ode\x18\x07 \x01(\x05\x12\x0b\n\x03msg\x18\x08 \x01(\t\"-\n\x14systemTmpCycleTx_msg\x12\x15\n\rcycle_tx_data\x18\x01 \x03(\x03\"%\n\nLoraCfgReq\x12\n\n\x02op\x18\x01 \x01(\x05\x12\x0b\n\x03\x63\x66g\x18\x02 \x01(\t\"F\n\nLoraCfgRsp\x12\x0e\n\x06result\x18\x01 \x01(\x05\x12\n\n\x02op\x18\x02 \x01(\x05\x12\x0b\n\x03\x63\x66g\x18\x03 \x01(\t\x12\x0f\n\x07\x66\x61\x63_cfg\x18\x04 \x01(\t\">\n\x0bmod_fw_info\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x10\n\x08identify\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"L\n\x0e\x64\x65vice_fw_info\x12\x0e\n\x06result\x18\x01 \x01(\x05\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x19\n\x03mod\x18\x03 \x03(\x0b\x32\x0c.mod_fw_info\"@\n\x11mow_to_app_info_t\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0b\n\x03\x63md\x18\x02 \x01(\x05\x12\x10\n\x08mow_data\x18\x03 \x03(\x05\"a\n\x1a\x64\x65vice_product_type_info_t\x12\x0e\n\x06result\x18\x01 \x01(\x05\x12\x19\n\x11main_product_type\x18\x02 \x01(\t\x12\x18\n\x10sub_product_type\x18\x03 \x01(\t\"P\n\x0fQCAppTestExcept\x12\x13\n\x0b\x65xcept_type\x18\x01 \x01(\t\x12(\n\nconditions\x18\x02 \x03(\x0b\x32\x14.QCAppTestConditions\"t\n\x13QCAppTestConditions\x12\x11\n\tcond_type\x18\x01 \x01(\t\x12\x0f\n\x07int_val\x18\x02 \x01(\x05\x12\x11\n\tfloat_val\x18\x03 \x01(\x02\x12\x12\n\ndouble_val\x18\x04 \x01(\x01\x12\x12\n\nstring_val\x18\x05 \x01(\t\"\x99\x01\n\x19mow_to_app_qctools_info_t\x12\x1a\n\x04type\x18\x01 \x01(\x0e\x32\x0c.QCAppTestId\x12\x16\n\x0etimeOfDuration\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\x12\x16\n\x0eresult_details\x18\x04 \x01(\t\x12 \n\x06\x65xcept\x18\x05 \x03(\x0b\x32\x10.QCAppTestExcept\"O\n\x16mCtrlSimulationCmdData\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x10\n\x08param_id\x18\x02 \x01(\x05\x12\x13\n\x0bparam_value\x18\x03 \x03(\x05\"\x8f\x01\n\x08rpt_lora\x12\x16\n\x0epair_code_scan\x18\x01 \x01(\x05\x12\x19\n\x11pair_code_channel\x18\x02 \x01(\x05\x12\x17\n\x0fpair_code_locid\x18\x03 \x01(\x05\x12\x17\n\x0fpair_code_netid\x18\x04 \x01(\x05\x12\x1e\n\x16lora_connection_status\x18\x05 \x01(\x05\"\xf1\x01\n\x07rpt_rtk\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x11\n\tpos_level\x18\x02 \x01(\x05\x12\x11\n\tgps_stars\x18\x03 \x01(\x05\x12\x0b\n\x03\x61ge\x18\x04 \x01(\x05\x12\x0f\n\x07lat_std\x18\x05 \x01(\x05\x12\x0f\n\x07lon_std\x18\x06 \x01(\x05\x12\x10\n\x08l2_stars\x18\x07 \x01(\x05\x12\x12\n\ndis_status\x18\x08 \x01(\x03\x12\x17\n\x0ftop4_total_mean\x18\t \x01(\x03\x12\x15\n\rco_view_stars\x18\n \x01(\x05\x12\r\n\x05reset\x18\x0b \x01(\x05\x12\x1c\n\tlora_info\x18\x0c \x01(\x0b\x32\t.rpt_lora\"\x86\x01\n\x10rpt_dev_location\x12\x12\n\nreal_pos_x\x18\x01 \x01(\x05\x12\x12\n\nreal_pos_y\x18\x02 \x01(\x05\x12\x13\n\x0breal_toward\x18\x03 \x01(\x05\x12\x10\n\x08pos_type\x18\x04 \x01(\x05\x12\x11\n\tzone_hash\x18\x05 \x01(\x03\x12\x10\n\x08\x62ol_hash\x18\x06 \x01(\x03\"4\n\x13vio_survival_info_t\x12\x1d\n\x15vio_survival_distance\x18\x01 \x01(\x02\";\n\x12\x63ollector_status_t\x12%\n\x1d\x63ollector_installation_status\x18\x01 \x01(\x05\"\"\n\x0clock_state_t\x12\x12\n\nlock_state\x18\x01 \x01(\r\"\xca\x02\n\x0erpt_dev_status\x12\x12\n\nsys_status\x18\x01 \x01(\x05\x12\x14\n\x0c\x63harge_state\x18\x02 \x01(\x05\x12\x13\n\x0b\x62\x61ttery_val\x18\x03 \x01(\x05\x12\x15\n\rsensor_status\x18\x04 \x01(\x05\x12\x13\n\x0blast_status\x18\x05 \x01(\x05\x12\x16\n\x0esys_time_stamp\x18\x06 \x01(\x03\x12\x14\n\x0cvslam_status\x18\x07 \x01(\x05\x12\x1c\n\tmnet_info\x18\x08 \x01(\x0b\x32\t.MnetInfo\x12/\n\x11vio_survival_info\x18\t \x01(\x0b\x32\x14.vio_survival_info_t\x12-\n\x10\x63ollector_status\x18\n \x01(\x0b\x32\x13.collector_status_t\x12!\n\nlock_state\x18\x0b \x01(\x0b\x32\r.lock_state_t\"\xaa\x01\n\x12rpt_connect_status\x12\x14\n\x0c\x63onnect_type\x18\x01 \x01(\x05\x12\x10\n\x08\x62le_rssi\x18\x02 \x01(\x05\x12\x11\n\twifi_rssi\x18\x03 \x01(\x05\x12\x11\n\tlink_type\x18\x04 \x01(\x05\x12\x11\n\tmnet_rssi\x18\x05 \x01(\x05\x12\x11\n\tmnet_inet\x18\x06 \x01(\x05\x12 \n\x08used_net\x18\x07 \x01(\x0e\x32\x0e.net_used_type\"\xa0\x03\n\x08rpt_work\x12\x0c\n\x04plan\x18\x01 \x01(\x05\x12\x11\n\tpath_hash\x18\x02 \x01(\x03\x12\x10\n\x08progress\x18\x03 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x04 \x01(\x05\x12\x0f\n\x07\x62p_info\x18\x05 \x01(\x05\x12\x0f\n\x07\x62p_hash\x18\x06 \x01(\x03\x12\x10\n\x08\x62p_pos_x\x18\x07 \x01(\x05\x12\x10\n\x08\x62p_pos_y\x18\x08 \x01(\x05\x12\x15\n\rreal_path_num\x18\t \x01(\x03\x12\x12\n\npath_pos_x\x18\n \x01(\x05\x12\x12\n\npath_pos_y\x18\x0b \x01(\x05\x12\x14\n\x0cub_zone_hash\x18\x0c \x01(\x03\x12\x14\n\x0cub_path_hash\x18\r \x01(\x03\x12\x15\n\rinit_cfg_hash\x18\x0e \x01(\x03\x12\x15\n\rub_ecode_hash\x18\x0f \x01(\x03\x12\x14\n\x0cnav_run_mode\x18\x10 \x01(\x05\x12\x18\n\x10test_mode_status\x18\x11 \x01(\x03\x12\x15\n\rman_run_speed\x18\x12 \x01(\x05\x12\x17\n\x0fnav_edit_status\x18\x13 \x01(\x05\x12\x14\n\x0cknife_height\x18\x14 \x01(\x05\"F\n\x0crpt_maintain\x12\x0f\n\x07mileage\x18\x01 \x01(\x03\x12\x11\n\twork_time\x18\x02 \x01(\x05\x12\x12\n\nbat_cycles\x18\x03 \x01(\x05\"\x8f\x01\n\x0freport_info_cfg\x12\x15\n\x03\x61\x63t\x18\x01 \x01(\x0e\x32\x08.rpt_act\x12\x0f\n\x07timeout\x18\x02 \x01(\x05\x12\x0e\n\x06period\x18\x03 \x01(\x05\x12\x18\n\x10no_change_period\x18\x04 \x01(\x05\x12\r\n\x05\x63ount\x18\x05 \x01(\x05\x12\x1b\n\x03sub\x18\x06 \x03(\x0e\x32\x0e.rpt_info_type\"\x8c\x03\n\x10report_info_data\x12$\n\x07\x63onnect\x18\x01 \x01(\x0b\x32\x13.rpt_connect_status\x12\x1c\n\x03\x64\x65v\x18\x02 \x01(\x0b\x32\x0f.rpt_dev_status\x12\x15\n\x03rtk\x18\x03 \x01(\x0b\x32\x08.rpt_rtk\x12$\n\tlocations\x18\x04 \x03(\x0b\x32\x11.rpt_dev_location\x12\x17\n\x04work\x18\x05 \x01(\x0b\x32\t.rpt_work\x12 \n\x07\x66w_info\x18\x06 \x01(\x0b\x32\x0f.device_fw_info\x12\x1f\n\x08maintain\x18\x07 \x01(\x0b\x32\r.rpt_maintain\x12\x31\n\x11vision_point_info\x18\x08 \x03(\x0b\x32\x16.vision_point_info_msg\x12-\n\x0fvio_to_app_info\x18\t \x01(\x0b\x32\x14.vio_to_app_info_msg\x12\x39\n\x15vision_statistic_info\x18\n \x01(\x0b\x32\x1a.vision_statistic_info_msg\"\x9c\x0c\n\x07MctlSys\x12\"\n\rtoapp_batinfo\x18\x01 \x01(\x0b\x32\t.SysBatUpH\x00\x12)\n\x10toapp_work_state\x18\x02 \x01(\x0b\x32\r.SysWorkStateH\x00\x12*\n\x0ftodev_time_zone\x18\x03 \x01(\x0b\x32\x0f.SysSetTimeZoneH\x00\x12*\n\x0ftodev_data_time\x18\x04 \x01(\x0b\x32\x0f.SysSetDateTimeH\x00\x12\x1f\n\x08job_plan\x18\x06 \x01(\x0b\x32\x0b.SysJobPlanH\x00\x12(\n\x0etoapp_err_code\x18\x07 \x01(\x0b\x32\x0e.SysDevErrCodeH\x00\x12.\n\x13todev_job_plan_time\x18\n \x01(\x0b\x32\x0f.SysJobPlanTimeH\x00\x12%\n\x0etoapp_mow_info\x18\x0b \x01(\x0b\x32\x0b.SysMowInfoH\x00\x12&\n\x0f\x62idire_comm_cmd\x18\x0c \x01(\x0b\x32\x0b.SysCommCmdH\x00\x12\x16\n\x0cplan_job_del\x18\x0e \x01(\x03H\x00\x12\x1c\n\x06\x62order\x18\x0f \x01(\x0b\x32\n.SysBorderH\x00\x12.\n\x11toapp_plan_status\x18\x12 \x01(\x0b\x32\x11.SysPlanJobStatusH\x00\x12\x34\n\x12toapp_ul_fprogress\x18\x13 \x01(\x0b\x32\x16.SysUploadFileProgressH\x00\x12*\n\x10todev_deljobplan\x18\x14 \x01(\x0b\x32\x0e.SysDelJobPlanH\x00\x12\x1b\n\x11todev_mow_info_up\x18\x15 \x01(\x05H\x00\x12,\n\x10todev_knife_ctrl\x18\x16 \x01(\x0b\x32\x10.SysKnifeControlH\x00\x12\x1c\n\x12todev_reset_system\x18\x17 \x01(\x05H\x00\x12:\n\x19todev_reset_system_status\x18\x18 \x01(\x0b\x32\x15.SysResetSystemStatusH\x00\x12=\n\x16systemRapidStateTunnel\x18\x19 \x01(\x0b\x32\x1b.systemRapidStateTunnel_msgH\x00\x12;\n\x15systemTardStateTunnel\x18\x1a \x01(\x0b\x32\x1a.systemTardStateTunnel_msgH\x00\x12/\n\x0fsystemUpdateBuf\x18\x1b \x01(\x0b\x32\x14.systemUpdateBuf_msgH\x00\x12/\n\x15todev_time_ctrl_light\x18\x1c \x01(\x0b\x32\x0e.TimeCtrlLightH\x00\x12\x31\n\x10systemTmpCycleTx\x18\x1d \x01(\x0b\x32\x15.systemTmpCycleTx_msgH\x00\x12\x30\n\x14todev_off_chip_flash\x18\x1e \x01(\x0b\x32\x10.SysOffChipFlashH\x00\x12\x1f\n\x15todev_get_dev_fw_info\x18\x1f \x01(\x05H\x00\x12,\n\x11toapp_dev_fw_info\x18 \x01(\x0b\x32\x0f.device_fw_infoH\x00\x12)\n\x12todev_lora_cfg_req\x18! \x01(\x0b\x32\x0b.LoraCfgReqH\x00\x12)\n\x12toapp_lora_cfg_rsp\x18\" \x01(\x0b\x32\x0b.LoraCfgRspH\x00\x12-\n\x0fmow_to_app_info\x18# \x01(\x0b\x32\x12.mow_to_app_info_tH\x00\x12?\n\x18\x64\x65vice_product_type_info\x18$ \x01(\x0b\x32\x1b.device_product_type_info_tH\x00\x12=\n\x17mow_to_app_qctools_info\x18% \x01(\x0b\x32\x1a.mow_to_app_qctools_info_tH\x00\x12,\n\x10todev_report_cfg\x18& \x01(\x0b\x32\x10.report_info_cfgH\x00\x12.\n\x11toapp_report_data\x18\' \x01(\x0b\x32\x11.report_info_dataH\x00\x12\x31\n\x0esimulation_cmd\x18* \x01(\x0b\x32\x17.mCtrlSimulationCmdDataH\x00\x42\x0b\n\tSubSysMsg*+\n\tOperation\x12\t\n\x05WRITE\x10\x00\x12\x08\n\x04READ\x10\x01\x12\t\n\x05\x45RASE\x10\x02*\x8d\x02\n\tOffPartId\x12\x13\n\x0fOFF_PART_DL_IMG\x10\x00\x12\x19\n\x15OFF_PART_UPDINFO_BACK\x10\x01\x12\x14\n\x10OFF_PART_UPDINFO\x10\x02\x12\x13\n\x0fOFF_PART_NAKEDB\x10\x03\x12\x14\n\x10OFF_PART_FLASHDB\x10\x04\x12\x18\n\x14OFF_PART_UPD_APP_IMG\x10\x05\x12\x18\n\x14OFF_PART_UPD_BMS_IMG\x10\x06\x12\x18\n\x14OFF_PART_UPD_TMP_IMG\x10\x07\x12\x15\n\x11OFF_PART_DEV_INFO\x10\x08\x12\x18\n\x14OFF_PART_NAKEDB_BACK\x10\t\x12\x10\n\x0cOFF_PART_MAX\x10\n*\x95\x07\n\x0bQCAppTestId\x12!\n\x1dQC_APP_ITEM_ON_CHARGESATSTION\x10\x00\x12\x1a\n\x16QC_APP_TEST_X3_SPEAKER\x10\x01\x12)\n%QC_APP_TEST_STATIC_OBSTACLE_DETECTION\x10\x02\x12\"\n\x1eQC_APP_TEST_CHARGESTATION_TEMP\x10\x03\x12\x13\n\x0fQC_APP_ITEM_KEY\x10\x04\x12 \n\x1cQC_APP_TEST_BUMPER_FRONTLEFT\x10\x05\x12!\n\x1dQC_APP_TEST_BUMPER_FRONTRIGHT\x10\x06\x12\x14\n\x10QC_APP_TEST_STOP\x10\x07\x12\x16\n\x12QC_APP_TEST_UNLOCK\x10\x08\x12\x14\n\x10QC_APP_TEST_BUZZ\x10\t\x12\x14\n\x10QC_APP_TEST_LIFT\x10\n\x12\x16\n\x12QC_APP_ITEM_SENEOR\x10\x0b\x12\x19\n\x15QC_APP_TEST_ROLL_LEFT\x10\x0c\x12\x1a\n\x16QC_APP_TEST_ROLL_RIGHT\x10\r\x12\x1d\n\x19QC_APP_TEST_ULTRA_UNCOVER\x10\x0e\x12\x1c\n\x18QC_APP_TEST_ULTRA0_COVER\x10\x0f\x12\x1c\n\x18QC_APP_TEST_ULTRA1_COVER\x10\x10\x12\x1c\n\x18QC_APP_TEST_ULTRA2_COVER\x10\x11\x12\x14\n\x10QC_APP_TEST_RAIN\x10\x12\x12\x12\n\x0eQC_APP_ITEM_SQ\x10\x13\x12\x18\n\x14QC_APP_TEST_BLE_RSSI\x10\x14\x12 \n\x1cQC_APP_TEST_SATELLITES_ROVER\x10\x15\x12)\n%QC_APP_TEST_SATELLITES_REF_STATION_L1\x10\x16\x12)\n%QC_APP_TEST_SATELLITES_REF_STATION_L2\x10\x17\x12&\n\"QC_APP_TEST_SATELLITES_COMMON_VIEW\x10\x18\x12\x19\n\x15QC_APP_TEST_CNO_ROVER\x10\x19\x12\x1f\n\x1bQC_APP_TEST_CNO_REF_STATION\x10\x1a\x12\'\n#QC_APP_TEST_REF_STATION_LINK_STATUS\x10\x1b\x12\x1e\n\x1aQC_APP_TEST_LOCATION_STATE\x10\x1c\x12\x13\n\x0fQC_APP_TEST_MAX\x10\x1d*W\n\rnet_used_type\x12\x16\n\x12NET_USED_TYPE_NONE\x10\x00\x12\x16\n\x12NET_USED_TYPE_WIFI\x10\x01\x12\x16\n\x12NET_USED_TYPE_MNET\x10\x02*\xbf\x01\n\rrpt_info_type\x12\x0f\n\x0bRIT_CONNECT\x10\x00\x12\x0f\n\x0bRIT_DEV_STA\x10\x01\x12\x0b\n\x07RIT_RTK\x10\x02\x12\x11\n\rRIT_DEV_LOCAL\x10\x03\x12\x0c\n\x08RIT_WORK\x10\x04\x12\x0f\n\x0bRIT_FW_INFO\x10\x05\x12\x10\n\x0cRIT_MAINTAIN\x10\x06\x12\x14\n\x10RIT_VISION_POINT\x10\x07\x12\x0b\n\x07RIT_VIO\x10\x08\x12\x18\n\x14RIT_VISION_STATISTIC\x10\t*4\n\x07rpt_act\x12\r\n\tRPT_START\x10\x00\x12\x0c\n\x08RPT_STOP\x10\x01\x12\x0c\n\x08RPT_KEEP\x10\x02\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!pymammotion/proto/mctrl_sys.proto\x1a\x1fpymammotion/proto/dev_net.proto\"\x1a\n\x08SysBatUp\x12\x0e\n\x06\x62\x61tVal\x18\x01 \x01(\x05\"Z\n\x0cSysWorkState\x12\x13\n\x0b\x64\x65viceState\x18\x01 \x01(\x05\x12\x13\n\x0b\x63hargeState\x18\x02 \x01(\x05\x12\x0e\n\x06\x63mHash\x18\x03 \x01(\x03\x12\x10\n\x08pathHash\x18\x04 \x01(\x03\"5\n\x0eSysSetTimeZone\x12\x11\n\ttimeStamp\x18\x01 \x01(\x05\x12\x10\n\x08timeArea\x18\x02 \x01(\x05\"\x9e\x01\n\x0eSysSetDateTime\x12\x0c\n\x04Year\x18\x01 \x01(\x05\x12\r\n\x05Month\x18\x02 \x01(\x05\x12\x0c\n\x04\x44\x61te\x18\x03 \x01(\x05\x12\x0c\n\x04Week\x18\x04 \x01(\x05\x12\r\n\x05Hours\x18\x05 \x01(\x05\x12\x0f\n\x07Minutes\x18\x06 \x01(\x05\x12\x0f\n\x07Seconds\x18\x07 \x01(\x05\x12\x10\n\x08timeZone\x18\x08 \x01(\x05\x12\x10\n\x08\x64\x61ylight\x18\t \x01(\x05\"V\n\nSysJobPlan\x12\r\n\x05jobId\x18\x01 \x01(\x03\x12\x0f\n\x07jobMode\x18\x02 \x01(\x05\x12\x13\n\x0brainTactics\x18\x03 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x04 \x01(\x05\"\"\n\rSysDevErrCode\x12\x11\n\terrorCode\x18\x01 \x01(\x05\"!\n\x0cSysBoardType\x12\x11\n\tboardType\x18\x01 \x01(\x05\"5\n\x0cSysSwVersion\x12\x11\n\tboardType\x18\x01 \x01(\x05\x12\x12\n\nversionLen\x18\x02 \x01(\x05\"1\n\rSysDelJobPlan\x12\x10\n\x08\x64\x65viceId\x18\x01 \x01(\t\x12\x0e\n\x06planId\x18\x02 \x01(\t\"\xec\x01\n\x0eSysJobPlanTime\x12\x0e\n\x06planId\x18\x01 \x01(\x03\x12\x16\n\x0estart_job_time\x18\x02 \x01(\x05\x12\x14\n\x0c\x65nd_job_time\x18\x03 \x01(\x05\x12\x13\n\x0btime_in_day\x18\x04 \x01(\x05\x12\x15\n\rjob_plan_mode\x18\x05 \x01(\x05\x12\x17\n\x0fjob_plan_enable\x18\x06 \x01(\x05\x12\x0f\n\x07weekDay\x18\x07 \x03(\x05\x12\x15\n\rtimeInWeekDay\x18\x08 \x03(\x05\x12\x10\n\x08\x65veryDay\x18\t \x01(\x05\x12\x1d\n\x08job_plan\x18\n \x01(\x0b\x32\x0b.SysJobPlan\"m\n\nSysMowInfo\x12\x13\n\x0b\x64\x65viceState\x18\x01 \x01(\x05\x12\x0e\n\x06\x62\x61tVal\x18\x02 \x01(\x05\x12\x13\n\x0bknifeHeight\x18\x03 \x01(\x05\x12\x12\n\nrtk_status\x18\x04 \x01(\x05\x12\x11\n\trtk_stars\x18\x05 \x01(\x05\";\n\x0eSysOptiLineAck\x12\x13\n\x0bresponesCmd\x18\x01 \x01(\x05\x12\x14\n\x0c\x63urrentFrame\x18\x02 \x01(\x05\"5\n\nSysCommCmd\x12\n\n\x02rw\x18\x01 \x01(\x05\x12\n\n\x02id\x18\x02 \x01(\x05\x12\x0f\n\x07\x63ontext\x18\x03 \x01(\x05\"H\n\x15SysUploadFileProgress\x12\r\n\x05\x62izId\x18\x01 \x01(\t\x12\x0e\n\x06result\x18\x02 \x01(\x05\x12\x10\n\x08progress\x18\x03 \x01(\x05\"\x1f\n\x0cSysErrorCode\x12\x0f\n\x07\x63ode_no\x18\x01 \x01(\x05\"\x1e\n\tSysBorder\x12\x11\n\tborderval\x18\x01 \x01(\x05\"*\n\x10SysPlanJobStatus\x12\x16\n\x0eplanjob_status\x18\x01 \x01(\x05\"=\n\x0fSysKnifeControl\x12\x14\n\x0cknife_status\x18\x01 \x01(\x05\x12\x14\n\x0cknife_height\x18\x02 \x01(\x05\"+\n\x14SysResetSystemStatus\x12\x13\n\x0breset_staus\x18\x01 \x01(\x05\"\x8a\x01\n\rTimeCtrlLight\x12\x0f\n\x07operate\x18\x01 \x01(\x05\x12\x0e\n\x06\x65nable\x18\x02 \x01(\x05\x12\x12\n\nstart_hour\x18\x03 \x01(\x05\x12\x11\n\tstart_min\x18\x04 \x01(\x05\x12\x10\n\x08\x65nd_hour\x18\x05 \x01(\x05\x12\x0f\n\x07\x65nd_min\x18\x06 \x01(\x05\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\x05\"3\n\x10vision_point_msg\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"\\\n\x15vision_point_info_msg\x12\r\n\x05lable\x18\x01 \x01(\x05\x12\x0b\n\x03num\x18\x02 \x01(\x05\x12\'\n\x0cvision_point\x18\x03 \x03(\x0b\x32\x11.vision_point_msg\"\x9a\x01\n\x13vio_to_app_info_msg\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\x12\x0f\n\x07heading\x18\x03 \x01(\x01\x12\x11\n\tvio_state\x18\x04 \x01(\x05\x12\x12\n\nbrightness\x18\x05 \x01(\x05\x12\x1a\n\x12\x64\x65tect_feature_num\x18\x06 \x01(\x05\x12\x19\n\x11track_feature_num\x18\x07 \x01(\x05\"1\n\x14vision_statistic_msg\x12\x0c\n\x04mean\x18\x01 \x01(\x02\x12\x0b\n\x03var\x18\x02 \x01(\x02\"m\n\x19vision_statistic_info_msg\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12\x0b\n\x03num\x18\x02 \x01(\x05\x12\x30\n\x11vision_statistics\x18\x03 \x03(\x0b\x32\x15.vision_statistic_msg\"\xd3\x01\n\x1asystemRapidStateTunnel_msg\x12\x18\n\x10rapid_state_data\x18\x01 \x03(\x03\x12\x31\n\x11vision_point_info\x18\x02 \x03(\x0b\x32\x16.vision_point_info_msg\x12-\n\x0fvio_to_app_info\x18\x03 \x01(\x0b\x32\x14.vio_to_app_info_msg\x12\x39\n\x15vision_statistic_info\x18\x04 \x01(\x0b\x32\x1a.vision_statistic_info_msg\"4\n\x19systemTardStateTunnel_msg\x12\x17\n\x0ftard_state_data\x18\x01 \x03(\x03\".\n\x13systemUpdateBuf_msg\x12\x17\n\x0fupdate_buf_data\x18\x01 \x03(\x03\"\x9e\x01\n\x0fSysOffChipFlash\x12\x16\n\x02op\x18\x01 \x01(\x0e\x32\n.Operation\x12\x16\n\x02id\x18\x02 \x01(\x0e\x32\n.OffPartId\x12\x12\n\nstart_addr\x18\x03 \x01(\r\x12\x0e\n\x06offset\x18\x04 \x01(\r\x12\x0e\n\x06length\x18\x05 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\x0c\n\x04\x63ode\x18\x07 \x01(\x05\x12\x0b\n\x03msg\x18\x08 \x01(\t\"-\n\x14systemTmpCycleTx_msg\x12\x15\n\rcycle_tx_data\x18\x01 \x03(\x03\"%\n\nLoraCfgReq\x12\n\n\x02op\x18\x01 \x01(\x05\x12\x0b\n\x03\x63\x66g\x18\x02 \x01(\t\"F\n\nLoraCfgRsp\x12\x0e\n\x06result\x18\x01 \x01(\x05\x12\n\n\x02op\x18\x02 \x01(\x05\x12\x0b\n\x03\x63\x66g\x18\x03 \x01(\t\x12\x0f\n\x07\x66\x61\x63_cfg\x18\x04 \x01(\t\">\n\x0bmod_fw_info\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x10\n\x08identify\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"L\n\x0e\x64\x65vice_fw_info\x12\x0e\n\x06result\x18\x01 \x01(\x05\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x19\n\x03mod\x18\x03 \x03(\x0b\x32\x0c.mod_fw_info\"@\n\x11mow_to_app_info_t\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0b\n\x03\x63md\x18\x02 \x01(\x05\x12\x10\n\x08mow_data\x18\x03 \x03(\x05\"a\n\x1a\x64\x65vice_product_type_info_t\x12\x0e\n\x06result\x18\x01 \x01(\x05\x12\x19\n\x11main_product_type\x18\x02 \x01(\t\x12\x18\n\x10sub_product_type\x18\x03 \x01(\t\"P\n\x0fQCAppTestExcept\x12\x13\n\x0b\x65xcept_type\x18\x01 \x01(\t\x12(\n\nconditions\x18\x02 \x03(\x0b\x32\x14.QCAppTestConditions\"t\n\x13QCAppTestConditions\x12\x11\n\tcond_type\x18\x01 \x01(\t\x12\x0f\n\x07int_val\x18\x02 \x01(\x05\x12\x11\n\tfloat_val\x18\x03 \x01(\x02\x12\x12\n\ndouble_val\x18\x04 \x01(\x01\x12\x12\n\nstring_val\x18\x05 \x01(\t\"\x99\x01\n\x19mow_to_app_qctools_info_t\x12\x1a\n\x04type\x18\x01 \x01(\x0e\x32\x0c.QCAppTestId\x12\x16\n\x0etimeOfDuration\x18\x02 \x01(\x05\x12\x0e\n\x06result\x18\x03 \x01(\x05\x12\x16\n\x0eresult_details\x18\x04 \x01(\t\x12 \n\x06\x65xcept\x18\x05 \x03(\x0b\x32\x10.QCAppTestExcept\"O\n\x16mCtrlSimulationCmdData\x12\x0e\n\x06subCmd\x18\x01 \x01(\x05\x12\x10\n\x08param_id\x18\x02 \x01(\x05\x12\x13\n\x0bparam_value\x18\x03 \x03(\x05\"\x8f\x01\n\x08rpt_lora\x12\x16\n\x0epair_code_scan\x18\x01 \x01(\x05\x12\x19\n\x11pair_code_channel\x18\x02 \x01(\x05\x12\x17\n\x0fpair_code_locid\x18\x03 \x01(\x05\x12\x17\n\x0fpair_code_netid\x18\x04 \x01(\x05\x12\x1e\n\x16lora_connection_status\x18\x05 \x01(\x05\"\xf1\x01\n\x07rpt_rtk\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x11\n\tpos_level\x18\x02 \x01(\x05\x12\x11\n\tgps_stars\x18\x03 \x01(\x05\x12\x0b\n\x03\x61ge\x18\x04 \x01(\x05\x12\x0f\n\x07lat_std\x18\x05 \x01(\x05\x12\x0f\n\x07lon_std\x18\x06 \x01(\x05\x12\x10\n\x08l2_stars\x18\x07 \x01(\x05\x12\x12\n\ndis_status\x18\x08 \x01(\x03\x12\x17\n\x0ftop4_total_mean\x18\t \x01(\x03\x12\x15\n\rco_view_stars\x18\n \x01(\x05\x12\r\n\x05reset\x18\x0b \x01(\x05\x12\x1c\n\tlora_info\x18\x0c \x01(\x0b\x32\t.rpt_lora\"\x86\x01\n\x10rpt_dev_location\x12\x12\n\nreal_pos_x\x18\x01 \x01(\x05\x12\x12\n\nreal_pos_y\x18\x02 \x01(\x05\x12\x13\n\x0breal_toward\x18\x03 \x01(\x05\x12\x10\n\x08pos_type\x18\x04 \x01(\x05\x12\x11\n\tzone_hash\x18\x05 \x01(\x03\x12\x10\n\x08\x62ol_hash\x18\x06 \x01(\x03\"4\n\x13vio_survival_info_t\x12\x1d\n\x15vio_survival_distance\x18\x01 \x01(\x02\";\n\x12\x63ollector_status_t\x12%\n\x1d\x63ollector_installation_status\x18\x01 \x01(\x05\"\"\n\x0clock_state_t\x12\x12\n\nlock_state\x18\x01 \x01(\r\"\xca\x02\n\x0erpt_dev_status\x12\x12\n\nsys_status\x18\x01 \x01(\x05\x12\x14\n\x0c\x63harge_state\x18\x02 \x01(\x05\x12\x13\n\x0b\x62\x61ttery_val\x18\x03 \x01(\x05\x12\x15\n\rsensor_status\x18\x04 \x01(\x05\x12\x13\n\x0blast_status\x18\x05 \x01(\x05\x12\x16\n\x0esys_time_stamp\x18\x06 \x01(\x03\x12\x14\n\x0cvslam_status\x18\x07 \x01(\x05\x12\x1c\n\tmnet_info\x18\x08 \x01(\x0b\x32\t.MnetInfo\x12/\n\x11vio_survival_info\x18\t \x01(\x0b\x32\x14.vio_survival_info_t\x12-\n\x10\x63ollector_status\x18\n \x01(\x0b\x32\x13.collector_status_t\x12!\n\nlock_state\x18\x0b \x01(\x0b\x32\r.lock_state_t\"\xaa\x01\n\x12rpt_connect_status\x12\x14\n\x0c\x63onnect_type\x18\x01 \x01(\x05\x12\x10\n\x08\x62le_rssi\x18\x02 \x01(\x05\x12\x11\n\twifi_rssi\x18\x03 \x01(\x05\x12\x11\n\tlink_type\x18\x04 \x01(\x05\x12\x11\n\tmnet_rssi\x18\x05 \x01(\x05\x12\x11\n\tmnet_inet\x18\x06 \x01(\x05\x12 \n\x08used_net\x18\x07 \x01(\x0e\x32\x0e.net_used_type\"\xa0\x03\n\x08rpt_work\x12\x0c\n\x04plan\x18\x01 \x01(\x05\x12\x11\n\tpath_hash\x18\x02 \x01(\x03\x12\x10\n\x08progress\x18\x03 \x01(\x05\x12\x0c\n\x04\x61rea\x18\x04 \x01(\x05\x12\x0f\n\x07\x62p_info\x18\x05 \x01(\x05\x12\x0f\n\x07\x62p_hash\x18\x06 \x01(\x03\x12\x10\n\x08\x62p_pos_x\x18\x07 \x01(\x05\x12\x10\n\x08\x62p_pos_y\x18\x08 \x01(\x05\x12\x15\n\rreal_path_num\x18\t \x01(\x03\x12\x12\n\npath_pos_x\x18\n \x01(\x05\x12\x12\n\npath_pos_y\x18\x0b \x01(\x05\x12\x14\n\x0cub_zone_hash\x18\x0c \x01(\x03\x12\x14\n\x0cub_path_hash\x18\r \x01(\x03\x12\x15\n\rinit_cfg_hash\x18\x0e \x01(\x03\x12\x15\n\rub_ecode_hash\x18\x0f \x01(\x03\x12\x14\n\x0cnav_run_mode\x18\x10 \x01(\x05\x12\x18\n\x10test_mode_status\x18\x11 \x01(\x03\x12\x15\n\rman_run_speed\x18\x12 \x01(\x05\x12\x17\n\x0fnav_edit_status\x18\x13 \x01(\x05\x12\x14\n\x0cknife_height\x18\x14 \x01(\x05\"F\n\x0crpt_maintain\x12\x0f\n\x07mileage\x18\x01 \x01(\x03\x12\x11\n\twork_time\x18\x02 \x01(\x05\x12\x12\n\nbat_cycles\x18\x03 \x01(\x05\"\x8f\x01\n\x0freport_info_cfg\x12\x15\n\x03\x61\x63t\x18\x01 \x01(\x0e\x32\x08.rpt_act\x12\x0f\n\x07timeout\x18\x02 \x01(\x05\x12\x0e\n\x06period\x18\x03 \x01(\x05\x12\x18\n\x10no_change_period\x18\x04 \x01(\x05\x12\r\n\x05\x63ount\x18\x05 \x01(\x05\x12\x1b\n\x03sub\x18\x06 \x03(\x0e\x32\x0e.rpt_info_type\"\x8c\x03\n\x10report_info_data\x12$\n\x07\x63onnect\x18\x01 \x01(\x0b\x32\x13.rpt_connect_status\x12\x1c\n\x03\x64\x65v\x18\x02 \x01(\x0b\x32\x0f.rpt_dev_status\x12\x15\n\x03rtk\x18\x03 \x01(\x0b\x32\x08.rpt_rtk\x12$\n\tlocations\x18\x04 \x03(\x0b\x32\x11.rpt_dev_location\x12\x17\n\x04work\x18\x05 \x01(\x0b\x32\t.rpt_work\x12 \n\x07\x66w_info\x18\x06 \x01(\x0b\x32\x0f.device_fw_info\x12\x1f\n\x08maintain\x18\x07 \x01(\x0b\x32\r.rpt_maintain\x12\x31\n\x11vision_point_info\x18\x08 \x03(\x0b\x32\x16.vision_point_info_msg\x12-\n\x0fvio_to_app_info\x18\t \x01(\x0b\x32\x14.vio_to_app_info_msg\x12\x39\n\x15vision_statistic_info\x18\n \x01(\x0b\x32\x1a.vision_statistic_info_msg\"\x9c\x0c\n\x07MctlSys\x12\"\n\rtoapp_batinfo\x18\x01 \x01(\x0b\x32\t.SysBatUpH\x00\x12)\n\x10toapp_work_state\x18\x02 \x01(\x0b\x32\r.SysWorkStateH\x00\x12*\n\x0ftodev_time_zone\x18\x03 \x01(\x0b\x32\x0f.SysSetTimeZoneH\x00\x12*\n\x0ftodev_data_time\x18\x04 \x01(\x0b\x32\x0f.SysSetDateTimeH\x00\x12\x1f\n\x08job_plan\x18\x06 \x01(\x0b\x32\x0b.SysJobPlanH\x00\x12(\n\x0etoapp_err_code\x18\x07 \x01(\x0b\x32\x0e.SysDevErrCodeH\x00\x12.\n\x13todev_job_plan_time\x18\n \x01(\x0b\x32\x0f.SysJobPlanTimeH\x00\x12%\n\x0etoapp_mow_info\x18\x0b \x01(\x0b\x32\x0b.SysMowInfoH\x00\x12&\n\x0f\x62idire_comm_cmd\x18\x0c \x01(\x0b\x32\x0b.SysCommCmdH\x00\x12\x16\n\x0cplan_job_del\x18\x0e \x01(\x03H\x00\x12\x1c\n\x06\x62order\x18\x0f \x01(\x0b\x32\n.SysBorderH\x00\x12.\n\x11toapp_plan_status\x18\x12 \x01(\x0b\x32\x11.SysPlanJobStatusH\x00\x12\x34\n\x12toapp_ul_fprogress\x18\x13 \x01(\x0b\x32\x16.SysUploadFileProgressH\x00\x12*\n\x10todev_deljobplan\x18\x14 \x01(\x0b\x32\x0e.SysDelJobPlanH\x00\x12\x1b\n\x11todev_mow_info_up\x18\x15 \x01(\x05H\x00\x12,\n\x10todev_knife_ctrl\x18\x16 \x01(\x0b\x32\x10.SysKnifeControlH\x00\x12\x1c\n\x12todev_reset_system\x18\x17 \x01(\x05H\x00\x12:\n\x19todev_reset_system_status\x18\x18 \x01(\x0b\x32\x15.SysResetSystemStatusH\x00\x12=\n\x16systemRapidStateTunnel\x18\x19 \x01(\x0b\x32\x1b.systemRapidStateTunnel_msgH\x00\x12;\n\x15systemTardStateTunnel\x18\x1a \x01(\x0b\x32\x1a.systemTardStateTunnel_msgH\x00\x12/\n\x0fsystemUpdateBuf\x18\x1b \x01(\x0b\x32\x14.systemUpdateBuf_msgH\x00\x12/\n\x15todev_time_ctrl_light\x18\x1c \x01(\x0b\x32\x0e.TimeCtrlLightH\x00\x12\x31\n\x10systemTmpCycleTx\x18\x1d \x01(\x0b\x32\x15.systemTmpCycleTx_msgH\x00\x12\x30\n\x14todev_off_chip_flash\x18\x1e \x01(\x0b\x32\x10.SysOffChipFlashH\x00\x12\x1f\n\x15todev_get_dev_fw_info\x18\x1f \x01(\x05H\x00\x12,\n\x11toapp_dev_fw_info\x18 \x01(\x0b\x32\x0f.device_fw_infoH\x00\x12)\n\x12todev_lora_cfg_req\x18! \x01(\x0b\x32\x0b.LoraCfgReqH\x00\x12)\n\x12toapp_lora_cfg_rsp\x18\" \x01(\x0b\x32\x0b.LoraCfgRspH\x00\x12-\n\x0fmow_to_app_info\x18# \x01(\x0b\x32\x12.mow_to_app_info_tH\x00\x12?\n\x18\x64\x65vice_product_type_info\x18$ \x01(\x0b\x32\x1b.device_product_type_info_tH\x00\x12=\n\x17mow_to_app_qctools_info\x18% \x01(\x0b\x32\x1a.mow_to_app_qctools_info_tH\x00\x12,\n\x10todev_report_cfg\x18& \x01(\x0b\x32\x10.report_info_cfgH\x00\x12.\n\x11toapp_report_data\x18\' \x01(\x0b\x32\x11.report_info_dataH\x00\x12\x31\n\x0esimulation_cmd\x18* \x01(\x0b\x32\x17.mCtrlSimulationCmdDataH\x00\x42\x0b\n\tSubSysMsg*+\n\tOperation\x12\t\n\x05WRITE\x10\x00\x12\x08\n\x04READ\x10\x01\x12\t\n\x05\x45RASE\x10\x02*\x8d\x02\n\tOffPartId\x12\x13\n\x0fOFF_PART_DL_IMG\x10\x00\x12\x19\n\x15OFF_PART_UPDINFO_BACK\x10\x01\x12\x14\n\x10OFF_PART_UPDINFO\x10\x02\x12\x13\n\x0fOFF_PART_NAKEDB\x10\x03\x12\x14\n\x10OFF_PART_FLASHDB\x10\x04\x12\x18\n\x14OFF_PART_UPD_APP_IMG\x10\x05\x12\x18\n\x14OFF_PART_UPD_BMS_IMG\x10\x06\x12\x18\n\x14OFF_PART_UPD_TMP_IMG\x10\x07\x12\x15\n\x11OFF_PART_DEV_INFO\x10\x08\x12\x18\n\x14OFF_PART_NAKEDB_BACK\x10\t\x12\x10\n\x0cOFF_PART_MAX\x10\n*\x95\x07\n\x0bQCAppTestId\x12!\n\x1dQC_APP_ITEM_ON_CHARGESATSTION\x10\x00\x12\x1a\n\x16QC_APP_TEST_X3_SPEAKER\x10\x01\x12)\n%QC_APP_TEST_STATIC_OBSTACLE_DETECTION\x10\x02\x12\"\n\x1eQC_APP_TEST_CHARGESTATION_TEMP\x10\x03\x12\x13\n\x0fQC_APP_ITEM_KEY\x10\x04\x12 \n\x1cQC_APP_TEST_BUMPER_FRONTLEFT\x10\x05\x12!\n\x1dQC_APP_TEST_BUMPER_FRONTRIGHT\x10\x06\x12\x14\n\x10QC_APP_TEST_STOP\x10\x07\x12\x16\n\x12QC_APP_TEST_UNLOCK\x10\x08\x12\x14\n\x10QC_APP_TEST_BUZZ\x10\t\x12\x14\n\x10QC_APP_TEST_LIFT\x10\n\x12\x16\n\x12QC_APP_ITEM_SENEOR\x10\x0b\x12\x19\n\x15QC_APP_TEST_ROLL_LEFT\x10\x0c\x12\x1a\n\x16QC_APP_TEST_ROLL_RIGHT\x10\r\x12\x1d\n\x19QC_APP_TEST_ULTRA_UNCOVER\x10\x0e\x12\x1c\n\x18QC_APP_TEST_ULTRA0_COVER\x10\x0f\x12\x1c\n\x18QC_APP_TEST_ULTRA1_COVER\x10\x10\x12\x1c\n\x18QC_APP_TEST_ULTRA2_COVER\x10\x11\x12\x14\n\x10QC_APP_TEST_RAIN\x10\x12\x12\x12\n\x0eQC_APP_ITEM_SQ\x10\x13\x12\x18\n\x14QC_APP_TEST_BLE_RSSI\x10\x14\x12 \n\x1cQC_APP_TEST_SATELLITES_ROVER\x10\x15\x12)\n%QC_APP_TEST_SATELLITES_REF_STATION_L1\x10\x16\x12)\n%QC_APP_TEST_SATELLITES_REF_STATION_L2\x10\x17\x12&\n\"QC_APP_TEST_SATELLITES_COMMON_VIEW\x10\x18\x12\x19\n\x15QC_APP_TEST_CNO_ROVER\x10\x19\x12\x1f\n\x1bQC_APP_TEST_CNO_REF_STATION\x10\x1a\x12\'\n#QC_APP_TEST_REF_STATION_LINK_STATUS\x10\x1b\x12\x1e\n\x1aQC_APP_TEST_LOCATION_STATE\x10\x1c\x12\x13\n\x0fQC_APP_TEST_MAX\x10\x1d*W\n\rnet_used_type\x12\x16\n\x12NET_USED_TYPE_NONE\x10\x00\x12\x16\n\x12NET_USED_TYPE_WIFI\x10\x01\x12\x16\n\x12NET_USED_TYPE_MNET\x10\x02*\xbf\x01\n\rrpt_info_type\x12\x0f\n\x0bRIT_CONNECT\x10\x00\x12\x0f\n\x0bRIT_DEV_STA\x10\x01\x12\x0b\n\x07RIT_RTK\x10\x02\x12\x11\n\rRIT_DEV_LOCAL\x10\x03\x12\x0c\n\x08RIT_WORK\x10\x04\x12\x0f\n\x0bRIT_FW_INFO\x10\x05\x12\x10\n\x0cRIT_MAINTAIN\x10\x06\x12\x14\n\x10RIT_VISION_POINT\x10\x07\x12\x0b\n\x07RIT_VIO\x10\x08\x12\x18\n\x14RIT_VISION_STATISTIC\x10\t*4\n\x07rpt_act\x12\r\n\tRPT_START\x10\x00\x12\x0c\n\x08RPT_STOP\x10\x01\x12\x0c\n\x08RPT_KEEP\x10\x02\x62\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pymammotion.proto.mctrl_sys_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _OPERATION._serialized_start=7199 - _OPERATION._serialized_end=7242 - _OFFPARTID._serialized_start=7245 - _OFFPARTID._serialized_end=7514 - _QCAPPTESTID._serialized_start=7517 - _QCAPPTESTID._serialized_end=8434 - _NET_USED_TYPE._serialized_start=8436 - _NET_USED_TYPE._serialized_end=8523 - _RPT_INFO_TYPE._serialized_start=8526 - _RPT_INFO_TYPE._serialized_end=8717 - _RPT_ACT._serialized_start=8719 - _RPT_ACT._serialized_end=8771 + _OPERATION._serialized_start=7201 + _OPERATION._serialized_end=7244 + _OFFPARTID._serialized_start=7247 + _OFFPARTID._serialized_end=7516 + _QCAPPTESTID._serialized_start=7519 + _QCAPPTESTID._serialized_end=8436 + _NET_USED_TYPE._serialized_start=8438 + _NET_USED_TYPE._serialized_end=8525 + _RPT_INFO_TYPE._serialized_start=8528 + _RPT_INFO_TYPE._serialized_end=8719 + _RPT_ACT._serialized_start=8721 + _RPT_ACT._serialized_end=8773 _SYSBATUP._serialized_start=70 _SYSBATUP._serialized_end=96 _SYSWORKSTATE._serialized_start=98 @@ -54,89 +54,89 @@ _SYSJOBPLANTIME._serialized_start=672 _SYSJOBPLANTIME._serialized_end=908 _SYSMOWINFO._serialized_start=910 - _SYSMOWINFO._serialized_end=1017 - _SYSOPTILINEACK._serialized_start=1019 - _SYSOPTILINEACK._serialized_end=1078 - _SYSCOMMCMD._serialized_start=1080 - _SYSCOMMCMD._serialized_end=1133 - _SYSUPLOADFILEPROGRESS._serialized_start=1135 - _SYSUPLOADFILEPROGRESS._serialized_end=1207 - _SYSERRORCODE._serialized_start=1209 - _SYSERRORCODE._serialized_end=1240 - _SYSBORDER._serialized_start=1242 - _SYSBORDER._serialized_end=1272 - _SYSPLANJOBSTATUS._serialized_start=1274 - _SYSPLANJOBSTATUS._serialized_end=1316 - _SYSKNIFECONTROL._serialized_start=1318 - _SYSKNIFECONTROL._serialized_end=1379 - _SYSRESETSYSTEMSTATUS._serialized_start=1381 - _SYSRESETSYSTEMSTATUS._serialized_end=1424 - _TIMECTRLLIGHT._serialized_start=1427 - _TIMECTRLLIGHT._serialized_end=1565 - _VISION_POINT_MSG._serialized_start=1567 - _VISION_POINT_MSG._serialized_end=1618 - _VISION_POINT_INFO_MSG._serialized_start=1620 - _VISION_POINT_INFO_MSG._serialized_end=1712 - _VIO_TO_APP_INFO_MSG._serialized_start=1715 - _VIO_TO_APP_INFO_MSG._serialized_end=1869 - _VISION_STATISTIC_MSG._serialized_start=1871 - _VISION_STATISTIC_MSG._serialized_end=1920 - _VISION_STATISTIC_INFO_MSG._serialized_start=1922 - _VISION_STATISTIC_INFO_MSG._serialized_end=2031 - _SYSTEMRAPIDSTATETUNNEL_MSG._serialized_start=2034 - _SYSTEMRAPIDSTATETUNNEL_MSG._serialized_end=2245 - _SYSTEMTARDSTATETUNNEL_MSG._serialized_start=2247 - _SYSTEMTARDSTATETUNNEL_MSG._serialized_end=2299 - _SYSTEMUPDATEBUF_MSG._serialized_start=2301 - _SYSTEMUPDATEBUF_MSG._serialized_end=2347 - _SYSOFFCHIPFLASH._serialized_start=2350 - _SYSOFFCHIPFLASH._serialized_end=2508 - _SYSTEMTMPCYCLETX_MSG._serialized_start=2510 - _SYSTEMTMPCYCLETX_MSG._serialized_end=2555 - _LORACFGREQ._serialized_start=2557 - _LORACFGREQ._serialized_end=2594 - _LORACFGRSP._serialized_start=2596 - _LORACFGRSP._serialized_end=2666 - _MOD_FW_INFO._serialized_start=2668 - _MOD_FW_INFO._serialized_end=2730 - _DEVICE_FW_INFO._serialized_start=2732 - _DEVICE_FW_INFO._serialized_end=2808 - _MOW_TO_APP_INFO_T._serialized_start=2810 - _MOW_TO_APP_INFO_T._serialized_end=2874 - _DEVICE_PRODUCT_TYPE_INFO_T._serialized_start=2876 - _DEVICE_PRODUCT_TYPE_INFO_T._serialized_end=2973 - _QCAPPTESTEXCEPT._serialized_start=2975 - _QCAPPTESTEXCEPT._serialized_end=3055 - _QCAPPTESTCONDITIONS._serialized_start=3057 - _QCAPPTESTCONDITIONS._serialized_end=3173 - _MOW_TO_APP_QCTOOLS_INFO_T._serialized_start=3176 - _MOW_TO_APP_QCTOOLS_INFO_T._serialized_end=3329 - _MCTRLSIMULATIONCMDDATA._serialized_start=3331 - _MCTRLSIMULATIONCMDDATA._serialized_end=3410 - _RPT_LORA._serialized_start=3413 - _RPT_LORA._serialized_end=3556 - _RPT_RTK._serialized_start=3559 - _RPT_RTK._serialized_end=3800 - _RPT_DEV_LOCATION._serialized_start=3803 - _RPT_DEV_LOCATION._serialized_end=3937 - _VIO_SURVIVAL_INFO_T._serialized_start=3939 - _VIO_SURVIVAL_INFO_T._serialized_end=3991 - _COLLECTOR_STATUS_T._serialized_start=3993 - _COLLECTOR_STATUS_T._serialized_end=4052 - _LOCK_STATE_T._serialized_start=4054 - _LOCK_STATE_T._serialized_end=4088 - _RPT_DEV_STATUS._serialized_start=4091 - _RPT_DEV_STATUS._serialized_end=4421 - _RPT_CONNECT_STATUS._serialized_start=4424 - _RPT_CONNECT_STATUS._serialized_end=4594 - _RPT_WORK._serialized_start=4597 - _RPT_WORK._serialized_end=5013 - _RPT_MAINTAIN._serialized_start=5015 - _RPT_MAINTAIN._serialized_end=5085 - _REPORT_INFO_CFG._serialized_start=5088 - _REPORT_INFO_CFG._serialized_end=5231 - _REPORT_INFO_DATA._serialized_start=5234 - _REPORT_INFO_DATA._serialized_end=5630 - _MCTLSYS._serialized_start=5633 - _MCTLSYS._serialized_end=7197 + _SYSMOWINFO._serialized_end=1019 + _SYSOPTILINEACK._serialized_start=1021 + _SYSOPTILINEACK._serialized_end=1080 + _SYSCOMMCMD._serialized_start=1082 + _SYSCOMMCMD._serialized_end=1135 + _SYSUPLOADFILEPROGRESS._serialized_start=1137 + _SYSUPLOADFILEPROGRESS._serialized_end=1209 + _SYSERRORCODE._serialized_start=1211 + _SYSERRORCODE._serialized_end=1242 + _SYSBORDER._serialized_start=1244 + _SYSBORDER._serialized_end=1274 + _SYSPLANJOBSTATUS._serialized_start=1276 + _SYSPLANJOBSTATUS._serialized_end=1318 + _SYSKNIFECONTROL._serialized_start=1320 + _SYSKNIFECONTROL._serialized_end=1381 + _SYSRESETSYSTEMSTATUS._serialized_start=1383 + _SYSRESETSYSTEMSTATUS._serialized_end=1426 + _TIMECTRLLIGHT._serialized_start=1429 + _TIMECTRLLIGHT._serialized_end=1567 + _VISION_POINT_MSG._serialized_start=1569 + _VISION_POINT_MSG._serialized_end=1620 + _VISION_POINT_INFO_MSG._serialized_start=1622 + _VISION_POINT_INFO_MSG._serialized_end=1714 + _VIO_TO_APP_INFO_MSG._serialized_start=1717 + _VIO_TO_APP_INFO_MSG._serialized_end=1871 + _VISION_STATISTIC_MSG._serialized_start=1873 + _VISION_STATISTIC_MSG._serialized_end=1922 + _VISION_STATISTIC_INFO_MSG._serialized_start=1924 + _VISION_STATISTIC_INFO_MSG._serialized_end=2033 + _SYSTEMRAPIDSTATETUNNEL_MSG._serialized_start=2036 + _SYSTEMRAPIDSTATETUNNEL_MSG._serialized_end=2247 + _SYSTEMTARDSTATETUNNEL_MSG._serialized_start=2249 + _SYSTEMTARDSTATETUNNEL_MSG._serialized_end=2301 + _SYSTEMUPDATEBUF_MSG._serialized_start=2303 + _SYSTEMUPDATEBUF_MSG._serialized_end=2349 + _SYSOFFCHIPFLASH._serialized_start=2352 + _SYSOFFCHIPFLASH._serialized_end=2510 + _SYSTEMTMPCYCLETX_MSG._serialized_start=2512 + _SYSTEMTMPCYCLETX_MSG._serialized_end=2557 + _LORACFGREQ._serialized_start=2559 + _LORACFGREQ._serialized_end=2596 + _LORACFGRSP._serialized_start=2598 + _LORACFGRSP._serialized_end=2668 + _MOD_FW_INFO._serialized_start=2670 + _MOD_FW_INFO._serialized_end=2732 + _DEVICE_FW_INFO._serialized_start=2734 + _DEVICE_FW_INFO._serialized_end=2810 + _MOW_TO_APP_INFO_T._serialized_start=2812 + _MOW_TO_APP_INFO_T._serialized_end=2876 + _DEVICE_PRODUCT_TYPE_INFO_T._serialized_start=2878 + _DEVICE_PRODUCT_TYPE_INFO_T._serialized_end=2975 + _QCAPPTESTEXCEPT._serialized_start=2977 + _QCAPPTESTEXCEPT._serialized_end=3057 + _QCAPPTESTCONDITIONS._serialized_start=3059 + _QCAPPTESTCONDITIONS._serialized_end=3175 + _MOW_TO_APP_QCTOOLS_INFO_T._serialized_start=3178 + _MOW_TO_APP_QCTOOLS_INFO_T._serialized_end=3331 + _MCTRLSIMULATIONCMDDATA._serialized_start=3333 + _MCTRLSIMULATIONCMDDATA._serialized_end=3412 + _RPT_LORA._serialized_start=3415 + _RPT_LORA._serialized_end=3558 + _RPT_RTK._serialized_start=3561 + _RPT_RTK._serialized_end=3802 + _RPT_DEV_LOCATION._serialized_start=3805 + _RPT_DEV_LOCATION._serialized_end=3939 + _VIO_SURVIVAL_INFO_T._serialized_start=3941 + _VIO_SURVIVAL_INFO_T._serialized_end=3993 + _COLLECTOR_STATUS_T._serialized_start=3995 + _COLLECTOR_STATUS_T._serialized_end=4054 + _LOCK_STATE_T._serialized_start=4056 + _LOCK_STATE_T._serialized_end=4090 + _RPT_DEV_STATUS._serialized_start=4093 + _RPT_DEV_STATUS._serialized_end=4423 + _RPT_CONNECT_STATUS._serialized_start=4426 + _RPT_CONNECT_STATUS._serialized_end=4596 + _RPT_WORK._serialized_start=4599 + _RPT_WORK._serialized_end=5015 + _RPT_MAINTAIN._serialized_start=5017 + _RPT_MAINTAIN._serialized_end=5087 + _REPORT_INFO_CFG._serialized_start=5090 + _REPORT_INFO_CFG._serialized_end=5233 + _REPORT_INFO_DATA._serialized_start=5236 + _REPORT_INFO_DATA._serialized_end=5632 + _MCTLSYS._serialized_start=5635 + _MCTLSYS._serialized_end=7199 # @@protoc_insertion_point(module_scope) diff --git a/pymammotion/proto/mctrl_sys_pb2.pyi b/pymammotion/proto/mctrl_sys_pb2.pyi index 5f4b729..cd166bd 100644 --- a/pymammotion/proto/mctrl_sys_pb2.pyi +++ b/pymammotion/proto/mctrl_sys_pb2.pyi @@ -274,18 +274,18 @@ class SysKnifeControl(_message.Message): def __init__(self, knife_status: _Optional[int] = ..., knife_height: _Optional[int] = ...) -> None: ... class SysMowInfo(_message.Message): - __slots__ = ["RTKstars", "RTKstatus", "batVal", "deviceState", "knifeHeight"] + __slots__ = ["batVal", "deviceState", "knifeHeight", "rtk_stars", "rtk_status"] BATVAL_FIELD_NUMBER: _ClassVar[int] DEVICESTATE_FIELD_NUMBER: _ClassVar[int] KNIFEHEIGHT_FIELD_NUMBER: _ClassVar[int] - RTKSTARS_FIELD_NUMBER: _ClassVar[int] - RTKSTATUS_FIELD_NUMBER: _ClassVar[int] - RTKstars: int - RTKstatus: int + RTK_STARS_FIELD_NUMBER: _ClassVar[int] + RTK_STATUS_FIELD_NUMBER: _ClassVar[int] batVal: int deviceState: int knifeHeight: int - def __init__(self, deviceState: _Optional[int] = ..., batVal: _Optional[int] = ..., knifeHeight: _Optional[int] = ..., RTKstatus: _Optional[int] = ..., RTKstars: _Optional[int] = ...) -> None: ... + rtk_stars: int + rtk_status: int + def __init__(self, deviceState: _Optional[int] = ..., batVal: _Optional[int] = ..., knifeHeight: _Optional[int] = ..., rtk_status: _Optional[int] = ..., rtk_stars: _Optional[int] = ...) -> None: ... class SysOffChipFlash(_message.Message): __slots__ = ["code", "data", "id", "length", "msg", "offset", "op", "start_addr"] From 994ceb08d16776c7812b84e27330fabd08a66d92 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Wed, 14 Aug 2024 14:13:45 +1200 Subject: [PATCH 09/35] adding report info model and map conversions for lla enu --- poetry.lock | 56 +++++++++- pymammotion/data/model/device.py | 19 +++- pymammotion/data/model/location.py | 8 +- pymammotion/data/model/report_info.py | 121 +++++++++++++++++++++ pymammotion/data/state_manager.py | 2 +- pymammotion/mammotion/devices/mammotion.py | 2 +- pymammotion/proto/mctrl_nav.py | 2 +- pymammotion/proto/mctrl_sys.py | 1 + pymammotion/utility/map.py | 70 ++++++++++++ pyproject.toml | 5 +- tests/test2_instance.py | 1 + 11 files changed, 277 insertions(+), 10 deletions(-) create mode 100644 pymammotion/data/model/report_info.py create mode 100644 pymammotion/utility/map.py diff --git a/poetry.lock b/poetry.lock index 18ee493..ee550e4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1825,6 +1825,60 @@ files = [ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] +[[package]] +name = "numpy" +version = "2.0.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fbb536eac80e27a2793ffd787895242b7f18ef792563d742c2d673bfcb75134"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69ff563d43c69b1baba77af455dd0a839df8d25e8590e79c90fcbe1499ebde42"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1b902ce0e0a5bb7704556a217c4f63a7974f8f43e090aff03fcf262e0b135e02"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:f1659887361a7151f89e79b276ed8dff3d75877df906328f14d8bb40bb4f5101"}, + {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4658c398d65d1b25e1760de3157011a80375da861709abd7cef3bad65d6543f9"}, + {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4127d4303b9ac9f94ca0441138acead39928938660ca58329fe156f84b9f3015"}, + {file = "numpy-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e5eeca8067ad04bc8a2a8731183d51d7cbaac66d86085d5f4766ee6bf19c7f87"}, + {file = "numpy-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9adbd9bb520c866e1bfd7e10e1880a1f7749f1f6e5017686a5fbb9b72cf69f82"}, + {file = "numpy-2.0.1-cp310-cp310-win32.whl", hash = "sha256:7b9853803278db3bdcc6cd5beca37815b133e9e77ff3d4733c247414e78eb8d1"}, + {file = "numpy-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81b0893a39bc5b865b8bf89e9ad7807e16717f19868e9d234bdaf9b1f1393868"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b"}, + {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe"}, + {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67"}, + {file = "numpy-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7"}, + {file = "numpy-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55"}, + {file = "numpy-2.0.1-cp311-cp311-win32.whl", hash = "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4"}, + {file = "numpy-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587"}, + {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8"}, + {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a"}, + {file = "numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7"}, + {file = "numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b"}, + {file = "numpy-2.0.1-cp312-cp312-win32.whl", hash = "sha256:173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf"}, + {file = "numpy-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc085b28d62ff4009364e7ca34b80a9a080cbd97c2c0630bb5f7f770dae9414"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fae4ebbf95a179c1156fab0b142b74e4ba4204c87bde8d3d8b6f9c34c5825ef"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:72dc22e9ec8f6eaa206deb1b1355eb2e253899d7347f5e2fae5f0af613741d06"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:ec87f5f8aca726117a1c9b7083e7656a9d0d606eec7299cc067bb83d26f16e0c"}, + {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f682ea61a88479d9498bf2091fdcd722b090724b08b31d63e022adc063bad59"}, + {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8efc84f01c1cd7e34b3fb310183e72fcdf55293ee736d679b6d35b35d80bba26"}, + {file = "numpy-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3fdabe3e2a52bc4eff8dc7a5044342f8bd9f11ef0934fcd3289a788c0eb10018"}, + {file = "numpy-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:24a0e1befbfa14615b49ba9659d3d8818a0f4d8a1c5822af8696706fbda7310c"}, + {file = "numpy-2.0.1-cp39-cp39-win32.whl", hash = "sha256:f9cf5ea551aec449206954b075db819f52adc1638d46a6738253a712d553c7b4"}, + {file = "numpy-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9e81fa9017eaa416c056e5d9e71be93d05e2c3c2ab308d23307a8bc4443c368"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61728fba1e464f789b11deb78a57805c70b2ed02343560456190d0501ba37b0f"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:12f5d865d60fb9734e60a60f1d5afa6d962d8d4467c120a1c0cda6eb2964437d"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eacf3291e263d5a67d8c1a581a8ebbcfd6447204ef58828caf69a5e3e8c75990"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c3a346ae20cfd80b6cfd3e60dc179963ef2ea58da5ec074fd3d9e7a1e7ba97f"}, + {file = "numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3"}, +] + [[package]] name = "orjson" version = "3.10.6" @@ -2909,4 +2963,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "8ef308c71b11c211a2e067752ca808e0e99a20b8ea45edaab05c52e800a7bc3d" +content-hash = "43521ce164b2b3101c40a1d2e0a309d9a6b87d1c398f94be729b72b3d426afc0" diff --git a/pymammotion/data/model/device.py b/pymammotion/data/model/device.py index 63edf16..3674fde 100644 --- a/pymammotion/data/model/device.py +++ b/pymammotion/data/model/device.py @@ -2,9 +2,12 @@ from dataclasses import dataclass +import betterproto + from pymammotion.data.model import HashList from pymammotion.data.model.device_config import DeviceLimits from pymammotion.data.model.location import Location +from pymammotion.data.model.report_info import ReportData from pymammotion.proto.dev_net import DevNet from pymammotion.proto.luba_msg import LubaMsg from pymammotion.proto.luba_mul import SocMul @@ -12,7 +15,8 @@ from pymammotion.proto.mctrl_nav import MctlNav from pymammotion.proto.mctrl_ota import MctlOta from pymammotion.proto.mctrl_pept import MctlPept -from pymammotion.proto.mctrl_sys import MctlSys, MowToAppInfoT, SystemUpdateBufMsg +from pymammotion.proto.mctrl_sys import MctlSys, MowToAppInfoT, SystemUpdateBufMsg, ReportInfoData +from pymammotion.utility.map import CoordinateConverter @dataclass @@ -27,6 +31,7 @@ def __init__(self): self.device = LubaMsg() self.map = HashList(area={}, path={}, obstacle={}) self.location = Location() + self.report_data = ReportData() self.err_code_list = [] self.err_code_list_time = [] self.limits = DeviceLimits(30, 70, 0.2, 0.6) @@ -84,6 +89,18 @@ def buffer(self, buffer_list: SystemUpdateBufMsg): ] ) + def update_report_data(self, toapp_report_data: ReportInfoData): + coordinate_converter = CoordinateConverter(self.location.RTK.latitude, self.location.RTK.longitude) + for index, location in enumerate(toapp_report_data.locations): + if index == 0: + self.location.position_type = location.pos_type + self.location.orientation = location.real_toward / 10000 + self.location.device = coordinate_converter.enu_to_lla(location.real_pos_y, location.real_pos_x) + + self.report_data = self.report_data.from_dict(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE)) + + + def mow_info(self, toapp_mow_info: MowToAppInfoT): pass diff --git a/pymammotion/data/model/location.py b/pymammotion/data/model/location.py index a53fc1d..4f6896f 100644 --- a/pymammotion/data/model/location.py +++ b/pymammotion/data/model/location.py @@ -10,9 +10,9 @@ class Point: latitude: float longitude: float - def __init__(self): - self.latitude = 0 - self.longitude = 0 + def __init__(self, latitude=0.0, longitude=0.0): + self.latitude = latitude + self.longitude = longitude @dataclass @@ -33,6 +33,8 @@ class Location: device: Point RTK: Point dock: Dock + position_type: int + orientation: int # 360 degree rotation +- def __init__(self): self.device = Point() diff --git a/pymammotion/data/model/report_info.py b/pymammotion/data/model/report_info.py new file mode 100644 index 0000000..259ffff --- /dev/null +++ b/pymammotion/data/model/report_info.py @@ -0,0 +1,121 @@ +from dataclasses import dataclass, field + +@dataclass +class ConnectData: + connect_type: int = 0 + ble_rssi: int = 0 + wifi_rssi: int = 0 + + @classmethod + def from_dict(cls, data: dict): + return cls( + connect_type=data.get('connect_type', 0), + ble_rssi=data.get('ble_rssi', 0), + wifi_rssi=data.get('wifi_rssi', 0) + ) + +@dataclass +class DeviceData: + sys_status: int = 0 + charge_state: int = 0 + battery_val: int = 0 + sensor_status: int = 0 + last_status: int = 0 + sys_time_stamp: str = "" + + @classmethod + def from_dict(cls, data: dict): + return cls( + sys_status=data.get('sys_status', 0), + charge_state=data.get('charge_state', 0), + battery_val=data.get('battery_val', 0), + sensor_status=data.get('sensor_status', 0), + last_status=data.get('last_status', 0), + sys_time_stamp=data.get('sys_time_stamp', "") + ) + +@dataclass +class RTKData: + status: int = 0 + pos_level: int = 0 + gps_stars: int = 0 + dis_status: str = "" + co_view_stars: int = 0 + + @classmethod + def from_dict(cls, data: dict): + return cls( + status=data.get('status', 0), + pos_level=data.get('pos_level', 0), + gps_stars=data.get('gps_stars', 0), + dis_status=data.get('dis_status', ""), + co_view_stars=data.get('co_view_stars', 0) + ) + +@dataclass +class LocationData: + real_pos_x: int = 0 + real_pos_y: int = 0 + real_toward: int = 0 + pos_type: int = 0 + bol_hash: str = "" + + @classmethod + def from_dict(cls, data: dict): + return cls( + real_pos_x=data.get('real_pos_x', 0), + real_pos_y=data.get('real_pos_y', 0), + real_toward=data.get('real_toward', 0), + pos_type=data.get('pos_type', 0), + bol_hash=data.get('bol_hash', "") + ) + +@dataclass +class WorkData: + path_hash: str = "" + progress: int = 0 + area: int = 0 + bp_info: int = 0 + bp_hash: str = "" + bp_pos_x: int = 0 + bp_pos_y: int = 0 + real_path_num: str = "" + ub_zone_hash: str = "" + init_cfg_hash: str = "" + ub_ecode_hash: str = "" + knife_height: int = 0 + + @classmethod + def from_dict(cls, data: dict): + return cls( + path_hash=data.get('path_hash', ""), + progress=data.get('progress', 0), + area=data.get('area', 0), + bp_info=data.get('bp_info', 0), + bp_hash=data.get('bp_hash', ""), + bp_pos_x=data.get('bp_pos_x', 0), + bp_pos_y=data.get('bp_pos_y', 0), + real_path_num=data.get('real_path_num', ""), + ub_zone_hash=data.get('ub_zone_hash', ""), + init_cfg_hash=data.get('init_cfg_hash', ""), + ub_ecode_hash=data.get('ub_ecode_hash', ""), + knife_height=data.get('knife_height', 0) + ) + +@dataclass +class ReportData: + connect: ConnectData = field(default_factory=ConnectData) + dev: DeviceData = field(default_factory=DeviceData) + rtk: RTKData = field(default_factory=RTKData) + locations: list[LocationData] = field(default_factory=list) + work: WorkData = field(default_factory=WorkData) + + @classmethod + def from_dict(cls, data: dict): + return cls( + connect=ConnectData.from_dict(data.get('connect', {})), + dev=DeviceData.from_dict(data.get('dev', {})), + rtk=RTKData.from_dict(data.get('rtk', {})), + locations=[LocationData.from_dict(loc) for loc in data.get('locations', [])], + work=WorkData.from_dict(data.get('work', {})) + ) \ No newline at end of file diff --git a/pymammotion/data/state_manager.py b/pymammotion/data/state_manager.py index 3546e06..670a1f4 100644 --- a/pymammotion/data/state_manager.py +++ b/pymammotion/data/state_manager.py @@ -68,7 +68,7 @@ def _update_sys_data(self, message): case "system_update_buf": self._device.buffer(sys_msg[1]) case "toapp_report_data": - pass + self._device.update_report_data(sys_msg[1]) case "mow_to_app_info": self._device.mow_info(sys_msg[1]) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index ec49f56..6c55723 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -534,12 +534,12 @@ async def _send_command_locked(self, key: str, command: bytes) -> bytes: async def _notification_handler(self, _sender: BleakGATTCharacteristic, data: bytearray) -> None: """Handle notification responses.""" - _LOGGER.debug("%s: Received notification: %s", self.name, data) result = self._message.parseNotification(data) if result == 0: data = await self._message.parseBlufiNotifyData(True) self._update_raw_data(data) self._message.clearNotification() + _LOGGER.debug("%s: Received notification: %s", self.name, data) else: return new_msg = LubaMsg().parse(data) diff --git a/pymammotion/proto/mctrl_nav.py b/pymammotion/proto/mctrl_nav.py index 3553849..9bcdde1 100644 --- a/pymammotion/proto/mctrl_nav.py +++ b/pymammotion/proto/mctrl_nav.py @@ -2,7 +2,7 @@ # sources: pymammotion/proto/mctrl_nav.proto # plugin: python-betterproto from dataclasses import dataclass - +from .common import * import betterproto diff --git a/pymammotion/proto/mctrl_sys.py b/pymammotion/proto/mctrl_sys.py index 21ba9c3..feafc17 100644 --- a/pymammotion/proto/mctrl_sys.py +++ b/pymammotion/proto/mctrl_sys.py @@ -3,6 +3,7 @@ # plugin: python-betterproto from dataclasses import dataclass +from .dev_net import * import betterproto diff --git a/pymammotion/utility/map.py b/pymammotion/utility/map.py new file mode 100644 index 0000000..5ad12aa --- /dev/null +++ b/pymammotion/utility/map.py @@ -0,0 +1,70 @@ +import math +import numpy as np + +from pymammotion.data.model.location import Point + + +class CoordinateConverter: + def __init__(self, lat_degrees: float, lon_degrees: float): + # Initialize constants + self.WGS84A = 6378137.0 + self.f_ = 3.3528106647474805E-21 + self.b_ = (1.0 - self.f_) * self.WGS84A + self.e2_ = (2.0 - self.f_) * self.f_ + self.e2m_ = (1.0 - self.f_) * (1.0 - self.f_) + self.ep2_ = ((self.WGS84A ** 2) - (self.b_ ** 2)) / (self.b_ ** 2) + + # Initialize rotation matrix + self.R_ = np.zeros((3, 3)) + + # Variables to store initial LLA + self.x0_ = 0.0 + self.y0_ = 0.0 + self.z0_ = 0.0 + + # Call set_init_lla with provided lat/lon + latitude_rad = math.radians(lat_degrees) + longitude_rad = math.radians(lon_degrees) + self.set_init_lla(latitude_rad, longitude_rad) + + def set_init_lla(self, lat_rad, lon_rad): + sin_lat = math.sin(lat_rad) + cos_lat = math.cos(lat_rad) + sin_lon = math.sin(lon_rad) + cos_lon = math.cos(lon_rad) + + sqrt = self.WGS84A / math.sqrt(1.0 - (self.e2_ * (sin_lat ** 2))) + d3 = sqrt * cos_lat + + self.x0_ = d3 * cos_lon + self.y0_ = d3 * sin_lon + self.z0_ = self.e2m_ * sqrt * sin_lat + + self.R_[0][0] = -sin_lon + self.R_[0][1] = cos_lon + self.R_[0][2] = 0.0 + + self.R_[1][0] = -cos_lon * sin_lat + self.R_[1][1] = -sin_lon * sin_lat + self.R_[1][2] = cos_lat + + self.R_[2][0] = cos_lon * cos_lat + self.R_[2][1] = sin_lon * cos_lat + self.R_[2][2] = sin_lat + + def enu_to_lla(self, e, n) -> Point: + d3 = self.R_[0][0] * n + self.R_[1][0] * e + self.x0_ + d4 = self.R_[0][1] * n + self.R_[1][1] * e + self.y0_ + d5 = self.R_[0][2] * n + self.R_[1][2] * e + self.z0_ + + hypot = math.hypot(d3, d4) + atan2_lat = math.atan2(self.WGS84A * d5, self.b_ * hypot) + + sin_lat = math.sin(atan2_lat) + cos_lat = math.cos(atan2_lat) + + lon = math.atan2(d4, d3) * 180.0 / math.pi + lat = math.atan2(d5 + self.ep2_ * self.b_ * (sin_lat ** 3), hypot - self.e2_ * self.WGS84A * (cos_lat ** 3)) * 180.0 / math.pi + + return Point(latitude=lat, longitude=lon) + diff --git a/pyproject.toml b/pyproject.toml index 9f8485e..c374808 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.1.1" +version = "0.1.3" license = "GNU-3.0" description = "" readme = "README.md" @@ -31,6 +31,7 @@ orjson = "^3.9.15" betterproto = "^1.2.5" pyjoystick = "^1.2.4" nest-asyncio = "^1.6.0" +numpy = "^2.0.1" [tool.poetry.group.dev.dependencies] @@ -47,7 +48,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.1.1" +current_version = "0.1.3" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/tests/test2_instance.py b/tests/test2_instance.py index 7bd3b1b..29c2211 100644 --- a/tests/test2_instance.py +++ b/tests/test2_instance.py @@ -7,6 +7,7 @@ from pymammotion.event.event import BleNotificationEvent from pymammotion.mammotion.devices.mammotion import MammotionBaseBLEDevice, has_field +from pymammotion.proto.mctrl_sys import MctlSys, RptAct, RptInfoType bleNotificationEvt = BleNotificationEvent() From 79e05f61282d2b3247fc7c3ef35124c8a7374082 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Wed, 14 Aug 2024 19:07:16 +1200 Subject: [PATCH 10/35] release 0.1.7 --- poetry.lock | 369 +++++++++++++------------- pymammotion/data/model/device.py | 14 +- pymammotion/data/model/report_info.py | 16 ++ pymammotion/utility/map.py | 4 +- pyproject.toml | 6 +- 5 files changed, 212 insertions(+), 197 deletions(-) diff --git a/poetry.lock b/poetry.lock index ee550e4..23df474 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,87 +13,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.1" +version = "3.10.3" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:47b4c2412960e64d97258f40616efddaebcb34ff664c8a972119ed38fac2a62c"}, - {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7dbf637f87dd315fa1f36aaed8afa929ee2c607454fb7791e74c88a0d94da59"}, - {file = "aiohttp-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c8fb76214b5b739ce59e2236a6489d9dc3483649cfd6f563dbf5d8e40dbdd57d"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c577cdcf8f92862363b3d598d971c6a84ed8f0bf824d4cc1ce70c2fb02acb4a"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:777e23609899cb230ad2642b4bdf1008890f84968be78de29099a8a86f10b261"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b07286a1090483799599a2f72f76ac396993da31f6e08efedb59f40876c144fa"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9db600a86414a9a653e3c1c7f6a2f6a1894ab8f83d11505247bd1b90ad57157"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c3f1eb280008e51965a8d160a108c333136f4a39d46f516c64d2aa2e6a53f2"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f5dd109a925fee4c9ac3f6a094900461a2712df41745f5d04782ebcbe6479ccb"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8c81ff4afffef9b1186639506d70ea90888218f5ddfff03870e74ec80bb59970"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2a384dfbe8bfebd203b778a30a712886d147c61943675f4719b56725a8bbe803"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b9fb6508893dc31cfcbb8191ef35abd79751db1d6871b3e2caee83959b4d91eb"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:88596384c3bec644a96ae46287bb646d6a23fa6014afe3799156aef42669c6bd"}, - {file = "aiohttp-3.10.1-cp310-cp310-win32.whl", hash = "sha256:68164d43c580c2e8bf8e0eb4960142919d304052ccab92be10250a3a33b53268"}, - {file = "aiohttp-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:d6bbe2c90c10382ca96df33b56e2060404a4f0f88673e1e84b44c8952517e5f3"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6979b4f20d3e557a867da9d9227de4c156fcdcb348a5848e3e6190fd7feb972"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03c0c380c83f8a8d4416224aafb88d378376d6f4cadebb56b060688251055cd4"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c2b104e81b3c3deba7e6f5bc1a9a0e9161c380530479970766a6655b8b77c7c"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b023b68c61ab0cd48bd38416b421464a62c381e32b9dc7b4bdfa2905807452a4"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a07c76a82390506ca0eabf57c0540cf5a60c993c442928fe4928472c4c6e5e6"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:41d8dab8c64ded1edf117d2a64f353efa096c52b853ef461aebd49abae979f16"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:615348fab1a9ef7d0960a905e83ad39051ae9cb0d2837da739b5d3a7671e497a"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:256ee6044214ee9d66d531bb374f065ee94e60667d6bbeaa25ca111fc3997158"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d5bb926805022508b7ddeaad957f1fce7a8d77532068d7bdb431056dc630cd"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:028faf71b338f069077af6315ad54281612705d68889f5d914318cbc2aab0d50"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5c12310d153b27aa630750be44e79313acc4e864c421eb7d2bc6fa3429c41bf8"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:de1a91d5faded9054957ed0a9e01b9d632109341942fc123947ced358c5d9009"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9c186b270979fb1dee3ababe2d12fb243ed7da08b30abc83ebac3a928a4ddb15"}, - {file = "aiohttp-3.10.1-cp311-cp311-win32.whl", hash = "sha256:4a9ce70f5e00380377aac0e568abd075266ff992be2e271765f7b35d228a990c"}, - {file = "aiohttp-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:a77c79bac8d908d839d32c212aef2354d2246eb9deb3e2cb01ffa83fb7a6ea5d"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2212296cdb63b092e295c3e4b4b442e7b7eb41e8a30d0f53c16d5962efed395d"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4dcb127ca3eb0a61205818a606393cbb60d93b7afb9accd2fd1e9081cc533144"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb8b79a65332e1a426ccb6290ce0409e1dc16b4daac1cc5761e059127fa3d134"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68cc24f707ed9cb961f6ee04020ca01de2c89b2811f3cf3361dc7c96a14bfbcc"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cb54f5725b4b37af12edf6c9e834df59258c82c15a244daa521a065fbb11717"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d03e948e53b3639ce4d438f3d1d8202898ec6655cadcc09ec99229d4adc2a9"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:786299d719eb5d868f161aeec56d589396b053925b7e0ce36e983d30d0a3e55c"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abda4009a30d51d3f06f36bc7411a62b3e647fa6cc935ef667e3e3d3a7dd09b1"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67f7639424c313125213954e93a6229d3a1d386855d70c292a12628f600c7150"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e5a26d7aac4c0d8414a347da162696eea0629fdce939ada6aedf951abb1d745"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:120548d89f14b76a041088b582454d89389370632ee12bf39d919cc5c561d1ca"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f5293726943bdcea24715b121d8c4ae12581441d22623b0e6ab12d07ce85f9c4"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f8605e573ed6c44ec689d94544b2c4bb1390aaa723a8b5a2cc0a5a485987a68"}, - {file = "aiohttp-3.10.1-cp312-cp312-win32.whl", hash = "sha256:e7168782621be4448d90169a60c8b37e9b0926b3b79b6097bc180c0a8a119e73"}, - {file = "aiohttp-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fbf8c0ded367c5c8eaf585f85ca8dd85ff4d5b73fb8fe1e6ac9e1b5e62e11f7"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:54b7f4a20d7cc6bfa4438abbde069d417bb7a119f870975f78a2b99890226d55"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fa643ca990323db68911b92f3f7a0ca9ae300ae340d0235de87c523601e58d9"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8311d0d690487359fe2247ec5d2cac9946e70d50dced8c01ce9e72341c21151"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222821c60b8f6a64c5908cb43d69c0ee978a1188f6a8433d4757d39231b42cdb"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7b55d9ede66af7feb6de87ff277e0ccf6d51c7db74cc39337fe3a0e31b5872d"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a95151a5567b3b00368e99e9c5334a919514f60888a6b6d2054fea5e66e527e"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9e9171d2fe6bfd9d3838a6fe63b1e91b55e0bf726c16edf265536e4eafed19"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a57e73f9523e980f6101dc9a83adcd7ac0006ea8bf7937ca3870391c7bb4f8ff"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0df51a3d70a2bfbb9c921619f68d6d02591f24f10e9c76de6f3388c89ed01de6"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:b0de63ff0307eac3961b4af74382d30220d4813f36b7aaaf57f063a1243b4214"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8db9b749f589b5af8e4993623dbda6716b2b7a5fcb0fa2277bf3ce4b278c7059"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6b14c19172eb53b63931d3e62a9749d6519f7c121149493e6eefca055fcdb352"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cd57ad998e3038aa87c38fe85c99ed728001bf5dde8eca121cadee06ee3f637"}, - {file = "aiohttp-3.10.1-cp38-cp38-win32.whl", hash = "sha256:df31641e3f02b77eb3c5fb63c0508bee0fc067cf153da0e002ebbb0db0b6d91a"}, - {file = "aiohttp-3.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:93094eba50bc2ad4c40ff4997ead1fdcd41536116f2e7d6cfec9596a8ecb3615"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:440954ddc6b77257e67170d57b1026aa9545275c33312357472504eef7b4cc0b"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9f8beed277488a52ee2b459b23c4135e54d6a819eaba2e120e57311015b58e9"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8a8221a63602008550022aa3a4152ca357e1dde7ab3dd1da7e1925050b56863"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a702bd3663b5cbf3916e84bf332400d24cdb18399f0877ca6b313ce6c08bfb43"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1988b370536eb14f0ce7f3a4a5b422ab64c4e255b3f5d7752c5f583dc8c967fc"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ccf1f0a304352c891d124ac1a9dea59b14b2abed1704aaa7689fc90ef9c5be1"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc3ea6ef2a83edad84bbdb5d96e22f587b67c68922cd7b6f9d8f24865e655bcf"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b47c125ab07f0831803b88aeb12b04c564d5f07a1c1a225d4eb4d2f26e8b5e"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21778552ef3d44aac3278cc6f6d13a6423504fa5f09f2df34bfe489ed9ded7f5"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bde0693073fd5e542e46ea100aa6c1a5d36282dbdbad85b1c3365d5421490a92"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bf66149bb348d8e713f3a8e0b4f5b952094c2948c408e1cfef03b49e86745d60"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:587237571a85716d6f71f60d103416c9df7d5acb55d96d3d3ced65f39bff9c0c"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bfe33cba6e127d0b5b417623c9aa621f0a69f304742acdca929a9fdab4593693"}, - {file = "aiohttp-3.10.1-cp39-cp39-win32.whl", hash = "sha256:9fbff00646cf8211b330690eb2fd64b23e1ce5b63a342436c1d1d6951d53d8dd"}, - {file = "aiohttp-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:5951c328f9ac42d7bce7a6ded535879bc9ae13032818d036749631fa27777905"}, - {file = "aiohttp-3.10.1.tar.gz", hash = "sha256:8b0d058e4e425d3b45e8ec70d49b402f4d6b21041e674798b1f91ba027c73f28"}, + {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc36cbdedf6f259371dbbbcaae5bb0e95b879bc501668ab6306af867577eb5db"}, + {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85466b5a695c2a7db13eb2c200af552d13e6a9313d7fa92e4ffe04a2c0ea74c1"}, + {file = "aiohttp-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71bb1d97bfe7e6726267cea169fdf5df7658831bb68ec02c9c6b9f3511e108bb"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baec1eb274f78b2de54471fc4c69ecbea4275965eab4b556ef7a7698dee18bf2"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13031e7ec1188274bad243255c328cc3019e36a5a907978501256000d57a7201"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bbc55a964b8eecb341e492ae91c3bd0848324d313e1e71a27e3d96e6ee7e8e8"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8cc0564b286b625e673a2615ede60a1704d0cbbf1b24604e28c31ed37dc62aa"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f817a54059a4cfbc385a7f51696359c642088710e731e8df80d0607193ed2b73"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8542c9e5bcb2bd3115acdf5adc41cda394e7360916197805e7e32b93d821ef93"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:671efce3a4a0281060edf9a07a2f7e6230dca3a1cbc61d110eee7753d28405f7"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0974f3b5b0132edcec92c3306f858ad4356a63d26b18021d859c9927616ebf27"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:44bb159b55926b57812dca1b21c34528e800963ffe130d08b049b2d6b994ada7"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6ae9ae382d1c9617a91647575255ad55a48bfdde34cc2185dd558ce476bf16e9"}, + {file = "aiohttp-3.10.3-cp310-cp310-win32.whl", hash = "sha256:aed12a54d4e1ee647376fa541e1b7621505001f9f939debf51397b9329fd88b9"}, + {file = "aiohttp-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:b51aef59370baf7444de1572f7830f59ddbabd04e5292fa4218d02f085f8d299"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"}, + {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"}, + {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"}, + {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"}, + {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"}, + {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d1100e68e70eb72eadba2b932b185ebf0f28fd2f0dbfe576cfa9d9894ef49752"}, + {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a541414578ff47c0a9b0b8b77381ea86b0c8531ab37fc587572cb662ccd80b88"}, + {file = "aiohttp-3.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d5548444ef60bf4c7b19ace21f032fa42d822e516a6940d36579f7bfa8513f9c"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba2e838b5e6a8755ac8297275c9460e729dc1522b6454aee1766c6de6d56e5e"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48665433bb59144aaf502c324694bec25867eb6630fcd831f7a893ca473fcde4"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bac352fceed158620ce2d701ad39d4c1c76d114255a7c530e057e2b9f55bdf9f"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0f670502100cdc567188c49415bebba947eb3edaa2028e1a50dd81bd13363f"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b09f38a67679e32d380fe512189ccb0b25e15afc79b23fbd5b5e48e4fc8fd9"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:cd788602e239ace64f257d1c9d39898ca65525583f0fbf0988bcba19418fe93f"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:214277dcb07ab3875f17ee1c777d446dcce75bea85846849cc9d139ab8f5081f"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:32007fdcaab789689c2ecaaf4b71f8e37bf012a15cd02c0a9db8c4d0e7989fa8"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:123e5819bfe1b87204575515cf448ab3bf1489cdeb3b61012bde716cda5853e7"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:812121a201f0c02491a5db335a737b4113151926a79ae9ed1a9f41ea225c0e3f"}, + {file = "aiohttp-3.10.3-cp38-cp38-win32.whl", hash = "sha256:b97dc9a17a59f350c0caa453a3cb35671a2ffa3a29a6ef3568b523b9113d84e5"}, + {file = "aiohttp-3.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:3731a73ddc26969d65f90471c635abd4e1546a25299b687e654ea6d2fc052394"}, + {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38d91b98b4320ffe66efa56cb0f614a05af53b675ce1b8607cdb2ac826a8d58e"}, + {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9743fa34a10a36ddd448bba8a3adc2a66a1c575c3c2940301bacd6cc896c6bf1"}, + {file = "aiohttp-3.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c126f532caf238031c19d169cfae3c6a59129452c990a6e84d6e7b198a001dc"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:926e68438f05703e500b06fe7148ef3013dd6f276de65c68558fa9974eeb59ad"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434b3ab75833accd0b931d11874e206e816f6e6626fd69f643d6a8269cd9166a"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d35235a44ec38109b811c3600d15d8383297a8fab8e3dec6147477ec8636712a"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59c489661edbd863edb30a8bd69ecb044bd381d1818022bc698ba1b6f80e5dd1"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50544fe498c81cb98912afabfc4e4d9d85e89f86238348e3712f7ca6a2f01dab"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09bc79275737d4dc066e0ae2951866bb36d9c6b460cb7564f111cc0427f14844"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:af4dbec58e37f5afff4f91cdf235e8e4b0bd0127a2a4fd1040e2cad3369d2f06"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b22cae3c9dd55a6b4c48c63081d31c00fc11fa9db1a20c8a50ee38c1a29539d2"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ba562736d3fbfe9241dad46c1a8994478d4a0e50796d80e29d50cabe8fbfcc3f"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f25d6c4e82d7489be84f2b1c8212fafc021b3731abdb61a563c90e37cced3a21"}, + {file = "aiohttp-3.10.3-cp39-cp39-win32.whl", hash = "sha256:b69d832e5f5fa15b1b6b2c8eb6a9fd2c0ec1fd7729cb4322ed27771afc9fc2ac"}, + {file = "aiohttp-3.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:673bb6e3249dc8825df1105f6ef74e2eab779b7ff78e96c15cadb78b04a83752"}, + {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"}, ] [package.dependencies] @@ -1827,116 +1827,113 @@ files = [ [[package]] name = "numpy" -version = "2.0.1" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "numpy-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fbb536eac80e27a2793ffd787895242b7f18ef792563d742c2d673bfcb75134"}, - {file = "numpy-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69ff563d43c69b1baba77af455dd0a839df8d25e8590e79c90fcbe1499ebde42"}, - {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1b902ce0e0a5bb7704556a217c4f63a7974f8f43e090aff03fcf262e0b135e02"}, - {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:f1659887361a7151f89e79b276ed8dff3d75877df906328f14d8bb40bb4f5101"}, - {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4658c398d65d1b25e1760de3157011a80375da861709abd7cef3bad65d6543f9"}, - {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4127d4303b9ac9f94ca0441138acead39928938660ca58329fe156f84b9f3015"}, - {file = "numpy-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e5eeca8067ad04bc8a2a8731183d51d7cbaac66d86085d5f4766ee6bf19c7f87"}, - {file = "numpy-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9adbd9bb520c866e1bfd7e10e1880a1f7749f1f6e5017686a5fbb9b72cf69f82"}, - {file = "numpy-2.0.1-cp310-cp310-win32.whl", hash = "sha256:7b9853803278db3bdcc6cd5beca37815b133e9e77ff3d4733c247414e78eb8d1"}, - {file = "numpy-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81b0893a39bc5b865b8bf89e9ad7807e16717f19868e9d234bdaf9b1f1393868"}, - {file = "numpy-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268"}, - {file = "numpy-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e"}, - {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343"}, - {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b"}, - {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe"}, - {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67"}, - {file = "numpy-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7"}, - {file = "numpy-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55"}, - {file = "numpy-2.0.1-cp311-cp311-win32.whl", hash = "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4"}, - {file = "numpy-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8"}, - {file = "numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac"}, - {file = "numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4"}, - {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1"}, - {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587"}, - {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8"}, - {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a"}, - {file = "numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7"}, - {file = "numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b"}, - {file = "numpy-2.0.1-cp312-cp312-win32.whl", hash = "sha256:173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf"}, - {file = "numpy-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171"}, - {file = "numpy-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc085b28d62ff4009364e7ca34b80a9a080cbd97c2c0630bb5f7f770dae9414"}, - {file = "numpy-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fae4ebbf95a179c1156fab0b142b74e4ba4204c87bde8d3d8b6f9c34c5825ef"}, - {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:72dc22e9ec8f6eaa206deb1b1355eb2e253899d7347f5e2fae5f0af613741d06"}, - {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:ec87f5f8aca726117a1c9b7083e7656a9d0d606eec7299cc067bb83d26f16e0c"}, - {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f682ea61a88479d9498bf2091fdcd722b090724b08b31d63e022adc063bad59"}, - {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8efc84f01c1cd7e34b3fb310183e72fcdf55293ee736d679b6d35b35d80bba26"}, - {file = "numpy-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3fdabe3e2a52bc4eff8dc7a5044342f8bd9f11ef0934fcd3289a788c0eb10018"}, - {file = "numpy-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:24a0e1befbfa14615b49ba9659d3d8818a0f4d8a1c5822af8696706fbda7310c"}, - {file = "numpy-2.0.1-cp39-cp39-win32.whl", hash = "sha256:f9cf5ea551aec449206954b075db819f52adc1638d46a6738253a712d553c7b4"}, - {file = "numpy-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9e81fa9017eaa416c056e5d9e71be93d05e2c3c2ab308d23307a8bc4443c368"}, - {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61728fba1e464f789b11deb78a57805c70b2ed02343560456190d0501ba37b0f"}, - {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:12f5d865d60fb9734e60a60f1d5afa6d962d8d4467c120a1c0cda6eb2964437d"}, - {file = "numpy-2.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eacf3291e263d5a67d8c1a581a8ebbcfd6447204ef58828caf69a5e3e8c75990"}, - {file = "numpy-2.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c3a346ae20cfd80b6cfd3e60dc179963ef2ea58da5ec074fd3d9e7a1e7ba97f"}, - {file = "numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] name = "orjson" -version = "3.10.6" +version = "3.10.7" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.6-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb0ee33124db6eaa517d00890fc1a55c3bfe1cf78ba4a8899d71a06f2d6ff5c7"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c1c4b53b24a4c06547ce43e5fee6ec4e0d8fe2d597f4647fc033fd205707365"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eadc8fd310edb4bdbd333374f2c8fec6794bbbae99b592f448d8214a5e4050c0"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61272a5aec2b2661f4fa2b37c907ce9701e821b2c1285d5c3ab0207ebd358d38"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57985ee7e91d6214c837936dc1608f40f330a6b88bb13f5a57ce5257807da143"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:633a3b31d9d7c9f02d49c4ab4d0a86065c4a6f6adc297d63d272e043472acab5"}, - {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1c680b269d33ec444afe2bdc647c9eb73166fa47a16d9a75ee56a374f4a45f43"}, - {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f759503a97a6ace19e55461395ab0d618b5a117e8d0fbb20e70cfd68a47327f2"}, - {file = "orjson-3.10.6-cp310-none-win32.whl", hash = "sha256:95a0cce17f969fb5391762e5719575217bd10ac5a189d1979442ee54456393f3"}, - {file = "orjson-3.10.6-cp310-none-win_amd64.whl", hash = "sha256:df25d9271270ba2133cc88ee83c318372bdc0f2cd6f32e7a450809a111efc45c"}, - {file = "orjson-3.10.6-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b1ec490e10d2a77c345def52599311849fc063ae0e67cf4f84528073152bb2ba"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d43d3feb8f19d07e9f01e5b9be4f28801cf7c60d0fa0d279951b18fae1932b"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3045267e98fe749408eee1593a142e02357c5c99be0802185ef2170086a863"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c27bc6a28ae95923350ab382c57113abd38f3928af3c80be6f2ba7eb8d8db0b0"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d27456491ca79532d11e507cadca37fb8c9324a3976294f68fb1eff2dc6ced5a"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05ac3d3916023745aa3b3b388e91b9166be1ca02b7c7e41045da6d12985685f0"}, - {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1335d4ef59ab85cab66fe73fd7a4e881c298ee7f63ede918b7faa1b27cbe5212"}, - {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4bbc6d0af24c1575edc79994c20e1b29e6fb3c6a570371306db0993ecf144dc5"}, - {file = "orjson-3.10.6-cp311-none-win32.whl", hash = "sha256:450e39ab1f7694465060a0550b3f6d328d20297bf2e06aa947b97c21e5241fbd"}, - {file = "orjson-3.10.6-cp311-none-win_amd64.whl", hash = "sha256:227df19441372610b20e05bdb906e1742ec2ad7a66ac8350dcfd29a63014a83b"}, - {file = "orjson-3.10.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ea2977b21f8d5d9b758bb3f344a75e55ca78e3ff85595d248eee813ae23ecdfb"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6f3d167d13a16ed263b52dbfedff52c962bfd3d270b46b7518365bcc2121eed"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f710f346e4c44a4e8bdf23daa974faede58f83334289df80bc9cd12fe82573c7"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7275664f84e027dcb1ad5200b8b18373e9c669b2a9ec33d410c40f5ccf4b257e"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0943e4c701196b23c240b3d10ed8ecd674f03089198cf503105b474a4f77f21f"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:446dee5a491b5bc7d8f825d80d9637e7af43f86a331207b9c9610e2f93fee22a"}, - {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:64c81456d2a050d380786413786b057983892db105516639cb5d3ee3c7fd5148"}, - {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:960db0e31c4e52fa0fc3ecbaea5b2d3b58f379e32a95ae6b0ebeaa25b93dfd34"}, - {file = "orjson-3.10.6-cp312-none-win32.whl", hash = "sha256:a6ea7afb5b30b2317e0bee03c8d34c8181bc5a36f2afd4d0952f378972c4efd5"}, - {file = "orjson-3.10.6-cp312-none-win_amd64.whl", hash = "sha256:874ce88264b7e655dde4aeaacdc8fd772a7962faadfb41abe63e2a4861abc3dc"}, - {file = "orjson-3.10.6-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:66680eae4c4e7fc193d91cfc1353ad6d01b4801ae9b5314f17e11ba55e934183"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caff75b425db5ef8e8f23af93c80f072f97b4fb3afd4af44482905c9f588da28"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3722fddb821b6036fd2a3c814f6bd9b57a89dc6337b9924ecd614ebce3271394"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2c116072a8533f2fec435fde4d134610f806bdac20188c7bd2081f3e9e0133f"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6eeb13218c8cf34c61912e9df2de2853f1d009de0e46ea09ccdf3d757896af0a"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:965a916373382674e323c957d560b953d81d7a8603fbeee26f7b8248638bd48b"}, - {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03c95484d53ed8e479cade8628c9cea00fd9d67f5554764a1110e0d5aa2de96e"}, - {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e060748a04cccf1e0a6f2358dffea9c080b849a4a68c28b1b907f272b5127e9b"}, - {file = "orjson-3.10.6-cp38-none-win32.whl", hash = "sha256:738dbe3ef909c4b019d69afc19caf6b5ed0e2f1c786b5d6215fbb7539246e4c6"}, - {file = "orjson-3.10.6-cp38-none-win_amd64.whl", hash = "sha256:d40f839dddf6a7d77114fe6b8a70218556408c71d4d6e29413bb5f150a692ff7"}, - {file = "orjson-3.10.6-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:697a35a083c4f834807a6232b3e62c8b280f7a44ad0b759fd4dce748951e70db"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd502f96bf5ea9a61cbc0b2b5900d0dd68aa0da197179042bdd2be67e51a1e4b"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f215789fb1667cdc874c1b8af6a84dc939fd802bf293a8334fce185c79cd359b"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2debd8ddce948a8c0938c8c93ade191d2f4ba4649a54302a7da905a81f00b56"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5410111d7b6681d4b0d65e0f58a13be588d01b473822483f77f513c7f93bd3b2"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb1f28a137337fdc18384079fa5726810681055b32b92253fa15ae5656e1dddb"}, - {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bf2fbbce5fe7cd1aa177ea3eab2b8e6a6bc6e8592e4279ed3db2d62e57c0e1b2"}, - {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:79b9b9e33bd4c517445a62b90ca0cc279b0f1f3970655c3df9e608bc3f91741a"}, - {file = "orjson-3.10.6-cp39-none-win32.whl", hash = "sha256:30b0a09a2014e621b1adf66a4f705f0809358350a757508ee80209b2d8dae219"}, - {file = "orjson-3.10.6-cp39-none-win_amd64.whl", hash = "sha256:49e3bc615652617d463069f91b867a4458114c5b104e13b7ae6872e5f79d0844"}, - {file = "orjson-3.10.6.tar.gz", hash = "sha256:e54b63d0a7c6c54a5f5f726bc93a2078111ef060fec4ecbf34c5db800ca3b3a7"}, + {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, + {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, + {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, + {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, + {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, + {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, + {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, + {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, + {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, + {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, + {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, + {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, + {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, + {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, + {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, + {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, + {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, + {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, + {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, + {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, ] [[package]] @@ -2442,18 +2439,18 @@ jeepney = ">=0.6" [[package]] name = "setuptools" -version = "72.1.0" +version = "72.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, + {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, ] [package.extras] core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -2501,13 +2498,13 @@ files = [ [[package]] name = "tomlkit" -version = "0.13.0" +version = "0.13.1" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" files = [ - {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, - {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, + {file = "tomlkit-0.13.1-py3-none-any.whl", hash = "sha256:fb12e89373b28f3cd6679035324770123d6df04488431e1c7bcecf17820ee2e4"}, + {file = "tomlkit-0.13.1.tar.gz", hash = "sha256:1be06879860054a26faba7acf2af62b45c94aa43b00a5f87fc445c5f930ad754"}, ] [[package]] @@ -2947,13 +2944,13 @@ multidict = ">=4.0" [[package]] name = "zipp" -version = "3.19.2" +version = "3.20.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, - {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, + {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, + {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, ] [package.extras] @@ -2963,4 +2960,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "43521ce164b2b3101c40a1d2e0a309d9a6b87d1c398f94be729b72b3d426afc0" +content-hash = "b9bbba81b4362576e8ac2c0d40609f5d270d3300a619448cbdb67b2ad9b47488" diff --git a/pymammotion/data/model/device.py b/pymammotion/data/model/device.py index 3674fde..c1e22f6 100644 --- a/pymammotion/data/model/device.py +++ b/pymammotion/data/model/device.py @@ -1,5 +1,5 @@ """MowingDevice class to wrap around the betterproto dataclasses.""" - +import math from dataclasses import dataclass import betterproto @@ -19,6 +19,10 @@ from pymammotion.utility.map import CoordinateConverter +def parse_double(val: float, d: float): + return val / math.pow(10.0, d) + + @dataclass class MowingDevice: """Wraps the betterproto dataclasses, so we can bypass the groups for keeping all data.""" @@ -52,10 +56,10 @@ def buffer(self, buffer_list: SystemUpdateBufMsg): match buffer_list.update_buf_data[0]: case 1: # 4 speed - self.location.RTK.latitude = buffer_list.update_buf_data[5] - self.location.RTK.longitude = buffer_list.update_buf_data[6] - self.location.dock.latitude = buffer_list.update_buf_data[7] - self.location.dock.longitude = buffer_list.update_buf_data[8] + self.location.RTK.latitude = parse_double(buffer_list.update_buf_data[5], 8.0) + self.location.RTK.longitude = parse_double(buffer_list.update_buf_data[6], 8.0) + self.location.dock.latitude = parse_double(buffer_list.update_buf_data[7], 4.0) + self.location.dock.longitude = parse_double(buffer_list.update_buf_data[8], 4.0) self.location.dock.rotation = buffer_list.update_buf_data[3] + 180 case 2: self.err_code_list.clear() diff --git a/pymammotion/data/model/report_info.py b/pymammotion/data/model/report_info.py index 259ffff..80566fd 100644 --- a/pymammotion/data/model/report_info.py +++ b/pymammotion/data/model/report_info.py @@ -72,6 +72,7 @@ def from_dict(cls, data: dict): @dataclass class WorkData: + path: int = 0 path_hash: str = "" progress: int = 0 area: int = 0 @@ -80,14 +81,22 @@ class WorkData: bp_pos_x: int = 0 bp_pos_y: int = 0 real_path_num: str = "" + path_pos_x: int = 0 + path_pos_y: int = 0 ub_zone_hash: str = "" + ub_path_hash: str = "" init_cfg_hash: str = "" ub_ecode_hash: str = "" + nav_run_mode: int = 0 + test_mode_status: int = 0 + man_run_speed: int = 0 + nav_edit_status: int = 0 knife_height: int = 0 @classmethod def from_dict(cls, data: dict): return cls( + path=data.get('path', 0), path_hash=data.get('path_hash', ""), progress=data.get('progress', 0), area=data.get('area', 0), @@ -96,9 +105,16 @@ def from_dict(cls, data: dict): bp_pos_x=data.get('bp_pos_x', 0), bp_pos_y=data.get('bp_pos_y', 0), real_path_num=data.get('real_path_num', ""), + path_pos_x=data.get('path_pos_x', 0), + path_pos_y=data.get('path_pos_y', 0), ub_zone_hash=data.get('ub_zone_hash', ""), + ub_path_hash=data.get('ub_path_hash', ""), init_cfg_hash=data.get('init_cfg_hash', ""), ub_ecode_hash=data.get('ub_ecode_hash', ""), + nav_run_mode=data.get('nav_run_mode', 0), + test_mode_status=data.get('test_mode_status', 0), + man_run_speed=data.get('man_run_speed', 0), + nav_edit_status=data.get('nav_edit_status', 0), knife_height=data.get('knife_height', 0) ) diff --git a/pymammotion/utility/map.py b/pymammotion/utility/map.py index 5ad12aa..4f716b9 100644 --- a/pymammotion/utility/map.py +++ b/pymammotion/utility/map.py @@ -5,7 +5,7 @@ class CoordinateConverter: - def __init__(self, lat_degrees: float, lon_degrees: float): + def __init__(self, latitude_rad: float, longitude_rad: float): # Initialize constants self.WGS84A = 6378137.0 self.f_ = 3.3528106647474805E-21 @@ -23,8 +23,6 @@ def __init__(self, lat_degrees: float, lon_degrees: float): self.z0_ = 0.0 # Call set_init_lla with provided lat/lon - latitude_rad = math.radians(lat_degrees) - longitude_rad = math.radians(lon_degrees) self.set_init_lla(latitude_rad, longitude_rad) def set_init_lla(self, lat_rad, lon_rad): diff --git a/pyproject.toml b/pyproject.toml index c374808..5a6c458 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.1.3" +version = "0.1.7" license = "GNU-3.0" description = "" readme = "README.md" @@ -31,7 +31,7 @@ orjson = "^3.9.15" betterproto = "^1.2.5" pyjoystick = "^1.2.4" nest-asyncio = "^1.6.0" -numpy = "^2.0.1" +numpy = "^1.26.0" [tool.poetry.group.dev.dependencies] @@ -48,7 +48,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.1.3" +current_version = "0.1.7" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From ede5f066e322688fc09662637c48a378ff5fc31b Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Wed, 14 Aug 2024 19:07:45 +1200 Subject: [PATCH 11/35] try to keep last data if its missing in report cfg --- pymammotion/data/model/report_info.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pymammotion/data/model/report_info.py b/pymammotion/data/model/report_info.py index 80566fd..efa560e 100644 --- a/pymammotion/data/model/report_info.py +++ b/pymammotion/data/model/report_info.py @@ -1,4 +1,5 @@ -from dataclasses import dataclass, field +from dataclasses import dataclass, field, asdict + @dataclass class ConnectData: @@ -128,10 +129,14 @@ class ReportData: @classmethod def from_dict(cls, data: dict): + locations = cls.locations + if data.get('locations') is not None: + locations=[LocationData.from_dict(loc) for loc in data.get('locations', [])] + return cls( - connect=ConnectData.from_dict(data.get('connect', {})), - dev=DeviceData.from_dict(data.get('dev', {})), - rtk=RTKData.from_dict(data.get('rtk', {})), - locations=[LocationData.from_dict(loc) for loc in data.get('locations', [])], - work=WorkData.from_dict(data.get('work', {})) + connect=ConnectData.from_dict(data.get('connect', asdict(cls.connect))), + dev=DeviceData.from_dict(data.get('dev', asdict(cls.dev))), + rtk=RTKData.from_dict(data.get('rtk', asdict(cls.rtk))), + locations=locations, + work=WorkData.from_dict(data.get('work', asdict(cls.work))) ) \ No newline at end of file From 39867b5d4eb453dbcb1ac8b05d5ba724d7765aef Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Thu, 15 Aug 2024 08:33:54 +1200 Subject: [PATCH 12/35] fix report info --- pymammotion/data/model/device.py | 2 +- pymammotion/data/model/report_info.py | 17 ++++++++--------- pyproject.toml | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pymammotion/data/model/device.py b/pymammotion/data/model/device.py index c1e22f6..02264c4 100644 --- a/pymammotion/data/model/device.py +++ b/pymammotion/data/model/device.py @@ -99,7 +99,7 @@ def update_report_data(self, toapp_report_data: ReportInfoData): if index == 0: self.location.position_type = location.pos_type self.location.orientation = location.real_toward / 10000 - self.location.device = coordinate_converter.enu_to_lla(location.real_pos_y, location.real_pos_x) + self.location.device = coordinate_converter.enu_to_lla(parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)) self.report_data = self.report_data.from_dict(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE)) diff --git a/pymammotion/data/model/report_info.py b/pymammotion/data/model/report_info.py index efa560e..1b2ca19 100644 --- a/pymammotion/data/model/report_info.py +++ b/pymammotion/data/model/report_info.py @@ -1,6 +1,5 @@ from dataclasses import dataclass, field, asdict - @dataclass class ConnectData: connect_type: int = 0 @@ -127,16 +126,16 @@ class ReportData: locations: list[LocationData] = field(default_factory=list) work: WorkData = field(default_factory=WorkData) - @classmethod - def from_dict(cls, data: dict): - locations = cls.locations + + def from_dict(self, data: dict): + locations = self.locations if data.get('locations') is not None: locations=[LocationData.from_dict(loc) for loc in data.get('locations', [])] - return cls( - connect=ConnectData.from_dict(data.get('connect', asdict(cls.connect))), - dev=DeviceData.from_dict(data.get('dev', asdict(cls.dev))), - rtk=RTKData.from_dict(data.get('rtk', asdict(cls.rtk))), + return ReportData( + connect=ConnectData.from_dict(data.get('connect', asdict(self.connect))), + dev=DeviceData.from_dict(data.get('dev', asdict(self.dev))), + rtk=RTKData.from_dict(data.get('rtk', asdict(self.rtk))), locations=locations, - work=WorkData.from_dict(data.get('work', asdict(cls.work))) + work=WorkData.from_dict(data.get('work', asdict(self.work))) ) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 5a6c458..ac3cdb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.1.7" +version = "0.1.9" license = "GNU-3.0" description = "" readme = "README.md" @@ -48,7 +48,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.1.7" +current_version = "0.1.9" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 21011b6f79479c2e39fcf48c81a98df05292dce3 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Sat, 17 Aug 2024 15:03:09 +1200 Subject: [PATCH 13/35] Add missing calls for ble sync as well as hook up callbacks, remove dead code and general clean up, added test call to verify things work --- .../dataclass/dev_by_account_response.py | 2 +- pymammotion/http/http.py | 4 +- pymammotion/mammotion/devices/mammotion.py | 73 ++++++++++++++----- pymammotion/mqtt/mammotion_mqtt.py | 63 ++++------------ tests/login_and_mqtt_test.py | 12 +-- 5 files changed, 77 insertions(+), 77 deletions(-) diff --git a/pymammotion/aliyun/dataclass/dev_by_account_response.py b/pymammotion/aliyun/dataclass/dev_by_account_response.py index 8ad2b2a..67ca475 100644 --- a/pymammotion/aliyun/dataclass/dev_by_account_response.py +++ b/pymammotion/aliyun/dataclass/dev_by_account_response.py @@ -6,7 +6,6 @@ @dataclass class Device(DataClassORJSONMixin): - productModel: str gmtModified: int netType: str nickName: str @@ -26,6 +25,7 @@ class Device(DataClassORJSONMixin): status: int productImage: Optional[str] = None categoryImage: Optional[str] = None + productModel: Optional[str] = None @dataclass diff --git a/pymammotion/http/http.py b/pymammotion/http/http.py index 932cb15..6a56a7f 100644 --- a/pymammotion/http/http.py +++ b/pymammotion/http/http.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Generic, Literal, TypeVar +from typing import Generic, Literal, TypeVar, Optional from aiohttp import ClientSession from mashumaro import DataClassDictMixin @@ -25,7 +25,7 @@ class Response(DataClassDictMixin, Generic[DataT]): class LoginResponseUserInformation(DataClassORJSONMixin): areaCode: str domainAbbreviation: str - email: str + email: Optional[str] userId: str userAccount: str authType: str diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index f0346c3..2a5bc42 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -8,7 +8,7 @@ import logging from abc import abstractmethod from enum import Enum -from typing import Any, cast +from typing import Any, cast, Optional, Callable from uuid import UUID import base64 @@ -730,25 +730,69 @@ def __init__( ) -> None: """Initialize MammotionBaseCloudDevice.""" super().__init__() + self._ble_sync_task = None + self.is_ready = False self._mqtt_client = mqtt_client self.iot_id = iot_id self.nick_name = nick_name self._command_futures = {} self._commands: MammotionCommand = MammotionCommand(device_name) self.currentID = "" + self.on_ready_callback: Optional[Callable[[], None]] = None self._operation_lock = asyncio.Lock() - def _on_mqtt_message(self, topic: str, payload: str) -> None: + self._mqtt_client.on_connected = self.on_connected + self._mqtt_client.on_disconnected = self.on_disconnected + self._mqtt_client.on_message = self._on_mqtt_message + self._mqtt_client.on_ready = self.on_ready + if self._mqtt_client.is_connected: + self._ble_sync() + self.run_periodic_sync_task() + + # temporary for testing only + # self._start_sync_task = self.loop.call_later(30, lambda: asyncio.ensure_future(self.start_sync(0))) + + + def on_ready(self): + """Callback for when MQTT is subscribed to events.""" + if self.on_ready_callback: + self.on_ready_callback() + + def on_connected(self): + """Callback for when MQTT connects.""" + self._ble_sync() + self.run_periodic_sync_task() + + def on_disconnected(self): + """Callback for when MQTT disconnects.""" + pass + + def _ble_sync(self): + command_bytes = self._commands.send_todev_ble_sync(3) + self._mqtt_client.get_cloud_client().send_cloud_command(self.iot_id, command_bytes) + + async def run_periodic_sync_task(self) -> None: + """Send ble sync to robot.""" + try: + self._ble_sync() + finally: + self.schedule_ble_sync() + + def schedule_ble_sync(self): + """Periodically sync to keep connection alive.""" + if self._mqtt_client is not None and self._mqtt_client.is_connected: + self._ble_sync_task = self.loop.call_later( + 160, lambda: asyncio.ensure_future(self.run_periodic_sync_task()) + ) + + def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: """Handle incoming MQTT messages.""" _LOGGER.debug("MQTT message received on topic %s: %s", topic, payload) json_str = json.dumps(payload) payload = json.loads(json_str) - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - loop.run_until_complete(self._handle_mqtt_message(topic, payload)) - loop.close() + self._handle_mqtt_message(topic, payload) async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: """Send command to device via MQTT and read response.""" @@ -848,29 +892,24 @@ def _extract_encoded_message(self, payload: dict) -> str: _LOGGER.error("Error extracting encoded message. Payload: %s", payload) return "" - async def _parse_mqtt_response(self, topic: str, payload: dict) -> None: + def _parse_mqtt_response(self, topic: str, payload: dict) -> None: """Parse the MQTT response.""" if topic.endswith("/app/down/thing/events"): - print (f"Thing event received") + _LOGGER.debug(f"Thing event received") event = ThingEventMessage.from_dicts(payload) params = event.params if params.identifier == "device_protobuf_msg_event": - print (f"Protobuf reveice") + _LOGGER.debug(f"Protobuf event") binary_data = base64.b64decode(params.value.get('content', '')) self._update_raw_data(cast(bytes, binary_data)) new_msg = LubaMsg().parse(cast(bytes, binary_data)) if self._notify_future and not self._notify_future.done(): self._notify_future.set_result(new_msg) - await self._state_manager.notification(new_msg) + asyncio.run(self._state_manager.notification(new_msg)) - async def _handle_mqtt_message(self, topic: str, payload: dict) -> None: + def _handle_mqtt_message(self, topic: str, payload: dict) -> None: """Async handler for incoming MQTT messages.""" - await self._parse_mqtt_response(topic=topic, payload=payload) - # message_id = self._extract_message_id(payload) - # print (f"Received message id: {self.currentID}") - # message_id = self.currentID - # if message_id and message_id in self._command_futures: - # print (f"Start parsing response") + self._parse_mqtt_response(topic=topic, payload=payload) async def _disconnect(self): diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index f5531ff..eb15d1c 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -1,11 +1,11 @@ """MammotionMQTT.""" - +import asyncio import hashlib import hmac import json import logging from logging import getLogger -from typing import Callable, Optional, cast +from typing import Callable, Optional, cast, Awaitable from linkkit.linkkit import LinkKit from paho.mqtt.client import Client, MQTTMessage, MQTTv311, connack_string @@ -22,7 +22,6 @@ class MammotionMQTT: """MQTT client for pymammotion.""" - _cloud_client = None def __init__( self, @@ -35,8 +34,10 @@ def __init__( ): """Create instance of MammotionMQTT.""" super().__init__() - + self.is_connected = False + self.is_ready = False self.on_connected: Optional[Callable[[], None]] = None + self.on_ready: Optional[Callable[[], None]] = None self.on_error: Optional[Callable[[str], None]] = None self.on_disconnected: Optional[Callable[[], None]] = None self.on_message: Optional[Callable[[str, str, str], None]] = None @@ -76,22 +77,6 @@ def __init__( # self._mqtt_host = "public.itls.eu-central-1.aliyuncs.com" self._mqtt_host = f"{self._product_key}.iot-as-mqtt.{region_id}.aliyuncs.com" - self._client = Client( - client_id=self._mqtt_client_id, - protocol=MQTTv311, - ) - self._client.on_message = self._on_message - self._client.on_connect = self._on_connect - self._client.on_disconnect = self._on_disconnect - self._client.username_pw_set(self._mqtt_username, self._mqtt_password) - self._client.enable_logger(logger.getChild("paho")) - - # region Connection handling - def connect(self): - """Connect to MQTT Server.""" - logger.info("Connecting...") - self._client.connect(host=self._mqtt_host) - self._client.loop_forever() def connect_async(self): """Connect async to MQTT Server.""" @@ -106,8 +91,8 @@ def disconnect(self): """Disconnect from MQTT Server.""" logger.info("Disconnecting...") self._linkkit_client.disconnect() - self._client.disconnect() - self._client.loop_stop() + # self._client.disconnect() + # self._client.loop_stop() def _thing_on_thing_enable(self, user_data): """Is called when Thing is enabled.""" @@ -139,6 +124,10 @@ def _thing_on_thing_enable(self, user_data): ), ) + + if self.on_ready: + self.is_ready = True + self.on_ready() # self._linkkit_client.query_ota_firmware() # command = MammotionCommand(device_name="Luba") # self._cloud_client.send_cloud_command(command.get_report_cfg()) @@ -158,39 +147,19 @@ def _thing_on_topic_message(self, topic, payload, qos, user_data): def _thing_on_connect(self, session_flag, rc, user_data): """Is called on thing connect.""" + self.is_connected = True + if self.on_connected is not None: + self.on_connected() logger.debug("on_connect, session_flag:%d, rc:%d", session_flag, rc) # self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/#") - def _on_connect(self, _client, _userdata, _flags: dict, rc: int): - """Is called when on connect.""" - if rc == 0: - logger.debug("Connected") - self._client.subscribe(f"/sys/{self._product_key}/{self._device_name}/#") - self._client.subscribe(f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply") - - self._client.publish( - f"/sys/{self._product_key}/{self._device_name}/app/up/account/bind", - json.dumps( - { - "id": "msgid1", - "version": "1.0", - "request": {"clientId": self._mqtt_username}, - "params": {"iotToken": self._iot_token}, - } - ), - ) - - if self.on_connected: - self.on_connected() - else: - logger.error("Could not connect %s", connack_string(rc)) - if self.on_error: - self.on_error(connack_string(rc)) def _on_disconnect(self, _client, _userdata, rc: int): """Is called on disconnect.""" logger.info("Disconnected") + self.is_connected = False + self.is_ready = False logger.debug(rc) if self.on_disconnected: self.on_disconnected() diff --git a/tests/login_and_mqtt_test.py b/tests/login_and_mqtt_test.py index f2a61d4..e4c2ac8 100644 --- a/tests/login_and_mqtt_test.py +++ b/tests/login_and_mqtt_test.py @@ -39,7 +39,7 @@ async def sync_status_and_map(cloud_device: MammotionBaseCloudDevice): await asyncio.sleep(1) await cloud_device.start_sync(0) await asyncio.sleep(2) - await cloud_device.start_map_sync() + # await cloud_device.start_map_sync() while(True): print(cloud_device.luba_msg) @@ -52,15 +52,13 @@ async def sync_status_and_map(cloud_device: MammotionBaseCloudDevice): asyncio.set_event_loop(event_loop) cloud_client = event_loop.run_until_complete(run()) - - _mammotion_mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, product_key=cloud_client._aep_response.data.productKey, device_name=cloud_client._aep_response.data.deviceName, device_secret=cloud_client._aep_response.data.deviceSecret, iot_token=cloud_client._session_by_authcode_response.data.iotToken, client_id=cloud_client._client_id) + _mammotion_mqtt._cloud_client = cloud_client - #mammotion.connect() blocks further calls _mammotion_mqtt.connect_async() _devices_list = [] @@ -74,11 +72,5 @@ async def sync_status_and_map(cloud_device: MammotionBaseCloudDevice): ) _devices_list.append(dev) - #Assign callback based on iotId - _mammotion_mqtt.on_message = lambda topic, payload, iot_id: [ - device._on_mqtt_message(topic, payload) for device in _devices_list if device.iot_id == iot_id - ] - - sync = event_loop.run_until_complete(sync_status_and_map(_devices_list[0])) event_loop.run_forever() \ No newline at end of file From 256f8226d6009a6adf263dfd2b98b0fd8394f304 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Sat, 17 Aug 2024 15:05:26 +1200 Subject: [PATCH 14/35] format and run ruff --- __init__.py | 0 pymammotion/__init__.py | 10 +- pymammotion/data/model/device.py | 9 +- pymammotion/data/model/location.py | 2 +- pymammotion/data/model/mowing_modes.py | 3 +- pymammotion/data/model/report_info.py | 101 +++++++++--------- pymammotion/data/mqtt/event.py | 28 +++-- pymammotion/http/http.py | 2 +- .../mammotion/commands/mammotion_command.py | 4 - .../mammotion/commands/messages/navigation.py | 5 +- pymammotion/mammotion/devices/mammotion.py | 63 +++++------ pymammotion/mqtt/mammotion_mqtt.py | 10 +- .../utility/constant/device_constant.py | 2 +- pymammotion/utility/map.py | 14 ++- 14 files changed, 116 insertions(+), 137 deletions(-) create mode 100644 __init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pymammotion/__init__.py b/pymammotion/__init__.py index fb60bb8..a248ea0 100644 --- a/pymammotion/__init__.py +++ b/pymammotion/__init__.py @@ -16,15 +16,7 @@ from pymammotion.mammotion.devices import MammotionBaseBLEDevice from pymammotion.mqtt import MammotionMQTT - - -__all__ = [ - 'LubaBLE', - 'LubaHTTP', - 'connect_http', - 'MammotionBaseBLEDevice', - 'MammotionMQTT' -] +__all__ = ["LubaBLE", "LubaHTTP", "connect_http", "MammotionBaseBLEDevice", "MammotionMQTT"] logger = logging.getLogger(__name__) diff --git a/pymammotion/data/model/device.py b/pymammotion/data/model/device.py index 02264c4..66cf6e2 100644 --- a/pymammotion/data/model/device.py +++ b/pymammotion/data/model/device.py @@ -1,4 +1,5 @@ """MowingDevice class to wrap around the betterproto dataclasses.""" + import math from dataclasses import dataclass @@ -15,7 +16,7 @@ from pymammotion.proto.mctrl_nav import MctlNav from pymammotion.proto.mctrl_ota import MctlOta from pymammotion.proto.mctrl_pept import MctlPept -from pymammotion.proto.mctrl_sys import MctlSys, MowToAppInfoT, SystemUpdateBufMsg, ReportInfoData +from pymammotion.proto.mctrl_sys import MctlSys, MowToAppInfoT, ReportInfoData, SystemUpdateBufMsg from pymammotion.utility.map import CoordinateConverter @@ -99,12 +100,12 @@ def update_report_data(self, toapp_report_data: ReportInfoData): if index == 0: self.location.position_type = location.pos_type self.location.orientation = location.real_toward / 10000 - self.location.device = coordinate_converter.enu_to_lla(parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)) + self.location.device = coordinate_converter.enu_to_lla( + parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0) + ) self.report_data = self.report_data.from_dict(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE)) - - def mow_info(self, toapp_mow_info: MowToAppInfoT): pass diff --git a/pymammotion/data/model/location.py b/pymammotion/data/model/location.py index 4f6896f..fa06177 100644 --- a/pymammotion/data/model/location.py +++ b/pymammotion/data/model/location.py @@ -34,7 +34,7 @@ class Location: RTK: Point dock: Dock position_type: int - orientation: int # 360 degree rotation +- + orientation: int # 360 degree rotation +- def __init__(self): self.device = Point() diff --git a/pymammotion/data/model/mowing_modes.py b/pymammotion/data/model/mowing_modes.py index 7a9b7ab..e04ac20 100644 --- a/pymammotion/data/model/mowing_modes.py +++ b/pymammotion/data/model/mowing_modes.py @@ -39,7 +39,8 @@ class MowOrder(IntEnum): class BypassStrategy(IntEnum): """Matches up with ultra_wave.""" + direct_touch = 0 slow_touch = 1 less_touch = 2 - no_touch = 3 # luba 2 yuka only or possibly value of 10 + no_touch = 3 # luba 2 yuka only or possibly value of 10 diff --git a/pymammotion/data/model/report_info.py b/pymammotion/data/model/report_info.py index 1b2ca19..f9db810 100644 --- a/pymammotion/data/model/report_info.py +++ b/pymammotion/data/model/report_info.py @@ -1,4 +1,5 @@ -from dataclasses import dataclass, field, asdict +from dataclasses import asdict, dataclass, field + @dataclass class ConnectData: @@ -9,11 +10,12 @@ class ConnectData: @classmethod def from_dict(cls, data: dict): return cls( - connect_type=data.get('connect_type', 0), - ble_rssi=data.get('ble_rssi', 0), - wifi_rssi=data.get('wifi_rssi', 0) + connect_type=data.get("connect_type", 0), + ble_rssi=data.get("ble_rssi", 0), + wifi_rssi=data.get("wifi_rssi", 0), ) + @dataclass class DeviceData: sys_status: int = 0 @@ -26,14 +28,15 @@ class DeviceData: @classmethod def from_dict(cls, data: dict): return cls( - sys_status=data.get('sys_status', 0), - charge_state=data.get('charge_state', 0), - battery_val=data.get('battery_val', 0), - sensor_status=data.get('sensor_status', 0), - last_status=data.get('last_status', 0), - sys_time_stamp=data.get('sys_time_stamp', "") + sys_status=data.get("sys_status", 0), + charge_state=data.get("charge_state", 0), + battery_val=data.get("battery_val", 0), + sensor_status=data.get("sensor_status", 0), + last_status=data.get("last_status", 0), + sys_time_stamp=data.get("sys_time_stamp", ""), ) + @dataclass class RTKData: status: int = 0 @@ -45,13 +48,14 @@ class RTKData: @classmethod def from_dict(cls, data: dict): return cls( - status=data.get('status', 0), - pos_level=data.get('pos_level', 0), - gps_stars=data.get('gps_stars', 0), - dis_status=data.get('dis_status', ""), - co_view_stars=data.get('co_view_stars', 0) + status=data.get("status", 0), + pos_level=data.get("pos_level", 0), + gps_stars=data.get("gps_stars", 0), + dis_status=data.get("dis_status", ""), + co_view_stars=data.get("co_view_stars", 0), ) + @dataclass class LocationData: real_pos_x: int = 0 @@ -63,13 +67,14 @@ class LocationData: @classmethod def from_dict(cls, data: dict): return cls( - real_pos_x=data.get('real_pos_x', 0), - real_pos_y=data.get('real_pos_y', 0), - real_toward=data.get('real_toward', 0), - pos_type=data.get('pos_type', 0), - bol_hash=data.get('bol_hash', "") + real_pos_x=data.get("real_pos_x", 0), + real_pos_y=data.get("real_pos_y", 0), + real_toward=data.get("real_toward", 0), + pos_type=data.get("pos_type", 0), + bol_hash=data.get("bol_hash", ""), ) + @dataclass class WorkData: path: int = 0 @@ -96,28 +101,29 @@ class WorkData: @classmethod def from_dict(cls, data: dict): return cls( - path=data.get('path', 0), - path_hash=data.get('path_hash', ""), - progress=data.get('progress', 0), - area=data.get('area', 0), - bp_info=data.get('bp_info', 0), - bp_hash=data.get('bp_hash', ""), - bp_pos_x=data.get('bp_pos_x', 0), - bp_pos_y=data.get('bp_pos_y', 0), - real_path_num=data.get('real_path_num', ""), - path_pos_x=data.get('path_pos_x', 0), - path_pos_y=data.get('path_pos_y', 0), - ub_zone_hash=data.get('ub_zone_hash', ""), - ub_path_hash=data.get('ub_path_hash', ""), - init_cfg_hash=data.get('init_cfg_hash', ""), - ub_ecode_hash=data.get('ub_ecode_hash', ""), - nav_run_mode=data.get('nav_run_mode', 0), - test_mode_status=data.get('test_mode_status', 0), - man_run_speed=data.get('man_run_speed', 0), - nav_edit_status=data.get('nav_edit_status', 0), - knife_height=data.get('knife_height', 0) + path=data.get("path", 0), + path_hash=data.get("path_hash", ""), + progress=data.get("progress", 0), + area=data.get("area", 0), + bp_info=data.get("bp_info", 0), + bp_hash=data.get("bp_hash", ""), + bp_pos_x=data.get("bp_pos_x", 0), + bp_pos_y=data.get("bp_pos_y", 0), + real_path_num=data.get("real_path_num", ""), + path_pos_x=data.get("path_pos_x", 0), + path_pos_y=data.get("path_pos_y", 0), + ub_zone_hash=data.get("ub_zone_hash", ""), + ub_path_hash=data.get("ub_path_hash", ""), + init_cfg_hash=data.get("init_cfg_hash", ""), + ub_ecode_hash=data.get("ub_ecode_hash", ""), + nav_run_mode=data.get("nav_run_mode", 0), + test_mode_status=data.get("test_mode_status", 0), + man_run_speed=data.get("man_run_speed", 0), + nav_edit_status=data.get("nav_edit_status", 0), + knife_height=data.get("knife_height", 0), ) + @dataclass class ReportData: connect: ConnectData = field(default_factory=ConnectData) @@ -126,16 +132,15 @@ class ReportData: locations: list[LocationData] = field(default_factory=list) work: WorkData = field(default_factory=WorkData) - def from_dict(self, data: dict): locations = self.locations - if data.get('locations') is not None: - locations=[LocationData.from_dict(loc) for loc in data.get('locations', [])] + if data.get("locations") is not None: + locations = [LocationData.from_dict(loc) for loc in data.get("locations", [])] return ReportData( - connect=ConnectData.from_dict(data.get('connect', asdict(self.connect))), - dev=DeviceData.from_dict(data.get('dev', asdict(self.dev))), - rtk=RTKData.from_dict(data.get('rtk', asdict(self.rtk))), + connect=ConnectData.from_dict(data.get("connect", asdict(self.connect))), + dev=DeviceData.from_dict(data.get("dev", asdict(self.dev))), + rtk=RTKData.from_dict(data.get("rtk", asdict(self.rtk))), locations=locations, - work=WorkData.from_dict(data.get('work', asdict(self.work))) - ) \ No newline at end of file + work=WorkData.from_dict(data.get("work", asdict(self.work))), + ) diff --git a/pymammotion/data/mqtt/event.py b/pymammotion/data/mqtt/event.py index 949c3c7..1da9ebf 100644 --- a/pymammotion/data/mqtt/event.py +++ b/pymammotion/data/mqtt/event.py @@ -1,7 +1,6 @@ from base64 import b64decode -import base64 from dataclasses import dataclass -from typing import Any, Literal, Union, Optional +from typing import Any, Literal, Optional, Union from google.protobuf import json_format from mashumaro.mixins.orjson import DataClassORJSONMixin @@ -34,6 +33,7 @@ def validate(cls, v: str): data.ParseFromString(binary) return json_format.MessageToDict(data) + @dataclass class DeviceProtobufMsgEventValue(DataClassORJSONMixin): content: Base64EncodedProtobuf @@ -78,6 +78,7 @@ class GeneralParams(DataClassORJSONMixin): deviceType: Optional[str] = None _traceId: Optional[str] = None + @dataclass class DeviceProtobufMsgEventParams(GeneralParams): identifier: Literal["device_protobuf_msg_event"] @@ -100,26 +101,21 @@ class ThingEventMessage(DataClassORJSONMixin): version: Literal["1.0"] @classmethod - def from_dicts(cls, payload: dict) -> 'ThingEventMessage': + def from_dicts(cls, payload: dict) -> "ThingEventMessage": """Deserializza il payload JSON in un'istanza di ThingEventMessage.""" - method = payload.get('method') - event_id = payload.get('id') - params_dict = payload.get('params', {}) - version = payload.get('version') + method = payload.get("method") + event_id = payload.get("id") + params_dict = payload.get("params", {}) + version = payload.get("version") # Determina quale classe usare per i parametri - identifier = params_dict.get('identifier') - if identifier == 'device_protobuf_msg_event': + identifier = params_dict.get("identifier") + if identifier == "device_protobuf_msg_event": params_obj = DeviceProtobufMsgEventParams(**params_dict) - elif identifier == 'device_warning_event': + elif identifier == "device_warning_event": params_obj = DeviceWarningEventParams(**params_dict) else: raise ValueError(f"Unknown identifier: {identifier}") # Crea e restituisce l'istanza di ThingEventMessage - return cls( - method=method, - id=event_id, - params=params_obj, - version=version - ) \ No newline at end of file + return cls(method=method, id=event_id, params=params_obj, version=version) diff --git a/pymammotion/http/http.py b/pymammotion/http/http.py index 6a56a7f..272763d 100644 --- a/pymammotion/http/http.py +++ b/pymammotion/http/http.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Generic, Literal, TypeVar, Optional +from typing import Generic, Literal, Optional, TypeVar from aiohttp import ClientSession from mashumaro import DataClassDictMixin diff --git a/pymammotion/mammotion/commands/mammotion_command.py b/pymammotion/mammotion/commands/mammotion_command.py index 3fb2e4c..fbf91b3 100644 --- a/pymammotion/mammotion/commands/mammotion_command.py +++ b/pymammotion/mammotion/commands/mammotion_command.py @@ -4,14 +4,11 @@ from pymammotion.mammotion.commands.messages.system import MessageSystem from pymammotion.mammotion.commands.messages.video import MessageVideo from pymammotion.proto import dev_net_pb2, luba_msg_pb2 -from pymammotion.utility.device_type import DeviceType class MammotionCommand(MessageSystem, MessageNavigation, MessageNetwork, MessageOta, MessageVideo): """MQTT commands for Luba.""" - - def __init__(self, device_name: str) -> None: self._device_name = device_name self._product_key = "" @@ -26,7 +23,6 @@ def get_device_product_key(self) -> str: def set_device_product_key(self, product_key: str) -> None: self._product_key = product_key - """BLE commands for Luba.""" def send_todev_ble_sync(self, sync_type: int) -> bytes: diff --git a/pymammotion/mammotion/commands/messages/navigation.py b/pymammotion/mammotion/commands/messages/navigation.py index e63f3ba..2d1b081 100644 --- a/pymammotion/mammotion/commands/messages/navigation.py +++ b/pymammotion/mammotion/commands/messages/navigation.py @@ -34,7 +34,10 @@ class MessageNavigation(AbstractMessage, ABC): def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice: """Changes the rcver name if it's not a luba1.""" - if not DeviceType.is_luba1(self.get_device_name(), self.get_device_product_key()) and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV: + if ( + not DeviceType.is_luba1(self.get_device_name(), self.get_device_product_key()) + and msg_type == MsgCmdType.MSG_CMD_TYPE_NAV + ): return MsgDevice.DEV_NAVIGATION return msg_device diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 2a5bc42..5e1e462 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -3,14 +3,14 @@ from __future__ import annotations import asyncio +import base64 import codecs import json import logging from abc import abstractmethod from enum import Enum -from typing import Any, cast, Optional, Callable +from typing import Any, Callable, Optional, cast from uuid import UUID -import base64 import betterproto from bleak import BleakClient @@ -72,7 +72,7 @@ def _sb_uuid(comms_type: str = "service") -> UUID | str: def slashescape(err): """Escape a slash character.""" # print err, dir(err), err.start, err.end, err.object[:err.start] - thebyte = err.object[err.start: err.end] + thebyte = err.object[err.start : err.end] repl = "\\x" + hex(ord(thebyte))[2:] return (repl, err.end) @@ -106,6 +106,7 @@ async def _handle_retry(fut: asyncio.Future[None], func, command: bytes) -> None if not fut.done(): await func(command) + async def _handle_retry_cloud(fut: asyncio.Future[None], func, iotId: str, command: bytes) -> None: """Handle a retry.""" if not fut.done(): @@ -126,9 +127,9 @@ class MammotionDevice: _ble_device: MammotionBaseBLEDevice | None = None def __init__( - self, - ble_device: BLEDevice, - preference: ConnectionPreference = ConnectionPreference.EITHER, + self, + ble_device: BLEDevice, + preference: ConnectionPreference = ConnectionPreference.EITHER, ) -> None: """Initialize MammotionDevice.""" if ble_device: @@ -324,10 +325,10 @@ async def start_map_sync(self): await self._send_command_with_args("get_hash_response", total_frame=1, current_frame=1) - await self._send_command_with_args("get_area_name_list", - device_id=self.luba_msg.device.net.toapp_wifi_iot_status.devicename) - - + await self._send_command_with_args( + "get_area_name_list", device_id=self.luba_msg.device.net.toapp_wifi_iot_status.devicename + ) + # sub_cmd 3 is job hashes?? # sub_cmd 4 is dump location (yuka) # jobs list @@ -553,7 +554,6 @@ async def _notification_handler(self, _sender: BleakGATTCharacteristic, data: by new_msg = LubaMsg().parse(data) if betterproto.serialized_on_wire(new_msg.net): if new_msg.net.todev_ble_sync != 0 or has_field(new_msg.net.toapp_wifi_iot_status): - if has_field(new_msg.net.toapp_wifi_iot_status) and self._commands.get_device_product_key() == "": self._commands.set_device_product_key(new_msg.net.toapp_wifi_iot_status.productkey) @@ -752,7 +752,6 @@ def __init__( # temporary for testing only # self._start_sync_task = self.loop.call_later(30, lambda: asyncio.ensure_future(self.start_sync(0))) - def on_ready(self): """Callback for when MQTT is subscribed to events.""" if self.on_ready_callback: @@ -765,7 +764,6 @@ def on_connected(self): def on_disconnected(self): """Callback for when MQTT disconnects.""" - pass def _ble_sync(self): command_bytes = self._commands.send_todev_ble_sync(3) @@ -797,23 +795,16 @@ def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: """Send command to device via MQTT and read response.""" if self._operation_lock.locked(): - _LOGGER.debug( - "%s: Operation already in progress, waiting for it to complete;", - self.nick_name - ) - + _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.nick_name) + async with self._operation_lock: try: command_bytes = getattr(self._commands, key)() return await self._send_command_locked(key, command_bytes) except Exception as ex: - _LOGGER.exception( - "%s: error in sending command - %s", - self.nick_name, - ex - ) + _LOGGER.exception("%s: error in sending command - %s", self.nick_name, ex) raise - + async def _send_command_locked(self, key: str, command: bytes) -> bytes: """Send command to device and read response.""" try: @@ -827,7 +818,7 @@ async def _send_command_locked(self, key: str, command: bytes) -> bytes: ex, ) raise - + async def _execute_command_locked(self, key: str, command: bytes) -> bytes: """Execute command and read response.""" assert self._mqtt_client is not None @@ -839,7 +830,9 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: retry_handle = self.loop.call_at( self.loop.time() + 20, lambda: asyncio.ensure_future( - _handle_retry_cloud(self._notify_future, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) + _handle_retry_cloud( + self._notify_future, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command + ) ), ) timeout = 20 @@ -863,20 +856,13 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: async def _send_command_with_args(self, key: str, **kwargs: any) -> bytes | None: """Send command with arguments to device via MQTT and read response.""" if self._operation_lock.locked(): - _LOGGER.debug( - "%s: Operation already in progress, waiting for it to complete;", - self.nick_name - ) + _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.nick_name) async with self._operation_lock: try: command_bytes = getattr(self._commands, key)(**kwargs) return await self._send_command_locked(key, command_bytes) except Exception as ex: - _LOGGER.exception( - "%s: error in sending command - %s", - self.nick_name, - ex - ) + _LOGGER.exception("%s: error in sending command - %s", self.nick_name, ex) raise def _extract_message_id(self, payload: dict) -> str: @@ -895,12 +881,12 @@ def _extract_encoded_message(self, payload: dict) -> str: def _parse_mqtt_response(self, topic: str, payload: dict) -> None: """Parse the MQTT response.""" if topic.endswith("/app/down/thing/events"): - _LOGGER.debug(f"Thing event received") + _LOGGER.debug("Thing event received") event = ThingEventMessage.from_dicts(payload) params = event.params if params.identifier == "device_protobuf_msg_event": - _LOGGER.debug(f"Protobuf event") - binary_data = base64.b64decode(params.value.get('content', '')) + _LOGGER.debug("Protobuf event") + binary_data = base64.b64decode(params.value.get("content", "")) self._update_raw_data(cast(bytes, binary_data)) new_msg = LubaMsg().parse(cast(bytes, binary_data)) if self._notify_future and not self._notify_future.done(): @@ -910,7 +896,6 @@ def _parse_mqtt_response(self, topic: str, payload: dict) -> None: def _handle_mqtt_message(self, topic: str, payload: dict) -> None: """Async handler for incoming MQTT messages.""" self._parse_mqtt_response(topic=topic, payload=payload) - async def _disconnect(self): """Disconnect the MQTT client.""" diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index eb15d1c..654aa02 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -1,14 +1,14 @@ """MammotionMQTT.""" -import asyncio + import hashlib import hmac import json import logging from logging import getLogger -from typing import Callable, Optional, cast, Awaitable +from typing import Callable, Optional, cast from linkkit.linkkit import LinkKit -from paho.mqtt.client import Client, MQTTMessage, MQTTv311, connack_string +from paho.mqtt.client import MQTTMessage from pymammotion.aliyun.cloud_gateway import CloudIOTGateway from pymammotion.data.mqtt.event import ThingEventMessage @@ -22,7 +22,6 @@ class MammotionMQTT: """MQTT client for pymammotion.""" - def __init__( self, region_id: str, @@ -77,7 +76,6 @@ def __init__( # self._mqtt_host = "public.itls.eu-central-1.aliyuncs.com" self._mqtt_host = f"{self._product_key}.iot-as-mqtt.{region_id}.aliyuncs.com" - def connect_async(self): """Connect async to MQTT Server.""" logger.info("Connecting...") @@ -124,7 +122,6 @@ def _thing_on_thing_enable(self, user_data): ), ) - if self.on_ready: self.is_ready = True self.on_ready() @@ -154,7 +151,6 @@ def _thing_on_connect(self, session_flag, rc, user_data): # self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/#") - def _on_disconnect(self, _client, _userdata, rc: int): """Is called on disconnect.""" logger.info("Disconnected") diff --git a/pymammotion/utility/constant/device_constant.py b/pymammotion/utility/constant/device_constant.py index 8530398..60c713a 100644 --- a/pymammotion/utility/constant/device_constant.py +++ b/pymammotion/utility/constant/device_constant.py @@ -257,6 +257,7 @@ def device_mode(value) -> str: class PosType(IntEnum): """Position of the robot.""" + AREA_BORDER_ON = 7 AREA_INSIDE = 1 AREA_OUT = 0 @@ -268,4 +269,3 @@ class PosType(IntEnum): OBS_ON = 2 TURN_AREA_INSIDE = 4 VIRTUAL_INSIDE = 6 - diff --git a/pymammotion/utility/map.py b/pymammotion/utility/map.py index 4f716b9..ddd0953 100644 --- a/pymammotion/utility/map.py +++ b/pymammotion/utility/map.py @@ -1,4 +1,5 @@ import math + import numpy as np from pymammotion.data.model.location import Point @@ -8,11 +9,11 @@ class CoordinateConverter: def __init__(self, latitude_rad: float, longitude_rad: float): # Initialize constants self.WGS84A = 6378137.0 - self.f_ = 3.3528106647474805E-21 + self.f_ = 3.3528106647474805e-21 self.b_ = (1.0 - self.f_) * self.WGS84A self.e2_ = (2.0 - self.f_) * self.f_ self.e2m_ = (1.0 - self.f_) * (1.0 - self.f_) - self.ep2_ = ((self.WGS84A ** 2) - (self.b_ ** 2)) / (self.b_ ** 2) + self.ep2_ = ((self.WGS84A**2) - (self.b_**2)) / (self.b_**2) # Initialize rotation matrix self.R_ = np.zeros((3, 3)) @@ -31,7 +32,7 @@ def set_init_lla(self, lat_rad, lon_rad): sin_lon = math.sin(lon_rad) cos_lon = math.cos(lon_rad) - sqrt = self.WGS84A / math.sqrt(1.0 - (self.e2_ * (sin_lat ** 2))) + sqrt = self.WGS84A / math.sqrt(1.0 - (self.e2_ * (sin_lat**2))) d3 = sqrt * cos_lat self.x0_ = d3 * cos_lon @@ -62,7 +63,10 @@ def enu_to_lla(self, e, n) -> Point: cos_lat = math.cos(atan2_lat) lon = math.atan2(d4, d3) * 180.0 / math.pi - lat = math.atan2(d5 + self.ep2_ * self.b_ * (sin_lat ** 3), hypot - self.e2_ * self.WGS84A * (cos_lat ** 3)) * 180.0 / math.pi + lat = ( + math.atan2(d5 + self.ep2_ * self.b_ * (sin_lat**3), hypot - self.e2_ * self.WGS84A * (cos_lat**3)) + * 180.0 + / math.pi + ) return Point(latitude=lat, longitude=lon) - From a88ef95dc702ebf7307bfa8a16218564b5226b8a Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Sat, 17 Aug 2024 15:44:43 +1200 Subject: [PATCH 15/35] add cloud as part of base MammotionDevice --- pymammotion/data/model/account.py | 8 +++ pymammotion/data/model/device_config.py | 4 +- pymammotion/mammotion/devices/mammotion.py | 70 +++++++++++++++++++++- pymammotion/mqtt/mammotion_mqtt.py | 6 +- 4 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 pymammotion/data/model/account.py diff --git a/pymammotion/data/model/account.py b/pymammotion/data/model/account.py new file mode 100644 index 0000000..4094e1b --- /dev/null +++ b/pymammotion/data/model/account.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass + + +@dataclass +class Credentials: + email: str + password: str + account_id: str \ No newline at end of file diff --git a/pymammotion/data/model/device_config.py b/pymammotion/data/model/device_config.py index 46749c5..29fc077 100644 --- a/pymammotion/data/model/device_config.py +++ b/pymammotion/data/model/device_config.py @@ -3,7 +3,7 @@ @dataclass class DeviceLimits: - cutter_height_min: int - cutter_height_max: int + blade_height_min: int + blade_height_max: int working_speed_min: float working_speed_max: float diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 5e1e462..a540c12 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -13,6 +13,7 @@ from uuid import UUID import betterproto +from aiohttp import ClientSession from bleak import BleakClient from bleak.backends.device import BLEDevice from bleak.backends.service import BleakGATTCharacteristic, BleakGATTServiceCollection @@ -24,11 +25,15 @@ establish_connection, ) +from pymammotion.aliyun.cloud_gateway import CloudIOTGateway from pymammotion.bluetooth import BleMessage +from pymammotion.const import MAMMOTION_DOMAIN from pymammotion.data.model import RegionData +from pymammotion.data.model.account import Credentials from pymammotion.data.model.device import MowingDevice from pymammotion.data.mqtt.event import ThingEventMessage from pymammotion.data.state_manager import StateManager +from pymammotion.http.http import LubaHTTP from pymammotion.mammotion.commands.mammotion_command import MammotionCommand from pymammotion.mqtt import MammotionMQTT from pymammotion.proto.luba_msg import LubaMsg @@ -125,10 +130,13 @@ class MammotionDevice: """Represents a Mammotion device.""" _ble_device: MammotionBaseBLEDevice | None = None + _cloud_device: MammotionBaseCloudDevice | None = None + _devices_list = [] def __init__( self, ble_device: BLEDevice, + cloud_credentials: Credentials | None = None, preference: ConnectionPreference = ConnectionPreference.EITHER, ) -> None: """Initialize MammotionDevice.""" @@ -136,9 +144,69 @@ def __init__( self._ble_device = MammotionBaseBLEDevice(ble_device) self._preference = preference + if cloud_credentials: + self.initiate_cloud_connection(cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password) + + async def initiate_cloud_connection(self, account: str, password: str): + cloud_client = await self.login(account, password) + + _mammotion_mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, + product_key=cloud_client._aep_response.data.productKey, + device_name=cloud_client._aep_response.data.deviceName, + device_secret=cloud_client._aep_response.data.deviceSecret, + iot_token=cloud_client._session_by_authcode_response.data.iotToken, + client_id=cloud_client._client_id) + + _mammotion_mqtt._cloud_client = cloud_client + _mammotion_mqtt.connect_async() + + self._devices_list = [] + for device in cloud_client._listing_dev_by_account_response.data.data: + if (device.deviceName.startswith(("Luba-", "Yuka-"))): + dev = MammotionBaseCloudDevice( + mqtt_client=_mammotion_mqtt, + iot_id=device.iotId, + device_name=device.deviceName, + nick_name=device.nickName + ) + self._devices_list.append(dev) + + @staticmethod + async def login(account: str, password: str) -> CloudIOTGateway: + """Login to mammotion cloud.""" + cloud_client = CloudIOTGateway() + async with ClientSession(MAMMOTION_DOMAIN) as session: + luba_http = await LubaHTTP.login(session, account, password) + country_code = luba_http.data.userInformation.domainAbbreviation + _LOGGER.debug("CountryCode: " + country_code) + _LOGGER.debug("AuthCode: " + luba_http.data.authorization_code) + cloud_client.get_region(country_code, luba_http.data.authorization_code) + await cloud_client.connect() + await cloud_client.login_by_oauth(country_code, luba_http.data.authorization_code) + cloud_client.aep_handle() + cloud_client.session_by_auth_code() + + cloud_client.list_binding_by_account() + return cloud_client + + async def send_command(self, key: str): """Send a command to the device.""" - return await self._ble_device.command(key) + if self._preference is ConnectionPreference.BLUETOOTH: + return await self._ble_device.command(key) + if self._preference is ConnectionPreference.WIFI: + return await self._cloud_device.command(key) + # TODO work with both with EITHER + + async def send_command_with_args(self, key, kwargs): + """Send a command with args to the device.""" + if self._preference is ConnectionPreference.BLUETOOTH: + return await self._ble_device.command(key, **kwargs) + if self._preference is ConnectionPreference.WIFI: + return await self._cloud_device.command(key, **kwargs) + # TODO work with both with EITHER + + def has_field(message: betterproto.Message) -> bool: diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index 654aa02..0211450 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -33,6 +33,7 @@ def __init__( ): """Create instance of MammotionMQTT.""" super().__init__() + self._cloud_client = None self.is_connected = False self.is_ready = False self.on_connected: Optional[Callable[[], None]] = None @@ -73,7 +74,6 @@ def __init__( self._linkkit_client.on_disconnect = self._on_disconnect self._linkkit_client.on_thing_enable = self._thing_on_thing_enable self._linkkit_client.on_topic_message = self._thing_on_topic_message - # self._mqtt_host = "public.itls.eu-central-1.aliyuncs.com" self._mqtt_host = f"{self._product_key}.iot-as-mqtt.{region_id}.aliyuncs.com" def connect_async(self): @@ -81,16 +81,12 @@ def connect_async(self): logger.info("Connecting...") self._linkkit_client.thing_setup() self._linkkit_client.connect_async() - # self._client.connect_async(host=self._mqtt_host) - # self._client.loop_start() self._linkkit_client.start_worker_loop() def disconnect(self): """Disconnect from MQTT Server.""" logger.info("Disconnecting...") self._linkkit_client.disconnect() - # self._client.disconnect() - # self._client.loop_stop() def _thing_on_thing_enable(self, user_data): """Is called when Thing is enabled.""" From cf89c0b72085476c3014bda87d6225c19143cda7 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 19 Aug 2024 09:59:14 +1200 Subject: [PATCH 16/35] rename http class --- pymammotion/__init__.py | 4 ++-- pymammotion/http/http.py | 8 ++++---- pymammotion/mammotion/devices/mammotion.py | 12 ++++++------ tests/login_and_mqtt_test.py | 4 ++-- tests/login_test.py | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pymammotion/__init__.py b/pymammotion/__init__.py index a248ea0..afe738f 100644 --- a/pymammotion/__init__.py +++ b/pymammotion/__init__.py @@ -10,13 +10,13 @@ # works outside HA on its own from pymammotion.bluetooth.ble import LubaBLE -from pymammotion.http.http import LubaHTTP, connect_http +from pymammotion.http.http import MammotionHTTP, connect_http # TODO make a working device that will work outside HA too. from pymammotion.mammotion.devices import MammotionBaseBLEDevice from pymammotion.mqtt import MammotionMQTT -__all__ = ["LubaBLE", "LubaHTTP", "connect_http", "MammotionBaseBLEDevice", "MammotionMQTT"] +__all__ = ["LubaBLE", "MammotionHTTP", "connect_http", "MammotionBaseBLEDevice", "MammotionMQTT"] logger = logging.getLogger(__name__) diff --git a/pymammotion/http/http.py b/pymammotion/http/http.py index 272763d..e28bbec 100644 --- a/pymammotion/http/http.py +++ b/pymammotion/http/http.py @@ -44,7 +44,7 @@ class LoginResponseData(DataClassORJSONMixin): jti: str -class LubaHTTP: +class MammotionHTTP: def __init__(self, session: ClientSession, login: LoginResponseData): self._session = session self._session.headers["Authorization"] = f"Bearer {login.access_token}" @@ -70,7 +70,7 @@ async def login(cls, session: ClientSession, username: str, password: str) -> Re return Response(data=login_response_data, code=data["code"], msg=data["msg"]) -async def connect_http(username: str, password: str) -> LubaHTTP: +async def connect_http(username: str, password: str) -> MammotionHTTP: async with ClientSession(MAMMOTION_DOMAIN) as session: - login_response = await LubaHTTP.login(session, username, password) - return LubaHTTP(session, login_response.data) + login_response = await MammotionHTTP.login(session, username, password) + return MammotionHTTP(session, login_response.data) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index a540c12..5e6b7b0 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -33,7 +33,7 @@ from pymammotion.data.model.device import MowingDevice from pymammotion.data.mqtt.event import ThingEventMessage from pymammotion.data.state_manager import StateManager -from pymammotion.http.http import LubaHTTP +from pymammotion.http.http import MammotionHTTP from pymammotion.mammotion.commands.mammotion_command import MammotionCommand from pymammotion.mqtt import MammotionMQTT from pymammotion.proto.luba_msg import LubaMsg @@ -176,13 +176,13 @@ async def login(account: str, password: str) -> CloudIOTGateway: """Login to mammotion cloud.""" cloud_client = CloudIOTGateway() async with ClientSession(MAMMOTION_DOMAIN) as session: - luba_http = await LubaHTTP.login(session, account, password) - country_code = luba_http.data.userInformation.domainAbbreviation + mammotion_http = await MammotionHTTP.login(session, account, password) + country_code = mammotion_http.data.userInformation.domainAbbreviation _LOGGER.debug("CountryCode: " + country_code) - _LOGGER.debug("AuthCode: " + luba_http.data.authorization_code) - cloud_client.get_region(country_code, luba_http.data.authorization_code) + _LOGGER.debug("AuthCode: " + mammotion_http.data.authorization_code) + cloud_client.get_region(country_code, mammotion_http.data.authorization_code) await cloud_client.connect() - await cloud_client.login_by_oauth(country_code, luba_http.data.authorization_code) + await cloud_client.login_by_oauth(country_code, mammotion_http.data.authorization_code) cloud_client.aep_handle() cloud_client.session_by_auth_code() diff --git a/tests/login_and_mqtt_test.py b/tests/login_and_mqtt_test.py index e4c2ac8..ee29bdc 100644 --- a/tests/login_and_mqtt_test.py +++ b/tests/login_and_mqtt_test.py @@ -4,7 +4,7 @@ from aiohttp import ClientSession -from pymammotion import LubaHTTP +from pymammotion import MammotionHTTP from pymammotion.aliyun.cloud_gateway import CloudIOTGateway from pymammotion.const import MAMMOTION_DOMAIN from pymammotion.mammotion.commands.mammotion_command import MammotionCommand @@ -22,7 +22,7 @@ async def run(): async with ClientSession(MAMMOTION_DOMAIN) as session: - luba_http = await LubaHTTP.login(session, EMAIL, PASSWORD) + luba_http = await MammotionHTTP.login(session, EMAIL, PASSWORD) country_code = luba_http.data.userInformation.domainAbbreviation logger.debug("CountryCode: " + country_code) logger.debug("AuthCode: " + luba_http.data.authorization_code) diff --git a/tests/login_test.py b/tests/login_test.py index f289682..e37b21a 100644 --- a/tests/login_test.py +++ b/tests/login_test.py @@ -4,7 +4,7 @@ from aiohttp import ClientSession -from pymammotion import LubaHTTP +from pymammotion import MammotionHTTP from pymammotion.aliyun.cloud_gateway import CloudIOTGateway from pymammotion.const import MAMMOTION_DOMAIN from pymammotion.mammotion.commands.mammotion_command import MammotionCommand @@ -21,7 +21,7 @@ async def run(): async with ClientSession(MAMMOTION_DOMAIN) as session: - luba_http = await LubaHTTP.login(session, EMAIL, PASSWORD) + luba_http = await MammotionHTTP.login(session, EMAIL, PASSWORD) country_code = luba_http.data.userInformation.domainAbbreviation logger.debug("CountryCode: " + country_code) logger.debug("AuthCode: " + luba_http.data.authorization_code) From bc87dd4da185542198f7f10503e73b14429385a0 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 19 Aug 2024 10:58:45 +1200 Subject: [PATCH 17/35] fix up the client sesion for http --- pymammotion/aliyun/cloud_gateway.py | 5 ++-- pymammotion/http/http.py | 13 +++++----- pymammotion/mammotion/devices/mammotion.py | 12 +++++----- tests/login_and_mqtt_test.py | 28 ++++++++++------------ 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index bc32676..1eb1df2 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -391,8 +391,6 @@ def session_by_auth_code(self): def check_or_refresh_session(self): """Check or refresh the session.""" - if self.load_saved_params() is False: - return False config = Config( app_key=self._app_key, app_secret=self._app_secret, @@ -436,7 +434,7 @@ def check_or_refresh_session(self): # Carica la stringa JSON in un dizionario json.loads(response_body_str) - def list_binding_by_account(self): + def list_binding_by_account(self) -> ListingDevByAccountResponse: """List bindings by account.""" config = Config( app_key=self._app_key, @@ -476,6 +474,7 @@ def list_binding_by_account(self): raise Exception("Error in creating session: " + response_body_dict["msg"]) self._listing_dev_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict) + return self._listing_dev_by_account_response def send_cloud_command(self, iot_id: str, command: bytes) -> str: """Send a cloud command to the specified IoT device.""" diff --git a/pymammotion/http/http.py b/pymammotion/http/http.py index e28bbec..9bb0448 100644 --- a/pymammotion/http/http.py +++ b/pymammotion/http/http.py @@ -2,9 +2,11 @@ from typing import Generic, Literal, Optional, TypeVar from aiohttp import ClientSession +from aiohttp.hdrs import AUTHORIZATION from mashumaro import DataClassDictMixin from mashumaro.mixins.orjson import DataClassORJSONMixin +from pymammotion.aliyun.dataclass.connect_response import Device from pymammotion.const import ( MAMMOTION_CLIENT_ID, MAMMOTION_CLIENT_SECRET, @@ -45,10 +47,10 @@ class LoginResponseData(DataClassORJSONMixin): class MammotionHTTP: - def __init__(self, session: ClientSession, login: LoginResponseData): - self._session = session - self._session.headers["Authorization"] = f"Bearer {login.access_token}" - self._login = login + def __init__(self, login: LoginResponseData): + self._headers = dict() + self._headers["Authorization"] = f"Bearer {login.access_token}" + self.login = login @classmethod async def login(cls, session: ClientSession, username: str, password: str) -> Response[LoginResponseData]: @@ -63,7 +65,6 @@ async def login(cls, session: ClientSession, username: str, password: str) -> Re ), ) as resp: data = await resp.json() - print(data) # TODO catch errors from mismatch user / password # Assuming the data format matches the expected structure login_response_data = LoginResponseData.from_dict(data["data"]) @@ -73,4 +74,4 @@ async def login(cls, session: ClientSession, username: str, password: str) -> Re async def connect_http(username: str, password: str) -> MammotionHTTP: async with ClientSession(MAMMOTION_DOMAIN) as session: login_response = await MammotionHTTP.login(session, username, password) - return MammotionHTTP(session, login_response.data) + return MammotionHTTP(login_response.data) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 5e6b7b0..c75f2af 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -33,7 +33,7 @@ from pymammotion.data.model.device import MowingDevice from pymammotion.data.mqtt.event import ThingEventMessage from pymammotion.data.state_manager import StateManager -from pymammotion.http.http import MammotionHTTP +from pymammotion.http.http import MammotionHTTP, connect_http from pymammotion.mammotion.commands.mammotion_command import MammotionCommand from pymammotion.mqtt import MammotionMQTT from pymammotion.proto.luba_msg import LubaMsg @@ -176,13 +176,13 @@ async def login(account: str, password: str) -> CloudIOTGateway: """Login to mammotion cloud.""" cloud_client = CloudIOTGateway() async with ClientSession(MAMMOTION_DOMAIN) as session: - mammotion_http = await MammotionHTTP.login(session, account, password) - country_code = mammotion_http.data.userInformation.domainAbbreviation + mammotion_http = await connect_http(account, password) + country_code = mammotion_http.login.userInformation.domainAbbreviation _LOGGER.debug("CountryCode: " + country_code) - _LOGGER.debug("AuthCode: " + mammotion_http.data.authorization_code) - cloud_client.get_region(country_code, mammotion_http.data.authorization_code) + _LOGGER.debug("AuthCode: " + mammotion_http.login.authorization_code) + cloud_client.get_region(country_code, mammotion_http.login.authorization_code) await cloud_client.connect() - await cloud_client.login_by_oauth(country_code, mammotion_http.data.authorization_code) + await cloud_client.login_by_oauth(country_code, mammotion_http.login.authorization_code) cloud_client.aep_handle() cloud_client.session_by_auth_code() diff --git a/tests/login_and_mqtt_test.py b/tests/login_and_mqtt_test.py index ee29bdc..9d2ab73 100644 --- a/tests/login_and_mqtt_test.py +++ b/tests/login_and_mqtt_test.py @@ -7,6 +7,7 @@ from pymammotion import MammotionHTTP from pymammotion.aliyun.cloud_gateway import CloudIOTGateway from pymammotion.const import MAMMOTION_DOMAIN +from pymammotion.http.http import connect_http from pymammotion.mammotion.commands.mammotion_command import MammotionCommand from pymammotion.mqtt.mammotion_mqtt import MammotionMQTT, logger from pymammotion.mammotion.devices.mammotion import MammotionBaseCloudDevice @@ -19,21 +20,18 @@ async def run(): PASSWORD = os.environ.get('PASSWORD') cloud_client = CloudIOTGateway() - - - async with ClientSession(MAMMOTION_DOMAIN) as session: - luba_http = await MammotionHTTP.login(session, EMAIL, PASSWORD) - country_code = luba_http.data.userInformation.domainAbbreviation - logger.debug("CountryCode: " + country_code) - logger.debug("AuthCode: " + luba_http.data.authorization_code) - cloud_client.get_region(country_code, luba_http.data.authorization_code) - await cloud_client.connect() - await cloud_client.login_by_oauth(country_code, luba_http.data.authorization_code) - cloud_client.aep_handle() - cloud_client.session_by_auth_code() - - cloud_client.list_binding_by_account() - return cloud_client + mammotion_http = await connect_http(EMAIL, PASSWORD) + country_code = mammotion_http.login.userInformation.domainAbbreviation + logger.debug("CountryCode: " + country_code) + logger.debug("AuthCode: " + mammotion_http.login.authorization_code) + cloud_client.get_region(country_code, mammotion_http.login.authorization_code) + await cloud_client.connect() + await cloud_client.login_by_oauth(country_code, mammotion_http.login.authorization_code) + cloud_client.aep_handle() + cloud_client.session_by_auth_code() + + print(cloud_client.list_binding_by_account()) + return cloud_client async def sync_status_and_map(cloud_device: MammotionBaseCloudDevice): await asyncio.sleep(1) From d366e23dd03180cfa464029a2cb04cb50ccc4a00 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 19 Aug 2024 12:19:21 +1200 Subject: [PATCH 18/35] update login to login_info --- pymammotion/aliyun/dataclass/aep_response.py | 4 ++++ pymammotion/aliyun/dataclass/dev_by_account_response.py | 4 ++++ pymammotion/aliyun/dataclass/regions_response.py | 4 ++++ pymammotion/http/http.py | 2 +- pymammotion/mammotion/devices/mammotion.py | 8 ++++---- pyproject.toml | 4 ++-- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/pymammotion/aliyun/dataclass/aep_response.py b/pymammotion/aliyun/dataclass/aep_response.py index 6906727..09224fe 100644 --- a/pymammotion/aliyun/dataclass/aep_response.py +++ b/pymammotion/aliyun/dataclass/aep_response.py @@ -1,6 +1,7 @@ from dataclasses import dataclass from typing import Optional +from mashumaro.config import BaseConfig from mashumaro.mixins.orjson import DataClassORJSONMixin @@ -16,3 +17,6 @@ class AepResponse(DataClassORJSONMixin): code: int data: DeviceData id: Optional[str] = None + + class Config(BaseConfig): + omit_default = True diff --git a/pymammotion/aliyun/dataclass/dev_by_account_response.py b/pymammotion/aliyun/dataclass/dev_by_account_response.py index 67ca475..d2e718f 100644 --- a/pymammotion/aliyun/dataclass/dev_by_account_response.py +++ b/pymammotion/aliyun/dataclass/dev_by_account_response.py @@ -1,6 +1,7 @@ from dataclasses import dataclass from typing import List, Optional +from mashumaro.config import BaseConfig from mashumaro.mixins.orjson import DataClassORJSONMixin @@ -27,6 +28,9 @@ class Device(DataClassORJSONMixin): categoryImage: Optional[str] = None productModel: Optional[str] = None + class Config(BaseConfig): + omit_default = True + @dataclass class Data(DataClassORJSONMixin): diff --git a/pymammotion/aliyun/dataclass/regions_response.py b/pymammotion/aliyun/dataclass/regions_response.py index abde201..15822dc 100644 --- a/pymammotion/aliyun/dataclass/regions_response.py +++ b/pymammotion/aliyun/dataclass/regions_response.py @@ -2,6 +2,7 @@ from typing import Optional, TypeVar from mashumaro import DataClassDictMixin +from mashumaro.config import BaseConfig from mashumaro.mixins.orjson import DataClassORJSONMixin DataT = TypeVar("DataT") @@ -24,3 +25,6 @@ class RegionResponse(DataClassDictMixin): code: int id: Optional[str] = None msg: Optional[str] = None + + class Config(BaseConfig): + omit_default = True diff --git a/pymammotion/http/http.py b/pymammotion/http/http.py index 9bb0448..7888308 100644 --- a/pymammotion/http/http.py +++ b/pymammotion/http/http.py @@ -50,7 +50,7 @@ class MammotionHTTP: def __init__(self, login: LoginResponseData): self._headers = dict() self._headers["Authorization"] = f"Bearer {login.access_token}" - self.login = login + self.login_info = login @classmethod async def login(cls, session: ClientSession, username: str, password: str) -> Response[LoginResponseData]: diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index c75f2af..8103bc3 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -177,12 +177,12 @@ async def login(account: str, password: str) -> CloudIOTGateway: cloud_client = CloudIOTGateway() async with ClientSession(MAMMOTION_DOMAIN) as session: mammotion_http = await connect_http(account, password) - country_code = mammotion_http.login.userInformation.domainAbbreviation + country_code = mammotion_http.login_info.userInformation.domainAbbreviation _LOGGER.debug("CountryCode: " + country_code) - _LOGGER.debug("AuthCode: " + mammotion_http.login.authorization_code) - cloud_client.get_region(country_code, mammotion_http.login.authorization_code) + _LOGGER.debug("AuthCode: " + mammotion_http.login_info.authorization_code) + cloud_client.get_region(country_code, mammotion_http.login_info.authorization_code) await cloud_client.connect() - await cloud_client.login_by_oauth(country_code, mammotion_http.login.authorization_code) + await cloud_client.login_by_oauth(country_code, mammotion_http.login_info.authorization_code) cloud_client.aep_handle() cloud_client.session_by_auth_code() diff --git a/pyproject.toml b/pyproject.toml index ac3cdb4..d06cd91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.1.9" +version = "0.2.0" license = "GNU-3.0" description = "" readme = "README.md" @@ -48,7 +48,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.1.9" +current_version = "0.2.0" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 0145abdb6199dc58f10d80b8f863fa28fe018180 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 19 Aug 2024 12:19:48 +1200 Subject: [PATCH 19/35] bump version to 0.2.1 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d06cd91..f58e2ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.2.0" +version = "0.2.1" license = "GNU-3.0" description = "" readme = "README.md" @@ -48,7 +48,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.0" +current_version = "0.2.1" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 1bd8261e7cc4a7437a8bab695c40c2e9ee305cb1 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 19 Aug 2024 13:29:28 +1200 Subject: [PATCH 20/35] add start sync and sync maps calls to MammotionDevice --- pymammotion/mammotion/devices/mammotion.py | 13 ++++++++++++- pyproject.toml | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 8103bc3..31fc5a6 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -206,8 +206,19 @@ async def send_command_with_args(self, key, kwargs): return await self._cloud_device.command(key, **kwargs) # TODO work with both with EITHER + async def start_sync(self, retry: int): + if self._preference is ConnectionPreference.BLUETOOTH: + return await self._ble_device.start_sync(retry) + if self._preference is ConnectionPreference.WIFI: + return await self._cloud_device.start_sync(retry) + # TODO work with both with EITHER - + async def start_map_sync(self): + if self._preference is ConnectionPreference.BLUETOOTH: + return await self._ble_device.start_map_sync() + if self._preference is ConnectionPreference.WIFI: + return await self._cloud_device.start_map_sync() + # TODO work with both with EITHER def has_field(message: betterproto.Message) -> bool: """Check if the message has any fields serialized on wire.""" diff --git a/pyproject.toml b/pyproject.toml index f58e2ab..0c346b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pymammotion" -version = "0.2.1" +version = "0.2.2" license = "GNU-3.0" description = "" readme = "README.md" @@ -48,7 +48,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.1" +current_version = "0.2.2" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 198ba02af6efcf45aa8c3734f0338e0e8b25fe97 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 20 Aug 2024 11:09:42 +1200 Subject: [PATCH 21/35] refactor mammotion to handle multiple devices and match to a name --- pymammotion/aliyun/cloud_gateway.py | 4 + pymammotion/mammotion/devices/mammotion.py | 194 ++++++++++++++------- 2 files changed, 135 insertions(+), 63 deletions(-) diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index 1eb1df2..98766ea 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -527,3 +527,7 @@ def send_cloud_command(self, iot_id: str, command: bytes) -> str: return "" return message_id + + @property + def listing_dev_by_account_response(self): + return self._listing_dev_by_account_response diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 31fc5a6..bfa5ae5 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -26,6 +26,7 @@ ) from pymammotion.aliyun.cloud_gateway import CloudIOTGateway +from pymammotion.aliyun.dataclass.dev_by_account_response import Device from pymammotion.bluetooth import BleMessage from pymammotion.const import MAMMOTION_DOMAIN from pymammotion.data.model import RegionData @@ -33,7 +34,7 @@ from pymammotion.data.model.device import MowingDevice from pymammotion.data.mqtt.event import ThingEventMessage from pymammotion.data.state_manager import StateManager -from pymammotion.http.http import MammotionHTTP, connect_http +from pymammotion.http.http import connect_http from pymammotion.mammotion.commands.mammotion_command import MammotionCommand from pymammotion.mqtt import MammotionMQTT from pymammotion.proto.luba_msg import LubaMsg @@ -125,23 +126,82 @@ class ConnectionPreference(Enum): WIFI = 1 BLUETOOTH = 2 +class MammotionMixedDeviceManager: + _ble_device: MammotionBaseBLEDevice | None = None + _cloud_device: MammotionBaseCloudDevice | None = None + _mowing_state: MowingDevice = MowingDevice() + + def __init__(self, name: str, cloud_device: Device | None = None, + ble_device: BLEDevice | None = None, mqtt: MammotionMQTT | None = None) -> None: + self.name = name + self.add_ble(ble_device) + self.add_cloud(cloud_device, mqtt) + + def mower_state(self): + return self._mowing_state + + def ble(self) -> MammotionBaseBLEDevice | None: + return self._ble_device + + def cloud(self) -> MammotionBaseCloudDevice | None: + return self._cloud_device + + def add_ble(self, ble_device: BLEDevice) -> None: + if ble_device is not None: + self._ble_device = MammotionBaseBLEDevice(self._mowing_state, ble_device) + + def add_cloud(self, cloud_device: Device | None = None, mqtt: MammotionMQTT | None = None) -> None: + if cloud_device is not None: + self._cloud_device = MammotionBaseCloudDevice( + mqtt_client=mqtt, + cloud_device=cloud_device, + mowing_state=self._mowing_state) + + def replace_cloud(self, cloud_device:MammotionBaseCloudDevice) -> None: + self._cloud_device = cloud_device + + def replace_ble(self, ble_device:MammotionBaseBLEDevice) -> None: + self._ble_device = ble_device + + def has_cloud(self) -> bool: + return self._cloud_device is not None + + def has_ble(self) -> bool: + return self._ble_device is not None + + +class MammotionDevices: -class MammotionDevice: + devices = dict[str, MammotionMixedDeviceManager] = {} + + def add_device(self, mammotion_device: MammotionMixedDeviceManager) -> None: + exists: MammotionMixedDeviceManager | None = self.devices.get(mammotion_device.name) + if not exists: + self.devices[mammotion_device.name] = mammotion_device + if mammotion_device.has_cloud(): + exists.replace_cloud(mammotion_device.cloud()) + if mammotion_device.has_ble(): + exists.replace_ble(mammotion_device.ble()) + + def get_device(self, mammotion_device_name: str) -> MammotionMixedDeviceManager: + return self.devices.get(mammotion_device_name) + +class Mammotion: """Represents a Mammotion device.""" - _ble_device: MammotionBaseBLEDevice | None = None - _cloud_device: MammotionBaseCloudDevice | None = None - _devices_list = [] + devices = MammotionDevices() def __init__( self, ble_device: BLEDevice, cloud_credentials: Credentials | None = None, - preference: ConnectionPreference = ConnectionPreference.EITHER, + preference: ConnectionPreference = ConnectionPreference.BLUETOOTH, ) -> None: """Initialize MammotionDevice.""" if ble_device: - self._ble_device = MammotionBaseBLEDevice(ble_device) + self.devices.add_device(MammotionMixedDeviceManager(name=ble_device.name, ble_device=ble_device)) + + if preference: self._preference = preference if cloud_credentials: @@ -160,16 +220,10 @@ async def initiate_cloud_connection(self, account: str, password: str): _mammotion_mqtt._cloud_client = cloud_client _mammotion_mqtt.connect_async() - self._devices_list = [] - for device in cloud_client._listing_dev_by_account_response.data.data: - if (device.deviceName.startswith(("Luba-", "Yuka-"))): - dev = MammotionBaseCloudDevice( - mqtt_client=_mammotion_mqtt, - iot_id=device.iotId, - device_name=device.deviceName, - nick_name=device.nickName - ) - self._devices_list.append(dev) + for device in cloud_client.listing_dev_by_account_response.data.data: + if device.deviceName.startswith(("Luba-", "Yuka-")): + self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=_mammotion_mqtt)) + @staticmethod async def login(account: str, password: str) -> CloudIOTGateway: @@ -189,36 +243,51 @@ async def login(account: str, password: str) -> CloudIOTGateway: cloud_client.list_binding_by_account() return cloud_client + def get_device_by_name(self, name: str) -> MammotionMixedDeviceManager: + return self.devices.get_device(name) - async def send_command(self, key: str): + async def send_command(self, name: str, key: str): """Send a command to the device.""" - if self._preference is ConnectionPreference.BLUETOOTH: - return await self._ble_device.command(key) - if self._preference is ConnectionPreference.WIFI: - return await self._cloud_device.command(key) - # TODO work with both with EITHER - - async def send_command_with_args(self, key, kwargs): + device = self.get_device_by_name(name) + if device: + if self._preference is ConnectionPreference.BLUETOOTH: + return await device.ble().command(key) + if self._preference is ConnectionPreference.WIFI: + return await device.cloud().command(key) + # TODO work with both with EITHER + + async def send_command_with_args(self,name: str, key: str, kwargs): """Send a command with args to the device.""" - if self._preference is ConnectionPreference.BLUETOOTH: - return await self._ble_device.command(key, **kwargs) - if self._preference is ConnectionPreference.WIFI: - return await self._cloud_device.command(key, **kwargs) - # TODO work with both with EITHER - - async def start_sync(self, retry: int): - if self._preference is ConnectionPreference.BLUETOOTH: - return await self._ble_device.start_sync(retry) - if self._preference is ConnectionPreference.WIFI: - return await self._cloud_device.start_sync(retry) - # TODO work with both with EITHER - - async def start_map_sync(self): - if self._preference is ConnectionPreference.BLUETOOTH: - return await self._ble_device.start_map_sync() - if self._preference is ConnectionPreference.WIFI: - return await self._cloud_device.start_map_sync() - # TODO work with both with EITHER + device = self.get_device_by_name(name) + if device: + if self._preference is ConnectionPreference.BLUETOOTH: + return await device.ble().command(key, **kwargs) + if self._preference is ConnectionPreference.WIFI: + return await device.cloud().command(key, **kwargs) + # TODO work with both with EITHER + + async def start_sync(self,name:str, retry: int): + device = self.get_device_by_name(name) + if device: + if self._preference is ConnectionPreference.BLUETOOTH: + return await device.ble().start_sync(retry) + if self._preference is ConnectionPreference.WIFI: + return await device.cloud().start_sync(retry) + # TODO work with both with EITHER + + async def start_map_sync(self, name:str): + device = self.get_device_by_name(name) + if device: + if self._preference is ConnectionPreference.BLUETOOTH: + return await device.ble().start_map_sync() + if self._preference is ConnectionPreference.WIFI: + return await device.cloud().start_map_sync() + # TODO work with both with EITHER + + def mower(self, name: str): + device = self.get_device_by_name(name) + if device: + return device.mower_state def has_field(message: betterproto.Message) -> bool: """Check if the message has any fields serialized on wire.""" @@ -228,15 +297,15 @@ def has_field(message: betterproto.Message) -> bool: class MammotionBaseDevice: """Base class for Mammotion devices.""" - _luba_msg: MowingDevice + _mower: MowingDevice _state_manager: StateManager - def __init__(self) -> None: + def __init__(self, device: MowingDevice) -> None: """Initialize MammotionBaseDevice.""" self.loop = asyncio.get_event_loop() self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE) - self._luba_msg = MowingDevice() - self._state_manager = StateManager(self._luba_msg) + self._mower = device + self._state_manager = StateManager(self._mower) self._state_manager.gethash_ack_callback.add_subscribers(self.datahash_response) self._state_manager.get_commondata_ack_callback.add_subscribers(self.commdata_response) @@ -259,7 +328,7 @@ async def commdata_response(self, common_data: NavGetCommDataAck): if total_frame == current_frame: # get next in hash ack list - data_hash = find_next_integer(self.luba_msg.nav.toapp_gethash_ack.data_couple, common_data.hash) + data_hash = find_next_integer(self._mower.nav.toapp_gethash_ack.data_couple, common_data.hash) if data_hash is None: return result_hash = 0 @@ -296,7 +365,7 @@ def _update_raw_data(self, data: bytes) -> None: case "ota": self._update_ota_data(tmp_msg) - self._luba_msg.update_raw(self._raw_data) + self._mower.update_raw(self._raw_data) def _update_nav_data(self, tmp_msg): """Update navigation data.""" @@ -370,9 +439,9 @@ def raw_data(self) -> dict[str, Any]: return self._raw_data @property - def luba_msg(self) -> MowingDevice: + def mower(self) -> MowingDevice: """Get the LubaMsg of the device.""" - return self._luba_msg + return self._mower @abstractmethod async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: @@ -405,7 +474,7 @@ async def start_map_sync(self): await self._send_command_with_args("get_hash_response", total_frame=1, current_frame=1) await self._send_command_with_args( - "get_area_name_list", device_id=self.luba_msg.device.net.toapp_wifi_iot_status.devicename + "get_area_name_list", device_id=self._mower.device.net.toapp_wifi_iot_status.devicename ) # sub_cmd 3 is job hashes?? @@ -421,13 +490,14 @@ async def command(self, key: str, **kwargs): class MammotionBaseBLEDevice(MammotionBaseDevice): """Base class for Mammotion BLE devices.""" - def __init__(self, device: BLEDevice, interface: int = 0, **kwargs: Any) -> None: + def __init__(self, mowing_state: MowingDevice, device: BLEDevice, interface: int = 0, **kwargs: Any) -> None: """Initialize MammotionBaseBLEDevice.""" - super().__init__() + super().__init__(mowing_state) self._ble_sync_task = None self._prev_notification = None self._interface = f"hci{interface}" self._device = device + self._mower = mowing_state self._client: BleakClientWithServiceCache | None = None self._read_char: BleakGATTCharacteristic | None = None self._write_char: BleakGATTCharacteristic | None = None @@ -802,20 +872,18 @@ class MammotionBaseCloudDevice(MammotionBaseDevice): def __init__( self, mqtt_client: MammotionMQTT, - iot_id: str, - device_name: str, - nick_name: str, - **kwargs: Any, + cloud_device: Device, + mowing_state: MowingDevice ) -> None: """Initialize MammotionBaseCloudDevice.""" - super().__init__() + super().__init__(mowing_state) self._ble_sync_task = None self.is_ready = False self._mqtt_client = mqtt_client - self.iot_id = iot_id - self.nick_name = nick_name + self.iot_id = cloud_device.iotId + self._mower = mowing_state self._command_futures = {} - self._commands: MammotionCommand = MammotionCommand(device_name) + self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName) self.currentID = "" self.on_ready_callback: Optional[Callable[[], None]] = None self._operation_lock = asyncio.Lock() From 2734378c0601376a3542109df057df5999a0d44e Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 20 Aug 2024 11:17:28 +1200 Subject: [PATCH 22/35] update test examples --- tests/login_and_mqtt_test.py | 14 ++++++-------- tests/login_test.py | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/login_and_mqtt_test.py b/tests/login_and_mqtt_test.py index 9d2ab73..3eff6e6 100644 --- a/tests/login_and_mqtt_test.py +++ b/tests/login_and_mqtt_test.py @@ -21,12 +21,12 @@ async def run(): cloud_client = CloudIOTGateway() mammotion_http = await connect_http(EMAIL, PASSWORD) - country_code = mammotion_http.login.userInformation.domainAbbreviation + country_code = mammotion_http.login_info.userInformation.domainAbbreviation logger.debug("CountryCode: " + country_code) - logger.debug("AuthCode: " + mammotion_http.login.authorization_code) - cloud_client.get_region(country_code, mammotion_http.login.authorization_code) + logger.debug("AuthCode: " + mammotion_http.login_info.authorization_code) + cloud_client.get_region(country_code, mammotion_http.login_info.authorization_code) await cloud_client.connect() - await cloud_client.login_by_oauth(country_code, mammotion_http.login.authorization_code) + await cloud_client.login_by_oauth(country_code, mammotion_http.login_info.authorization_code) cloud_client.aep_handle() cloud_client.session_by_auth_code() @@ -40,7 +40,7 @@ async def sync_status_and_map(cloud_device: MammotionBaseCloudDevice): # await cloud_device.start_map_sync() while(True): - print(cloud_device.luba_msg) + print(cloud_device.mower) await asyncio.sleep(5) if __name__ == '__main__': @@ -64,9 +64,7 @@ async def sync_status_and_map(cloud_device: MammotionBaseCloudDevice): if(device.deviceName.startswith(("Luba-", "Yuka-"))): dev = MammotionBaseCloudDevice ( mqtt_client=_mammotion_mqtt, - iot_id=device.iotId, - device_name=device.deviceName, - nick_name=device.nickName + cloud_device=device ) _devices_list.append(dev) diff --git a/tests/login_test.py b/tests/login_test.py index e37b21a..0be48ae 100644 --- a/tests/login_test.py +++ b/tests/login_test.py @@ -8,7 +8,7 @@ from pymammotion.aliyun.cloud_gateway import CloudIOTGateway from pymammotion.const import MAMMOTION_DOMAIN from pymammotion.mammotion.commands.mammotion_command import MammotionCommand -from pymammotion.mqtt.mqtt import LubaMQTT, logger +from pymammotion.mqtt import MammotionMQTT logger = logging.getLogger(__name__) @@ -42,7 +42,7 @@ async def run(): logging.basicConfig(level=logging.DEBUG) logger.getChild("paho").setLevel(logging.WARNING) - luba = LubaMQTT(region_id=cloud_client._region.data.regionId, + luba = MammotionMQTT(region_id=cloud_client._region.data.regionId, product_key=cloud_client._aep_response.data.productKey, device_name=cloud_client._aep_response.data.deviceName, device_secret=cloud_client._aep_response.data.deviceSecret, iot_token=cloud_client._session_by_authcode_response.data.iotToken, client_id=cloud_client._client_id) From de8d1f1e2813894a3067938dce887a872c2bdc4a Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Wed, 21 Aug 2024 18:35:01 +1200 Subject: [PATCH 23/35] fix oops --- pymammotion/mammotion/devices/mammotion.py | 3 ++- pyproject.toml | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index bfa5ae5..795cb21 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -172,7 +172,7 @@ def has_ble(self) -> bool: class MammotionDevices: - devices = dict[str, MammotionMixedDeviceManager] = {} + devices: dict[str, MammotionMixedDeviceManager] = {} def add_device(self, mammotion_device: MammotionMixedDeviceManager) -> None: exists: MammotionMixedDeviceManager | None = self.devices.get(mammotion_device.name) @@ -881,6 +881,7 @@ def __init__( self.is_ready = False self._mqtt_client = mqtt_client self.iot_id = cloud_device.iotId + self.device = cloud_device self._mower = mowing_state self._command_futures = {} self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName) diff --git a/pyproject.toml b/pyproject.toml index 0c346b4..5348a22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,10 @@ +[project] +name = "pymammotion" +version = "0.2.4" + [tool.poetry] name = "pymammotion" -version = "0.2.2" +version = "0.2.4" license = "GNU-3.0" description = "" readme = "README.md" @@ -48,7 +52,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.2" +current_version = "0.2.4" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 511bfbfb7716a44fb7f51eb3bfc1b75be5104b50 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Wed, 21 Aug 2024 23:34:14 +1200 Subject: [PATCH 24/35] fix quite a few errors and issues --- pymammotion/data/model/account.py | 6 +-- pymammotion/mammotion/devices/mammotion.py | 58 ++++++++++++++-------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/pymammotion/data/model/account.py b/pymammotion/data/model/account.py index 4094e1b..80948e4 100644 --- a/pymammotion/data/model/account.py +++ b/pymammotion/data/model/account.py @@ -3,6 +3,6 @@ @dataclass class Credentials: - email: str - password: str - account_id: str \ No newline at end of file + email: str = None + password: str = None + account_id: str = None \ No newline at end of file diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 795cb21..f5b27c0 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -176,8 +176,9 @@ class MammotionDevices: def add_device(self, mammotion_device: MammotionMixedDeviceManager) -> None: exists: MammotionMixedDeviceManager | None = self.devices.get(mammotion_device.name) - if not exists: + if exists is None: self.devices[mammotion_device.name] = mammotion_device + return if mammotion_device.has_cloud(): exists.replace_cloud(mammotion_device.cloud()) if mammotion_device.has_ble(): @@ -191,11 +192,25 @@ class Mammotion: devices = MammotionDevices() + @classmethod + async def create(cls, ble_device: BLEDevice, + cloud_credentials: Credentials | None = None, + preference: ConnectionPreference = ConnectionPreference.BLUETOOTH): + cloud_client = await Mammotion.login(cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password) + self = cls(ble_device, cloud_client, preference) + + if cloud_credentials: + await self.initiate_cloud_connection(cloud_client) + + return self + + + def __init__( self, - ble_device: BLEDevice, - cloud_credentials: Credentials | None = None, - preference: ConnectionPreference = ConnectionPreference.BLUETOOTH, + ble_device: BLEDevice, + cloud_client: CloudIOTGateway | None = None, + preference: ConnectionPreference = ConnectionPreference.BLUETOOTH ) -> None: """Initialize MammotionDevice.""" if ble_device: @@ -204,11 +219,7 @@ def __init__( if preference: self._preference = preference - if cloud_credentials: - self.initiate_cloud_connection(cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password) - - async def initiate_cloud_connection(self, account: str, password: str): - cloud_client = await self.login(account, password) + async def initiate_cloud_connection(self, cloud_client: CloudIOTGateway) -> None: _mammotion_mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, product_key=cloud_client._aep_response.data.productKey, @@ -218,7 +229,8 @@ async def initiate_cloud_connection(self, account: str, password: str): client_id=cloud_client._client_id) _mammotion_mqtt._cloud_client = cloud_client - _mammotion_mqtt.connect_async() + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, _mammotion_mqtt.connect_async) for device in cloud_client.listing_dev_by_account_response.data.data: if device.deviceName.startswith(("Luba-", "Yuka-")): @@ -234,13 +246,14 @@ async def login(account: str, password: str) -> CloudIOTGateway: country_code = mammotion_http.login_info.userInformation.domainAbbreviation _LOGGER.debug("CountryCode: " + country_code) _LOGGER.debug("AuthCode: " + mammotion_http.login_info.authorization_code) - cloud_client.get_region(country_code, mammotion_http.login_info.authorization_code) + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, cloud_client.get_region, country_code, mammotion_http.login_info.authorization_code) await cloud_client.connect() await cloud_client.login_by_oauth(country_code, mammotion_http.login_info.authorization_code) - cloud_client.aep_handle() - cloud_client.session_by_auth_code() + await loop.run_in_executor(None, cloud_client.aep_handle) + await loop.run_in_executor(None, cloud_client.session_by_auth_code) - cloud_client.list_binding_by_account() + await loop.run_in_executor(None, cloud_client.list_binding_by_account) return cloud_client def get_device_by_name(self, name: str) -> MammotionMixedDeviceManager: @@ -943,14 +956,14 @@ def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: """Send command to device via MQTT and read response.""" if self._operation_lock.locked(): - _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.nick_name) + _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.device.nickName) async with self._operation_lock: try: command_bytes = getattr(self._commands, key)() return await self._send_command_locked(key, command_bytes) except Exception as ex: - _LOGGER.exception("%s: error in sending command - %s", self.nick_name, ex) + _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) raise async def _send_command_locked(self, key: str, command: bytes) -> bytes: @@ -962,7 +975,7 @@ async def _send_command_locked(self, key: str, command: bytes) -> bytes: await asyncio.sleep(DBUS_ERROR_BACKOFF_TIME) _LOGGER.debug( "%s: error in _send_command_locked: %s", - self.nick_name, + self.device.nickName, ex, ) raise @@ -972,8 +985,9 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: assert self._mqtt_client is not None self._notify_future = self.loop.create_future() self._key = key - _LOGGER.debug("%s: Sending command: %s", self.nick_name, key) - self._mqtt_client.get_cloud_client().send_cloud_command(self.iot_id, command) + _LOGGER.debug("%s: Sending command: %s", self.device.nickName, key) + loop = asyncio.get_running_loop() + loop.run_in_executor(None, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) retry_handle = self.loop.call_at( self.loop.time() + 20, @@ -997,20 +1011,20 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: retry_handle.cancel() self._notify_future = None - _LOGGER.debug("%s: Message received", self.nick_name) + _LOGGER.debug("%s: Message received", self.device.nickName) return notify_msg async def _send_command_with_args(self, key: str, **kwargs: any) -> bytes | None: """Send command with arguments to device via MQTT and read response.""" if self._operation_lock.locked(): - _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.nick_name) + _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.device.nickName) async with self._operation_lock: try: command_bytes = getattr(self._commands, key)(**kwargs) return await self._send_command_locked(key, command_bytes) except Exception as ex: - _LOGGER.exception("%s: error in sending command - %s", self.nick_name, ex) + _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) raise def _extract_message_id(self, payload: dict) -> str: From 3bfb15efea6755034a59f405ea9cc46a526c4eeb Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Thu, 22 Aug 2024 00:01:38 +1200 Subject: [PATCH 25/35] stability improvements --- pymammotion/mammotion/devices/mammotion.py | 42 +++++++++++++--------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index f5b27c0..9da51ae 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -9,6 +9,7 @@ import logging from abc import abstractmethod from enum import Enum +from functools import cache from typing import Any, Callable, Optional, cast from uuid import UUID @@ -187,29 +188,30 @@ def add_device(self, mammotion_device: MammotionMixedDeviceManager) -> None: def get_device(self, mammotion_device_name: str) -> MammotionMixedDeviceManager: return self.devices.get(mammotion_device_name) -class Mammotion: - """Represents a Mammotion device.""" +async def create_devices(ble_device: BLEDevice, + cloud_credentials: Credentials | None = None, + preference: ConnectionPreference = ConnectionPreference.BLUETOOTH): + cloud_client = await Mammotion.login(cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password) + mammotion = Mammotion(ble_device, preference) - devices = MammotionDevices() + if cloud_credentials: + await mammotion.initiate_cloud_connection(cloud_client) - @classmethod - async def create(cls, ble_device: BLEDevice, - cloud_credentials: Credentials | None = None, - preference: ConnectionPreference = ConnectionPreference.BLUETOOTH): - cloud_client = await Mammotion.login(cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password) - self = cls(ble_device, cloud_client, preference) + return mammotion - if cloud_credentials: - await self.initiate_cloud_connection(cloud_client) - return self +@cache +class Mammotion(object): + """Represents a Mammotion device.""" + + devices = MammotionDevices() + _mammotion_mqtt: MammotionMQTT | None = None def __init__( self, ble_device: BLEDevice, - cloud_client: CloudIOTGateway | None = None, preference: ConnectionPreference = ConnectionPreference.BLUETOOTH ) -> None: """Initialize MammotionDevice.""" @@ -220,21 +222,27 @@ def __init__( self._preference = preference async def initiate_cloud_connection(self, cloud_client: CloudIOTGateway) -> None: + if self._mammotion_mqtt is not None: + if not self._mammotion_mqtt.is_connected: + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, self._mammotion_mqtt.connect_async) + return + - _mammotion_mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, + self._mammotion_mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, product_key=cloud_client._aep_response.data.productKey, device_name=cloud_client._aep_response.data.deviceName, device_secret=cloud_client._aep_response.data.deviceSecret, iot_token=cloud_client._session_by_authcode_response.data.iotToken, client_id=cloud_client._client_id) - _mammotion_mqtt._cloud_client = cloud_client + self._mammotion_mqtt._cloud_client = cloud_client loop = asyncio.get_running_loop() - await loop.run_in_executor(None, _mammotion_mqtt.connect_async) + await loop.run_in_executor(None, self._mammotion_mqtt.connect_async) for device in cloud_client.listing_dev_by_account_response.data.data: if device.deviceName.startswith(("Luba-", "Yuka-")): - self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=_mammotion_mqtt)) + self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=self._mammotion_mqtt)) @staticmethod From 12347149938112929be9276b9fa796130101629d Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Thu, 22 Aug 2024 12:04:18 +1200 Subject: [PATCH 26/35] fix more issues --- pymammotion/aliyun/cloud_gateway.py | 2 +- .../mammotion/commands/mammotion_command.py | 3 ++- pymammotion/mammotion/devices/mammotion.py | 21 ++++++++++++------- pymammotion/mqtt/mammotion_mqtt.py | 3 +-- tests/pyjoystick_example.py | 17 ++++++++++++--- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index 98766ea..76e74d5 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -522,7 +522,7 @@ def send_cloud_command(self, iot_id: str, command: bytes) -> str: logger.error( "Error in sending cloud command: %s - %s", str(response_body_dict.get("code")), - str(response_body_dict["msg"]), + str(response_body_dict.get("msg")), ) return "" diff --git a/pymammotion/mammotion/commands/mammotion_command.py b/pymammotion/mammotion/commands/mammotion_command.py index fbf91b3..39c9bc2 100644 --- a/pymammotion/mammotion/commands/mammotion_command.py +++ b/pymammotion/mammotion/commands/mammotion_command.py @@ -1,3 +1,4 @@ +from pymammotion.mammotion.commands.messages.driver import MessageDriver from pymammotion.mammotion.commands.messages.navigation import MessageNavigation from pymammotion.mammotion.commands.messages.network import MessageNetwork from pymammotion.mammotion.commands.messages.ota import MessageOta @@ -6,7 +7,7 @@ from pymammotion.proto import dev_net_pb2, luba_msg_pb2 -class MammotionCommand(MessageSystem, MessageNavigation, MessageNetwork, MessageOta, MessageVideo): +class MammotionCommand(MessageSystem, MessageNavigation, MessageNetwork, MessageOta, MessageVideo, MessageDriver): """MQTT commands for Luba.""" def __init__(self, device_name: str) -> None: diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 9da51ae..1eca0d6 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -264,6 +264,7 @@ async def login(account: str, password: str) -> CloudIOTGateway: await loop.run_in_executor(None, cloud_client.list_binding_by_account) return cloud_client + def get_device_by_name(self, name: str) -> MammotionMixedDeviceManager: return self.devices.get_device(name) @@ -277,7 +278,7 @@ async def send_command(self, name: str, key: str): return await device.cloud().command(key) # TODO work with both with EITHER - async def send_command_with_args(self,name: str, key: str, kwargs): + async def send_command_with_args(self,name: str, key: str, **kwargs: any): """Send a command with args to the device.""" device = self.get_device_by_name(name) if device: @@ -308,7 +309,7 @@ async def start_map_sync(self, name:str): def mower(self, name: str): device = self.get_device_by_name(name) if device: - return device.mower_state + return device.mower_state() def has_field(message: betterproto.Message) -> bool: """Check if the message has any fields serialized on wire.""" @@ -926,22 +927,26 @@ def on_ready(self): if self.on_ready_callback: self.on_ready_callback() + asyncio.run(self._ble_sync()) + asyncio.run(self.run_periodic_sync_task()) + def on_connected(self): """Callback for when MQTT connects.""" - self._ble_sync() - self.run_periodic_sync_task() + def on_disconnected(self): """Callback for when MQTT disconnects.""" - def _ble_sync(self): + async def _ble_sync(self): command_bytes = self._commands.send_todev_ble_sync(3) - self._mqtt_client.get_cloud_client().send_cloud_command(self.iot_id, command_bytes) + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command_bytes) + async def run_periodic_sync_task(self) -> None: """Send ble sync to robot.""" try: - self._ble_sync() + await self._ble_sync() finally: self.schedule_ble_sync() @@ -995,7 +1000,7 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: self._key = key _LOGGER.debug("%s: Sending command: %s", self.device.nickName, key) loop = asyncio.get_running_loop() - loop.run_in_executor(None, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) + await loop.run_in_executor(None, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) retry_handle = self.loop.call_at( self.loop.time() + 20, diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index 0211450..4bf2958 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -147,12 +147,11 @@ def _thing_on_connect(self, session_flag, rc, user_data): # self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/#") - def _on_disconnect(self, _client, _userdata, rc: int): + def _on_disconnect(self, _client, _userdata): """Is called on disconnect.""" logger.info("Disconnected") self.is_connected = False self.is_ready = False - logger.debug(rc) if self.on_disconnected: self.on_disconnected() diff --git a/tests/pyjoystick_example.py b/tests/pyjoystick_example.py index bf0bb94..6b70858 100644 --- a/tests/pyjoystick_example.py +++ b/tests/pyjoystick_example.py @@ -9,6 +9,7 @@ from pymammotion.bluetooth.ble import LubaBLE from pymammotion.bluetooth.ble_message import BleMessage from pymammotion.event.event import BleNotificationEvent +from pymammotion.utility.rocker_util import RockerControlUtil bleNotificationEvt = BleNotificationEvent() @@ -49,6 +50,17 @@ def __init__(self, ble_message: BleMessage): def _movement_finished(self): self.ignore_events = False + @staticmethod + def transform_both_speeds(linear: float, angular: float, linear_percent: float, angular_percent: float): + transfrom3 = RockerControlUtil.getInstance().transfrom3(linear, linear_percent) + transform4 = RockerControlUtil.getInstance().transfrom3(angular, angular_percent) + + if transfrom3 is not None and len(transfrom3) > 0: + linear_speed = transfrom3[0] * 10 + angular_speed = int(transform4[1] * 4.5) + print(linear_speed, angular_speed) + return linear_speed, angular_speed + def run_movement(self): if (self.linear_percent == 0.0 and self.angular_percent == 0.0): if self.stopped: @@ -56,14 +68,13 @@ def run_movement(self): self.stopped = True self.stopped = False - asyncio.run( - self._client.transformBothSpeeds( + + self.transform_both_speeds( self.linear_speed, self.angular_speed, self.linear_percent, self.angular_percent, ) - ) def print_add(self, joy): print("Added", joy) From 5e13d6d29b37a75a0f782974c575c1c350b43e43 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Sun, 25 Aug 2024 09:38:33 +1200 Subject: [PATCH 27/35] adding movement commands --- pymammotion/mammotion/devices/mammotion.py | 23 ++++++++++++++++++++++ pyproject.toml | 6 +++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 1eca0d6..41fb7d0 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -40,6 +40,7 @@ from pymammotion.mqtt import MammotionMQTT from pymammotion.proto.luba_msg import LubaMsg from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck +from pymammotion.utility.rocker_util import RockerControlUtil class CharacteristicMissingError(Exception): @@ -504,6 +505,28 @@ async def start_map_sync(self): # jobs list # hash_list_result = await self._send_command_with_args("get_all_boundary_hash_list", sub_cmd=3) + async def move_forward(self): + linear_speed = 1.0 + angular_speed = 0.0 + transfrom3 = RockerControlUtil.getInstance().transfrom3(90, 1000) + transform4 = RockerControlUtil.getInstance().transfrom3(0, 0) + + if transfrom3 is not None and len(transfrom3) > 0: + linear_speed = transfrom3[0] * 10 + angular_speed = int(transform4[1] * 4.5) + await self._send_command_with_args("send_movement", linear_speed=linear_speed, angular_speed=angular_speed) + + async def move_stop(self): + linear_speed = 0.0 + angular_speed = 0.0 + transfrom3 = RockerControlUtil.getInstance().transfrom3(0, 0) + transform4 = RockerControlUtil.getInstance().transfrom3(0, 0) + + if transfrom3 is not None and len(transfrom3) > 0: + linear_speed = transfrom3[0] * 10 + angular_speed = int(transform4[1] * 4.5) + await self._send_command_with_args("send_movement", linear_speed=linear_speed, angular_speed=angular_speed) + async def command(self, key: str, **kwargs): """Send a command to the device.""" return await self._send_command_with_args(key, **kwargs) diff --git a/pyproject.toml b/pyproject.toml index 5348a22..0309dc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "pymammotion" -version = "0.2.4" +version = "0.2.5" [tool.poetry] name = "pymammotion" -version = "0.2.4" +version = "0.2.5" license = "GNU-3.0" description = "" readme = "README.md" @@ -52,7 +52,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.4" +current_version = "0.2.5" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 24d3bfa1274a70bbb6f49167aaa69765d8134267 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Sun, 25 Aug 2024 15:16:38 +1200 Subject: [PATCH 28/35] fix issue with device identification messing up sync maps --- pymammotion/aliyun/cloud_gateway.py | 9 +++-- pymammotion/data/model/location.py | 10 +++--- pymammotion/mammotion/devices/mammotion.py | 41 ++++++++++++++++------ pymammotion/mqtt/mammotion_mqtt.py | 5 +-- pyproject.toml | 6 ++-- tests/test2_instance.py | 4 ++- 6 files changed, 52 insertions(+), 23 deletions(-) diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index 76e74d5..4640f51 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -9,7 +9,7 @@ import string import time import uuid -from logging import getLogger +from logging import getLogger, exception from aiohttp import ClientSession from alibabacloud_iot_api_gateway.client import Client @@ -45,6 +45,10 @@ ) +class SetupException(Exception): + pass + + class CloudIOTGateway: """Class for interacting with Aliyun Cloud IoT Gateway.""" @@ -524,7 +528,8 @@ def send_cloud_command(self, iot_id: str, command: bytes) -> str: str(response_body_dict.get("code")), str(response_body_dict.get("msg")), ) - return "" + if response_body_dict.get("code") == 29003: + raise SetupException(response_body_dict.get("code")) return message_id diff --git a/pymammotion/data/model/location.py b/pymammotion/data/model/location.py index fa06177..c66a77b 100644 --- a/pymammotion/data/model/location.py +++ b/pymammotion/data/model/location.py @@ -7,8 +7,8 @@ class Point: """Returns a lat long.""" - latitude: float - longitude: float + latitude: float = 0.0 + longitude: float = 0.0 def __init__(self, latitude=0.0, longitude=0.0): self.latitude = latitude @@ -19,7 +19,7 @@ def __init__(self, latitude=0.0, longitude=0.0): class Dock(Point): """Stores robot dock position.""" - rotation: int + rotation: int = 0 def __init__(self): super().__init__() @@ -33,8 +33,8 @@ class Location: device: Point RTK: Point dock: Dock - position_type: int - orientation: int # 360 degree rotation +- + position_type: int = 0 + orientation: int = 0 # 360 degree rotation +- def __init__(self): self.device = Point() diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 41fb7d0..c36239b 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -115,10 +115,12 @@ async def _handle_retry(fut: asyncio.Future[None], func, command: bytes) -> None await func(command) -async def _handle_retry_cloud(fut: asyncio.Future[None], func, iotId: str, command: bytes) -> None: +async def _handle_retry_cloud(fut: asyncio.Future[None], func, iot_id: str, command: bytes) -> None: """Handle a retry.""" + if not fut.done(): - func(iotId, command) + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, func, iot_id, command) class ConnectionPreference(Enum): @@ -224,10 +226,8 @@ def __init__( async def initiate_cloud_connection(self, cloud_client: CloudIOTGateway) -> None: if self._mammotion_mqtt is not None: - if not self._mammotion_mqtt.is_connected: - loop = asyncio.get_running_loop() - await loop.run_in_executor(None, self._mammotion_mqtt.connect_async) - return + if self._mammotion_mqtt.is_connected: + return self._mammotion_mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, @@ -245,6 +245,11 @@ async def initiate_cloud_connection(self, cloud_client: CloudIOTGateway) -> None if device.deviceName.startswith(("Luba-", "Yuka-")): self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=self._mammotion_mqtt)) + def set_disconnect_strategy(self, disconnect: bool): + for device_name, device in self.devices.devices: + if device.ble() is not None: + ble_device: MammotionBaseBLEDevice = device.ble() + ble_device.set_disconnect_strategy(disconnect) @staticmethod async def login(account: str, password: str) -> CloudIOTGateway: @@ -538,6 +543,7 @@ class MammotionBaseBLEDevice(MammotionBaseDevice): def __init__(self, mowing_state: MowingDevice, device: BLEDevice, interface: int = 0, **kwargs: Any) -> None: """Initialize MammotionBaseBLEDevice.""" super().__init__(mowing_state) + self._disconnect_strategy = True self._ble_sync_task = None self._prev_notification = None self._interface = f"hci{interface}" @@ -644,7 +650,10 @@ def name(self) -> str: @property def rssi(self) -> int: """Return RSSI of device.""" - return 0 + try: + return self._mower.device.sys.toapp_report_data.connect.ble_rssi + finally: + return 0 async def _ensure_connected(self): """Ensure connection to device is established.""" @@ -674,12 +683,11 @@ async def _ensure_connected(self): return _LOGGER.debug("%s: Connecting; RSSI: %s", self.name, self.rssi) client: BleakClientWithServiceCache = await establish_connection( - BleakClient, + BleakClientWithServiceCache, self._device, self.name, self._disconnected, max_attempts=10, - use_services_cache=True, ble_device_callback=lambda: self._device, ) _LOGGER.debug("%s: Connected; RSSI: %s", self.name, self.rssi) @@ -861,6 +869,8 @@ async def _execute_forced_disconnect(self) -> None: async def _execute_timed_disconnect(self) -> None: """Execute timed disconnection.""" + if not self._disconnect_strategy: + return _LOGGER.debug( "%s: Executing timed disconnect after timeout of %s", self.name, @@ -910,6 +920,9 @@ async def _disconnect(self) -> bool: if self._client is not None: return await self._client.disconnect() + def set_disconnect_strategy(self, disconnect): + self._disconnect_strategy = disconnect + class MammotionBaseCloudDevice(MammotionBaseDevice): """Base class for Mammotion Cloud devices.""" @@ -1026,7 +1039,7 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: await loop.run_in_executor(None, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) retry_handle = self.loop.call_at( - self.loop.time() + 20, + self.loop.time() + 10, lambda: asyncio.ensure_future( _handle_retry_cloud( self._notify_future, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command @@ -1087,6 +1100,14 @@ def _parse_mqtt_response(self, topic: str, payload: dict) -> None: binary_data = base64.b64decode(params.value.get("content", "")) self._update_raw_data(cast(bytes, binary_data)) new_msg = LubaMsg().parse(cast(bytes, binary_data)) + + if self._commands.get_device_product_key() == "" and self._commands.get_device_name() == event.params.deviceName: + self._commands.set_device_product_key(event.params.productKey) + + if betterproto.serialized_on_wire(new_msg.net): + if new_msg.net.todev_ble_sync != 0 or has_field(new_msg.net.toapp_wifi_iot_status): + return + if self._notify_future and not self._notify_future.done(): self._notify_future.set_result(new_msg) asyncio.run(self._state_manager.notification(new_msg)) diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index 4bf2958..7b4b4c4 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -1,5 +1,5 @@ """MammotionMQTT.""" - +import asyncio import hashlib import hmac import json @@ -81,7 +81,7 @@ def connect_async(self): logger.info("Connecting...") self._linkkit_client.thing_setup() self._linkkit_client.connect_async() - self._linkkit_client.start_worker_loop() + def disconnect(self): """Disconnect from MQTT Server.""" @@ -91,6 +91,7 @@ def disconnect(self): def _thing_on_thing_enable(self, user_data): """Is called when Thing is enabled.""" logger.debug("on_thing_enable") + self.is_connected = True # logger.debug('subscribe_topic, topic:%s' % echo_topic) # self._linkkit_client.subscribe_topic(echo_topic, 0) self._linkkit_client.subscribe_topic( diff --git a/pyproject.toml b/pyproject.toml index 0309dc9..6362d8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "pymammotion" -version = "0.2.5" +version = "0.2.6" [tool.poetry] name = "pymammotion" -version = "0.2.5" +version = "0.2.6" license = "GNU-3.0" description = "" readme = "README.md" @@ -52,7 +52,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.5" +current_version = "0.2.6" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/tests/test2_instance.py b/tests/test2_instance.py index 29c2211..c475ef9 100644 --- a/tests/test2_instance.py +++ b/tests/test2_instance.py @@ -5,6 +5,7 @@ from bleak import BleakScanner from bleak.backends.device import BLEDevice +from pymammotion.data.model.device import MowingDevice from pymammotion.event.event import BleNotificationEvent from pymammotion.mammotion.devices.mammotion import MammotionBaseBLEDevice, has_field from pymammotion.proto.mctrl_sys import MctlSys, RptAct, RptInfoType @@ -58,7 +59,8 @@ async def run(loop): return luba_ble = MammotionBaseBLEDevice( - device=luba_device + device=luba_device, + mowing_state=MowingDevice() ) await asyncio.sleep(2) From a12e9137e5c076cf213229fcd2c3e09c37612daa Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 26 Aug 2024 11:32:00 +1200 Subject: [PATCH 29/35] remove operation lock for mqtt --- poetry.lock | 2 +- pymammotion/aliyun/cloud_gateway.py | 2 + pymammotion/mammotion/devices/mammotion.py | 105 +++++++++------------ pymammotion/mqtt/mammotion_future.py | 25 +++++ pymammotion/mqtt/mammotion_mqtt.py | 30 +++--- pyproject.toml | 1 + 6 files changed, 92 insertions(+), 73 deletions(-) create mode 100644 pymammotion/mqtt/mammotion_future.py diff --git a/poetry.lock b/poetry.lock index 23df474..d1f4db4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2960,4 +2960,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "b9bbba81b4362576e8ac2c0d40609f5d270d3300a619448cbdb67b2ad9b47488" +content-hash = "2eaa799c34314a30a737839eeed1621af21a23b4669e96b924fde37ea1ff96b3" diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index 4640f51..bc9e183 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -530,6 +530,8 @@ def send_cloud_command(self, iot_id: str, command: bytes) -> str: ) if response_body_dict.get("code") == 29003: raise SetupException(response_body_dict.get("code")) + if response_body_dict.get("code") == 6205: + """Device is offline.""" return message_id diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index c36239b..9868222 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -2,12 +2,15 @@ from __future__ import annotations +import queue +import threading import asyncio import base64 import codecs import json import logging from abc import abstractmethod +from collections import deque from enum import Enum from functools import cache from typing import Any, Callable, Optional, cast @@ -38,6 +41,7 @@ from pymammotion.http.http import connect_http from pymammotion.mammotion.commands.mammotion_command import MammotionCommand from pymammotion.mqtt import MammotionMQTT +from pymammotion.mqtt.mammotion_future import MammotionFuture from pymammotion.proto.luba_msg import LubaMsg from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck from pymammotion.utility.rocker_util import RockerControlUtil @@ -88,10 +92,10 @@ def slashescape(err): codecs.register_error("slashescape", slashescape) -def find_next_integer(lst: list[int], current_int: int) -> int | None: +def find_next_integer(lst: list[int], current_hash: float) -> int | None: try: # Find the index of the current integer - current_index = lst.index(current_int) + current_index = lst.index(current_hash) # Check if there is a next integer in the list if current_index + 1 < len(lst): @@ -115,12 +119,12 @@ async def _handle_retry(fut: asyncio.Future[None], func, command: bytes) -> None await func(command) -async def _handle_retry_cloud(fut: asyncio.Future[None], func, iot_id: str, command: bytes) -> None: +async def _handle_retry_cloud(self, fut: asyncio.Future[None], func, iot_id: str, command: bytes) -> None: """Handle a retry.""" if not fut.done(): - loop = asyncio.get_running_loop() - await loop.run_in_executor(None, func, iot_id, command) + self._operation_lock.release() + await self.loop.run_in_executor(None, func, iot_id, command) class ConnectionPreference(Enum): @@ -334,7 +338,6 @@ def __init__(self, device: MowingDevice) -> None: self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE) self._mower = device self._state_manager = StateManager(self._mower) - self._state_manager.gethash_ack_callback.add_subscribers(self.datahash_response) self._state_manager.get_commondata_ack_callback.add_subscribers(self.commdata_response) self._notify_future: asyncio.Future[bytes] | None = None @@ -945,7 +948,7 @@ def __init__( self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName) self.currentID = "" self.on_ready_callback: Optional[Callable[[], None]] = None - self._operation_lock = asyncio.Lock() + self._waiting_queue = deque() self._mqtt_client.on_connected = self.on_connected self._mqtt_client.on_disconnected = self.on_disconnected @@ -958,19 +961,19 @@ def __init__( # temporary for testing only # self._start_sync_task = self.loop.call_later(30, lambda: asyncio.ensure_future(self.start_sync(0))) - def on_ready(self): + async def on_ready(self): """Callback for when MQTT is subscribed to events.""" if self.on_ready_callback: self.on_ready_callback() - asyncio.run(self._ble_sync()) - asyncio.run(self.run_periodic_sync_task()) + await self._ble_sync() + await self.run_periodic_sync_task() - def on_connected(self): + async def on_connected(self): """Callback for when MQTT connects.""" - def on_disconnected(self): + async def on_disconnected(self): """Callback for when MQTT disconnects.""" async def _ble_sync(self): @@ -993,27 +996,24 @@ def schedule_ble_sync(self): 160, lambda: asyncio.ensure_future(self.run_periodic_sync_task()) ) - def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: + async def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: """Handle incoming MQTT messages.""" - _LOGGER.debug("MQTT message received on topic %s: %s", topic, payload) + _LOGGER.debug("MQTT message received on topic %s: %s", topic, payload, iot_id) json_str = json.dumps(payload) payload = json.loads(json_str) - self._handle_mqtt_message(topic, payload) + await self._handle_mqtt_message(topic, payload) async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: """Send command to device via MQTT and read response.""" - if self._operation_lock.locked(): - _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.device.nickName) - async with self._operation_lock: - try: - command_bytes = getattr(self._commands, key)() - return await self._send_command_locked(key, command_bytes) - except Exception as ex: - _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) - raise + try: + command_bytes = getattr(self._commands, key)() + return await self._send_command_locked(key, command_bytes) + except Exception as ex: + _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) + raise async def _send_command_locked(self, key: str, command: bytes) -> bytes: """Send command to device and read response.""" @@ -1032,33 +1032,16 @@ async def _send_command_locked(self, key: str, command: bytes) -> bytes: async def _execute_command_locked(self, key: str, command: bytes) -> bytes: """Execute command and read response.""" assert self._mqtt_client is not None - self._notify_future = self.loop.create_future() self._key = key _LOGGER.debug("%s: Sending command: %s", self.device.nickName, key) - loop = asyncio.get_running_loop() - await loop.run_in_executor(None, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) - - retry_handle = self.loop.call_at( - self.loop.time() + 10, - lambda: asyncio.ensure_future( - _handle_retry_cloud( - self._notify_future, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command - ) - ), - ) + await self.loop.run_in_executor(None, self._mqtt_client.get_cloud_client().send_cloud_command, self.iot_id, command) + future = MammotionFuture() + self._waiting_queue.append(future) timeout = 20 - timeout_handle = self.loop.call_at(self.loop.time() + timeout, _handle_timeout, self._notify_future) - timeout_expired = False try: - notify_msg = await self._notify_future + notify_msg = await future.async_get(timeout) except asyncio.TimeoutError: - timeout_expired = True raise - finally: - if not timeout_expired: - timeout_handle.cancel() - retry_handle.cancel() - self._notify_future = None _LOGGER.debug("%s: Message received", self.device.nickName) @@ -1066,15 +1049,12 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: async def _send_command_with_args(self, key: str, **kwargs: any) -> bytes | None: """Send command with arguments to device via MQTT and read response.""" - if self._operation_lock.locked(): - _LOGGER.debug("%s: Operation already in progress, waiting for it to complete;", self.device.nickName) - async with self._operation_lock: - try: - command_bytes = getattr(self._commands, key)(**kwargs) - return await self._send_command_locked(key, command_bytes) - except Exception as ex: - _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) - raise + try: + command_bytes = getattr(self._commands, key)(**kwargs) + return await self._send_command_locked(key, command_bytes) + except Exception as ex: + _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) + raise def _extract_message_id(self, payload: dict) -> str: """Extract the message ID from the payload.""" @@ -1089,7 +1069,7 @@ def _extract_encoded_message(self, payload: dict) -> str: _LOGGER.error("Error extracting encoded message. Payload: %s", payload) return "" - def _parse_mqtt_response(self, topic: str, payload: dict) -> None: + async def _parse_mqtt_response(self, topic: str, payload: dict) -> None: """Parse the MQTT response.""" if topic.endswith("/app/down/thing/events"): _LOGGER.debug("Thing event received") @@ -1108,14 +1088,17 @@ def _parse_mqtt_response(self, topic: str, payload: dict) -> None: if new_msg.net.todev_ble_sync != 0 or has_field(new_msg.net.toapp_wifi_iot_status): return - if self._notify_future and not self._notify_future.done(): - self._notify_future.set_result(new_msg) - asyncio.run(self._state_manager.notification(new_msg)) - def _handle_mqtt_message(self, topic: str, payload: dict) -> None: + if len(self._waiting_queue) > 0: + fut: MammotionFuture = self._waiting_queue.popleft() + fut.resolve(cast(bytes, binary_data)) + await self._state_manager.notification(new_msg) + + async def _handle_mqtt_message(self, topic: str, payload: dict) -> None: """Async handler for incoming MQTT messages.""" - self._parse_mqtt_response(topic=topic, payload=payload) + await self._parse_mqtt_response(topic=topic, payload=payload) - async def _disconnect(self): + def _disconnect(self): """Disconnect the MQTT client.""" self._mqtt_client.disconnect() + diff --git a/pymammotion/mqtt/mammotion_future.py b/pymammotion/mqtt/mammotion_future.py new file mode 100644 index 0000000..cae6c84 --- /dev/null +++ b/pymammotion/mqtt/mammotion_future.py @@ -0,0 +1,25 @@ +from asyncio import Future +from typing import Any + +import async_timeout + + +class MammotionFuture: + """Create futures for each MQTT Message.""" + def __init__(self): + self.fut: Future = Future() + self.loop = self.fut.get_loop() + + def _resolve(self, item: bytes) -> None: + if not self.fut.cancelled(): + self.fut.set_result(item) + + def resolve(self, item: bytes) -> None: + self.loop.call_soon_threadsafe(self._resolve, item) + + async def async_get(self, timeout: float | int) -> bytes: + try: + async with async_timeout.timeout(timeout): + return await self.fut + finally: + self.fut.cancel() diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index 7b4b4c4..37774d0 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -5,7 +5,7 @@ import json import logging from logging import getLogger -from typing import Callable, Optional, cast +from typing import Callable, Optional, cast, Awaitable from linkkit.linkkit import LinkKit from paho.mqtt.client import MQTTMessage @@ -36,11 +36,11 @@ def __init__( self._cloud_client = None self.is_connected = False self.is_ready = False - self.on_connected: Optional[Callable[[], None]] = None - self.on_ready: Optional[Callable[[], None]] = None - self.on_error: Optional[Callable[[str], None]] = None - self.on_disconnected: Optional[Callable[[], None]] = None - self.on_message: Optional[Callable[[str, str, str], None]] = None + self.on_connected: Optional[Callable[[],Awaitable[None]]] = None + self.on_ready: Optional[Callable[[],Awaitable[None]]] = None + self.on_error: Optional[Callable[[str],Awaitable[None]]] = None + self.on_disconnected: Optional[Callable[[],Awaitable[None]]] = None + self.on_message: Optional[Callable[[str, str, str],Awaitable[None]]] = None self._product_key = product_key self._device_name = device_name @@ -57,6 +57,7 @@ def __init__( ).hexdigest() self._client_id = client_id + self.loop = asyncio.get_event_loop() self._linkkit_client = LinkKit( region_id, @@ -79,7 +80,6 @@ def __init__( def connect_async(self): """Connect async to MQTT Server.""" logger.info("Connecting...") - self._linkkit_client.thing_setup() self._linkkit_client.connect_async() @@ -121,7 +121,8 @@ def _thing_on_thing_enable(self, user_data): if self.on_ready: self.is_ready = True - self.on_ready() + future = asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop) + asyncio.wrap_future(future, loop=self.loop) # self._linkkit_client.query_ota_firmware() # command = MammotionCommand(device_name="Luba") # self._cloud_client.send_cloud_command(command.get_report_cfg()) @@ -137,13 +138,17 @@ def _thing_on_topic_message(self, topic, payload, qos, user_data): payload = json.loads(payload) iot_id = payload.get("params", {}).get("iotId", "") if iot_id != "" and self.on_message: - self.on_message(topic, payload, iot_id) + future = asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop) + return asyncio.wrap_future(future, loop=self.loop) + def _thing_on_connect(self, session_flag, rc, user_data): """Is called on thing connect.""" self.is_connected = True if self.on_connected is not None: - self.on_connected() + future = asyncio.run_coroutine_threadsafe(self.on_connected(), self.loop) + asyncio.wrap_future(future, loop=self.loop) + logger.debug("on_connect, session_flag:%d, rc:%d", session_flag, rc) # self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/#") @@ -154,7 +159,9 @@ def _on_disconnect(self, _client, _userdata): self.is_connected = False self.is_ready = False if self.on_disconnected: - self.on_disconnected() + future = asyncio.run_coroutine_threadsafe(self.on_disconnected(), self.loop) + asyncio.wrap_future(future, loop=self.loop) + def _on_message(self, _client, _userdata, message: MQTTMessage): """Is called when message is received.""" @@ -185,3 +192,4 @@ def _on_message(self, _client, _userdata, message: MQTTMessage): def get_cloud_client(self) -> Optional[CloudIOTGateway]: """Return internal cloud client.""" return self._cloud_client + diff --git a/pyproject.toml b/pyproject.toml index 6362d8c..65183ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ betterproto = "^1.2.5" pyjoystick = "^1.2.4" nest-asyncio = "^1.6.0" numpy = "^1.26.0" +async-timeout = "^4.0.3" [tool.poetry.group.dev.dependencies] From f4e852f8e760173f8c6a55d7f40e1ade33c38fe4 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Mon, 26 Aug 2024 11:33:25 +1200 Subject: [PATCH 30/35] publish 0.2.7 --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 65183ee..cb88c70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "pymammotion" -version = "0.2.6" +version = "0.2.7" [tool.poetry] name = "pymammotion" -version = "0.2.6" +version = "0.2.7" license = "GNU-3.0" description = "" readme = "README.md" @@ -53,7 +53,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.6" +current_version = "0.2.7" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From 2f96392fb634e8c92b3d08d3049085c1643aa985 Mon Sep 17 00:00:00 2001 From: d3dfantasy99 <34962618+d3dfantasy99@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:23:15 +0200 Subject: [PATCH 31/35] Add check and refresh token into send_cloud_command (#56) Co-authored-by: Andrea Cagnola Co-authored-by: Michael Arthur --- pymammotion/aliyun/cloud_gateway.py | 44 +++++++++++++++++-- .../dataclass/session_by_authcode_response.py | 4 +- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index bc9e183..62d5a5d 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -10,6 +10,7 @@ import time import uuid from logging import getLogger, exception +from datetime import datetime from aiohttp import ClientSession from alibabacloud_iot_api_gateway.client import Client @@ -63,6 +64,8 @@ class CloudIOTGateway: _listing_dev_by_account_response = None _region = None + _iot_token_issued_at : int = None + converter = DatatypeConverter() def __init__(self): @@ -390,11 +393,13 @@ def session_by_auth_code(self): raise Exception("Error in creating session: " + response_body_dict["msg"]) self._session_by_authcode_response = SessionByAuthCodeResponse.from_dict(response_body_dict) + self._iot_token_issued_at = int(time.time()) return response.body def check_or_refresh_session(self): """Check or refresh the session.""" + logger.debug("Try to refresh token") config = Config( app_key=self._app_key, app_secret=self._app_secret, @@ -431,12 +436,32 @@ def check_or_refresh_session(self): logger.debug(response.status_code) logger.debug(response.body) - # self._region = response.body.data - # Decodifica il corpo della risposta + # Decode the response body response_body_str = response.body.decode("utf-8") - # Carica la stringa JSON in un dizionario - json.loads(response_body_str) + # Load the JSON string into a dictionary + response_body_dict = json.loads(response_body_str) + + if int(response_body_dict.get("code")) != 200: + raise Exception("Error check or refresh token: " + response_body_dict.get('msg', '')) + + identityId = response_body_dict.get('data', {}).get('identityId', None) + refreshTokenExpire = response_body_dict.get('data', {}).get('refreshTokenExpire', None) + iotToken = response_body_dict.get('data', {}).get('iotToken', None) + iotTokenExpire = response_body_dict.get('data', {}).get('iotTokenExpire', None) + refreshToken = response_body_dict.get('data', {}).get('refreshToken', None) + + if (identityId is None or refreshTokenExpire is None or iotToken is None or iotTokenExpire is None or refreshToken is None): + raise Exception("Error check or refresh token: Parameters not correct") + + self._session_by_authcode_response.data.identityId = identityId + self._session_by_authcode_response.data.refreshTokenExpire = refreshTokenExpire + self._session_by_authcode_response.data.iotToken = iotToken + self._session_by_authcode_response.data.iotTokenExpire = iotTokenExpire + self._session_by_authcode_response.data.refreshToken = refreshToken + self._iot_token_issued_at = int(time.time()) + + def list_binding_by_account(self) -> ListingDevByAccountResponse: """List bindings by account.""" @@ -482,6 +507,17 @@ def list_binding_by_account(self) -> ListingDevByAccountResponse: def send_cloud_command(self, iot_id: str, command: bytes) -> str: """Send a cloud command to the specified IoT device.""" + + """Check if iotToken is expired""" + if self._iot_token_issued_at + self._session_by_authcode_response.data.iotTokenExpire <= (int(time.time()) + (5 * 3600)): + """Token expired - Try to refresh - Check if refreshToken is not expired""" + if self._iot_token_issued_at + self._session_by_authcode_response.data.refreshTokenExpire > (int(time.time())): + self.check_or_refresh_session() + else: + raise Exception("Refresh token expired. Please re-login") + + + config = Config( app_key=self._app_key, app_secret=self._app_secret, diff --git a/pymammotion/aliyun/dataclass/session_by_authcode_response.py b/pymammotion/aliyun/dataclass/session_by_authcode_response.py index 15cee0a..b22ba38 100644 --- a/pymammotion/aliyun/dataclass/session_by_authcode_response.py +++ b/pymammotion/aliyun/dataclass/session_by_authcode_response.py @@ -1,4 +1,5 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from datetime import time from mashumaro.mixins.orjson import DataClassORJSONMixin @@ -11,7 +12,6 @@ class TokenData(DataClassORJSONMixin): iotTokenExpire: int refreshToken: str - @dataclass class SessionByAuthCodeResponse(DataClassORJSONMixin): code: int From 32d3ba985911d5d8b546a8111b1dedf4cee8be1e Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 27 Aug 2024 12:17:56 +1200 Subject: [PATCH 32/35] add methods for getting credentials for storing in HA when reloading or restarting without having to re-login --- pymammotion/aliyun/cloud_gateway.py | 76 ++++++++++++------- .../dataclass/session_by_authcode_response.py | 4 +- pymammotion/data/model/location.py | 4 - pymammotion/data/state_manager.py | 14 ++-- pymammotion/mammotion/devices/mammotion.py | 68 ++++++++++------- pymammotion/mqtt/mammotion_mqtt.py | 8 +- 6 files changed, 104 insertions(+), 70 deletions(-) diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index 62d5a5d..4ab2fea 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -49,6 +49,9 @@ class SetupException(Exception): pass +class AuthRefreshException(Exception): + """Raise exception when library cannot refresh token.""" + class CloudIOTGateway: """Class for interacting with Aliyun Cloud IoT Gateway.""" @@ -57,18 +60,18 @@ class CloudIOTGateway: _device_sn = "" _utdid = "" - _connect_response = None - _login_by_oauth_response = None - _aep_response = None - _session_by_authcode_response = None - _listing_dev_by_account_response = None - _region = None + _connect_response: ConnectResponse | None = None + _login_by_oauth_response: LoginByOAuthResponse | None = None + _aep_response: AepResponse | None = None + _session_by_authcode_response: SessionByAuthCodeResponse | None = None + _devices_by_account_response: ListingDevByAccountResponse | None = None + _region_response = None _iot_token_issued_at : int = None converter = DatatypeConverter() - def __init__(self): + def __init__(self, connect_response: ConnectResponse | None, login_by_oauth_response: LoginByOAuthResponse | None, aep_response: AepResponse | None, session_by_authcode_response: SessionByAuthCodeResponse | None, region_response: RegionResponse | None, dev_by_account: ListingDevByAccountResponse | None): """Initialize the CloudIOTGateway.""" self._app_key = APP_KEY self._app_secret = APP_SECRET @@ -77,6 +80,12 @@ def __init__(self): self._client_id = self.generate_hardware_string(8) # 8 characters self._device_sn = self.generate_hardware_string(32) # 32 characters self._utdid = self.generate_hardware_string(32) # 32 characters + self._connect_response = connect_response + self._login_by_oauth_response = login_by_oauth_response + self._aep_response = aep_response + self._session_by_authcode_response = session_by_authcode_response + self._region_response = region_response + self._devices_by_account_response = dev_by_account @staticmethod def generate_random_string(length): @@ -105,6 +114,24 @@ def sign(self, data): hashlib.sha1, ).hexdigest() + def get_connect_response(self): + return self._connect_response + + def get_login_by_oauth_response(self): + return self._login_by_oauth_response + + def get_aep_response(self): + return self._aep_response + + def get_session_by_authcode_response(self): + return self._session_by_authcode_response + + def get_devices_by_account_response(self): + return self._devices_by_account_response + + def get_region_response(self): + return self._region_response + def get_region(self, country_code: str, auth_code: str): """Get the region based on country code and auth code.""" config = Config( @@ -143,8 +170,8 @@ def get_region(self, country_code: str, auth_code: str): if int(response_body_dict.get("code")) != 200: raise Exception("Error in getting regions: " + response_body_dict["msg"]) - self._region = RegionResponse.from_dict(response_body_dict) - logger.debug("Endpoint: %s", self._region.data.mqttEndpoint) + self._region_response = RegionResponse.from_dict(response_body_dict) + logger.debug("Endpoint: %s", self._region_response.data.mqttEndpoint) return response.body @@ -152,8 +179,8 @@ def aep_handle(self): """Handle AEP authentication.""" aep_domain = self.domain - if self._region.data.apiGatewayEndpoint is not None: - aep_domain = self._region.data.apiGatewayEndpoint + if self._region_response.data.apiGatewayEndpoint is not None: + aep_domain = self._region_response.data.apiGatewayEndpoint config = Config( app_key=self._app_key, @@ -278,7 +305,7 @@ async def connect(self): async def login_by_oauth(self, country_code: str, auth_code: str): """Login by OAuth.""" - region_url = self._region.data.oaApiGatewayEndpoint + region_url = self._region_response.data.oaApiGatewayEndpoint async with ClientSession() as session: headers = { @@ -350,7 +377,7 @@ def session_by_auth_code(self): config = Config( app_key=self._app_key, app_secret=self._app_secret, - domain=self._region.data.apiGatewayEndpoint, + domain=self._region_response.data.apiGatewayEndpoint, ) client = Client(config) @@ -403,7 +430,7 @@ def check_or_refresh_session(self): config = Config( app_key=self._app_key, app_secret=self._app_secret, - domain=self._region.data.apiGatewayEndpoint, + domain=self._region_response.data.apiGatewayEndpoint, ) client = Client(config) @@ -451,14 +478,11 @@ def check_or_refresh_session(self): iotTokenExpire = response_body_dict.get('data', {}).get('iotTokenExpire', None) refreshToken = response_body_dict.get('data', {}).get('refreshToken', None) + if (identityId is None or refreshTokenExpire is None or iotToken is None or iotTokenExpire is None or refreshToken is None): raise Exception("Error check or refresh token: Parameters not correct") - - self._session_by_authcode_response.data.identityId = identityId - self._session_by_authcode_response.data.refreshTokenExpire = refreshTokenExpire - self._session_by_authcode_response.data.iotToken = iotToken - self._session_by_authcode_response.data.iotTokenExpire = iotTokenExpire - self._session_by_authcode_response.data.refreshToken = refreshToken + + self._session_by_authcode_response = SessionByAuthCodeResponse.from_dict(response_body_dict) self._iot_token_issued_at = int(time.time()) @@ -468,7 +492,7 @@ def list_binding_by_account(self) -> ListingDevByAccountResponse: config = Config( app_key=self._app_key, app_secret=self._app_secret, - domain=self._region.data.apiGatewayEndpoint, + domain=self._region_response.data.apiGatewayEndpoint, ) client = Client(config) @@ -502,8 +526,8 @@ def list_binding_by_account(self) -> ListingDevByAccountResponse: if int(response_body_dict.get("code")) != 200: raise Exception("Error in creating session: " + response_body_dict["msg"]) - self._listing_dev_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict) - return self._listing_dev_by_account_response + self._devices_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict) + return self._devices_by_account_response def send_cloud_command(self, iot_id: str, command: bytes) -> str: """Send a cloud command to the specified IoT device.""" @@ -514,14 +538,14 @@ def send_cloud_command(self, iot_id: str, command: bytes) -> str: if self._iot_token_issued_at + self._session_by_authcode_response.data.refreshTokenExpire > (int(time.time())): self.check_or_refresh_session() else: - raise Exception("Refresh token expired. Please re-login") + raise AuthRefreshException("Refresh token expired. Please re-login") config = Config( app_key=self._app_key, app_secret=self._app_secret, - domain=self._region.data.apiGatewayEndpoint, + domain=self._region_response.data.apiGatewayEndpoint, ) client = Client(config) @@ -573,4 +597,4 @@ def send_cloud_command(self, iot_id: str, command: bytes) -> str: @property def listing_dev_by_account_response(self): - return self._listing_dev_by_account_response + return self._devices_by_account_response diff --git a/pymammotion/aliyun/dataclass/session_by_authcode_response.py b/pymammotion/aliyun/dataclass/session_by_authcode_response.py index b22ba38..594c796 100644 --- a/pymammotion/aliyun/dataclass/session_by_authcode_response.py +++ b/pymammotion/aliyun/dataclass/session_by_authcode_response.py @@ -5,7 +5,7 @@ @dataclass -class TokenData(DataClassORJSONMixin): +class SessionOauthToken(DataClassORJSONMixin): identityId: str refreshTokenExpire: int iotToken: str @@ -15,4 +15,4 @@ class TokenData(DataClassORJSONMixin): @dataclass class SessionByAuthCodeResponse(DataClassORJSONMixin): code: int - data: TokenData + data: SessionOauthToken diff --git a/pymammotion/data/model/location.py b/pymammotion/data/model/location.py index c66a77b..cc3377f 100644 --- a/pymammotion/data/model/location.py +++ b/pymammotion/data/model/location.py @@ -21,10 +21,6 @@ class Dock(Point): rotation: int = 0 - def __init__(self): - super().__init__() - self.rotation = 0 - @dataclass class Location: diff --git a/pymammotion/data/state_manager.py b/pymammotion/data/state_manager.py index 670a1f4..adcd376 100644 --- a/pymammotion/data/state_manager.py +++ b/pymammotion/data/state_manager.py @@ -1,24 +1,22 @@ """Manage state from notifications into MowingDevice.""" +from typing import Optional, Callable, Awaitable import betterproto from pymammotion.data.model.device import MowingDevice -from pymammotion.event.event import DataEvent from pymammotion.proto.luba_msg import LubaMsg -from pymammotion.proto.mctrl_nav import NavGetCommDataAck +from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck class StateManager: """Manage state.""" _device: MowingDevice - gethash_ack_callback: DataEvent - get_commondata_ack_callback: DataEvent def __init__(self, device: MowingDevice): self._device = device - self.gethash_ack_callback = DataEvent() - self.get_commondata_ack_callback = DataEvent() + self.gethash_ack_callback: Optional[Callable[[NavGetHashListAck],Awaitable[None]]] = None + self.get_commondata_ack_callback: Optional[Callable[[NavGetCommDataAck],Awaitable[None]]] = None def get_device(self) -> MowingDevice: """Get device.""" @@ -54,12 +52,12 @@ async def _update_nav_data(self, message): self._device.map.obstacle = dict() self._device.map.area = dict() self._device.map.path = dict() - await self.gethash_ack_callback.data_event(nav_msg[1]) + await self.gethash_ack_callback(nav_msg[1]) case "toapp_get_commondata_ack": common_data: NavGetCommDataAck = nav_msg[1] updated = self._device.map.update(common_data) if updated: - await self.get_commondata_ack_callback.data_event(common_data) + await self.get_commondata_ack_callback(common_data) def _update_sys_data(self, message): """Update system.""" diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index 9868222..b2a52a4 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -13,7 +13,7 @@ from collections import deque from enum import Enum from functools import cache -from typing import Any, Callable, Optional, cast +from typing import Any, Callable, Optional, cast, Awaitable from uuid import UUID import betterproto @@ -212,7 +212,8 @@ class Mammotion(object): """Represents a Mammotion device.""" devices = MammotionDevices() - _mammotion_mqtt: MammotionMQTT | None = None + cloud_client: CloudIOTGateway | None = None + mqtt: MammotionMQTT | None = None @@ -229,25 +230,25 @@ def __init__( self._preference = preference async def initiate_cloud_connection(self, cloud_client: CloudIOTGateway) -> None: - if self._mammotion_mqtt is not None: - if self._mammotion_mqtt.is_connected: + if self.mqtt is not None: + if self.mqtt.is_connected: return - - self._mammotion_mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, + self.cloud_client = cloud_client + self.mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, product_key=cloud_client._aep_response.data.productKey, device_name=cloud_client._aep_response.data.deviceName, device_secret=cloud_client._aep_response.data.deviceSecret, iot_token=cloud_client._session_by_authcode_response.data.iotToken, client_id=cloud_client._client_id) - self._mammotion_mqtt._cloud_client = cloud_client + self.mqtt._cloud_client = cloud_client loop = asyncio.get_running_loop() - await loop.run_in_executor(None, self._mammotion_mqtt.connect_async) + await loop.run_in_executor(None, self.mqtt.connect_async) for device in cloud_client.listing_dev_by_account_response.data.data: if device.deviceName.startswith(("Luba-", "Yuka-")): - self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=self._mammotion_mqtt)) + self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=self.mqtt)) def set_disconnect_strategy(self, disconnect: bool): for device_name, device in self.devices.devices: @@ -338,8 +339,8 @@ def __init__(self, device: MowingDevice) -> None: self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE) self._mower = device self._state_manager = StateManager(self._mower) - self._state_manager.gethash_ack_callback.add_subscribers(self.datahash_response) - self._state_manager.get_commondata_ack_callback.add_subscribers(self.commdata_response) + self._state_manager.gethash_ack_callback = self.datahash_response + self._state_manager.get_commondata_ack_callback = self.commdata_response self._notify_future: asyncio.Future[bytes] | None = None async def datahash_response(self, hash_ack: NavGetHashListAck): @@ -947,8 +948,9 @@ def __init__( self._command_futures = {} self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName) self.currentID = "" - self.on_ready_callback: Optional[Callable[[], None]] = None + self.on_ready_callback: Optional[Callable[[], Awaitable[None]]] = None self._waiting_queue = deque() + self._operation_lock = threading.Lock() self._mqtt_client.on_connected = self.on_connected self._mqtt_client.on_disconnected = self.on_disconnected @@ -998,7 +1000,7 @@ def schedule_ble_sync(self): async def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: """Handle incoming MQTT messages.""" - _LOGGER.debug("MQTT message received on topic %s: %s", topic, payload, iot_id) + _LOGGER.debug("MQTT message received on topic %s: %s, iot_id: %s", topic, payload, iot_id) json_str = json.dumps(payload) payload = json.loads(json_str) @@ -1007,13 +1009,18 @@ async def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: """Send command to device via MQTT and read response.""" - - try: - command_bytes = getattr(self._commands, key)() - return await self._send_command_locked(key, command_bytes) - except Exception as ex: - _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) - raise + if self._operation_lock.locked(): + _LOGGER.debug( + "%s: Operation already in progress, waiting for it to complete;", + self.device.nickName + ) + with self._operation_lock: + try: + command_bytes = getattr(self._commands, key)() + return await self._send_command_locked(key, command_bytes) + except Exception as ex: + _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) + raise async def _send_command_locked(self, key: str, command: bytes) -> bytes: """Send command to device and read response.""" @@ -1049,12 +1056,18 @@ async def _execute_command_locked(self, key: str, command: bytes) -> bytes: async def _send_command_with_args(self, key: str, **kwargs: any) -> bytes | None: """Send command with arguments to device via MQTT and read response.""" - try: - command_bytes = getattr(self._commands, key)(**kwargs) - return await self._send_command_locked(key, command_bytes) - except Exception as ex: - _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) - raise + if self._operation_lock.locked(): + _LOGGER.debug( + "%s: Operation already in progress, waiting for it to complete;", + self.device.nickName + ) + with self._operation_lock: + try: + command_bytes = getattr(self._commands, key)(**kwargs) + return await self._send_command_locked(key, command_bytes) + except Exception as ex: + _LOGGER.exception("%s: error in sending command - %s", self.device.nickName, ex) + raise def _extract_message_id(self, payload: dict) -> str: """Extract the message ID from the payload.""" @@ -1091,6 +1104,8 @@ async def _parse_mqtt_response(self, topic: str, payload: dict) -> None: if len(self._waiting_queue) > 0: fut: MammotionFuture = self._waiting_queue.popleft() + while fut.fut.cancelled(): + fut: MammotionFuture = self._waiting_queue.popleft() fut.resolve(cast(bytes, binary_data)) await self._state_manager.notification(new_msg) @@ -1102,3 +1117,4 @@ def _disconnect(self): """Disconnect the MQTT client.""" self._mqtt_client.disconnect() + diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index 37774d0..ff90b4c 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -80,6 +80,8 @@ def __init__( def connect_async(self): """Connect async to MQTT Server.""" logger.info("Connecting...") + if self._linkkit_client.check_state() is LinkKit.LinkKitState.INITIALIZED: + self._linkkit_client.thing_setup() self._linkkit_client.connect_async() @@ -121,8 +123,7 @@ def _thing_on_thing_enable(self, user_data): if self.on_ready: self.is_ready = True - future = asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop) - asyncio.wrap_future(future, loop=self.loop) + asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop).result() # self._linkkit_client.query_ota_firmware() # command = MammotionCommand(device_name="Luba") # self._cloud_client.send_cloud_command(command.get_report_cfg()) @@ -138,8 +139,7 @@ def _thing_on_topic_message(self, topic, payload, qos, user_data): payload = json.loads(payload) iot_id = payload.get("params", {}).get("iotId", "") if iot_id != "" and self.on_message: - future = asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop) - return asyncio.wrap_future(future, loop=self.loop) + asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop).result() def _thing_on_connect(self, session_flag, rc, user_data): From a09869b9eb6801979a00e5ff0e9202c852165f1b Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 27 Aug 2024 13:42:59 +1200 Subject: [PATCH 33/35] fix queuing of requests for mqtt, sync maps now works bump version to 0.2.9 --- pymammotion/aliyun/cloud_gateway.py | 2 +- pymammotion/mammotion/devices/mammotion.py | 65 ++++++++++++++++++---- pymammotion/mqtt/mammotion_mqtt.py | 5 +- pyproject.toml | 6 +- 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/pymammotion/aliyun/cloud_gateway.py b/pymammotion/aliyun/cloud_gateway.py index 4ab2fea..c561430 100644 --- a/pymammotion/aliyun/cloud_gateway.py +++ b/pymammotion/aliyun/cloud_gateway.py @@ -71,7 +71,7 @@ class CloudIOTGateway: converter = DatatypeConverter() - def __init__(self, connect_response: ConnectResponse | None, login_by_oauth_response: LoginByOAuthResponse | None, aep_response: AepResponse | None, session_by_authcode_response: SessionByAuthCodeResponse | None, region_response: RegionResponse | None, dev_by_account: ListingDevByAccountResponse | None): + def __init__(self, connect_response: ConnectResponse | None = None, login_by_oauth_response: LoginByOAuthResponse | None = None, aep_response: AepResponse | None = None, session_by_authcode_response: SessionByAuthCodeResponse | None = None, region_response: RegionResponse | None = None, dev_by_account: ListingDevByAccountResponse | None = None): """Initialize the CloudIOTGateway.""" self._app_key = APP_KEY self._app_secret = APP_SECRET diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index b2a52a4..c7383f8 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -235,7 +235,7 @@ async def initiate_cloud_connection(self, cloud_client: CloudIOTGateway) -> None return self.cloud_client = cloud_client - self.mqtt = MammotionMQTT(region_id=cloud_client._region.data.regionId, + self.mqtt = MammotionMQTT(region_id=cloud_client._region_response.data.regionId, product_key=cloud_client._aep_response.data.productKey, device_name=cloud_client._aep_response.data.deviceName, device_secret=cloud_client._aep_response.data.deviceSecret, @@ -332,8 +332,9 @@ class MammotionBaseDevice: _mower: MowingDevice _state_manager: StateManager + _cloud_device: Device | None = None - def __init__(self, device: MowingDevice) -> None: + def __init__(self, device: MowingDevice, cloud_device: Device | None = None) -> None: """Initialize MammotionBaseDevice.""" self.loop = asyncio.get_event_loop() self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE) @@ -342,12 +343,13 @@ def __init__(self, device: MowingDevice) -> None: self._state_manager.gethash_ack_callback = self.datahash_response self._state_manager.get_commondata_ack_callback = self.commdata_response self._notify_future: asyncio.Future[bytes] | None = None + self._cloud_device = cloud_device async def datahash_response(self, hash_ack: NavGetHashListAck): """Handle datahash responses.""" result_hash = 0 while hash_ack.data_couple[0] != result_hash: - data = await self._send_command_with_args("synchronize_hash_data", hash_num=hash_ack.data_couple[0]) + data = await self.queue_command("synchronize_hash_data", hash_num=hash_ack.data_couple[0]) msg = LubaMsg().parse(data) if betterproto.serialized_on_wire(msg.nav.toapp_get_commondata_ack): result_hash = msg.nav.toapp_get_commondata_ack.hash @@ -365,7 +367,7 @@ async def commdata_response(self, common_data: NavGetCommDataAck): return result_hash = 0 while data_hash != result_hash: - data = await self._send_command_with_args("synchronize_hash_data", hash_num=data_hash) + data = await self.queue_command("synchronize_hash_data", hash_num=data_hash) msg = LubaMsg().parse(data) if betterproto.serialized_on_wire(msg.nav.toapp_get_commondata_ack): result_hash = msg.nav.toapp_get_commondata_ack.hash @@ -377,7 +379,7 @@ async def commdata_response(self, common_data: NavGetCommDataAck): region_data.type = common_data.type region_data.total_frame = total_frame region_data.current_frame = current_frame - await self._send_command_with_args("get_regional_data", regional_data=region_data) + await self.queue_command("get_regional_data", regional_data=region_data) def _update_raw_data(self, data: bytes) -> None: """Update raw and model data from notifications.""" @@ -475,6 +477,10 @@ def mower(self) -> MowingDevice: """Get the LubaMsg of the device.""" return self._mower + @abstractmethod + async def queue_command(self, key: str, **kwargs: any) -> bytes: + """Queue commands to mower.""" + @abstractmethod async def _send_command(self, key: str, retry: int | None = None) -> bytes | None: """Send command to device and read response.""" @@ -505,9 +511,15 @@ async def start_map_sync(self): await self._send_command_with_args("get_hash_response", total_frame=1, current_frame=1) - await self._send_command_with_args( - "get_area_name_list", device_id=self._mower.device.net.toapp_wifi_iot_status.devicename - ) + if self._cloud_device: + await self._send_command_with_args( + "get_area_name_list", device_id=self._cloud_device.deviceName + ) + if has_field(self._mower.net.toapp_wifi_iot_status): + await self._send_command_with_args( + "get_area_name_list", device_id=self._mower.net.toapp_wifi_iot_status.devicename + ) + # sub_cmd 3 is job hashes?? # sub_cmd 4 is dump location (yuka) @@ -586,6 +598,9 @@ def schedule_ble_sync(self): 130, lambda: asyncio.ensure_future(self.run_periodic_sync_task()) ) + async def queue_command(self, key: str, **kwargs: any) -> bytes | None: + return await self._send_command_with_args(key, **kwargs) + async def _send_command_with_args(self, key: str, **kwargs) -> bytes | None: """Send command to device and read response.""" if self._operation_lock.locked(): @@ -655,7 +670,7 @@ def name(self) -> str: def rssi(self) -> int: """Return RSSI of device.""" try: - return self._mower.device.sys.toapp_report_data.connect.ble_rssi + return self._mower.sys.toapp_report_data.connect.ble_rssi finally: return 0 @@ -938,9 +953,11 @@ def __init__( mowing_state: MowingDevice ) -> None: """Initialize MammotionBaseCloudDevice.""" - super().__init__(mowing_state) + super().__init__(mowing_state, cloud_device) self._ble_sync_task = None self.is_ready = False + self.command_queue = asyncio.Queue() + self.processing_task = asyncio.create_task(self._process_queue()) self._mqtt_client = mqtt_client self.iot_id = cloud_device.iotId self.device = cloud_device @@ -966,7 +983,7 @@ def __init__( async def on_ready(self): """Callback for when MQTT is subscribed to events.""" if self.on_ready_callback: - self.on_ready_callback() + await self.on_ready_callback() await self._ble_sync() await self.run_periodic_sync_task() @@ -998,6 +1015,32 @@ def schedule_ble_sync(self): 160, lambda: asyncio.ensure_future(self.run_periodic_sync_task()) ) + async def queue_command(self, key: str, **kwargs: any) -> bytes: + # Create a future to hold the result + _LOGGER.debug("Queueing command: %s", key) + future = asyncio.Future() + # Put the command in the queue as a tuple (key, command, future) + command_bytes = getattr(self._commands, key)(**kwargs) + await self.command_queue.put((key, command_bytes, future)) + # Wait for the future to be resolved + return await future + + async def _process_queue(self): + while True: + # Get the next item from the queue + key, command, future = await self.command_queue.get() + try: + # Process the command using _execute_command_locked + result = await self._execute_command_locked(key, command) + # Set the result on the future + future.set_result(result) + except Exception as ex: + # Set the exception on the future if something goes wrong + future.set_exception(ex) + finally: + # Mark the task as done + self.command_queue.task_done() + async def _on_mqtt_message(self, topic: str, payload: str, iot_id: str) -> None: """Handle incoming MQTT messages.""" _LOGGER.debug("MQTT message received on topic %s: %s, iot_id: %s", topic, payload, iot_id) diff --git a/pymammotion/mqtt/mammotion_mqtt.py b/pymammotion/mqtt/mammotion_mqtt.py index ff90b4c..40b10ec 100644 --- a/pymammotion/mqtt/mammotion_mqtt.py +++ b/pymammotion/mqtt/mammotion_mqtt.py @@ -57,7 +57,7 @@ def __init__( ).hexdigest() self._client_id = client_id - self.loop = asyncio.get_event_loop() + self.loop = asyncio.get_running_loop() self._linkkit_client = LinkKit( region_id, @@ -139,7 +139,8 @@ def _thing_on_topic_message(self, topic, payload, qos, user_data): payload = json.loads(payload) iot_id = payload.get("params", {}).get("iotId", "") if iot_id != "" and self.on_message: - asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop).result() + future = asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop) + asyncio.wrap_future(future, loop=self.loop) def _thing_on_connect(self, session_flag, rc, user_data): diff --git a/pyproject.toml b/pyproject.toml index cb88c70..2cd702d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "pymammotion" -version = "0.2.7" +version = "0.2.9" [tool.poetry] name = "pymammotion" -version = "0.2.7" +version = "0.2.9" license = "GNU-3.0" description = "" readme = "README.md" @@ -53,7 +53,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.7" +current_version = "0.2.9" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From f563646095182638c2ff54377545c76b7db21244 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 27 Aug 2024 15:11:25 +1200 Subject: [PATCH 34/35] don't create cloud or initiate cloud connection if there isn't credentials --- pymammotion/mammotion/devices/mammotion.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index c7383f8..ad1eb72 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -198,10 +198,11 @@ def get_device(self, mammotion_device_name: str) -> MammotionMixedDeviceManager: async def create_devices(ble_device: BLEDevice, cloud_credentials: Credentials | None = None, preference: ConnectionPreference = ConnectionPreference.BLUETOOTH): - cloud_client = await Mammotion.login(cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password) mammotion = Mammotion(ble_device, preference) if cloud_credentials: + cloud_client = await Mammotion.login(cloud_credentials.account_id or cloud_credentials.email, + cloud_credentials.password) await mammotion.initiate_cloud_connection(cloud_client) return mammotion From 12975643b3aa82d3df30d80feb061d04d4aeae69 Mon Sep 17 00:00:00 2001 From: Michael Arthur Date: Tue, 27 Aug 2024 18:48:07 +1200 Subject: [PATCH 35/35] bump version to 0.2.11 and catch small issue with futures queue being empty --- pymammotion/mammotion/devices/mammotion.py | 5 +++-- pyproject.toml | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pymammotion/mammotion/devices/mammotion.py b/pymammotion/mammotion/devices/mammotion.py index ad1eb72..e9e17a5 100644 --- a/pymammotion/mammotion/devices/mammotion.py +++ b/pymammotion/mammotion/devices/mammotion.py @@ -1148,9 +1148,10 @@ async def _parse_mqtt_response(self, topic: str, payload: dict) -> None: if len(self._waiting_queue) > 0: fut: MammotionFuture = self._waiting_queue.popleft() - while fut.fut.cancelled(): + while fut.fut.cancelled() and len(self._waiting_queue) > 0: fut: MammotionFuture = self._waiting_queue.popleft() - fut.resolve(cast(bytes, binary_data)) + if not fut.fut.cancelled(): + fut.resolve(cast(bytes, binary_data)) await self._state_manager.notification(new_msg) async def _handle_mqtt_message(self, topic: str, payload: dict) -> None: diff --git a/pyproject.toml b/pyproject.toml index 2cd702d..aa7689a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "pymammotion" -version = "0.2.9" +version = "0.2.11" [tool.poetry] name = "pymammotion" -version = "0.2.9" +version = "0.2.11" license = "GNU-3.0" description = "" readme = "README.md" @@ -53,7 +53,7 @@ mypy = "^1.10.0" pre-commit = "^3.7.1" [tool.bumpver] -current_version = "0.2.9" +current_version = "0.2.11" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true