pymammotion 0.3.8__py3-none-any.whl → 0.4.0__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.
Files changed (69) hide show
  1. pymammotion/__init__.py +2 -2
  2. pymammotion/aliyun/cloud_gateway.py +12 -9
  3. pymammotion/aliyun/model/aep_response.py +1 -2
  4. pymammotion/aliyun/model/dev_by_account_response.py +7 -8
  5. pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
  6. pymammotion/aliyun/model/regions_response.py +3 -3
  7. pymammotion/aliyun/model/session_by_authcode_response.py +1 -2
  8. pymammotion/aliyun/model/stream_subscription_response.py +1 -2
  9. pymammotion/bluetooth/ble.py +5 -5
  10. pymammotion/bluetooth/ble_message.py +9 -13
  11. pymammotion/data/model/device.py +31 -228
  12. pymammotion/data/model/device_config.py +0 -10
  13. pymammotion/data/model/device_info.py +13 -0
  14. pymammotion/data/model/device_limits.py +49 -0
  15. pymammotion/data/model/generate_route_information.py +1 -1
  16. pymammotion/data/model/hash_list.py +6 -2
  17. pymammotion/data/model/plan.py +0 -3
  18. pymammotion/data/model/raw_data.py +215 -0
  19. pymammotion/data/model/region_data.py +10 -11
  20. pymammotion/data/model/report_info.py +1 -1
  21. pymammotion/data/mqtt/event.py +18 -14
  22. pymammotion/data/mqtt/properties.py +1 -1
  23. pymammotion/data/mqtt/status.py +1 -1
  24. pymammotion/data/state_manager.py +83 -23
  25. pymammotion/http/encryption.py +220 -0
  26. pymammotion/http/http.py +92 -39
  27. pymammotion/http/model/http.py +2 -2
  28. pymammotion/mammotion/commands/abstract_message.py +2 -2
  29. pymammotion/mammotion/commands/messages/driver.py +28 -21
  30. pymammotion/mammotion/commands/messages/media.py +10 -14
  31. pymammotion/mammotion/commands/messages/navigation.py +14 -11
  32. pymammotion/mammotion/commands/messages/network.py +15 -12
  33. pymammotion/mammotion/commands/messages/ota.py +9 -14
  34. pymammotion/mammotion/commands/messages/system.py +27 -24
  35. pymammotion/mammotion/commands/messages/video.py +9 -14
  36. pymammotion/mammotion/devices/base.py +7 -14
  37. pymammotion/mammotion/devices/mammotion.py +22 -13
  38. pymammotion/mammotion/devices/mammotion_bluetooth.py +15 -4
  39. pymammotion/mammotion/devices/mammotion_cloud.py +30 -12
  40. pymammotion/mqtt/linkkit/__init__.py +5 -0
  41. pymammotion/mqtt/linkkit/h2client.py +585 -0
  42. pymammotion/mqtt/linkkit/linkkit.py +3020 -0
  43. pymammotion/mqtt/mammotion_mqtt.py +13 -9
  44. pymammotion/proto/__init__.py +2176 -1
  45. pymammotion/proto/luba_mul.proto +1 -0
  46. pymammotion/proto/luba_mul_pb2.py +8 -8
  47. pymammotion/proto/luba_mul_pb2.pyi +1 -0
  48. pymammotion/proto/mctrl_nav_pb2.py +69 -67
  49. pymammotion/proto/mctrl_nav_pb2.pyi +13 -5
  50. pymammotion/proto/mctrl_sys_pb2.py +41 -37
  51. pymammotion/proto/mctrl_sys_pb2.pyi +34 -11
  52. pymammotion/utility/constant/device_constant.py +14 -5
  53. pymammotion/utility/device_config.py +754 -0
  54. pymammotion/utility/device_type.py +64 -16
  55. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/METADATA +9 -9
  56. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/RECORD +58 -62
  57. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/WHEEL +1 -1
  58. pymammotion/aliyun/cloud_service.py +0 -65
  59. pymammotion/proto/basestation.py +0 -59
  60. pymammotion/proto/common.py +0 -12
  61. pymammotion/proto/dev_net.py +0 -381
  62. pymammotion/proto/luba_msg.py +0 -81
  63. pymammotion/proto/luba_mul.py +0 -76
  64. pymammotion/proto/mctrl_driver.py +0 -100
  65. pymammotion/proto/mctrl_nav.py +0 -664
  66. pymammotion/proto/mctrl_ota.py +0 -48
  67. pymammotion/proto/mctrl_pept.py +0 -41
  68. pymammotion/proto/mctrl_sys.py +0 -574
  69. {pymammotion-0.3.8.dist-info → pymammotion-0.4.0.dist-info}/LICENSE +0 -0
pymammotion/__init__.py CHANGED
@@ -12,7 +12,7 @@ from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
12
12
 
13
13
  # works outside HA on its own
14
14
  from pymammotion.bluetooth.ble import MammotionBLE
15
- from pymammotion.http.http import MammotionHTTP, connect_http
15
+ from pymammotion.http.http import MammotionHTTP
16
16
 
17
17
  # TODO make a working device that will work outside HA too.
18
18
  from pymammotion.mqtt import MammotionMQTT
@@ -20,7 +20,7 @@ from pymammotion.mqtt import MammotionMQTT
20
20
  logger = logging.getLogger(__name__)
21
21
 
22
22
 
23
- __all__ = ["MammotionBLE", "MammotionHTTP", "connect_http", "MammotionMQTT", "logger"]
23
+ __all__ = ["MammotionBLE", "MammotionHTTP", "MammotionMQTT", "logger"]
24
24
 
25
25
 
26
26
  # TODO provide interface to pick between mqtt/cloud/bluetooth
@@ -5,11 +5,11 @@ import hashlib
5
5
  import hmac
6
6
  import itertools
7
7
  import json
8
+ from logging import getLogger
8
9
  import random
9
10
  import string
10
11
  import time
11
12
  import uuid
12
- from logging import getLogger
13
13
 
14
14
  from aiohttp import ClientSession
15
15
  from alibabacloud_iot_api_gateway.client import Client
@@ -19,14 +19,10 @@ from alibabacloud_tea_util.models import RuntimeOptions
19
19
 
20
20
  from pymammotion.aliyun.model.aep_response import AepResponse
21
21
  from pymammotion.aliyun.model.connect_response import ConnectResponse
22
- from pymammotion.aliyun.model.dev_by_account_response import (
23
- ListingDevByAccountResponse,
24
- )
22
+ from pymammotion.aliyun.model.dev_by_account_response import ListingDevByAccountResponse
25
23
  from pymammotion.aliyun.model.login_by_oauth_response import LoginByOAuthResponse
26
24
  from pymammotion.aliyun.model.regions_response import RegionResponse
27
- from pymammotion.aliyun.model.session_by_authcode_response import (
28
- SessionByAuthCodeResponse,
29
- )
25
+ from pymammotion.aliyun.model.session_by_authcode_response import SessionByAuthCodeResponse
30
26
  from pymammotion.const import ALIYUN_DOMAIN, APP_KEY, APP_SECRET, APP_VERSION
31
27
  from pymammotion.http.http import MammotionHTTP
32
28
  from pymammotion.utility.datatype_converter import DatatypeConverter
@@ -58,6 +54,10 @@ class DeviceOfflineException(Exception):
58
54
  """Raise exception when device is offline."""
59
55
 
60
56
 
57
+ class GatewayTimeoutException(Exception):
58
+ """Raise exception when the gateway times out."""
59
+
60
+
61
61
  class LoginException(Exception):
62
62
  """Raise exception when library cannot log in."""
63
63
 
@@ -657,7 +657,7 @@ class CloudIOTGateway:
657
657
  iot_token=self._session_by_authcode_response.data.iotToken,
658
658
  )
659
659
 
660
- # TODO move to using InvokeThingServiceRequest()
660
+ # TODO move to using InvokeThingServiceRequest()
661
661
 
662
662
  message_id = str(uuid.uuid4())
663
663
 
@@ -689,13 +689,16 @@ class CloudIOTGateway:
689
689
  str(response_body_dict.get("code")),
690
690
  str(response_body_dict.get("message")),
691
691
  )
692
+ if response_body_dict.get("code") == 20056:
693
+ logger.debug("Gateway timeout.")
694
+ raise GatewayTimeoutException(response_body_dict.get("code"))
695
+
692
696
  if response_body_dict.get("code") == 29003:
693
697
  logger.debug(self._session_by_authcode_response.data.identityId)
694
698
  self.sign_out()
695
699
  raise SetupException(response_body_dict.get("code"))
696
700
  if response_body_dict.get("code") == 6205:
697
701
  raise DeviceOfflineException(response_body_dict.get("code"))
698
- """Device is offline."""
699
702
 
700
703
  return message_id
701
704
 
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import Optional
3
2
 
4
3
  from mashumaro.config import BaseConfig
5
4
  from mashumaro.mixins.orjson import DataClassORJSONMixin
@@ -16,7 +15,7 @@ class DeviceData(DataClassORJSONMixin):
16
15
  class AepResponse(DataClassORJSONMixin):
17
16
  code: int
18
17
  data: DeviceData
19
- id: Optional[str] = None
18
+ id: str | None = None
20
19
 
21
20
  class Config(BaseConfig):
22
21
  omit_default = True
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import Optional
3
2
 
4
3
  from mashumaro.config import BaseConfig
5
4
  from mashumaro.mixins.orjson import DataClassORJSONMixin
@@ -23,11 +22,11 @@ class Device(DataClassORJSONMixin):
23
22
  identityId: str
24
23
  thingType: str
25
24
  status: int
26
- nickName: Optional[str] = None
27
- description: Optional[str] = None
28
- productImage: Optional[str] = None
29
- categoryImage: Optional[str] = None
30
- productModel: Optional[str] = None
25
+ nickName: str | None = None
26
+ description: str | None = None
27
+ productImage: str | None = None
28
+ categoryImage: str | None = None
29
+ productModel: str | None = None
31
30
 
32
31
  class Config(BaseConfig):
33
32
  omit_default = True
@@ -44,5 +43,5 @@ class Data(DataClassORJSONMixin):
44
43
  @dataclass
45
44
  class ListingDevByAccountResponse(DataClassORJSONMixin):
46
45
  code: int
47
- data: Optional[Data]
48
- id: Optional[str] = None
46
+ data: Data | None
47
+ id: str | None = None
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import Optional
3
2
 
4
3
  from mashumaro.mixins.orjson import DataClassORJSONMixin
5
4
 
@@ -18,7 +17,7 @@ class OpenAccount(DataClassORJSONMixin):
18
17
  domainId: int
19
18
  enableDevice: str
20
19
  status: int
21
- country: Optional[str] = None
20
+ country: str | None = None
22
21
 
23
22
 
24
23
  @dataclass
@@ -54,7 +53,7 @@ class InnerData(DataClassORJSONMixin):
54
53
  subCode: int
55
54
  message: str
56
55
  successful: str
57
- deviceId: Optional[str] = None
56
+ deviceId: str | None = None
58
57
 
59
58
 
60
59
  @dataclass
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import Optional, TypeVar
2
+ from typing import TypeVar
3
3
 
4
4
  from mashumaro.config import BaseConfig
5
5
  from mashumaro.mixins.orjson import DataClassORJSONMixin
@@ -22,8 +22,8 @@ class RegionResponseData(DataClassORJSONMixin):
22
22
  class RegionResponse(DataClassORJSONMixin):
23
23
  data: RegionResponseData
24
24
  code: int
25
- id: Optional[str] = None
26
- msg: Optional[str] = None
25
+ id: str | None = None
26
+ msg: str | None = None
27
27
 
28
28
  class Config(BaseConfig):
29
29
  omit_default = True
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import Optional
3
2
 
4
3
  from mashumaro.mixins.orjson import DataClassORJSONMixin
5
4
 
@@ -16,4 +15,4 @@ class SessionOauthToken(DataClassORJSONMixin):
16
15
  @dataclass
17
16
  class SessionByAuthCodeResponse(DataClassORJSONMixin):
18
17
  code: int
19
- data: Optional[SessionOauthToken] = None
18
+ data: SessionOauthToken | None = None
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import List
3
2
 
4
3
  from mashumaro.mixins.orjson import DataClassORJSONMixin
5
4
 
@@ -13,7 +12,7 @@ class Camera(DataClassORJSONMixin):
13
12
  @dataclass
14
13
  class StreamSubscriptionResponse(DataClassORJSONMixin):
15
14
  appid: str
16
- cameras: List[Camera]
15
+ cameras: list[Camera]
17
16
  channelName: str
18
17
  token: str
19
18
  uid: int
@@ -1,14 +1,13 @@
1
1
  from bleak import BleakClient, BleakScanner, BLEDevice
2
2
  from bleak.backends.characteristic import BleakGATTCharacteristic
3
3
 
4
- from pymammotion.bluetooth.const import (
5
- SERVICE_CHANGED_CHARACTERISTIC,
6
- UUID_NOTIFICATION_CHARACTERISTIC,
7
- )
4
+ from pymammotion.bluetooth.const import SERVICE_CHANGED_CHARACTERISTIC, UUID_NOTIFICATION_CHARACTERISTIC
8
5
  from pymammotion.event.event import BleNotificationEvent
9
6
 
10
7
 
11
8
  class MammotionBLE:
9
+ """Class for basic ble connections to mowers."""
10
+
12
11
  client: BleakClient
13
12
 
14
13
  def __init__(self, bleEvt: BleNotificationEvent) -> None:
@@ -60,5 +59,6 @@ class MammotionBLE:
60
59
  await self.client.start_notify(UUID_NOTIFICATION_CHARACTERISTIC, self.notification_handler)
61
60
  await self.client.start_notify(SERVICE_CHANGED_CHARACTERISTIC, self.service_changed_handler)
62
61
 
63
- def getClient(self):
62
+ def get_client(self):
63
+ """Returns the ble client."""
64
64
  return self.client
@@ -1,12 +1,11 @@
1
+ from asyncio import sleep
2
+ from io import BytesIO
1
3
  import itertools
2
4
  import json
3
5
  import logging
4
6
  import queue
5
7
  import sys
6
8
  import time
7
- from asyncio import sleep
8
- from io import BytesIO
9
- from typing import Union
10
9
 
11
10
  from bleak import BleakClient
12
11
  from jsonic.serializable import serialize
@@ -17,10 +16,7 @@ from pymammotion.bluetooth.data.framectrldata import FrameCtrlData
17
16
  from pymammotion.bluetooth.data.notifydata import BlufiNotifyData
18
17
  from pymammotion.bluetooth.model.atomic_integer import AtomicInteger
19
18
  from pymammotion.data.model.execute_boarder import ExecuteBorder
20
- from pymammotion.proto import (
21
- dev_net_pb2,
22
- )
23
- from pymammotion.proto.luba_msg import LubaMsg, MsgAttr, MsgCmdType, MsgDevice
19
+ from pymammotion.proto import DevNet, DrvDevInfoReq, LubaMsg, MsgAttr, MsgCmdType, MsgDevice
24
20
  from pymammotion.utility.constant.device_constant import bleOrderCmd
25
21
 
26
22
  _LOGGER = logging.getLogger(__name__)
@@ -320,7 +316,7 @@ class BleMessage:
320
316
  hash_map = {"ctrl": 1}
321
317
  await self.post_custom_data(self.get_json_string(bleOrderCmd.bleAlive, hash_map))
322
318
 
323
- def get_json_string(self, cmd: int, hash_map: dict[str, object]) -> str:
319
+ def get_json_string(self, cmd: int, hash_map: dict[str, int]) -> str:
324
320
  jSONObject = {}
325
321
  try:
326
322
  jSONObject["cmd"] = cmd
@@ -334,7 +330,7 @@ class BleMessage:
334
330
  print(e)
335
331
  return ""
336
332
 
337
- def clearNotification(self) -> None:
333
+ def clear_notification(self) -> None:
338
334
  self.notification = None
339
335
  self.notification = BlufiNotifyData()
340
336
 
@@ -344,14 +340,14 @@ class BleMessage:
344
340
  async def send_device_info(self) -> None:
345
341
  """Currently not called"""
346
342
  luba_msg = LubaMsg(
347
- msgtype=MsgCmdType.MSG_CMD_TYPE_ESP,
343
+ msgtype=MsgCmdType.ESP,
348
344
  sender=MsgDevice.DEV_MOBILEAPP,
349
345
  rcver=MsgDevice.DEV_COMM_ESP,
350
- msgattr=MsgAttr.MSG_ATTR_REQ,
346
+ msgattr=MsgAttr.REQ,
351
347
  seqs=1,
352
348
  version=1,
353
349
  subtype=1,
354
- net=dev_net_pb2.DevNet(todev_ble_sync=1, todev_devinfo_req=dev_net_pb2.DrvDevInfoReq()),
350
+ net=DevNet(todev_ble_sync=1, todev_devinfo_req=DrvDevInfoReq()),
355
351
  )
356
352
  byte_arr = luba_msg.SerializeToString()
357
353
  await self.post_custom_data_bytes(byte_arr)
@@ -656,7 +652,7 @@ class BleMessage:
656
652
  return byteOS.getvalue()
657
653
 
658
654
  @staticmethod
659
- def calc_crc(initial: int, data: Union[bytes, bytearray]) -> int:
655
+ def calc_crc(initial: int, data: bytes | bytearray) -> int:
660
656
  """Calculate CRC value for given initial value and byte array.
661
657
 
662
658
  Args:
@@ -1,32 +1,18 @@
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_config import DeviceLimits
11
- from pymammotion.data.model.device_info import MowerInfo
9
+ from pymammotion.data.model.device_info import DeviceFirmwares, MowerInfo
12
10
  from pymammotion.data.model.location import Location
13
11
  from pymammotion.data.model.report_info import ReportData
14
12
  from pymammotion.data.mqtt.properties import ThingPropertiesMessage
13
+ from pymammotion.data.mqtt.status import ThingStatusMessage
15
14
  from pymammotion.http.model.http import ErrorInfo
16
- from pymammotion.proto.dev_net import DevNet
17
- from pymammotion.proto.luba_msg import LubaMsg
18
- from pymammotion.proto.luba_mul import SocMul
19
- from pymammotion.proto.mctrl_driver import MctlDriver
20
- from pymammotion.proto.mctrl_nav import MctlNav
21
- from pymammotion.proto.mctrl_ota import MctlOta
22
- from pymammotion.proto.mctrl_pept import MctlPept
23
- from pymammotion.proto.mctrl_sys import (
24
- MctlSys,
25
- MowToAppInfoT,
26
- ReportInfoData,
27
- SystemRapidStateTunnelMsg,
28
- SystemUpdateBufMsg,
29
- )
15
+ from pymammotion.proto import DeviceFwInfo, MowToAppInfoT, ReportInfoData, SystemRapidStateTunnelMsg, SystemUpdateBufMsg
30
16
  from pymammotion.utility.constant import WorkMode
31
17
  from pymammotion.utility.conversions import parse_double
32
18
  from pymammotion.utility.map import CoordinateConverter
@@ -36,29 +22,19 @@ from pymammotion.utility.map import CoordinateConverter
36
22
  class MowingDevice(DataClassORJSONMixin):
37
23
  """Wraps the betterproto dataclasses, so we can bypass the groups for keeping all data."""
38
24
 
25
+ online: bool = True
39
26
  mower_state: MowerInfo = field(default_factory=MowerInfo)
40
27
  mqtt_properties: ThingPropertiesMessage | None = None
28
+ status_properties: ThingStatusMessage | None = None
41
29
  map: HashList = field(default_factory=HashList)
42
30
  location: Location = field(default_factory=Location)
43
31
  mowing_state: RapidState = field(default_factory=RapidState)
44
32
  report_data: ReportData = field(default_factory=ReportData)
33
+ device_firmwares: DeviceFirmwares = field(default_factory=DeviceFirmwares)
45
34
  err_code_list: list = field(default_factory=list)
46
- err_code_list_time: Optional[list] = field(default_factory=list)
47
- limits: DeviceLimits = field(default_factory=DeviceLimits)
48
- device: Optional[LubaMsg] = field(default_factory=LubaMsg)
35
+ err_code_list_time: list | None = field(default_factory=list)
49
36
  error_codes: dict[str, ErrorInfo] = field(default_factory=dict)
50
37
 
51
- @classmethod
52
- def from_raw(cls, raw: dict) -> "MowingDevice":
53
- """Take in raw data to hold in the betterproto dataclass."""
54
- mowing_device = MowingDevice()
55
- mowing_device.device = LubaMsg(**raw)
56
- return mowing_device
57
-
58
- def update_raw(self, raw: dict) -> None:
59
- """Update the raw LubaMsg data."""
60
- self.device = LubaMsg(**raw)
61
-
62
38
  def buffer(self, buffer_list: SystemUpdateBufMsg) -> None:
63
39
  """Update the device based on which buffer we are reading from."""
64
40
  match buffer_list.update_buf_data[0]:
@@ -106,7 +82,7 @@ class MowingDevice(DataClassORJSONMixin):
106
82
  for index, location in enumerate(toapp_report_data.locations):
107
83
  if index == 0 and location.real_pos_y != 0:
108
84
  self.location.position_type = location.pos_type
109
- self.location.orientation = location.real_toward / 10000
85
+ self.location.orientation = int(location.real_toward / 10000)
110
86
  self.location.device = coordinate_converter.enu_to_lla(
111
87
  parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)
112
88
  )
@@ -115,13 +91,16 @@ class MowingDevice(DataClassORJSONMixin):
115
91
  location.zone_hash if self.report_data.dev.sys_status == WorkMode.MODE_WORKING else 0
116
92
  )
117
93
 
94
+ if toapp_report_data.fw_info:
95
+ self.update_device_firmwares(toapp_report_data.fw_info)
96
+
118
97
  self.report_data.update(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE))
119
98
 
120
99
  def run_state_update(self, rapid_state: SystemRapidStateTunnelMsg) -> None:
121
100
  coordinate_converter = CoordinateConverter(self.location.RTK.latitude, self.location.RTK.longitude)
122
101
  self.mowing_state = RapidState().from_raw(rapid_state.rapid_state_data)
123
102
  self.location.position_type = self.mowing_state.pos_type
124
- self.location.orientation = self.mowing_state.toward / 10000
103
+ self.location.orientation = int(self.mowing_state.toward / 10000)
125
104
  self.location.device = coordinate_converter.enu_to_lla(
126
105
  parse_double(self.mowing_state.pos_y, 4.0), parse_double(self.mowing_state.pos_x, 4.0)
127
106
  )
@@ -136,198 +115,22 @@ class MowingDevice(DataClassORJSONMixin):
136
115
  def report_missing_data(self) -> None:
137
116
  """Report missing data so we can refetch it."""
138
117
 
139
- @property
140
- def net(self):
141
- """Will return a wrapped betterproto of net."""
142
- return DevNetData(net=self.device.net)
143
-
144
- @property
145
- def sys(self):
146
- """Will return a wrapped betterproto of sys."""
147
- return SysData(sys=self.device.sys)
148
-
149
- @property
150
- def nav(self):
151
- """Will return a wrapped betterproto of nav."""
152
- return NavData(nav=self.device.nav)
153
-
154
- @property
155
- def driver(self):
156
- """Will return a wrapped betterproto of driver."""
157
- return DriverData(driver=self.device.driver)
158
-
159
- @property
160
- def mul(self):
161
- """Will return a wrapped betterproto of mul."""
162
- return MulData(mul=self.device.mul)
163
-
164
- @property
165
- def ota(self):
166
- """Will return a wrapped betterproto of ota."""
167
- return OtaData(ota=self.device.ota)
168
-
169
- @property
170
- def pept(self):
171
- """Will return a wrapped betterproto of pept."""
172
- return PeptData(pept=self.device.pept)
173
-
174
-
175
- @dataclass
176
- class DevNetData(DataClassORJSONMixin):
177
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
178
-
179
- net: dict
180
-
181
- def __init__(self, net: DevNet) -> None:
182
- if isinstance(net, dict):
183
- self.net = net
184
- else:
185
- self.net = net.to_dict()
186
-
187
- def __getattr__(self, item):
188
- """Intercept call to get net in dict and return a betterproto dataclass."""
189
- if self.net.get(item) is None:
190
- return DevNet().__getattribute__(item)
191
-
192
- if not isinstance(self.net.get(item), dict):
193
- return self.net.get(item)
194
-
195
- return DevNet().__getattribute__(item).from_dict(value=self.net.get(item))
196
-
197
-
198
- @dataclass
199
- class SysData(DataClassORJSONMixin):
200
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
201
-
202
- sys: dict
203
-
204
- def __init__(self, sys: MctlSys) -> None:
205
- if isinstance(sys, dict):
206
- self.sys = sys
207
- else:
208
- self.sys = sys.to_dict()
209
-
210
- def __getattr__(self, item: str):
211
- """Intercept call to get sys in dict and return a betterproto dataclass."""
212
- if self.sys.get(item) is None:
213
- return MctlSys().__getattribute__(item)
214
-
215
- if not isinstance(self.sys.get(item), dict):
216
- return self.sys.get(item)
217
-
218
- return MctlSys().__getattribute__(item).from_dict(value=self.sys.get(item))
219
-
220
-
221
- @dataclass
222
- class NavData(DataClassORJSONMixin):
223
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
224
-
225
- nav: dict
226
-
227
- def __init__(self, nav: MctlNav) -> None:
228
- if isinstance(nav, dict):
229
- self.nav = nav
230
- else:
231
- self.nav = nav.to_dict()
232
-
233
- def __getattr__(self, item: str):
234
- """Intercept call to get nav in dict and return a betterproto dataclass."""
235
- if self.nav.get(item) is None:
236
- return MctlNav().__getattribute__(item)
237
-
238
- if not isinstance(self.nav.get(item), dict):
239
- return self.nav.get(item)
240
-
241
- return MctlNav().__getattribute__(item).from_dict(value=self.nav.get(item))
242
-
243
-
244
- @dataclass
245
- class DriverData(DataClassORJSONMixin):
246
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
247
-
248
- driver: dict
249
-
250
- def __init__(self, driver: MctlDriver) -> None:
251
- if isinstance(driver, dict):
252
- self.driver = driver
253
- else:
254
- self.driver = driver.to_dict()
255
-
256
- def __getattr__(self, item: str):
257
- """Intercept call to get driver in dict and return a betterproto dataclass."""
258
- if self.driver.get(item) is None:
259
- return MctlDriver().__getattribute__(item)
260
-
261
- if not isinstance(self.driver.get(item), dict):
262
- return self.driver.get(item)
263
-
264
- return MctlDriver().__getattribute__(item).from_dict(value=self.driver.get(item))
265
-
266
-
267
- @dataclass
268
- class MulData(DataClassORJSONMixin):
269
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
270
-
271
- mul: dict
272
-
273
- def __init__(self, mul: SocMul) -> None:
274
- if isinstance(mul, dict):
275
- self.mul = mul
276
- else:
277
- self.mul = mul.to_dict()
278
-
279
- def __getattr__(self, item: str):
280
- """Intercept call to get mul in dict and return a betterproto dataclass."""
281
- if self.mul.get(item) is None:
282
- return SocMul().__getattribute__(item)
283
-
284
- if not isinstance(self.mul.get(item), dict):
285
- return self.mul.get(item)
286
-
287
- return SocMul().__getattribute__(item).from_dict(value=self.mul.get(item))
288
-
289
-
290
- @dataclass
291
- class OtaData(DataClassORJSONMixin):
292
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
293
-
294
- ota: dict
295
-
296
- def __init__(self, ota: MctlOta) -> None:
297
- if isinstance(ota, dict):
298
- self.ota = ota
299
- else:
300
- self.ota = ota.to_dict()
301
-
302
- def __getattr__(self, item: str):
303
- """Intercept call to get ota in dict and return a betterproto dataclass."""
304
- if self.ota.get(item) is None:
305
- return MctlOta().__getattribute__(item)
306
-
307
- if not isinstance(self.ota.get(item), dict):
308
- return self.ota.get(item)
309
-
310
- return MctlOta().__getattribute__(item).from_dict(value=self.ota.get(item))
311
-
312
-
313
- @dataclass
314
- class PeptData(DataClassORJSONMixin):
315
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
316
-
317
- pept: dict
318
-
319
- def __init__(self, pept: MctlPept) -> None:
320
- if isinstance(pept, dict):
321
- self.pept = pept
322
- else:
323
- self.pept = pept.to_dict()
324
-
325
- def __getattr__(self, item: str):
326
- """Intercept call to get pept in dict and return a betterproto dataclass."""
327
- if self.pept.get(item) is None:
328
- return MctlPept().__getattribute__(item)
329
-
330
- if not isinstance(self.pept.get(item), dict):
331
- return self.pept.get(item)
332
-
333
- return MctlPept().__getattribute__(item).from_dict(value=self.pept.get(item))
118
+ def update_device_firmwares(self, fw_info: DeviceFwInfo) -> None:
119
+ """Sets firmware versions on all parts of the robot or RTK."""
120
+ for mod in fw_info.mod:
121
+ match mod.type:
122
+ case 1:
123
+ self.device_firmwares.main_controller = mod.version
124
+ case 3:
125
+ self.device_firmwares.left_motor_driver = mod.version
126
+ case 4:
127
+ self.device_firmwares.right_motor_driver = mod.version
128
+ case 5:
129
+ self.device_firmwares.rtk_rover_station = mod.version
130
+ case 101:
131
+ # RTK main board
132
+ self.device_firmwares.main_controller = mod.version
133
+ case 102:
134
+ self.device_firmwares.rtk_version = mod.version
135
+ case 103:
136
+ self.device_firmwares.lora_version = mod.version
@@ -5,16 +5,6 @@ from mashumaro.mixins.orjson import DataClassORJSONMixin
5
5
  from pymammotion.utility.device_type import DeviceType
6
6
 
7
7
 
8
- @dataclass
9
- class DeviceLimits(DataClassORJSONMixin):
10
- blade_height_min: int = 30
11
- blade_height_max: int = 100
12
- working_speed_min: float = 0.2
13
- working_speed_max: float = 1.2
14
- working_path_min: int = 15
15
- working_path_max: int = 35
16
-
17
-
18
8
  @dataclass
19
9
  class OperationSettings(DataClassORJSONMixin):
20
10
  """Operation settings for a device."""
@@ -23,3 +23,16 @@ class MowerInfo(DataClassORJSONMixin):
23
23
  swversion: str = ""
24
24
  product_key: str = ""
25
25
  model_id: str = ""
26
+ sub_model_id: str = ""
27
+
28
+
29
+ @dataclass
30
+ class DeviceFirmwares(DataClassORJSONMixin):
31
+ device_version: str = ""
32
+ left_motor_driver: str = ""
33
+ lora_version: str = ""
34
+ main_controller: str = ""
35
+ model_name: str = ""
36
+ right_motor_driver: str = ""
37
+ rtk_rover_station: str = ""
38
+ rtk_version: str = ""