pymammotion 0.4.0a5__py3-none-any.whl → 0.4.0a7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymammotion/aliyun/cloud_gateway.py +9 -1
- pymammotion/data/model/device.py +7 -4
- pymammotion/data/model/device_info.py +2 -1
- pymammotion/data/mqtt/event.py +18 -14
- pymammotion/data/mqtt/status.py +1 -1
- pymammotion/data/state_manager.py +23 -11
- pymammotion/mammotion/devices/mammotion_cloud.py +15 -7
- pymammotion/mqtt/linkkit/__init__.py +1 -1
- pymammotion/mqtt/linkkit/h2client.py +171 -135
- pymammotion/mqtt/linkkit/linkkit.py +448 -451
- pymammotion/mqtt/mammotion_mqtt.py +11 -7
- pymammotion/utility/device_config.py +657 -25
- pymammotion/utility/device_type.py +13 -1
- {pymammotion-0.4.0a5.dist-info → pymammotion-0.4.0a7.dist-info}/METADATA +3 -1
- {pymammotion-0.4.0a5.dist-info → pymammotion-0.4.0a7.dist-info}/RECORD +17 -17
- {pymammotion-0.4.0a5.dist-info → pymammotion-0.4.0a7.dist-info}/LICENSE +0 -0
- {pymammotion-0.4.0a5.dist-info → pymammotion-0.4.0a7.dist-info}/WHEEL +0 -0
@@ -58,6 +58,10 @@ class DeviceOfflineException(Exception):
|
|
58
58
|
"""Raise exception when device is offline."""
|
59
59
|
|
60
60
|
|
61
|
+
class GatewayTimeoutException(Exception):
|
62
|
+
"""Raise exception when the gateway times out."""
|
63
|
+
|
64
|
+
|
61
65
|
class LoginException(Exception):
|
62
66
|
"""Raise exception when library cannot log in."""
|
63
67
|
|
@@ -657,7 +661,7 @@ class CloudIOTGateway:
|
|
657
661
|
iot_token=self._session_by_authcode_response.data.iotToken,
|
658
662
|
)
|
659
663
|
|
660
|
-
# TODO move to using
|
664
|
+
# TODO move to using InvokeThingServiceRequest()
|
661
665
|
|
662
666
|
message_id = str(uuid.uuid4())
|
663
667
|
|
@@ -689,6 +693,10 @@ class CloudIOTGateway:
|
|
689
693
|
str(response_body_dict.get("code")),
|
690
694
|
str(response_body_dict.get("message")),
|
691
695
|
)
|
696
|
+
if response_body_dict.get("code") == 20056:
|
697
|
+
logger.debug("Gateway timeout.")
|
698
|
+
raise GatewayTimeoutException(response_body_dict.get("code"))
|
699
|
+
|
692
700
|
if response_body_dict.get("code") == 29003:
|
693
701
|
logger.debug(self._session_by_authcode_response.data.identityId)
|
694
702
|
self.sign_out()
|
pymammotion/data/model/device.py
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
"""MowingDevice class to wrap around the betterproto dataclasses."""
|
2
2
|
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import Optional
|
5
4
|
|
6
5
|
import betterproto
|
7
6
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
8
7
|
|
9
8
|
from pymammotion.data.model import HashList, RapidState
|
10
|
-
from pymammotion.data.model.device_info import
|
9
|
+
from pymammotion.data.model.device_info import DeviceFirmwares, MowerInfo
|
11
10
|
from pymammotion.data.model.location import Location
|
12
11
|
from pymammotion.data.model.report_info import ReportData
|
13
12
|
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
13
|
+
from pymammotion.data.mqtt.status import ThingStatusMessage
|
14
14
|
from pymammotion.http.model.http import ErrorInfo
|
15
15
|
from pymammotion.proto.mctrl_sys import (
|
16
|
+
DeviceFwInfo,
|
16
17
|
MowToAppInfoT,
|
17
18
|
ReportInfoData,
|
18
19
|
SystemRapidStateTunnelMsg,
|
19
|
-
SystemUpdateBufMsg,
|
20
|
+
SystemUpdateBufMsg,
|
20
21
|
)
|
21
22
|
from pymammotion.utility.constant import WorkMode
|
22
23
|
from pymammotion.utility.conversions import parse_double
|
@@ -27,15 +28,17 @@ from pymammotion.utility.map import CoordinateConverter
|
|
27
28
|
class MowingDevice(DataClassORJSONMixin):
|
28
29
|
"""Wraps the betterproto dataclasses, so we can bypass the groups for keeping all data."""
|
29
30
|
|
31
|
+
online: bool = True
|
30
32
|
mower_state: MowerInfo = field(default_factory=MowerInfo)
|
31
33
|
mqtt_properties: ThingPropertiesMessage | None = None
|
34
|
+
status_properties: ThingStatusMessage | None = None
|
32
35
|
map: HashList = field(default_factory=HashList)
|
33
36
|
location: Location = field(default_factory=Location)
|
34
37
|
mowing_state: RapidState = field(default_factory=RapidState)
|
35
38
|
report_data: ReportData = field(default_factory=ReportData)
|
36
39
|
device_firmwares: DeviceFirmwares = field(default_factory=DeviceFirmwares)
|
37
40
|
err_code_list: list = field(default_factory=list)
|
38
|
-
err_code_list_time:
|
41
|
+
err_code_list_time: list | None = field(default_factory=list)
|
39
42
|
error_codes: dict[str, ErrorInfo] = field(default_factory=dict)
|
40
43
|
|
41
44
|
def buffer(self, buffer_list: SystemUpdateBufMsg) -> None:
|
@@ -24,6 +24,7 @@ class MowerInfo(DataClassORJSONMixin):
|
|
24
24
|
product_key: str = ""
|
25
25
|
model_id: str = ""
|
26
26
|
|
27
|
+
|
27
28
|
@dataclass
|
28
29
|
class DeviceFirmwares(DataClassORJSONMixin):
|
29
30
|
device_version: str = ""
|
@@ -34,4 +35,4 @@ class DeviceFirmwares(DataClassORJSONMixin):
|
|
34
35
|
right_motor_driver: str = ""
|
35
36
|
rtk_rover_station: str = ""
|
36
37
|
rtk_version: str = ""
|
37
|
-
version: str = ""
|
38
|
+
version: str = ""
|
pymammotion/data/mqtt/event.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from base64 import b64decode
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any, Literal
|
3
|
+
from typing import Any, Literal
|
4
4
|
|
5
5
|
from google.protobuf import json_format
|
6
6
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
@@ -91,16 +91,16 @@ class GeneralParams(DataClassORJSONMixin):
|
|
91
91
|
tenantInstanceId: str
|
92
92
|
value: Any
|
93
93
|
|
94
|
-
identifier:
|
95
|
-
checkFailedData:
|
96
|
-
_tenantId:
|
97
|
-
generateTime:
|
98
|
-
JMSXDeliveryCount:
|
99
|
-
qos:
|
100
|
-
requestId:
|
101
|
-
_categoryKey:
|
102
|
-
deviceType:
|
103
|
-
_traceId:
|
94
|
+
identifier: str | None = None
|
95
|
+
checkFailedData: dict | None = None
|
96
|
+
_tenantId: str | None = None
|
97
|
+
generateTime: int | None = None
|
98
|
+
JMSXDeliveryCount: int | None = None
|
99
|
+
qos: int | None = None
|
100
|
+
requestId: str | None = None
|
101
|
+
_categoryKey: str | None = None
|
102
|
+
deviceType: str | None = None
|
103
|
+
_traceId: str | None = None
|
104
104
|
|
105
105
|
|
106
106
|
@dataclass
|
@@ -117,7 +117,7 @@ class DeviceNotificationEventParams(GeneralParams):
|
|
117
117
|
{'data': '{"localTime":1725159492000,"code":"1002"}'},
|
118
118
|
"""
|
119
119
|
|
120
|
-
identifier: Literal["device_notification_event", "device_warning_code_event"]
|
120
|
+
identifier: Literal["device_notification_event", "device_information_event", "device_warning_code_event"]
|
121
121
|
type: Literal["info"]
|
122
122
|
value: DeviceNotificationEventValue
|
123
123
|
|
@@ -146,7 +146,7 @@ class DeviceConfigurationRequestEvent(GeneralParams):
|
|
146
146
|
class ThingEventMessage(DataClassORJSONMixin):
|
147
147
|
method: Literal["thing.events", "thing.properties"]
|
148
148
|
id: str
|
149
|
-
params:
|
149
|
+
params: DeviceProtobufMsgEventParams | DeviceWarningEventParams | dict
|
150
150
|
version: Literal["1.0"]
|
151
151
|
|
152
152
|
@classmethod
|
@@ -170,7 +170,11 @@ class ThingEventMessage(DataClassORJSONMixin):
|
|
170
170
|
params_obj = DeviceBizReqEventParams.from_dict(params_dict)
|
171
171
|
elif identifier == "device_config_req_event":
|
172
172
|
params_obj = payload.get("params", {})
|
173
|
-
elif
|
173
|
+
elif (
|
174
|
+
identifier == "device_notification_event"
|
175
|
+
or identifier == "device_warning_code_event"
|
176
|
+
or identifier == "device_information_event"
|
177
|
+
):
|
174
178
|
params_obj = DeviceNotificationEventParams.from_dict(params_dict)
|
175
179
|
else:
|
176
180
|
raise ValueError(f"Unknown identifier: {identifier} {params_dict}")
|
pymammotion/data/mqtt/status.py
CHANGED
@@ -26,7 +26,7 @@ class Status(DataClassORJSONMixin):
|
|
26
26
|
@dataclass
|
27
27
|
class Params(DataClassORJSONMixin):
|
28
28
|
groupIdList: list[GroupIdListItem]
|
29
|
-
netType: Literal["NET_WIFI"]
|
29
|
+
netType: Literal["NET_WIFI", "NET_MNET"]
|
30
30
|
activeTime: int
|
31
31
|
ip: str
|
32
32
|
aliyunCommodityCode: Literal["iothub_senior"]
|
@@ -1,8 +1,9 @@
|
|
1
1
|
"""Manage state from notifications into MowingDevice."""
|
2
2
|
|
3
3
|
import logging
|
4
|
+
from collections.abc import Awaitable, Callable
|
4
5
|
from datetime import datetime
|
5
|
-
from typing import Any
|
6
|
+
from typing import Any
|
6
7
|
|
7
8
|
import betterproto
|
8
9
|
|
@@ -10,7 +11,8 @@ from pymammotion.data.model.device import MowingDevice
|
|
10
11
|
from pymammotion.data.model.device_info import SideLight
|
11
12
|
from pymammotion.data.model.hash_list import AreaHashNameList
|
12
13
|
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
13
|
-
from pymammotion.
|
14
|
+
from pymammotion.data.mqtt.status import ThingStatusMessage
|
15
|
+
from pymammotion.proto.dev_net import DrvDevInfoResp, WifiIotStatusReport
|
14
16
|
from pymammotion.proto.luba_msg import LubaMsg
|
15
17
|
from pymammotion.proto.mctrl_nav import AppGetAllAreaHashName, NavGetCommDataAck, NavGetHashListAck, SvgMessageAckT
|
16
18
|
from pymammotion.proto.mctrl_sys import DeviceProductTypeInfoT, TimeCtrlLight
|
@@ -26,12 +28,10 @@ class StateManager:
|
|
26
28
|
|
27
29
|
def __init__(self, device: MowingDevice) -> None:
|
28
30
|
self._device = device
|
29
|
-
self.gethash_ack_callback:
|
30
|
-
self.get_commondata_ack_callback:
|
31
|
-
|
32
|
-
|
33
|
-
self.on_notification_callback: Optional[Callable[[tuple[str, Any | None]], Awaitable[None]]] = None
|
34
|
-
self.queue_command_callback: Optional[Callable[[str, dict[str, Any]], Awaitable[bytes]]] = None
|
31
|
+
self.gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
|
32
|
+
self.get_commondata_ack_callback: Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None = None
|
33
|
+
self.on_notification_callback: Callable[[tuple[str, Any | None]], Awaitable[None]] | None = None
|
34
|
+
self.queue_command_callback: Callable[[str, dict[str, Any]], Awaitable[bytes]] | None = None
|
35
35
|
self.last_updated_at = datetime.now()
|
36
36
|
|
37
37
|
def get_device(self) -> MowingDevice:
|
@@ -43,8 +43,20 @@ class StateManager:
|
|
43
43
|
self._device = device
|
44
44
|
|
45
45
|
async def properties(self, properties: ThingPropertiesMessage) -> None:
|
46
|
-
|
47
|
-
|
46
|
+
self._device.mqtt_properties = properties
|
47
|
+
|
48
|
+
async def status(self, status: ThingStatusMessage) -> None:
|
49
|
+
if not self._device.online:
|
50
|
+
self._device.online = True
|
51
|
+
self._device.status_properties = status
|
52
|
+
|
53
|
+
@property
|
54
|
+
def online(self) -> bool:
|
55
|
+
return self._device.online
|
56
|
+
|
57
|
+
@online.setter
|
58
|
+
def online(self, value: bool) -> None:
|
59
|
+
self._device.online = value
|
48
60
|
|
49
61
|
async def notification(self, message: LubaMsg) -> None:
|
50
62
|
"""Handle protobuf notifications."""
|
@@ -124,7 +136,7 @@ class StateManager:
|
|
124
136
|
case "toapp_devinfo_resp":
|
125
137
|
toapp_devinfo_resp: DrvDevInfoResp = net_msg[1]
|
126
138
|
for resp in toapp_devinfo_resp.resp_ids:
|
127
|
-
if resp.res
|
139
|
+
if resp.res == "DRV_RESULT_SUC":
|
128
140
|
self._device.mower_state.swversion = resp.info
|
129
141
|
self._device.device_firmwares.device_version = resp.info
|
130
142
|
|
@@ -4,7 +4,8 @@ import json
|
|
4
4
|
import logging
|
5
5
|
from asyncio import TimerHandle
|
6
6
|
from collections import deque
|
7
|
-
from
|
7
|
+
from collections.abc import Awaitable, Callable
|
8
|
+
from typing import Any, cast
|
8
9
|
|
9
10
|
import betterproto
|
10
11
|
|
@@ -13,6 +14,7 @@ from pymammotion.aliyun.cloud_gateway import DeviceOfflineException
|
|
13
14
|
from pymammotion.aliyun.model.dev_by_account_response import Device
|
14
15
|
from pymammotion.data.mqtt.event import ThingEventMessage
|
15
16
|
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
17
|
+
from pymammotion.data.mqtt.status import ThingStatusMessage
|
16
18
|
from pymammotion.data.state_manager import StateManager
|
17
19
|
from pymammotion.event.event import DataEvent
|
18
20
|
from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
|
@@ -35,6 +37,7 @@ class MammotionCloud:
|
|
35
37
|
self._waiting_queue = deque()
|
36
38
|
self.mqtt_message_event = DataEvent()
|
37
39
|
self.mqtt_properties_event = DataEvent()
|
40
|
+
self.mqtt_status_event = DataEvent()
|
38
41
|
self.on_ready_event = DataEvent()
|
39
42
|
self.on_disconnected_event = DataEvent()
|
40
43
|
self.on_connected_event = DataEvent()
|
@@ -116,7 +119,7 @@ class MammotionCloud:
|
|
116
119
|
json_str = json.dumps(payload)
|
117
120
|
payload = json.loads(json_str)
|
118
121
|
|
119
|
-
await self.
|
122
|
+
await self._parse_mqtt_response(topic, payload)
|
120
123
|
|
121
124
|
async def _parse_mqtt_response(self, topic: str, payload: dict) -> None:
|
122
125
|
"""Parse the MQTT response."""
|
@@ -134,10 +137,9 @@ class MammotionCloud:
|
|
134
137
|
if event.method == "thing.properties":
|
135
138
|
await self.mqtt_properties_event.data_event(event)
|
136
139
|
_LOGGER.debug(event)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
await self._parse_mqtt_response(topic=topic, payload=payload)
|
140
|
+
elif topic.endswith("/app/down/thing/status"):
|
141
|
+
status = ThingStatusMessage.from_dict(payload)
|
142
|
+
await self.mqtt_status_event.data_event(status)
|
141
143
|
|
142
144
|
def _disconnect(self) -> None:
|
143
145
|
"""Disconnect the MQTT client."""
|
@@ -156,7 +158,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
156
158
|
super().__init__(state_manager, cloud_device)
|
157
159
|
self._ble_sync_task: TimerHandle | None = None
|
158
160
|
self.stopped = False
|
159
|
-
self.on_ready_callback:
|
161
|
+
self.on_ready_callback: Callable[[], Awaitable[None]] | None = None
|
160
162
|
self.loop = asyncio.get_event_loop()
|
161
163
|
self._mqtt = mqtt
|
162
164
|
self.iot_id = cloud_device.iotId
|
@@ -166,6 +168,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
166
168
|
self.currentID = ""
|
167
169
|
self._mqtt.mqtt_message_event.add_subscribers(self._parse_message_for_device)
|
168
170
|
self._mqtt.mqtt_properties_event.add_subscribers(self._parse_message_properties_for_device)
|
171
|
+
self._mqtt.mqtt_status_event.add_subscribers(self._parse_message_status_for_device)
|
169
172
|
self._mqtt.on_ready_event.add_subscribers(self.on_ready)
|
170
173
|
self._mqtt.on_disconnected_event.add_subscribers(self.on_disconnect)
|
171
174
|
self._mqtt.on_connected_event.add_subscribers(self.on_connect)
|
@@ -278,6 +281,11 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
278
281
|
return
|
279
282
|
self.state_manager.properties(event)
|
280
283
|
|
284
|
+
async def _parse_message_status_for_device(self, status: ThingStatusMessage) -> None:
|
285
|
+
if status.params.iotId != self.iot_id:
|
286
|
+
return
|
287
|
+
self.state_manager.status(status)
|
288
|
+
|
281
289
|
async def _parse_message_for_device(self, event: ThingEventMessage) -> None:
|
282
290
|
_LOGGER.debug("_parse_message_for_device")
|
283
291
|
params = event.params
|