pymammotion 0.4.0a2__py3-none-any.whl → 0.5.51__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymammotion might be problematic. Click here for more details.
- pymammotion/__init__.py +5 -4
- pymammotion/aliyun/client.py +235 -0
- pymammotion/aliyun/cloud_gateway.py +312 -64
- pymammotion/aliyun/model/aep_response.py +1 -2
- pymammotion/aliyun/model/dev_by_account_response.py +170 -23
- pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
- pymammotion/aliyun/model/regions_response.py +3 -3
- pymammotion/aliyun/model/session_by_authcode_response.py +2 -2
- pymammotion/aliyun/model/thing_response.py +12 -0
- pymammotion/aliyun/regions.py +62 -0
- pymammotion/aliyun/tea/core.py +297 -0
- pymammotion/bluetooth/ble.py +7 -9
- pymammotion/bluetooth/ble_message.py +10 -14
- pymammotion/const.py +3 -0
- pymammotion/data/model/__init__.py +1 -2
- pymammotion/data/model/device.py +95 -27
- pymammotion/data/model/device_config.py +4 -4
- pymammotion/data/model/device_info.py +35 -0
- pymammotion/data/model/device_limits.py +10 -10
- pymammotion/data/model/enums.py +12 -2
- pymammotion/data/model/errors.py +12 -0
- pymammotion/data/model/events.py +14 -0
- pymammotion/data/model/generate_geojson.py +521 -0
- pymammotion/data/model/generate_route_information.py +2 -2
- pymammotion/data/model/hash_list.py +370 -57
- pymammotion/data/model/location.py +4 -4
- pymammotion/data/model/mowing_modes.py +17 -1
- pymammotion/data/model/raw_data.py +2 -10
- pymammotion/data/model/region_data.py +10 -11
- pymammotion/data/model/report_info.py +31 -5
- pymammotion/data/model/work.py +27 -0
- pymammotion/data/mower_state_manager.py +316 -0
- pymammotion/data/mqtt/event.py +73 -28
- pymammotion/data/mqtt/mammotion_properties.py +257 -0
- pymammotion/data/mqtt/properties.py +93 -78
- pymammotion/data/mqtt/status.py +18 -17
- pymammotion/event/event.py +27 -6
- pymammotion/homeassistant/__init__.py +3 -0
- pymammotion/homeassistant/mower_api.py +484 -0
- pymammotion/homeassistant/rtk_api.py +54 -0
- pymammotion/http/encryption.py +5 -6
- pymammotion/http/http.py +574 -28
- pymammotion/http/model/__init__.py +0 -0
- pymammotion/{aliyun/model/stream_subscription_response.py → http/model/camera_stream.py} +14 -2
- pymammotion/http/model/http.py +129 -4
- pymammotion/http/model/response_factory.py +61 -0
- pymammotion/http/model/rtk.py +16 -0
- pymammotion/mammotion/commands/abstract_message.py +7 -5
- pymammotion/mammotion/commands/mammotion_command.py +30 -1
- pymammotion/mammotion/commands/messages/basestation.py +43 -0
- pymammotion/mammotion/commands/messages/driver.py +61 -29
- pymammotion/mammotion/commands/messages/media.py +68 -15
- pymammotion/mammotion/commands/messages/navigation.py +61 -25
- pymammotion/mammotion/commands/messages/network.py +17 -23
- pymammotion/mammotion/commands/messages/ota.py +18 -18
- pymammotion/mammotion/commands/messages/system.py +32 -49
- pymammotion/mammotion/commands/messages/video.py +15 -16
- pymammotion/mammotion/devices/__init__.py +27 -3
- pymammotion/mammotion/devices/base.py +40 -131
- pymammotion/mammotion/devices/mammotion.py +436 -201
- pymammotion/mammotion/devices/mammotion_bluetooth.py +57 -47
- pymammotion/mammotion/devices/mammotion_cloud.py +134 -105
- pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
- pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
- pymammotion/mammotion/devices/managers/managers.py +81 -0
- pymammotion/mammotion/devices/mower_device.py +124 -0
- pymammotion/mammotion/devices/mower_manager.py +107 -0
- pymammotion/mammotion/devices/rtk_ble.py +89 -0
- pymammotion/mammotion/devices/rtk_cloud.py +113 -0
- pymammotion/mammotion/devices/rtk_device.py +50 -0
- pymammotion/mammotion/devices/rtk_manager.py +122 -0
- pymammotion/mqtt/__init__.py +2 -1
- pymammotion/mqtt/aliyun_mqtt.py +232 -0
- pymammotion/mqtt/linkkit/__init__.py +5 -0
- pymammotion/mqtt/linkkit/h2client.py +585 -0
- pymammotion/mqtt/linkkit/linkkit.py +3023 -0
- pymammotion/mqtt/mammotion_mqtt.py +176 -169
- pymammotion/mqtt/mqtt_models.py +66 -0
- pymammotion/proto/__init__.py +4839 -4
- pymammotion/proto/basestation.proto +8 -0
- pymammotion/proto/basestation_pb2.py +11 -9
- pymammotion/proto/basestation_pb2.pyi +16 -2
- pymammotion/proto/dev_net.proto +79 -55
- pymammotion/proto/dev_net_pb2.py +60 -56
- pymammotion/proto/dev_net_pb2.pyi +49 -6
- pymammotion/proto/luba_msg.proto +2 -1
- pymammotion/proto/luba_msg_pb2.py +6 -6
- pymammotion/proto/luba_msg_pb2.pyi +1 -0
- pymammotion/proto/luba_mul.proto +62 -1
- pymammotion/proto/luba_mul_pb2.py +38 -22
- pymammotion/proto/luba_mul_pb2.pyi +94 -7
- pymammotion/proto/mctrl_driver.proto +44 -4
- pymammotion/proto/mctrl_driver_pb2.py +26 -14
- pymammotion/proto/mctrl_driver_pb2.pyi +66 -11
- pymammotion/proto/mctrl_nav.proto +93 -52
- pymammotion/proto/mctrl_nav_pb2.py +75 -67
- pymammotion/proto/mctrl_nav_pb2.pyi +142 -56
- pymammotion/proto/mctrl_ota.proto +40 -2
- pymammotion/proto/mctrl_ota_pb2.py +23 -13
- pymammotion/proto/mctrl_ota_pb2.pyi +67 -4
- pymammotion/proto/mctrl_pept.proto +8 -3
- pymammotion/proto/mctrl_pept_pb2.py +8 -6
- pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
- pymammotion/proto/mctrl_sys.proto +325 -86
- pymammotion/proto/mctrl_sys_pb2.py +162 -98
- pymammotion/proto/mctrl_sys_pb2.pyi +451 -25
- pymammotion/proto/message_pool.py +3 -0
- pymammotion/proto/py.typed +0 -0
- pymammotion/utility/constant/device_constant.py +29 -5
- pymammotion/utility/datatype_converter.py +13 -12
- pymammotion/utility/device_config.py +522 -130
- pymammotion/utility/device_type.py +218 -21
- pymammotion/utility/map.py +238 -51
- pymammotion/utility/mur_mur_hash.py +159 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/METADATA +26 -31
- pymammotion-0.5.51.dist-info/RECORD +152 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/WHEEL +1 -1
- pymammotion/aliyun/cloud_service.py +0 -65
- pymammotion/data/model/plan.py +0 -58
- pymammotion/data/state_manager.py +0 -129
- pymammotion/proto/basestation.py +0 -59
- pymammotion/proto/common.py +0 -12
- pymammotion/proto/dev_net.py +0 -381
- pymammotion/proto/luba_msg.py +0 -81
- pymammotion/proto/luba_mul.py +0 -76
- pymammotion/proto/mctrl_driver.py +0 -100
- pymammotion/proto/mctrl_nav.py +0 -664
- pymammotion/proto/mctrl_ota.py +0 -48
- pymammotion/proto/mctrl_pept.py +0 -41
- pymammotion/proto/mctrl_sys.py +0 -574
- pymammotion-0.4.0a2.dist-info/RECORD +0 -131
- /pymammotion/http/{_init_.py → __init__.py} +0 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
+
from enum import StrEnum
|
|
2
3
|
|
|
3
4
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
4
5
|
|
|
5
6
|
|
|
7
|
+
class NetUsedType(StrEnum):
|
|
8
|
+
NONE = "NONE"
|
|
9
|
+
WIFI = "WIFI"
|
|
10
|
+
MNET = "MNET"
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
@dataclass
|
|
7
14
|
class ConnectData(DataClassORJSONMixin):
|
|
8
15
|
connect_type: int = 0
|
|
9
16
|
ble_rssi: int = 0
|
|
10
17
|
wifi_rssi: int = 0
|
|
11
|
-
|
|
18
|
+
link_type: int = 0
|
|
19
|
+
mnet_rssi: int = 0
|
|
20
|
+
mnet_inet: int = 0
|
|
21
|
+
used_net: str = "NONE"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# mnet_cfg:
|
|
12
25
|
|
|
13
26
|
|
|
14
27
|
@dataclass
|
|
@@ -28,6 +41,11 @@ class LockStateT(DataClassORJSONMixin):
|
|
|
28
41
|
lock_state: int = 0
|
|
29
42
|
|
|
30
43
|
|
|
44
|
+
@dataclass
|
|
45
|
+
class VioSurvivalInfo(DataClassORJSONMixin):
|
|
46
|
+
vio_survival_distance: float = 0.0
|
|
47
|
+
|
|
48
|
+
|
|
31
49
|
@dataclass
|
|
32
50
|
class DeviceData(DataClassORJSONMixin):
|
|
33
51
|
sys_status: int = 0
|
|
@@ -35,10 +53,11 @@ class DeviceData(DataClassORJSONMixin):
|
|
|
35
53
|
battery_val: int = 0
|
|
36
54
|
sensor_status: int = 0
|
|
37
55
|
last_status: int = 0
|
|
38
|
-
vslam_status: int = 0
|
|
39
56
|
sys_time_stamp: str = ""
|
|
40
|
-
|
|
57
|
+
vslam_status: int = 0
|
|
41
58
|
mnet_info: MnetInfo = field(default_factory=MnetInfo)
|
|
59
|
+
vio_survival_info: VioSurvivalInfo = field(default_factory=VioSurvivalInfo)
|
|
60
|
+
collector_status: CollectorStatus = field(default_factory=CollectorStatus)
|
|
42
61
|
lock_state: LockStateT = field(default_factory=LockStateT)
|
|
43
62
|
|
|
44
63
|
|
|
@@ -60,8 +79,15 @@ class LocationData(DataClassORJSONMixin):
|
|
|
60
79
|
bol_hash: str = ""
|
|
61
80
|
|
|
62
81
|
|
|
82
|
+
@dataclass
|
|
83
|
+
class BladeUsed(DataClassORJSONMixin):
|
|
84
|
+
blade_used_time: int = 0
|
|
85
|
+
blade_used_warn_time: int = 0
|
|
86
|
+
|
|
87
|
+
|
|
63
88
|
@dataclass
|
|
64
89
|
class Maintain(DataClassORJSONMixin):
|
|
90
|
+
blade_used_time: BladeUsed = field(default_factory=BladeUsed)
|
|
65
91
|
mileage: int = 0
|
|
66
92
|
work_time: int = 0
|
|
67
93
|
bat_cycles: int = 0
|
|
@@ -73,7 +99,7 @@ class VisionInfo(DataClassORJSONMixin):
|
|
|
73
99
|
y: float = 0.0
|
|
74
100
|
heading: float = 0.0
|
|
75
101
|
vio_state: int = 0
|
|
76
|
-
brightness: int =
|
|
102
|
+
brightness: int = 0
|
|
77
103
|
detect_feature_num: int = 0
|
|
78
104
|
track_feature_num: int = 0
|
|
79
105
|
|
|
@@ -121,6 +147,6 @@ class ReportData(DataClassORJSONMixin):
|
|
|
121
147
|
self.dev = DeviceData.from_dict(data.get("dev", self.dev.to_dict()))
|
|
122
148
|
self.rtk = RTKData.from_dict(data.get("rtk", self.rtk.to_dict()))
|
|
123
149
|
self.maintenance = Maintain.from_dict(data.get("maintain", self.maintenance.to_dict()))
|
|
124
|
-
self.vision_info = VisionInfo.from_dict(data.get("vio_to_app_info",
|
|
150
|
+
self.vision_info = VisionInfo.from_dict(data.get("vio_to_app_info", VisionInfo().to_dict()))
|
|
125
151
|
self.locations = locations
|
|
126
152
|
self.work = WorkData.from_dict(data.get("work", self.work.to_dict()))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""bidire_reqconver_path as a model."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class CurrentTaskSettings(DataClassORJSONMixin):
|
|
10
|
+
pver: int = 0
|
|
11
|
+
job_id: int = 0
|
|
12
|
+
job_ver: int = 0
|
|
13
|
+
job_mode: int = 0
|
|
14
|
+
sub_cmd: int = 0
|
|
15
|
+
edge_mode: int = 0
|
|
16
|
+
knife_height: int = 0
|
|
17
|
+
channel_width: int = 0
|
|
18
|
+
ultra_wave: int = 0
|
|
19
|
+
channel_mode: int = 0
|
|
20
|
+
toward: int = 0
|
|
21
|
+
speed: float = 0.0
|
|
22
|
+
zone_hashs: list[int] = field(default_factory=list)
|
|
23
|
+
path_hash: int = 0
|
|
24
|
+
reserved: str = ""
|
|
25
|
+
result: int = 0
|
|
26
|
+
toward_mode: int = 0
|
|
27
|
+
toward_included_angle: int = 0
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""Manage state from notifications into MowingDevice."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import betterproto2
|
|
9
|
+
|
|
10
|
+
from pymammotion.data.model.device import MowingDevice
|
|
11
|
+
from pymammotion.data.model.device_info import SideLight
|
|
12
|
+
from pymammotion.data.model.hash_list import (
|
|
13
|
+
AreaHashNameList,
|
|
14
|
+
MowPath,
|
|
15
|
+
NavGetCommData,
|
|
16
|
+
NavGetHashListData,
|
|
17
|
+
Plan,
|
|
18
|
+
SvgMessage,
|
|
19
|
+
)
|
|
20
|
+
from pymammotion.data.model.work import CurrentTaskSettings
|
|
21
|
+
from pymammotion.data.mqtt.event import ThingEventMessage
|
|
22
|
+
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
|
23
|
+
from pymammotion.data.mqtt.status import ThingStatusMessage
|
|
24
|
+
from pymammotion.event.event import DataEvent
|
|
25
|
+
from pymammotion.proto import (
|
|
26
|
+
AppGetAllAreaHashName,
|
|
27
|
+
AppGetCutterWorkMode,
|
|
28
|
+
AppSetCutterWorkMode,
|
|
29
|
+
CoverPathUploadT,
|
|
30
|
+
DeviceFwInfo,
|
|
31
|
+
DeviceProductTypeInfoT,
|
|
32
|
+
DrvDevInfoResp,
|
|
33
|
+
DrvDevInfoResult,
|
|
34
|
+
Getlamprsp,
|
|
35
|
+
GetNetworkInfoRsp,
|
|
36
|
+
LubaMsg,
|
|
37
|
+
NavGetCommDataAck,
|
|
38
|
+
NavGetHashListAck,
|
|
39
|
+
NavPlanJobSet,
|
|
40
|
+
NavReqCoverPath,
|
|
41
|
+
NavSysParamMsg,
|
|
42
|
+
NavUnableTimeSet,
|
|
43
|
+
SvgMessageAckT,
|
|
44
|
+
TimeCtrlLight,
|
|
45
|
+
WifiIotStatusReport,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class MowerStateManager:
|
|
52
|
+
"""Manage state."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, device: MowingDevice) -> None:
|
|
55
|
+
"""Initialize state manager with a device."""
|
|
56
|
+
self._device: MowingDevice = device
|
|
57
|
+
self.last_updated_at = datetime.now(UTC)
|
|
58
|
+
self.cloud_gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
|
|
59
|
+
self.cloud_get_commondata_ack_callback: (
|
|
60
|
+
Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None
|
|
61
|
+
) = None
|
|
62
|
+
self.cloud_on_notification_callback = DataEvent()
|
|
63
|
+
self.cloud_queue_command_callback = DataEvent()
|
|
64
|
+
|
|
65
|
+
self.cloud_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
|
|
66
|
+
self.ble_gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
|
|
67
|
+
self.ble_get_commondata_ack_callback: Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None = (
|
|
68
|
+
None
|
|
69
|
+
)
|
|
70
|
+
self.ble_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
|
|
71
|
+
self.ble_on_notification_callback = DataEvent()
|
|
72
|
+
self.ble_queue_command_callback = DataEvent()
|
|
73
|
+
|
|
74
|
+
self.properties_callback = DataEvent()
|
|
75
|
+
self.status_callback = DataEvent()
|
|
76
|
+
self.device_event_callback = DataEvent()
|
|
77
|
+
|
|
78
|
+
def get_device(self) -> MowingDevice:
|
|
79
|
+
"""Get device."""
|
|
80
|
+
return self._device
|
|
81
|
+
|
|
82
|
+
def set_device(self, device: MowingDevice) -> None:
|
|
83
|
+
"""Set device."""
|
|
84
|
+
self._device = device
|
|
85
|
+
|
|
86
|
+
async def properties(self, thing_properties: ThingPropertiesMessage) -> None:
|
|
87
|
+
"""Update device properties and invoke callback."""
|
|
88
|
+
# TODO update device based off thing properties
|
|
89
|
+
self._device.mqtt_properties = thing_properties
|
|
90
|
+
await self.on_properties_callback(thing_properties)
|
|
91
|
+
|
|
92
|
+
async def status(self, thing_status: ThingStatusMessage) -> None:
|
|
93
|
+
"""Update device status and invoke callback."""
|
|
94
|
+
if not self._device.online:
|
|
95
|
+
self._device.online = True
|
|
96
|
+
self._device.status_properties = thing_status
|
|
97
|
+
if self._device.mower_state.product_key == "":
|
|
98
|
+
self._device.mower_state.product_key = thing_status.params.productKey
|
|
99
|
+
await self.on_status_callback(thing_status)
|
|
100
|
+
|
|
101
|
+
async def device_event(self, device_event: ThingEventMessage) -> None:
|
|
102
|
+
"""Sets MQTT event and calls callback."""
|
|
103
|
+
self._device.mqtt_device_event = device_event
|
|
104
|
+
await self.on_device_event_callback(device_event)
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def online(self) -> bool:
|
|
108
|
+
"""Return online status."""
|
|
109
|
+
return self._device.online
|
|
110
|
+
|
|
111
|
+
@online.setter
|
|
112
|
+
def online(self, value: bool) -> None:
|
|
113
|
+
"""Set online status."""
|
|
114
|
+
self._device.online = value
|
|
115
|
+
|
|
116
|
+
async def gethash_ack_callback(self, msg: NavGetHashListAck) -> None:
|
|
117
|
+
"""Dispatch hash list acknowledgment to available callback."""
|
|
118
|
+
if self.cloud_gethash_ack_callback:
|
|
119
|
+
await self.cloud_gethash_ack_callback(msg)
|
|
120
|
+
elif self.ble_gethash_ack_callback:
|
|
121
|
+
await self.ble_gethash_ack_callback(msg)
|
|
122
|
+
|
|
123
|
+
async def on_notification_callback(self, res: tuple[str, Any | None]) -> None:
|
|
124
|
+
"""Dispatch notification to available callback."""
|
|
125
|
+
if self.cloud_on_notification_callback:
|
|
126
|
+
await self.cloud_on_notification_callback.data_event(res)
|
|
127
|
+
elif self.ble_on_notification_callback:
|
|
128
|
+
await self.ble_on_notification_callback.data_event(res)
|
|
129
|
+
|
|
130
|
+
async def on_properties_callback(self, thing_properties: ThingPropertiesMessage) -> None:
|
|
131
|
+
"""Call properties callback if it exists."""
|
|
132
|
+
if self.properties_callback:
|
|
133
|
+
await self.properties_callback.data_event(thing_properties)
|
|
134
|
+
|
|
135
|
+
async def on_status_callback(self, thing_status: ThingStatusMessage) -> None:
|
|
136
|
+
"""Execute the status callback if it is set."""
|
|
137
|
+
if self.status_callback:
|
|
138
|
+
await self.status_callback.data_event(thing_status)
|
|
139
|
+
|
|
140
|
+
async def on_device_event_callback(self, device_event: ThingEventMessage) -> None:
|
|
141
|
+
"""Executes the event callback if it is set."""
|
|
142
|
+
if self.device_event_callback:
|
|
143
|
+
await self.device_event_callback.data_event(device_event)
|
|
144
|
+
|
|
145
|
+
async def get_commondata_ack_callback(self, comm_data: NavGetCommDataAck | SvgMessageAckT) -> None:
|
|
146
|
+
"""Asynchronously calls the appropriate callback based on available handlers."""
|
|
147
|
+
if self.cloud_get_commondata_ack_callback:
|
|
148
|
+
await self.cloud_get_commondata_ack_callback(comm_data)
|
|
149
|
+
elif self.ble_get_commondata_ack_callback:
|
|
150
|
+
await self.ble_get_commondata_ack_callback(comm_data)
|
|
151
|
+
|
|
152
|
+
async def get_plan_callback(self, planjob: NavPlanJobSet) -> None:
|
|
153
|
+
"""Dispatch plan job to available callback."""
|
|
154
|
+
if self.cloud_get_plan_callback:
|
|
155
|
+
await self.cloud_get_plan_callback(planjob)
|
|
156
|
+
elif self.ble_get_plan_callback:
|
|
157
|
+
await self.ble_get_plan_callback(planjob)
|
|
158
|
+
|
|
159
|
+
async def notification(self, message: LubaMsg) -> None:
|
|
160
|
+
"""Handle protobuf notifications."""
|
|
161
|
+
res = betterproto2.which_one_of(message, "LubaSubMsg")
|
|
162
|
+
self.last_updated_at = datetime.now(UTC)
|
|
163
|
+
# additional catch all if we don't get a status update
|
|
164
|
+
if not self._device.online:
|
|
165
|
+
self._device.online = True
|
|
166
|
+
|
|
167
|
+
match res[0]:
|
|
168
|
+
case "nav":
|
|
169
|
+
await self._update_nav_data(message)
|
|
170
|
+
case "sys":
|
|
171
|
+
self._update_sys_data(message)
|
|
172
|
+
case "driver":
|
|
173
|
+
self._update_driver_data(message)
|
|
174
|
+
case "net":
|
|
175
|
+
self._update_net_data(message)
|
|
176
|
+
case "mul":
|
|
177
|
+
self._update_mul_data(message)
|
|
178
|
+
case "ota":
|
|
179
|
+
self._update_ota_data(message)
|
|
180
|
+
|
|
181
|
+
await self.on_notification_callback(res)
|
|
182
|
+
|
|
183
|
+
async def _update_nav_data(self, message: LubaMsg) -> None:
|
|
184
|
+
"""Update nav data."""
|
|
185
|
+
nav_msg = betterproto2.which_one_of(message.nav, "SubNavMsg")
|
|
186
|
+
match nav_msg[0]:
|
|
187
|
+
case "toapp_gethash_ack":
|
|
188
|
+
hashlist_ack: NavGetHashListAck = nav_msg[1]
|
|
189
|
+
self._device.map.update_root_hash_list(
|
|
190
|
+
NavGetHashListData.from_dict(hashlist_ack.to_dict(casing=betterproto2.Casing.SNAKE))
|
|
191
|
+
)
|
|
192
|
+
await self.gethash_ack_callback(nav_msg[1])
|
|
193
|
+
case "toapp_get_commondata_ack":
|
|
194
|
+
common_data: NavGetCommDataAck = nav_msg[1]
|
|
195
|
+
updated = self._device.map.update(
|
|
196
|
+
NavGetCommData.from_dict(common_data.to_dict(casing=betterproto2.Casing.SNAKE))
|
|
197
|
+
)
|
|
198
|
+
if updated:
|
|
199
|
+
await self.get_commondata_ack_callback(common_data)
|
|
200
|
+
case "cover_path_upload":
|
|
201
|
+
mow_path: CoverPathUploadT = nav_msg[1]
|
|
202
|
+
self._device.map.update_mow_path(MowPath.from_dict(mow_path.to_dict(casing=betterproto2.Casing.SNAKE)))
|
|
203
|
+
|
|
204
|
+
case "todev_planjob_set":
|
|
205
|
+
planjob: NavPlanJobSet = nav_msg[1]
|
|
206
|
+
self._device.map.update_plan(Plan.from_dict(planjob.to_dict(casing=betterproto2.Casing.SNAKE)))
|
|
207
|
+
await self.get_plan_callback(planjob)
|
|
208
|
+
|
|
209
|
+
case "toapp_svg_msg":
|
|
210
|
+
common_svg_data: SvgMessageAckT = nav_msg[1]
|
|
211
|
+
updated = self._device.map.update(
|
|
212
|
+
SvgMessage.from_dict(common_svg_data.to_dict(casing=betterproto2.Casing.SNAKE))
|
|
213
|
+
)
|
|
214
|
+
if updated:
|
|
215
|
+
await self.get_commondata_ack_callback(common_svg_data)
|
|
216
|
+
|
|
217
|
+
case "toapp_all_hash_name":
|
|
218
|
+
hash_names: AppGetAllAreaHashName = nav_msg[1]
|
|
219
|
+
converted_list = [AreaHashNameList(name=item.name, hash=item.hash) for item in hash_names.hashnames]
|
|
220
|
+
self._device.map.area_name = converted_list
|
|
221
|
+
|
|
222
|
+
case "bidire_reqconver_path":
|
|
223
|
+
work_settings: NavReqCoverPath = nav_msg[1]
|
|
224
|
+
self._device.work = CurrentTaskSettings.from_dict(
|
|
225
|
+
work_settings.to_dict(casing=betterproto2.Casing.SNAKE)
|
|
226
|
+
)
|
|
227
|
+
case "nav_sys_param_cmd":
|
|
228
|
+
settings: NavSysParamMsg = nav_msg[1]
|
|
229
|
+
match settings.id:
|
|
230
|
+
case 3:
|
|
231
|
+
self._device.mower_state.rain_detection = bool(settings.context)
|
|
232
|
+
case 6:
|
|
233
|
+
self._device.mower_state.turning_mode = settings.context
|
|
234
|
+
case 7:
|
|
235
|
+
self._device.mower_state.traversal_mode = settings.context
|
|
236
|
+
case "todev_unable_time_set":
|
|
237
|
+
nav_non_work_time: NavUnableTimeSet = nav_msg[1]
|
|
238
|
+
self._device.non_work_hours.non_work_sub_cmd = nav_non_work_time.sub_cmd
|
|
239
|
+
self._device.non_work_hours.start_time = nav_non_work_time.unable_start_time
|
|
240
|
+
self._device.non_work_hours.end_time = nav_non_work_time.unable_end_time
|
|
241
|
+
|
|
242
|
+
def _update_sys_data(self, message) -> None:
|
|
243
|
+
"""Update system."""
|
|
244
|
+
sys_msg = betterproto2.which_one_of(message.sys, "SubSysMsg")
|
|
245
|
+
match sys_msg[0]:
|
|
246
|
+
case "system_update_buf":
|
|
247
|
+
self._device.buffer(sys_msg[1])
|
|
248
|
+
case "toapp_report_data":
|
|
249
|
+
self._device.update_report_data(sys_msg[1])
|
|
250
|
+
case "mow_to_app_info":
|
|
251
|
+
self._device.mow_info(sys_msg[1])
|
|
252
|
+
case "system_tard_state_tunnel":
|
|
253
|
+
self._device.run_state_update(sys_msg[1])
|
|
254
|
+
case "todev_time_ctrl_light":
|
|
255
|
+
ctrl_light: TimeCtrlLight = sys_msg[1]
|
|
256
|
+
side_led: SideLight = SideLight.from_dict(ctrl_light.to_dict(casing=betterproto2.Casing.SNAKE))
|
|
257
|
+
self._device.mower_state.side_led = side_led
|
|
258
|
+
case "device_product_type_info":
|
|
259
|
+
device_product_type: DeviceProductTypeInfoT = sys_msg[1]
|
|
260
|
+
if device_product_type.main_product_type != "" or device_product_type.sub_product_type != "":
|
|
261
|
+
self._device.mower_state.model_id = device_product_type.main_product_type
|
|
262
|
+
self._device.mower_state.sub_model_id = device_product_type.sub_product_type
|
|
263
|
+
case "toapp_dev_fw_info":
|
|
264
|
+
device_fw_info: DeviceFwInfo = sys_msg[1]
|
|
265
|
+
self._device.device_firmwares.device_version = device_fw_info.version
|
|
266
|
+
self._device.mower_state.swversion = device_fw_info.version
|
|
267
|
+
|
|
268
|
+
def _update_driver_data(self, message) -> None:
|
|
269
|
+
"""Update driver data."""
|
|
270
|
+
driver_msg = betterproto2.which_one_of(message.driver, "SubDrvMsg")
|
|
271
|
+
match driver_msg[0]:
|
|
272
|
+
case "current_cutter_mode":
|
|
273
|
+
cutter_work_mode: AppGetCutterWorkMode = driver_msg[1]
|
|
274
|
+
self._device.mower_state.cutter_mode = cutter_work_mode.current_cutter_mode
|
|
275
|
+
self._device.mower_state.cutter_rpm = cutter_work_mode.current_cutter_rpm
|
|
276
|
+
case "cutter_mode_ctrl_by_hand":
|
|
277
|
+
cutter_work_mode_set: AppSetCutterWorkMode = driver_msg[1]
|
|
278
|
+
self._device.mower_state.cutter_mode = cutter_work_mode_set.cutter_mode
|
|
279
|
+
|
|
280
|
+
def _update_net_data(self, message) -> None:
|
|
281
|
+
"""Update network data."""
|
|
282
|
+
net_msg = betterproto2.which_one_of(message.net, "NetSubType")
|
|
283
|
+
match net_msg[0]:
|
|
284
|
+
case "toapp_wifi_iot_status":
|
|
285
|
+
wifi_iot_status: WifiIotStatusReport = net_msg[1]
|
|
286
|
+
self._device.mower_state.product_key = wifi_iot_status.productkey
|
|
287
|
+
case "toapp_devinfo_resp":
|
|
288
|
+
toapp_devinfo_resp: DrvDevInfoResp = net_msg[1]
|
|
289
|
+
for resp in toapp_devinfo_resp.resp_ids:
|
|
290
|
+
if resp.res == DrvDevInfoResult.DRV_RESULT_SUC and resp.id == 1 and resp.type == 6:
|
|
291
|
+
self._device.mower_state.swversion = resp.info
|
|
292
|
+
self._device.device_firmwares.device_version = resp.info
|
|
293
|
+
case "toapp_networkinfo_rsp":
|
|
294
|
+
get_network_info_resp: GetNetworkInfoRsp = net_msg[1]
|
|
295
|
+
self._device.mower_state.wifi_mac = get_network_info_resp.wifi_mac
|
|
296
|
+
|
|
297
|
+
def _update_mul_data(self, message) -> None:
|
|
298
|
+
"""Media and video states."""
|
|
299
|
+
mul_msg = betterproto2.which_one_of(message.mul, "SubMul")
|
|
300
|
+
match mul_msg[0]:
|
|
301
|
+
case "get_lamp_rsp":
|
|
302
|
+
lamp_resp: Getlamprsp = mul_msg[1]
|
|
303
|
+
self._device.mower_state.lamp_info.lamp_bright = lamp_resp.lamp_bright
|
|
304
|
+
if lamp_resp.get_ids in (1126, 1127):
|
|
305
|
+
self._device.mower_state.lamp_info.lamp_bright = lamp_resp.lamp_bright
|
|
306
|
+
self._device.mower_state.lamp_info.manual_light = bool(lamp_resp.lamp_manual_ctrl.value) or bool(
|
|
307
|
+
lamp_resp.lamp_bright
|
|
308
|
+
)
|
|
309
|
+
if lamp_resp.get_ids == 1123:
|
|
310
|
+
self._device.mower_state.lamp_info.lamp_bright = lamp_resp.lamp_bright
|
|
311
|
+
self._device.mower_state.lamp_info.night_light = bool(lamp_resp.lamp_ctrl.value) or bool(
|
|
312
|
+
lamp_resp.lamp_bright
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def _update_ota_data(self, message) -> None:
|
|
316
|
+
"""Update OTA data."""
|
pymammotion/data/mqtt/event.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from base64 import b64decode
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Any, Literal
|
|
3
|
+
from typing import Annotated, Any, Literal
|
|
4
4
|
|
|
5
5
|
from google.protobuf import json_format
|
|
6
6
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
7
|
-
from mashumaro.types import SerializableType
|
|
7
|
+
from mashumaro.types import Alias, SerializableType
|
|
8
8
|
|
|
9
9
|
from pymammotion.proto import luba_msg_pb2
|
|
10
10
|
|
|
@@ -73,34 +73,35 @@ class DeviceBizReqEventValue(DataClassORJSONMixin):
|
|
|
73
73
|
|
|
74
74
|
@dataclass
|
|
75
75
|
class GeneralParams(DataClassORJSONMixin):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
group_id_list: Annotated[list[str], Alias("groupIdList")]
|
|
77
|
+
group_id: Annotated[str, Alias("groupId")]
|
|
78
|
+
category_key: Annotated[Literal["LawnMower", "Tracker"], Alias("categoryKey")]
|
|
79
|
+
batch_id: Annotated[str, Alias("batchId")]
|
|
80
|
+
gmt_create: Annotated[int, Alias("gmtCreate")]
|
|
81
|
+
product_key: Annotated[str, Alias("productKey")]
|
|
82
82
|
type: str
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
device_name: Annotated[str, Alias("deviceName")]
|
|
84
|
+
iot_id: Annotated[str, Alias("iotId")]
|
|
85
|
+
check_level: Annotated[int, Alias("checkLevel")]
|
|
86
86
|
namespace: str
|
|
87
|
-
|
|
87
|
+
tenant_id: Annotated[str, Alias("tenantId")]
|
|
88
88
|
name: str
|
|
89
|
-
|
|
89
|
+
thing_type: Annotated[Literal["DEVICE"], Alias("thingType")]
|
|
90
90
|
time: int
|
|
91
|
-
|
|
91
|
+
tenant_instance_id: Annotated[str, Alias("tenantInstanceId")]
|
|
92
92
|
value: Any
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
94
|
+
# Optional fields
|
|
95
|
+
identifier: str | None = None
|
|
96
|
+
check_failed_data: Annotated[dict | None, Alias("checkFailedData")] = None
|
|
97
|
+
_tenant_id: Annotated[str | None, Alias("_tenantId")] = None
|
|
98
|
+
generate_time: Annotated[int | None, Alias("generateTime")] = None
|
|
99
|
+
jmsx_delivery_count: Annotated[int | None, Alias("JMSXDeliveryCount")] = None
|
|
100
|
+
qos: int | None = None
|
|
101
|
+
request_id: Annotated[str | None, Alias("requestId")] = None
|
|
102
|
+
_category_key: Annotated[str | None, Alias("_categoryKey")] = None
|
|
103
|
+
device_type: Annotated[str | None, Alias("deviceType")] = None
|
|
104
|
+
_trace_id: Annotated[str | None, Alias("_traceId")] = None
|
|
104
105
|
|
|
105
106
|
|
|
106
107
|
@dataclass
|
|
@@ -117,7 +118,7 @@ class DeviceNotificationEventParams(GeneralParams):
|
|
|
117
118
|
{'data': '{"localTime":1725159492000,"code":"1002"}'},
|
|
118
119
|
"""
|
|
119
120
|
|
|
120
|
-
identifier: Literal["device_notification_event", "device_warning_code_event"]
|
|
121
|
+
identifier: Literal["device_notification_event", "device_information_event", "device_warning_code_event"]
|
|
121
122
|
type: Literal["info"]
|
|
122
123
|
value: DeviceNotificationEventValue
|
|
123
124
|
|
|
@@ -142,11 +143,26 @@ class DeviceConfigurationRequestEvent(GeneralParams):
|
|
|
142
143
|
value: DeviceConfigurationRequestValue
|
|
143
144
|
|
|
144
145
|
|
|
146
|
+
@dataclass
|
|
147
|
+
class DeviceLogProgressEventParams(GeneralParams):
|
|
148
|
+
identifier: Literal["device_log_progress_event"]
|
|
149
|
+
type: Literal["info"]
|
|
150
|
+
value: DeviceNotificationEventValue
|
|
151
|
+
|
|
152
|
+
|
|
145
153
|
@dataclass
|
|
146
154
|
class ThingEventMessage(DataClassORJSONMixin):
|
|
147
155
|
method: Literal["thing.events", "thing.properties"]
|
|
148
156
|
id: str
|
|
149
|
-
params:
|
|
157
|
+
params: (
|
|
158
|
+
DeviceProtobufMsgEventParams
|
|
159
|
+
| DeviceWarningEventParams
|
|
160
|
+
| DeviceNotificationEventParams
|
|
161
|
+
| DeviceLogProgressEventParams
|
|
162
|
+
| DeviceBizReqEventParams
|
|
163
|
+
| DeviceConfigurationRequestEvent
|
|
164
|
+
| dict
|
|
165
|
+
)
|
|
150
166
|
version: Literal["1.0"]
|
|
151
167
|
|
|
152
168
|
@classmethod
|
|
@@ -157,7 +173,6 @@ class ThingEventMessage(DataClassORJSONMixin):
|
|
|
157
173
|
params_dict = payload.get("params", {})
|
|
158
174
|
version = payload.get("version")
|
|
159
175
|
|
|
160
|
-
# Determina quale classe usare per i parametri
|
|
161
176
|
identifier = params_dict.get("identifier")
|
|
162
177
|
if identifier is None:
|
|
163
178
|
"""Request configuration event."""
|
|
@@ -168,11 +183,41 @@ class ThingEventMessage(DataClassORJSONMixin):
|
|
|
168
183
|
params_obj = DeviceWarningEventParams.from_dict(params_dict)
|
|
169
184
|
elif identifier == "device_biz_req_event":
|
|
170
185
|
params_obj = DeviceBizReqEventParams.from_dict(params_dict)
|
|
186
|
+
elif identifier == "device_log_progress_event":
|
|
187
|
+
params_obj = DeviceLogProgressEventParams.from_dict(params_dict)
|
|
171
188
|
elif identifier == "device_config_req_event":
|
|
172
189
|
params_obj = payload.get("params", {})
|
|
173
|
-
elif
|
|
190
|
+
elif (
|
|
191
|
+
identifier == "device_notification_event"
|
|
192
|
+
or identifier == "device_warning_code_event"
|
|
193
|
+
or identifier == "device_information_event"
|
|
194
|
+
):
|
|
174
195
|
params_obj = DeviceNotificationEventParams.from_dict(params_dict)
|
|
175
196
|
else:
|
|
176
197
|
raise ValueError(f"Unknown identifier: {identifier} {params_dict}")
|
|
177
198
|
|
|
178
199
|
return cls(method=method, id=event_id, params=params_obj, version=version)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass
|
|
203
|
+
class MammotionProtoMsgParams(DataClassORJSONMixin, SerializableType):
|
|
204
|
+
value: DeviceProtobufMsgEventValue
|
|
205
|
+
iot_id: str = ""
|
|
206
|
+
product_key: str = ""
|
|
207
|
+
device_name: str = ""
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def _deserialize(cls, d: dict[str, Any]) -> "MammotionProtoMsgParams":
|
|
211
|
+
"""Override from_dict to allow dict manipulation before conversion."""
|
|
212
|
+
proto: str = d["content"]
|
|
213
|
+
|
|
214
|
+
return cls(value=DeviceProtobufMsgEventValue(content=proto))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@dataclass
|
|
218
|
+
class MammotionEventMessage(DataClassORJSONMixin):
|
|
219
|
+
id: str
|
|
220
|
+
version: str
|
|
221
|
+
sys: dict
|
|
222
|
+
params: MammotionProtoMsgParams
|
|
223
|
+
method: str
|