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.
@@ -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 InvokeThingServiceRequest()
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()
@@ -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 MowerInfo, DeviceFirmwares
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, DeviceFwInfo,
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: Optional[list] = field(default_factory=list)
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 = ""
@@ -1,6 +1,6 @@
1
1
  from base64 import b64decode
2
2
  from dataclasses import dataclass
3
- from typing import Any, Literal, Optional, Union
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: Optional[str] = None
95
- checkFailedData: Optional[dict] = None
96
- _tenantId: Optional[str] = None
97
- generateTime: Optional[int] = None
98
- JMSXDeliveryCount: Optional[int] = None
99
- qos: Optional[int] = None
100
- requestId: Optional[str] = None
101
- _categoryKey: Optional[str] = None
102
- deviceType: Optional[str] = None
103
- _traceId: Optional[str] = None
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: Union[DeviceProtobufMsgEventParams, DeviceWarningEventParams, dict]
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 identifier == "device_notification_event" or identifier == "device_warning_code_event":
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}")
@@ -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, Awaitable, Callable, Optional
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.proto.dev_net import WifiIotStatusReport, DrvDevInfoResp
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: Optional[Callable[[NavGetHashListAck], Awaitable[None]]] = None
30
- self.get_commondata_ack_callback: Optional[Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]]] = (
31
- None
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
- params = properties.params
47
- self._device.mqtt_properties = params
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 is "DRV_RESULT_SUC":
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 typing import Any, Awaitable, Callable, Optional, cast
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._handle_mqtt_message(topic, payload)
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
- async def _handle_mqtt_message(self, topic: str, payload: dict) -> None:
139
- """Async handler for incoming MQTT messages."""
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: Optional[Callable[[], Awaitable[None]]] = None
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
@@ -2,4 +2,4 @@
2
2
 
3
3
  from .linkkit import LinkKit
4
4
 
5
- __all__ = ["LinkKit"]
5
+ __all__ = ["LinkKit"]