pymammotion 0.4.56__tar.gz → 0.5.0__tar.gz
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-0.4.56 → pymammotion-0.5.0}/PKG-INFO +2 -2
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/cloud_gateway.py +13 -3
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/device.py +2 -1
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/mqtt/properties.py +56 -44
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/state_manager.py +22 -8
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/event/event.py +27 -6
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/http/http.py +43 -8
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/http/model/http.py +46 -1
- pymammotion-0.5.0/pymammotion/http/model/response_factory.py +39 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/abstract_message.py +1 -4
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/devices/mammotion.py +30 -9
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/devices/mammotion_bluetooth.py +21 -10
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/devices/mammotion_cloud.py +13 -4
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/__init__.py +2 -6
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/basestation.proto +8 -0
- pymammotion-0.5.0/pymammotion/proto/basestation_pb2.py +35 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/basestation_pb2.pyi +16 -2
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/dev_net.proto +2 -0
- pymammotion-0.5.0/pymammotion/proto/dev_net_pb2.py +111 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/dev_net_pb2.pyi +8 -4
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/luba_mul.proto +2 -2
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/luba_mul_pb2.py +15 -15
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/luba_mul_pb2.pyi +1 -1
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_driver.proto +23 -4
- pymammotion-0.5.0/pymammotion/proto/mctrl_driver_pb2.py +57 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_driver_pb2.pyi +38 -10
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_nav.proto +18 -1
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_nav_pb2.py +5 -3
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_nav_pb2.pyi +34 -2
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_pept.proto +6 -1
- pymammotion-0.5.0/pymammotion/proto/mctrl_pept_pb2.py +33 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_sys.proto +82 -9
- pymammotion-0.5.0/pymammotion/proto/mctrl_sys_pb2.py +206 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_sys_pb2.pyi +151 -34
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/device_type.py +3 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pyproject.toml +4 -4
- pymammotion-0.4.56/pymammotion/proto/basestation_pb2.py +0 -33
- pymammotion-0.4.56/pymammotion/proto/dev_net_pb2.py +0 -111
- pymammotion-0.4.56/pymammotion/proto/mctrl_driver_pb2.py +0 -51
- pymammotion-0.4.56/pymammotion/proto/mctrl_pept_pb2.py +0 -31
- pymammotion-0.4.56/pymammotion/proto/mctrl_sys_pb2.py +0 -190
- {pymammotion-0.4.56 → pymammotion-0.5.0}/LICENSE +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/README.md +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/client.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/model/aep_response.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/model/connect_response.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/model/dev_by_account_response.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/model/login_by_oauth_response.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/model/regions_response.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/model/session_by_authcode_response.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/regions.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/tea/core.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/aliyun/tmp_constant.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/ble.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/ble_message.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/const.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/data/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/data/convert.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/data/framectrldata.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/data/notifydata.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/model/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/bluetooth/model/atomic_integer.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/const.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/account.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/device_config.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/device_info.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/device_limits.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/enums.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/excute_boarder_params.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/execute_boarder.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/generate_route_information.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/hash_list.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/location.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/mowing_modes.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/rapid_state.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/raw_data.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/region_data.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/report_info.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/model/work.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/mqtt/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/mqtt/event.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/data/mqtt/status.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/event/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/http/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/http/_init_.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/http/encryption.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/http/model/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/http/model/camera_stream.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/mammotion_command.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/driver.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/media.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/navigation.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/network.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/ota.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/system.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/commands/messages/video.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/control/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/control/joystick.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/devices/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mammotion/devices/base.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mqtt/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mqtt/linkkit/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mqtt/linkkit/h2client.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mqtt/linkkit/linkkit.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mqtt/mammotion_future.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/mqtt/mammotion_mqtt.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/common.proto +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/common_pb2.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/common_pb2.pyi +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/luba_msg.proto +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/luba_msg_pb2.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/luba_msg_pb2.pyi +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_ota.proto +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_ota_pb2.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/proto/mctrl_ota_pb2.pyi +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/py.typed +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/constant/__init__.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/constant/device_constant.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/conversions.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/datatype_converter.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/device_config.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/map.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/movement.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/mur_mur_hash.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/periodic.py +0 -0
- {pymammotion-0.4.56 → pymammotion-0.5.0}/pymammotion/utility/rocker_util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pymammotion
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary:
|
|
5
5
|
License: GPL-3.0
|
|
6
6
|
Author: Michael Arthur
|
|
@@ -18,7 +18,7 @@ Requires-Dist: alicloud-gateway-iot (>=1.0.0,<2.0.0)
|
|
|
18
18
|
Requires-Dist: async-timeout (>=4.0.3,<5.0.0)
|
|
19
19
|
Requires-Dist: betterproto (>=1.2.5,<2.0.0)
|
|
20
20
|
Requires-Dist: bleak (>=0.21.0)
|
|
21
|
-
Requires-Dist: bleak-retry-connector (>=3.5.0
|
|
21
|
+
Requires-Dist: bleak-retry-connector (>=3.5.0)
|
|
22
22
|
Requires-Dist: crcmod (>=1.7,<2.0)
|
|
23
23
|
Requires-Dist: cryptography (>=43.0.1)
|
|
24
24
|
Requires-Dist: jsonic (>=1.0.0,<2.0.0)
|
|
@@ -15,8 +15,8 @@ from aiohttp import ClientSession, ConnectionTimeoutError
|
|
|
15
15
|
from alibabacloud_iot_api_gateway.models import CommonParams, Config, IoTApiRequest
|
|
16
16
|
from alibabacloud_tea_util.client import Client as UtilClient
|
|
17
17
|
from alibabacloud_tea_util.models import RuntimeOptions
|
|
18
|
-
from Tea.exceptions import UnretryableException
|
|
19
18
|
from orjson.orjson import JSONDecodeError
|
|
19
|
+
from Tea.exceptions import UnretryableException
|
|
20
20
|
|
|
21
21
|
from pymammotion.aliyun.client import Client
|
|
22
22
|
from pymammotion.aliyun.model.aep_response import AepResponse
|
|
@@ -65,6 +65,14 @@ class DeviceOfflineException(Exception):
|
|
|
65
65
|
self.iot_id = args[1]
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
class FailedRequestException(Exception):
|
|
69
|
+
"""Raise exception when request response is bad."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, *args: object) -> None:
|
|
72
|
+
super().__init__(args)
|
|
73
|
+
self.iot_id = args[0]
|
|
74
|
+
|
|
75
|
+
|
|
68
76
|
class NoConnectionException(UnretryableException):
|
|
69
77
|
"""Raise exception when device is unreachable."""
|
|
70
78
|
|
|
@@ -145,7 +153,7 @@ class CloudIOTGateway:
|
|
|
145
153
|
return json.loads(response_body_str) if response_body_str is not None else {}
|
|
146
154
|
except JSONDecodeError:
|
|
147
155
|
logger.error("Couldn't decode message %s", response_body_str)
|
|
148
|
-
return {}
|
|
156
|
+
return {'code': 22000}
|
|
149
157
|
|
|
150
158
|
def sign(self, data):
|
|
151
159
|
"""Generate signature for the given data."""
|
|
@@ -739,7 +747,6 @@ class CloudIOTGateway:
|
|
|
739
747
|
logger.debug(response.body)
|
|
740
748
|
logger.debug(iot_id)
|
|
741
749
|
|
|
742
|
-
|
|
743
750
|
response_body_str = response.body.decode("utf-8")
|
|
744
751
|
response_body_dict = self.parse_json_response(response_body_str)
|
|
745
752
|
|
|
@@ -749,6 +756,9 @@ class CloudIOTGateway:
|
|
|
749
756
|
str(response_body_dict.get("code")),
|
|
750
757
|
str(response_body_dict.get("message")),
|
|
751
758
|
)
|
|
759
|
+
if response_body_dict.get("code") == 22000:
|
|
760
|
+
logger.error(response)
|
|
761
|
+
raise FailedRequestException(iot_id)
|
|
752
762
|
if response_body_dict.get("code") == 20056:
|
|
753
763
|
logger.debug("Gateway timeout.")
|
|
754
764
|
raise GatewayTimeoutException(response_body_dict.get("code"), iot_id)
|
|
@@ -12,7 +12,7 @@ from pymammotion.data.model.report_info import ReportData
|
|
|
12
12
|
from pymammotion.data.model.work import CurrentTaskSettings
|
|
13
13
|
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
|
14
14
|
from pymammotion.data.mqtt.status import ThingStatusMessage
|
|
15
|
-
from pymammotion.http.model.http import ErrorInfo
|
|
15
|
+
from pymammotion.http.model.http import CheckDeviceVersion, ErrorInfo
|
|
16
16
|
from pymammotion.proto import DeviceFwInfo, MowToAppInfoT, ReportInfoData, SystemRapidStateTunnelMsg, SystemUpdateBufMsg
|
|
17
17
|
from pymammotion.utility.constant import WorkMode
|
|
18
18
|
from pymammotion.utility.conversions import parse_double
|
|
@@ -26,6 +26,7 @@ class MowingDevice(DataClassORJSONMixin):
|
|
|
26
26
|
name: str = ""
|
|
27
27
|
online: bool = True
|
|
28
28
|
enabled: bool = True
|
|
29
|
+
update_check: CheckDeviceVersion = field(default_factory=CheckDeviceVersion)
|
|
29
30
|
mower_state: MowerInfo = field(default_factory=MowerInfo)
|
|
30
31
|
mqtt_properties: ThingPropertiesMessage | None = None
|
|
31
32
|
status_properties: ThingStatusMessage | None = None
|
|
@@ -15,85 +15,95 @@ class Item(DataClassDictMixin, Generic[DataT]):
|
|
|
15
15
|
|
|
16
16
|
@dataclass
|
|
17
17
|
class BatteryPercentageItems(DataClassORJSONMixin):
|
|
18
|
-
batteryPercentage:
|
|
18
|
+
batteryPercentage: int
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
@dataclass
|
|
22
22
|
class BMSHardwareVersionItems(DataClassORJSONMixin):
|
|
23
|
-
bmsHardwareVersion:
|
|
23
|
+
bmsHardwareVersion: str
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
@dataclass
|
|
27
27
|
class CoordinateItems(DataClassORJSONMixin):
|
|
28
|
-
coordinate:
|
|
28
|
+
coordinate: str # '{"lon":0.303903,"lat":1.051868}'
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@dataclass
|
|
32
32
|
class DeviceStateItems(DataClassORJSONMixin):
|
|
33
|
-
deviceState:
|
|
33
|
+
deviceState: int
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
@dataclass
|
|
37
37
|
class DeviceVersionItems(DataClassORJSONMixin):
|
|
38
|
-
deviceVersion:
|
|
38
|
+
deviceVersion: str
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
@dataclass
|
|
42
42
|
class DeviceVersionInfoItems(DataClassORJSONMixin):
|
|
43
|
-
deviceVersionInfo:
|
|
43
|
+
deviceVersionInfo: str
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
@dataclass
|
|
47
47
|
class ESP32VersionItems(DataClassORJSONMixin):
|
|
48
|
-
esp32Version:
|
|
48
|
+
esp32Version: str
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
@dataclass
|
|
52
52
|
class LeftMotorBootVersionItems(DataClassORJSONMixin):
|
|
53
|
-
leftMotorBootVersion:
|
|
53
|
+
leftMotorBootVersion: str
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
@dataclass
|
|
57
57
|
class LeftMotorVersionItems(DataClassORJSONMixin):
|
|
58
|
-
leftMotorVersion:
|
|
58
|
+
leftMotorVersion: str
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
@dataclass
|
|
62
62
|
class MCBootVersionItems(DataClassORJSONMixin):
|
|
63
|
-
mcBootVersion:
|
|
63
|
+
mcBootVersion: str
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
@dataclass
|
|
67
67
|
class NetworkInfoItems(DataClassORJSONMixin):
|
|
68
|
-
networkInfo:
|
|
68
|
+
networkInfo: str
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
@dataclass
|
|
72
72
|
class RightMotorBootVersionItems(DataClassORJSONMixin):
|
|
73
|
-
rightMotorBootVersion:
|
|
73
|
+
rightMotorBootVersion: str
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
@dataclass
|
|
77
77
|
class RightMotorVersionItems(DataClassORJSONMixin):
|
|
78
|
-
rightMotorVersion:
|
|
78
|
+
rightMotorVersion: str
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
@dataclass
|
|
82
82
|
class RTKVersionItems(DataClassORJSONMixin):
|
|
83
|
-
rtkVersion:
|
|
83
|
+
rtkVersion: str
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
@dataclass
|
|
87
87
|
class StationRTKVersionItems(DataClassORJSONMixin):
|
|
88
|
-
stationRtkVersion:
|
|
88
|
+
stationRtkVersion: str
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
@dataclass
|
|
92
92
|
class STM32H7VersionItems(DataClassORJSONMixin):
|
|
93
|
-
stm32H7Version:
|
|
93
|
+
stm32H7Version: str
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
@dataclass
|
|
97
|
+
class OTAProgressItems(DataClassORJSONMixin):
|
|
98
|
+
result: int
|
|
99
|
+
otaId: str
|
|
100
|
+
progress: int
|
|
101
|
+
message: str
|
|
102
|
+
version: str
|
|
103
|
+
properties: str
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
ItemTypes = Union[
|
|
97
107
|
BatteryPercentageItems,
|
|
98
108
|
BMSHardwareVersionItems,
|
|
99
109
|
CoordinateItems,
|
|
@@ -110,43 +120,45 @@ Items = Union[
|
|
|
110
120
|
RTKVersionItems,
|
|
111
121
|
StationRTKVersionItems,
|
|
112
122
|
STM32H7VersionItems,
|
|
123
|
+
OTAProgressItems,
|
|
113
124
|
]
|
|
114
125
|
|
|
115
126
|
|
|
116
127
|
@dataclass
|
|
117
128
|
class Item:
|
|
118
129
|
time: int
|
|
119
|
-
value: int | float | str | dict[str, Any] # Depending on the type of value
|
|
130
|
+
value: int | float | str | dict[str, Any] | ItemTypes # Depending on the type of value
|
|
120
131
|
|
|
121
132
|
|
|
122
133
|
@dataclass
|
|
123
134
|
class Items:
|
|
124
|
-
iotState: Item
|
|
125
|
-
extMod: Item
|
|
126
|
-
deviceVersionInfo: Item
|
|
127
|
-
leftMotorBootVersion: Item
|
|
128
|
-
knifeHeight: Item
|
|
129
|
-
rtMrMod: Item
|
|
130
|
-
iotMsgHz: Item
|
|
131
|
-
iotMsgTotal: Item
|
|
132
|
-
loraRawConfig: Item
|
|
133
|
-
loraGeneralConfig: Item
|
|
134
|
-
leftMotorVersion: Item
|
|
135
|
-
intMod: Item
|
|
136
|
-
coordinate: Item
|
|
137
|
-
bmsVersion: Item
|
|
138
|
-
rightMotorVersion: Item
|
|
139
|
-
stm32H7Version: Item
|
|
140
|
-
rightMotorBootVersion: Item
|
|
141
|
-
deviceVersion: Item
|
|
142
|
-
rtkVersion: Item
|
|
143
|
-
ltMrMod: Item
|
|
144
|
-
networkInfo: Item
|
|
145
|
-
bmsHardwareVersion: Item
|
|
146
|
-
batteryPercentage: Item
|
|
147
|
-
deviceState: Item
|
|
148
|
-
deviceOtherInfo: Item
|
|
149
|
-
mcBootVersion: Item
|
|
135
|
+
iotState: Item | None = None
|
|
136
|
+
extMod: Item | None = None
|
|
137
|
+
deviceVersionInfo: Item | None = None
|
|
138
|
+
leftMotorBootVersion: Item | None = None
|
|
139
|
+
knifeHeight: Item | None = None
|
|
140
|
+
rtMrMod: Item | None = None
|
|
141
|
+
iotMsgHz: Item | None = None
|
|
142
|
+
iotMsgTotal: Item | None = None
|
|
143
|
+
loraRawConfig: Item | None = None
|
|
144
|
+
loraGeneralConfig: Item | None = None
|
|
145
|
+
leftMotorVersion: Item | None = None
|
|
146
|
+
intMod: Item | None = None
|
|
147
|
+
coordinate: Item | None = None
|
|
148
|
+
bmsVersion: Item | None = None
|
|
149
|
+
rightMotorVersion: Item | None = None
|
|
150
|
+
stm32H7Version: Item | None = None
|
|
151
|
+
rightMotorBootVersion: Item | None = None
|
|
152
|
+
deviceVersion: Item | None = None
|
|
153
|
+
rtkVersion: Item | None = None
|
|
154
|
+
ltMrMod: Item | None = None
|
|
155
|
+
networkInfo: Item | None = None
|
|
156
|
+
bmsHardwareVersion: Item | None = None
|
|
157
|
+
batteryPercentage: Item | None = None
|
|
158
|
+
deviceState: Item | None = None
|
|
159
|
+
deviceOtherInfo: Item | None = None
|
|
160
|
+
mcBootVersion: Item | None = None
|
|
161
|
+
otaProgress: Item | None = None
|
|
150
162
|
|
|
151
163
|
|
|
152
164
|
@dataclass
|
|
@@ -14,6 +14,7 @@ from pymammotion.data.model.hash_list import AreaHashNameList, NavGetCommData, N
|
|
|
14
14
|
from pymammotion.data.model.work import CurrentTaskSettings
|
|
15
15
|
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
|
16
16
|
from pymammotion.data.mqtt.status import ThingStatusMessage
|
|
17
|
+
from pymammotion.event.event import DataEvent
|
|
17
18
|
from pymammotion.proto import (
|
|
18
19
|
AppGetAllAreaHashName,
|
|
19
20
|
DeviceFwInfo,
|
|
@@ -47,19 +48,20 @@ class StateManager:
|
|
|
47
48
|
self.cloud_get_commondata_ack_callback: (
|
|
48
49
|
Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None
|
|
49
50
|
) = None
|
|
50
|
-
self.
|
|
51
|
-
self.
|
|
52
|
-
|
|
53
|
-
self.cloud_queue_command_callback: Callable[[str, dict[str, Any]], Awaitable[bytes]] | None = None
|
|
51
|
+
self.cloud_on_notification_callback = DataEvent()
|
|
52
|
+
self.cloud_queue_command_callback = DataEvent()
|
|
54
53
|
|
|
54
|
+
self.cloud_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
|
|
55
55
|
self.ble_gethash_ack_callback: Callable[[NavGetHashListAck], Awaitable[None]] | None = None
|
|
56
56
|
self.ble_get_commondata_ack_callback: Callable[[NavGetCommDataAck | SvgMessageAckT], Awaitable[None]] | None = (
|
|
57
57
|
None
|
|
58
58
|
)
|
|
59
59
|
self.ble_get_plan_callback: Callable[[NavPlanJobSet], Awaitable[None]] | None = None
|
|
60
|
-
self.ble_on_notification_callback
|
|
60
|
+
self.ble_on_notification_callback = DataEvent()
|
|
61
|
+
self.ble_queue_command_callback = DataEvent()
|
|
61
62
|
|
|
62
|
-
self.
|
|
63
|
+
self.properties_callback = DataEvent()
|
|
64
|
+
self.status_callback = DataEvent()
|
|
63
65
|
|
|
64
66
|
def get_device(self) -> MowingDevice:
|
|
65
67
|
"""Get device."""
|
|
@@ -72,6 +74,7 @@ class StateManager:
|
|
|
72
74
|
def properties(self, thing_properties: ThingPropertiesMessage) -> None:
|
|
73
75
|
# TODO update device based off thing properties
|
|
74
76
|
self._device.mqtt_properties = thing_properties
|
|
77
|
+
self.on_properties_callback(thing_properties)
|
|
75
78
|
|
|
76
79
|
def status(self, thing_status: ThingStatusMessage) -> None:
|
|
77
80
|
if not self._device.online:
|
|
@@ -79,6 +82,7 @@ class StateManager:
|
|
|
79
82
|
self._device.status_properties = thing_status
|
|
80
83
|
if self._device.mower_state.product_key == "":
|
|
81
84
|
self._device.mower_state.product_key = thing_status.params.productKey
|
|
85
|
+
self.on_status_callback(thing_status)
|
|
82
86
|
|
|
83
87
|
@property
|
|
84
88
|
def online(self) -> bool:
|
|
@@ -96,9 +100,19 @@ class StateManager:
|
|
|
96
100
|
|
|
97
101
|
async def on_notification_callback(self, res: tuple[str, Any | None]) -> None:
|
|
98
102
|
if self.cloud_on_notification_callback:
|
|
99
|
-
await self.cloud_on_notification_callback(res)
|
|
103
|
+
await self.cloud_on_notification_callback.data_event(res)
|
|
100
104
|
elif self.ble_on_notification_callback:
|
|
101
|
-
await self.ble_on_notification_callback(res)
|
|
105
|
+
await self.ble_on_notification_callback.data_event(res)
|
|
106
|
+
|
|
107
|
+
async def on_properties_callback(self, thing_properties: ThingPropertiesMessage) -> None:
|
|
108
|
+
"""Check if we have a callback for properties."""
|
|
109
|
+
if self.properties_callback:
|
|
110
|
+
await self.properties_callback.data_event(thing_properties)
|
|
111
|
+
|
|
112
|
+
async def on_status_callback(self, thing_status: ThingStatusMessage) -> None:
|
|
113
|
+
"""Check if we have a callback for status."""
|
|
114
|
+
if self.status_callback:
|
|
115
|
+
await self.status_callback.data_event(thing_status)
|
|
102
116
|
|
|
103
117
|
async def get_commondata_ack_callback(self, comm_data: NavGetCommDataAck | SvgMessageAckT) -> None:
|
|
104
118
|
if self.cloud_get_commondata_ack_callback:
|
|
@@ -1,21 +1,42 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from types import MethodType
|
|
2
4
|
from typing import Any
|
|
5
|
+
import weakref
|
|
3
6
|
|
|
4
7
|
|
|
5
8
|
class Event:
|
|
6
9
|
def __init__(self) -> None:
|
|
7
|
-
self.__eventhandlers = []
|
|
10
|
+
self.__eventhandlers: list[weakref.ReferenceType] = []
|
|
8
11
|
|
|
9
|
-
def __iadd__(self, handler):
|
|
10
|
-
|
|
12
|
+
def __iadd__(self, handler: Callable) -> "Event":
|
|
13
|
+
if isinstance(handler, MethodType):
|
|
14
|
+
# Instance method, use WeakMethod
|
|
15
|
+
ref = weakref.WeakMethod(handler)
|
|
16
|
+
else:
|
|
17
|
+
# Function or static method, use weakref.ref
|
|
18
|
+
ref = weakref.ref(handler)
|
|
19
|
+
self.__eventhandlers.append(ref)
|
|
11
20
|
return self
|
|
12
21
|
|
|
13
|
-
def __isub__(self, handler):
|
|
14
|
-
self.__eventhandlers.
|
|
22
|
+
def __isub__(self, handler: Callable) -> "Event":
|
|
23
|
+
self.__eventhandlers = [ref for ref in self.__eventhandlers if ref() is not handler]
|
|
15
24
|
return self
|
|
16
25
|
|
|
17
26
|
async def __call__(self, *args: Any, **kwargs: Any) -> None:
|
|
18
|
-
|
|
27
|
+
live_handlers = []
|
|
28
|
+
for ref in self.__eventhandlers:
|
|
29
|
+
func = ref()
|
|
30
|
+
if func is not None:
|
|
31
|
+
live_handlers.append(func(*args, **kwargs))
|
|
32
|
+
await asyncio.gather(*live_handlers)
|
|
33
|
+
|
|
34
|
+
# Clean up dead references
|
|
35
|
+
self.__eventhandlers = [ref for ref in self.__eventhandlers if ref() is not None]
|
|
36
|
+
|
|
37
|
+
def has_dead_handlers(self) -> bool:
|
|
38
|
+
"""Check if any handlers have been garbage collected."""
|
|
39
|
+
return any(ref() is None for ref in self.__eventhandlers)
|
|
19
40
|
|
|
20
41
|
|
|
21
42
|
class MoveEvent:
|
|
@@ -6,7 +6,8 @@ from aiohttp import ClientSession
|
|
|
6
6
|
from pymammotion.const import MAMMOTION_API_DOMAIN, MAMMOTION_CLIENT_ID, MAMMOTION_CLIENT_SECRET, MAMMOTION_DOMAIN
|
|
7
7
|
from pymammotion.http.encryption import EncryptionUtils
|
|
8
8
|
from pymammotion.http.model.camera_stream import StreamSubscriptionResponse, VideoResourceResponse
|
|
9
|
-
from pymammotion.http.model.http import ErrorInfo, LoginResponseData, Response
|
|
9
|
+
from pymammotion.http.model.http import CheckDeviceVersion, ErrorInfo, LoginResponseData, Response
|
|
10
|
+
from pymammotion.http.model.response_factory import response_factory
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class MammotionHTTP:
|
|
@@ -17,7 +18,7 @@ class MammotionHTTP:
|
|
|
17
18
|
self._password = None
|
|
18
19
|
self.response: Response | None = None
|
|
19
20
|
self.login_info: LoginResponseData | None = None
|
|
20
|
-
self._headers = {"User-Agent": "okhttp/
|
|
21
|
+
self._headers = {"User-Agent": "okhttp/4.9.3", "App-Version": "google Pixel 2 XL taimen-Android 11,1.11.332"}
|
|
21
22
|
self.encryption_utils = EncryptionUtils()
|
|
22
23
|
|
|
23
24
|
@staticmethod
|
|
@@ -100,7 +101,7 @@ class MammotionHTTP:
|
|
|
100
101
|
headers={
|
|
101
102
|
"Authorization": f"Bearer {self.login_info.access_token}",
|
|
102
103
|
"Content-Type": "application/json",
|
|
103
|
-
"User-Agent": "okhttp/
|
|
104
|
+
"User-Agent": "okhttp/4.9.3",
|
|
104
105
|
},
|
|
105
106
|
) as resp:
|
|
106
107
|
data = await resp.json()
|
|
@@ -133,7 +134,7 @@ class MammotionHTTP:
|
|
|
133
134
|
headers={
|
|
134
135
|
"Authorization": f"Bearer {self.login_info.access_token}",
|
|
135
136
|
"Content-Type": "application/json",
|
|
136
|
-
"User-Agent": "okhttp/
|
|
137
|
+
"User-Agent": "okhttp/4.9.3",
|
|
137
138
|
},
|
|
138
139
|
) as resp:
|
|
139
140
|
data = await resp.json()
|
|
@@ -153,7 +154,7 @@ class MammotionHTTP:
|
|
|
153
154
|
headers={
|
|
154
155
|
"Authorization": f"Bearer {self.login_info.access_token}",
|
|
155
156
|
"Content-Type": "application/json",
|
|
156
|
-
"User-Agent": "okhttp/
|
|
157
|
+
"User-Agent": "okhttp/4.9.3",
|
|
157
158
|
},
|
|
158
159
|
) as resp:
|
|
159
160
|
data = await resp.json()
|
|
@@ -165,6 +166,40 @@ class MammotionHTTP:
|
|
|
165
166
|
response.data = VideoResourceResponse.from_dict(data.get("data", {}))
|
|
166
167
|
return response
|
|
167
168
|
|
|
169
|
+
async def get_device_ota_firmware(self, iot_ids: list[str]) -> Response[list[CheckDeviceVersion]]:
|
|
170
|
+
"""Device firmware upgrade check."""
|
|
171
|
+
async with ClientSession(MAMMOTION_API_DOMAIN) as session:
|
|
172
|
+
async with session.post(
|
|
173
|
+
"/device-server/v1/devices/version/check",
|
|
174
|
+
json={"deviceIds": iot_ids},
|
|
175
|
+
headers={
|
|
176
|
+
"Authorization": f"Bearer {self.login_info.access_token}",
|
|
177
|
+
"Content-Type": "application/json",
|
|
178
|
+
"User-Agent": "okhttp/4.9.3",
|
|
179
|
+
},
|
|
180
|
+
) as resp:
|
|
181
|
+
data = await resp.json()
|
|
182
|
+
# TODO catch errors from mismatch like token expire etc
|
|
183
|
+
# Assuming the data format matches the expected structure
|
|
184
|
+
return response_factory(Response[list[CheckDeviceVersion]], data)
|
|
185
|
+
|
|
186
|
+
async def start_ota_upgrade(self, iot_id: str, version: str) -> Response[str]:
|
|
187
|
+
"""Device firmware upgrade."""
|
|
188
|
+
async with ClientSession(MAMMOTION_API_DOMAIN) as session:
|
|
189
|
+
async with session.post(
|
|
190
|
+
"/device-server/v1/ota/device/upgrade",
|
|
191
|
+
json={"deviceId": iot_id, "version": version},
|
|
192
|
+
headers={
|
|
193
|
+
"Authorization": f"Bearer {self.login_info.access_token}",
|
|
194
|
+
"Content-Type": "application/json",
|
|
195
|
+
"User-Agent": "okhttp/4.9.3",
|
|
196
|
+
},
|
|
197
|
+
) as resp:
|
|
198
|
+
data = await resp.json()
|
|
199
|
+
# TODO catch errors from mismatch like token expire etc
|
|
200
|
+
# Assuming the data format matches the expected structure
|
|
201
|
+
return response_factory(Response[str], data)
|
|
202
|
+
|
|
168
203
|
async def refresh_login(self, account: str, password: str | None = None) -> Response[LoginResponseData]:
|
|
169
204
|
if self._password is None and password is not None:
|
|
170
205
|
self._password = password
|
|
@@ -179,7 +214,7 @@ class MammotionHTTP:
|
|
|
179
214
|
async with session.post(
|
|
180
215
|
"/oauth/token",
|
|
181
216
|
headers={
|
|
182
|
-
"User-Agent": "okhttp/
|
|
217
|
+
"User-Agent": "okhttp/4.9.3",
|
|
183
218
|
"App-Version": "google Pixel 2 XL taimen-Android 11,1.11.332",
|
|
184
219
|
"Encrypt-Key": self.encryption_utils.encrypt_by_public_key(),
|
|
185
220
|
"Decrypt-Type": "3",
|
|
@@ -197,11 +232,11 @@ class MammotionHTTP:
|
|
|
197
232
|
print(resp.json())
|
|
198
233
|
return Response.from_dict({"code": resp.status, "msg": "Login failed"})
|
|
199
234
|
data = await resp.json()
|
|
200
|
-
login_response = Response[LoginResponseData]
|
|
235
|
+
login_response = response_factory(Response[LoginResponseData], data)
|
|
201
236
|
if login_response.data is None:
|
|
202
237
|
print(login_response)
|
|
203
238
|
return Response.from_dict({"code": resp.status, "msg": "Login failed"})
|
|
204
|
-
self.login_info =
|
|
239
|
+
self.login_info = login_response.data
|
|
205
240
|
self._headers["Authorization"] = (
|
|
206
241
|
f"Bearer {self.login_info.access_token}" if login_response.data else None
|
|
207
242
|
)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Generic, Literal, TypeVar
|
|
2
|
+
from typing import Annotated, Generic, Literal, TypeVar
|
|
3
3
|
|
|
4
4
|
from mashumaro import DataClassDictMixin
|
|
5
5
|
from mashumaro.config import BaseConfig
|
|
6
6
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
7
|
+
from mashumaro.types import Alias
|
|
7
8
|
|
|
8
9
|
DataT = TypeVar("DataT")
|
|
9
10
|
|
|
@@ -103,3 +104,47 @@ class LoginResponseData(DataClassORJSONMixin):
|
|
|
103
104
|
|
|
104
105
|
class Config(BaseConfig):
|
|
105
106
|
omit_none = True
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class FirmwareVersions(DataClassORJSONMixin):
|
|
111
|
+
firmware_version: Annotated[str, Alias("firmwareVersion")] = ""
|
|
112
|
+
firmware_code: Annotated[str, Alias("firmwareCode")] = ""
|
|
113
|
+
firmware_latest_version: Annotated[str, Alias("firmwareLatestVersion")] = ""
|
|
114
|
+
firmware_type: Annotated[str, Alias("firmwareType")] = ""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class ProductVersionInfo(DataClassORJSONMixin):
|
|
119
|
+
release_note: Annotated[str, Alias("releaseNote")] = ""
|
|
120
|
+
release_version: Annotated[str, Alias("releaseVersion")] = ""
|
|
121
|
+
data_location: str | None = None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class CheckDeviceVersion(DataClassORJSONMixin):
|
|
126
|
+
cause_code: Annotated[int, Alias("causeCode")] = 0
|
|
127
|
+
product_version_info_vo: Annotated[ProductVersionInfo | None, Alias("productVersionInfoVo")] = None
|
|
128
|
+
progress: int | None = 0
|
|
129
|
+
upgradeable: bool = False
|
|
130
|
+
device_id: Annotated[str, Alias("deviceId")] = ""
|
|
131
|
+
device_name: Annotated[str | None, Alias("deviceName")] = ""
|
|
132
|
+
current_version: Annotated[str, Alias("currentVersion")] = ""
|
|
133
|
+
isupgrading: bool | None = False
|
|
134
|
+
cause_msg: Annotated[str, Alias("causeMsg")] = ""
|
|
135
|
+
|
|
136
|
+
def __eq__(self, other):
|
|
137
|
+
if not isinstance(other, CheckDeviceVersion):
|
|
138
|
+
return NotImplemented
|
|
139
|
+
|
|
140
|
+
if self.device_id != other.device_id or self.current_version != other.current_version:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
if self.product_version_info_vo and other.product_version_info_vo:
|
|
144
|
+
if self.product_version_info_vo.release_version != other.product_version_info_vo.release_version:
|
|
145
|
+
return False
|
|
146
|
+
return True
|
|
147
|
+
elif self.product_version_info_vo is None and other.product_version_info_vo is None:
|
|
148
|
+
return False
|
|
149
|
+
else:
|
|
150
|
+
return True
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import TypeVar, Union, get_args, get_origin
|
|
2
|
+
|
|
3
|
+
from pymammotion.http.model.http import Response
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def deserialize_data(value, target_type):
|
|
9
|
+
if value is None:
|
|
10
|
+
return None
|
|
11
|
+
|
|
12
|
+
origin = get_origin(target_type)
|
|
13
|
+
args = get_args(target_type)
|
|
14
|
+
|
|
15
|
+
if origin is list and args:
|
|
16
|
+
item_type = args[0]
|
|
17
|
+
return [deserialize_data(v, item_type) for v in value]
|
|
18
|
+
|
|
19
|
+
if origin is Union:
|
|
20
|
+
# Support Optional[T] = Union[T, None]
|
|
21
|
+
non_none_types = [t for t in args if t is not type(None)]
|
|
22
|
+
if len(non_none_types) == 1:
|
|
23
|
+
return deserialize_data(value, non_none_types[0])
|
|
24
|
+
|
|
25
|
+
if hasattr(target_type, "from_dict"):
|
|
26
|
+
return target_type.from_dict(value)
|
|
27
|
+
|
|
28
|
+
return value # fallback: unknown type, leave as-is
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def response_factory(response_cls: type[Response[T]], raw_dict: dict) -> Response[T]:
|
|
32
|
+
# Extract the type of the generic `data` field
|
|
33
|
+
data_type = get_args(response_cls)[0] if get_args(response_cls) else None
|
|
34
|
+
|
|
35
|
+
if data_type:
|
|
36
|
+
data_value = deserialize_data(raw_dict.get("data"), data_type)
|
|
37
|
+
return Response(code=raw_dict["code"], msg=raw_dict["msg"], data=data_value)
|
|
38
|
+
else:
|
|
39
|
+
return response_cls.from_dict(raw_dict)
|
|
@@ -18,9 +18,6 @@ class AbstractMessage:
|
|
|
18
18
|
|
|
19
19
|
def get_msg_device(self, msg_type: MsgCmdType, msg_device: MsgDevice) -> MsgDevice:
|
|
20
20
|
"""Changes the rcver name if it's not a luba1."""
|
|
21
|
-
if (
|
|
22
|
-
not DeviceType.is_luba1(self.get_device_name(), self.get_device_product_key())
|
|
23
|
-
and msg_type == MsgCmdType.NAV
|
|
24
|
-
):
|
|
21
|
+
if DeviceType.is_luba_pro(self.get_device_name(), self.get_device_product_key()) and msg_type == MsgCmdType.NAV:
|
|
25
22
|
return MsgDevice.DEV_NAVIGATION
|
|
26
23
|
return msg_device
|