pymammotion 0.5.42__py3-none-any.whl → 0.5.43__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymammotion might be problematic. Click here for more details.
- pymammotion/data/model/enums.py +2 -2
- pymammotion/data/model/hash_list.py +1 -1
- pymammotion/data/mqtt/event.py +47 -22
- pymammotion/data/mqtt/mammotion_properties.py +257 -0
- pymammotion/data/mqtt/properties.py +32 -29
- pymammotion/data/mqtt/status.py +17 -16
- pymammotion/http/http.py +7 -1
- pymammotion/mammotion/devices/mammotion.py +1 -2
- pymammotion/mammotion/devices/mammotion_cloud.py +22 -11
- pymammotion/mammotion/devices/rtk_cloud.py +2 -2
- pymammotion/mqtt/mammotion_mqtt.py +57 -17
- pymammotion/utility/datatype_converter.py +13 -12
- {pymammotion-0.5.42.dist-info → pymammotion-0.5.43.dist-info}/METADATA +1 -1
- {pymammotion-0.5.42.dist-info → pymammotion-0.5.43.dist-info}/RECORD +16 -15
- {pymammotion-0.5.42.dist-info → pymammotion-0.5.43.dist-info}/WHEEL +0 -0
- {pymammotion-0.5.42.dist-info → pymammotion-0.5.43.dist-info}/licenses/LICENSE +0 -0
pymammotion/data/model/enums.py
CHANGED
|
@@ -19,7 +19,7 @@ class PositionMode(Enum):
|
|
|
19
19
|
UNKNOWN = 4
|
|
20
20
|
|
|
21
21
|
@staticmethod
|
|
22
|
-
def from_value(value: int):
|
|
22
|
+
def from_value(value: int) -> "PositionMode":
|
|
23
23
|
if value == 0:
|
|
24
24
|
return PositionMode.FIX
|
|
25
25
|
elif value == 1:
|
|
@@ -52,7 +52,7 @@ class RTKStatus(Enum):
|
|
|
52
52
|
UNKNOWN = 6
|
|
53
53
|
|
|
54
54
|
@staticmethod
|
|
55
|
-
def from_value(value: int):
|
|
55
|
+
def from_value(value: int) -> "RTKStatus":
|
|
56
56
|
if value == 0:
|
|
57
57
|
return RTKStatus.NONE
|
|
58
58
|
elif value == 1 or value == 2:
|
|
@@ -220,7 +220,7 @@ class HashList(DataClassORJSONMixin):
|
|
|
220
220
|
self.svg = {hash_id: frames for hash_id, frames in self.svg.items() if hash_id in hashlist}
|
|
221
221
|
|
|
222
222
|
area_hashes = list(self.area.keys())
|
|
223
|
-
for hash_id, plan_task in self.plan.items():
|
|
223
|
+
for hash_id, plan_task in self.plan.copy().items():
|
|
224
224
|
for item in plan_task.zone_hashs:
|
|
225
225
|
if item not in area_hashes:
|
|
226
226
|
self.plan.pop(hash_id)
|
pymammotion/data/mqtt/event.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from base64 import b64decode
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Any, Literal
|
|
3
|
+
from typing import Annotated, Any, Literal
|
|
4
4
|
|
|
5
5
|
from google.protobuf import json_format
|
|
6
6
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
7
|
-
from mashumaro.types import SerializableType
|
|
7
|
+
from mashumaro.types import Alias, SerializableType
|
|
8
8
|
|
|
9
9
|
from pymammotion.proto import luba_msg_pb2
|
|
10
10
|
|
|
@@ -73,34 +73,35 @@ class DeviceBizReqEventValue(DataClassORJSONMixin):
|
|
|
73
73
|
|
|
74
74
|
@dataclass
|
|
75
75
|
class GeneralParams(DataClassORJSONMixin):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
group_id_list: Annotated[list[str], Alias("groupIdList")]
|
|
77
|
+
group_id: Annotated[str, Alias("groupId")]
|
|
78
|
+
category_key: Annotated[Literal["LawnMower", "Tracker"], Alias("categoryKey")]
|
|
79
|
+
batch_id: Annotated[str, Alias("batchId")]
|
|
80
|
+
gmt_create: Annotated[int, Alias("gmtCreate")]
|
|
81
|
+
product_key: Annotated[str, Alias("productKey")]
|
|
82
82
|
type: str
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
device_name: Annotated[str, Alias("deviceName")]
|
|
84
|
+
iot_id: Annotated[str, Alias("iotId")]
|
|
85
|
+
check_level: Annotated[int, Alias("checkLevel")]
|
|
86
86
|
namespace: str
|
|
87
|
-
|
|
87
|
+
tenant_id: Annotated[str, Alias("tenantId")]
|
|
88
88
|
name: str
|
|
89
|
-
|
|
89
|
+
thing_type: Annotated[Literal["DEVICE"], Alias("thingType")]
|
|
90
90
|
time: int
|
|
91
|
-
|
|
91
|
+
tenant_instance_id: Annotated[str, Alias("tenantInstanceId")]
|
|
92
92
|
value: Any
|
|
93
93
|
|
|
94
|
+
# Optional fields
|
|
94
95
|
identifier: str | None = None
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
check_failed_data: Annotated[dict | None, Alias("checkFailedData")] = None
|
|
97
|
+
_tenant_id: Annotated[str | None, Alias("_tenantId")] = None
|
|
98
|
+
generate_time: Annotated[int | None, Alias("generateTime")] = None
|
|
99
|
+
jmsx_delivery_count: Annotated[int | None, Alias("JMSXDeliveryCount")] = None
|
|
99
100
|
qos: int | None = None
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
request_id: Annotated[str | None, Alias("requestId")] = None
|
|
102
|
+
_category_key: Annotated[str | None, Alias("_categoryKey")] = None
|
|
103
|
+
device_type: Annotated[str | None, Alias("deviceType")] = None
|
|
104
|
+
_trace_id: Annotated[str | None, Alias("_traceId")] = None
|
|
104
105
|
|
|
105
106
|
|
|
106
107
|
@dataclass
|
|
@@ -196,3 +197,27 @@ class ThingEventMessage(DataClassORJSONMixin):
|
|
|
196
197
|
raise ValueError(f"Unknown identifier: {identifier} {params_dict}")
|
|
197
198
|
|
|
198
199
|
return cls(method=method, id=event_id, params=params_obj, version=version)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass
|
|
203
|
+
class MammotionProtoMsgParams(DataClassORJSONMixin, SerializableType):
|
|
204
|
+
value: DeviceProtobufMsgEventValue
|
|
205
|
+
iot_id: str = ""
|
|
206
|
+
product_key: str = ""
|
|
207
|
+
device_name: str = ""
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def _deserialize(cls, d: dict[str, Any]) -> "MammotionProtoMsgParams":
|
|
211
|
+
"""Override from_dict to allow dict manipulation before conversion."""
|
|
212
|
+
proto: str = d["content"]
|
|
213
|
+
|
|
214
|
+
return cls(value=DeviceProtobufMsgEventValue(content=proto))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@dataclass
|
|
218
|
+
class MammotionEventMessage(DataClassORJSONMixin):
|
|
219
|
+
id: str
|
|
220
|
+
version: str
|
|
221
|
+
sys: dict
|
|
222
|
+
params: MammotionProtoMsgParams
|
|
223
|
+
method: str
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
from mashumaro.config import BaseConfig
|
|
5
|
+
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
6
|
+
from mashumaro.types import Alias
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class FirmwareInfo(DataClassORJSONMixin):
|
|
11
|
+
t: str
|
|
12
|
+
c: str
|
|
13
|
+
v: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class DeviceVersionInfo(DataClassORJSONMixin):
|
|
18
|
+
dev_ver: Annotated[str, Alias("devVer")]
|
|
19
|
+
whole: int
|
|
20
|
+
fw_info: Annotated[list[FirmwareInfo], Alias("fwInfo")]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class Coordinate(DataClassORJSONMixin):
|
|
25
|
+
lon: float
|
|
26
|
+
lat: float
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class InternalNavigation(DataClassORJSONMixin):
|
|
31
|
+
nav: Annotated[str, Alias("NAV")]
|
|
32
|
+
pau: Annotated[str, Alias("Pau")]
|
|
33
|
+
r_pau: Annotated[str, Alias("rPau")]
|
|
34
|
+
mcu: Annotated[str, Alias("MCU")]
|
|
35
|
+
app: Annotated[str, Alias("APP")]
|
|
36
|
+
w_slp: Annotated[str, Alias("wSlp")]
|
|
37
|
+
i_slp: Annotated[str, Alias("iSlp")]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class BandwidthTraffic(DataClassORJSONMixin):
|
|
42
|
+
iot: Annotated[str, Alias("IoT")]
|
|
43
|
+
roi: Annotated[str, Alias("RoI")]
|
|
44
|
+
fpv: Annotated[str, Alias("FPV")]
|
|
45
|
+
inav: InternalNavigation
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class TrafficPeriod(DataClassORJSONMixin):
|
|
50
|
+
r: str
|
|
51
|
+
t: str
|
|
52
|
+
s: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class TrafficData(DataClassORJSONMixin):
|
|
57
|
+
upt: str
|
|
58
|
+
hour: Annotated[dict[str, TrafficPeriod], Alias("Hour")]
|
|
59
|
+
day: Annotated[dict[str, TrafficPeriod], Alias("Day")]
|
|
60
|
+
mon: Annotated[dict[str, TrafficPeriod], Alias("Mon")]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class NetworkInfo(DataClassORJSONMixin):
|
|
65
|
+
ssid: str
|
|
66
|
+
ip: str
|
|
67
|
+
wifi_sta_mac: str
|
|
68
|
+
wifi_rssi: int
|
|
69
|
+
wifi_available: int
|
|
70
|
+
bt_mac: str
|
|
71
|
+
mnet_model: str
|
|
72
|
+
imei: str
|
|
73
|
+
fw_ver: str
|
|
74
|
+
sim: str
|
|
75
|
+
imsi: str
|
|
76
|
+
iccid: str
|
|
77
|
+
sim_source: str
|
|
78
|
+
mnet_rssi: int
|
|
79
|
+
signal: int
|
|
80
|
+
mnet_link: int
|
|
81
|
+
mnet_option: str
|
|
82
|
+
mnet_ip: str
|
|
83
|
+
mnet_reg: str
|
|
84
|
+
mnet_rsrp: str
|
|
85
|
+
mnet_snr: str
|
|
86
|
+
mnet_enable: int
|
|
87
|
+
apn_num: int
|
|
88
|
+
apn_info: str
|
|
89
|
+
apn_cid: int
|
|
90
|
+
used_net: int
|
|
91
|
+
hub_reset: int
|
|
92
|
+
mnet_dis: int
|
|
93
|
+
airplane_times: int
|
|
94
|
+
lsusb_num: int
|
|
95
|
+
b_tra: Annotated[BandwidthTraffic, Alias("bTra")]
|
|
96
|
+
bw_tra: Annotated[BandwidthTraffic, Alias("bwTra")]
|
|
97
|
+
mnet_rx: str
|
|
98
|
+
mnet_tx: str
|
|
99
|
+
m_tra: Annotated[TrafficData, Alias("mTra")]
|
|
100
|
+
mnet_uniot: int
|
|
101
|
+
mnet_un_getiot: int
|
|
102
|
+
ssh_flag: str
|
|
103
|
+
mileage: str
|
|
104
|
+
work_time: str
|
|
105
|
+
wt_sec: int
|
|
106
|
+
bat_cycles: str
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class DeviceOtherInfo(DataClassORJSONMixin):
|
|
111
|
+
soc_up_time: Annotated[int, Alias("socUpTime")]
|
|
112
|
+
mcu_up_time: Annotated[int, Alias("mcuUpTime")]
|
|
113
|
+
soc_loads: Annotated[str, Alias("socLoads")]
|
|
114
|
+
soc_mem_free: Annotated[int, Alias("socMemFree")]
|
|
115
|
+
soc_mem_total: Annotated[int, Alias("socMemTotal")]
|
|
116
|
+
soc_mmc_life_time: Annotated[int, Alias("socMmcLifeTime")]
|
|
117
|
+
usb_dis_cnt: Annotated[int, Alias("usbDisCnt")]
|
|
118
|
+
soc_pstore: Annotated[int, Alias("socPstore")]
|
|
119
|
+
soc_coredump: Annotated[int, Alias("socCoredump")]
|
|
120
|
+
soc_tmp: Annotated[int, Alias("socTmp")]
|
|
121
|
+
mc_mcu: Annotated[str, Alias("mcMcu")]
|
|
122
|
+
tilt_degree: str
|
|
123
|
+
i_msg_free: Annotated[int, Alias("iMsgFree")]
|
|
124
|
+
i_msg_limit: Annotated[int, Alias("iMsgLimit")]
|
|
125
|
+
i_msg_raw: Annotated[int, Alias("iMsgRaw")]
|
|
126
|
+
i_msg_prop: Annotated[int, Alias("iMsgprop")]
|
|
127
|
+
i_msg_serv: Annotated[int, Alias("iMsgServ")]
|
|
128
|
+
i_msg_info: Annotated[int, Alias("iMsgInfo")]
|
|
129
|
+
i_msg_warn: Annotated[int, Alias("iMsgWarn")]
|
|
130
|
+
i_msg_fault: Annotated[int, Alias("iMsgFault")]
|
|
131
|
+
i_msg_ota_stage: Annotated[int, Alias("iMsgOtaStage")]
|
|
132
|
+
i_msg_protobuf: Annotated[int, Alias("iMsgProtobuf")]
|
|
133
|
+
i_msg_notify: Annotated[int, Alias("iMsgNotify")]
|
|
134
|
+
i_msg_log_prog: Annotated[int, Alias("iMsgLogProg")]
|
|
135
|
+
i_msg_biz_req: Annotated[int, Alias("iMsgBizReq")]
|
|
136
|
+
i_msg_cfg_req: Annotated[int, Alias("iMsgCfgReq")]
|
|
137
|
+
i_msg_voice: Annotated[int, Alias("iMsgVoice")]
|
|
138
|
+
i_msg_warn_code: Annotated[int, Alias("iMsgWarnCode")]
|
|
139
|
+
pb_net: Annotated[int, Alias("pbNet")]
|
|
140
|
+
pb_sys: Annotated[int, Alias("pbSys")]
|
|
141
|
+
pb_nav: Annotated[int, Alias("pbNav")]
|
|
142
|
+
pb_local: Annotated[int, Alias("pbLocal")]
|
|
143
|
+
pb_plan: Annotated[int, Alias("pbPlan")]
|
|
144
|
+
pb_e_drv: Annotated[int, Alias("pbEDrv")]
|
|
145
|
+
pb_e_sys: Annotated[int, Alias("pbESys")]
|
|
146
|
+
pb_midware: Annotated[int, Alias("pbMidware")]
|
|
147
|
+
pb_ota: Annotated[int, Alias("pbOta")]
|
|
148
|
+
pb_appl: Annotated[int, Alias("pbAppl")]
|
|
149
|
+
pb_mul: Annotated[int, Alias("pbMul")]
|
|
150
|
+
pb_other: Annotated[int, Alias("pbOther")]
|
|
151
|
+
lora_connect: Annotated[int, Alias("loraConnect")]
|
|
152
|
+
base_status: Annotated[int, Alias("Basestatus")]
|
|
153
|
+
mqtt_rtk_switch: int
|
|
154
|
+
mqtt_rtk_channel: int
|
|
155
|
+
mqtt_rtk_status: int
|
|
156
|
+
mqtt_rtcm_cnt: int
|
|
157
|
+
mqtt_conn_cnt: int
|
|
158
|
+
mqtt_disconn_cnt: int
|
|
159
|
+
mqtt_rtk_hb_flag: int
|
|
160
|
+
mqtt_rtk_hb_count: int
|
|
161
|
+
mqtt_start_cnt: int
|
|
162
|
+
mqtt_close_cnt: int
|
|
163
|
+
mqtt_rtk_ssl_fail: int
|
|
164
|
+
mqtt_rtk_wifi_config: int
|
|
165
|
+
nrtk_svc_prov: int
|
|
166
|
+
nrtk_svc_err: int
|
|
167
|
+
base_stn_id: int
|
|
168
|
+
rtk_status: int
|
|
169
|
+
charge_status: int
|
|
170
|
+
chassis_state: int
|
|
171
|
+
nav: str
|
|
172
|
+
ins_fusion: str
|
|
173
|
+
perception: str
|
|
174
|
+
vision_proxy: str
|
|
175
|
+
vslam_vio: str
|
|
176
|
+
iot_con_timeout: int
|
|
177
|
+
iot_con: int
|
|
178
|
+
iot_con_fail_max: str
|
|
179
|
+
iot_con_fail_min: Annotated[str, Alias("iot_con__fail_min")]
|
|
180
|
+
iot_url_count: int
|
|
181
|
+
iot_url_max: str
|
|
182
|
+
iot_url_min: str
|
|
183
|
+
iot_cn: int
|
|
184
|
+
iot_ap: int
|
|
185
|
+
iot_us: int
|
|
186
|
+
iot_eu: int
|
|
187
|
+
task_area: float
|
|
188
|
+
task_count: int
|
|
189
|
+
task_hash: str
|
|
190
|
+
systemio_boot_time: Annotated[str, Alias("systemioBootTime")]
|
|
191
|
+
dds_no_gdc: int
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass
|
|
195
|
+
class CheckData(DataClassORJSONMixin):
|
|
196
|
+
result: str
|
|
197
|
+
error: Annotated[list[int], Alias("Error")]
|
|
198
|
+
warn: Annotated[list[int], Alias("Warn")]
|
|
199
|
+
ok: Annotated[list[int], Alias("OK")]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass
|
|
203
|
+
class DeviceProperties(DataClassORJSONMixin):
|
|
204
|
+
device_state: Annotated[int, Alias("deviceState")]
|
|
205
|
+
battery_percentage: Annotated[int, Alias("batteryPercentage")]
|
|
206
|
+
device_version: Annotated[str, Alias("deviceVersion")]
|
|
207
|
+
knife_height: Annotated[int, Alias("knifeHeight")]
|
|
208
|
+
lora_general_config: Annotated[str, Alias("loraGeneralConfig")]
|
|
209
|
+
ext_mod: Annotated[str, Alias("extMod")]
|
|
210
|
+
int_mod: Annotated[str, Alias("intMod")]
|
|
211
|
+
iot_state: Annotated[int, Alias("iotState")]
|
|
212
|
+
iot_msg_total: Annotated[int, Alias("iotMsgTotal")]
|
|
213
|
+
iot_msg_hz: Annotated[int, Alias("iotMsgHz")]
|
|
214
|
+
lt_mr_mod: Annotated[str, Alias("ltMrMod")]
|
|
215
|
+
rt_mr_mod: Annotated[str, Alias("rtMrMod")]
|
|
216
|
+
bms_hardware_version: Annotated[str, Alias("bmsHardwareVersion")]
|
|
217
|
+
stm32_h7_version: Annotated[str, Alias("stm32H7Version")]
|
|
218
|
+
left_motor_version: Annotated[str, Alias("leftMotorVersion")]
|
|
219
|
+
right_motor_version: Annotated[str, Alias("rightMotorVersion")]
|
|
220
|
+
rtk_version: Annotated[str, Alias("rtkVersion")]
|
|
221
|
+
bms_version: Annotated[str, Alias("bmsVersion")]
|
|
222
|
+
mc_boot_version: Annotated[str, Alias("mcBootVersion")]
|
|
223
|
+
left_motor_boot_version: Annotated[str, Alias("leftMotorBootVersion")]
|
|
224
|
+
right_motor_boot_version: Annotated[str, Alias("rightMotorBootVersion")]
|
|
225
|
+
|
|
226
|
+
# Nested JSON objects
|
|
227
|
+
device_version_info: Annotated[DeviceVersionInfo, Alias("deviceVersionInfo")]
|
|
228
|
+
coordinate: Coordinate
|
|
229
|
+
device_other_info: Annotated[DeviceOtherInfo, Alias("deviceOtherInfo")]
|
|
230
|
+
network_info: Annotated[NetworkInfo, Alias("networkInfo")]
|
|
231
|
+
check_data: Annotated[CheckData, Alias("checkData")]
|
|
232
|
+
iot_id: str = ""
|
|
233
|
+
|
|
234
|
+
class Config(BaseConfig):
|
|
235
|
+
# Custom deserializer for nested JSON strings
|
|
236
|
+
serialization_strategy = {
|
|
237
|
+
DeviceVersionInfo: {
|
|
238
|
+
"deserialize": lambda x: DeviceVersionInfo.from_json(x) if isinstance(x, str) else x,
|
|
239
|
+
"serialize": lambda x: x.to_json() if hasattr(x, "to_json") else x,
|
|
240
|
+
},
|
|
241
|
+
Coordinate: {
|
|
242
|
+
"deserialize": lambda x: Coordinate.from_json(x) if isinstance(x, str) else x,
|
|
243
|
+
"serialize": lambda x: x.to_json() if hasattr(x, "to_json") else x,
|
|
244
|
+
},
|
|
245
|
+
DeviceOtherInfo: {
|
|
246
|
+
"deserialize": lambda x: DeviceOtherInfo.from_json(x) if isinstance(x, str) else x,
|
|
247
|
+
"serialize": lambda x: x.to_json() if hasattr(x, "to_json") else x,
|
|
248
|
+
},
|
|
249
|
+
NetworkInfo: {
|
|
250
|
+
"deserialize": lambda x: NetworkInfo.from_json(x) if isinstance(x, str) else x,
|
|
251
|
+
"serialize": lambda x: x.to_json() if hasattr(x, "to_json") else x,
|
|
252
|
+
},
|
|
253
|
+
CheckData: {
|
|
254
|
+
"deserialize": lambda x: CheckData.from_json(x) if isinstance(x, str) else x,
|
|
255
|
+
"serialize": lambda x: x.to_json() if hasattr(x, "to_json") else x,
|
|
256
|
+
},
|
|
257
|
+
}
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Any, Generic, Literal, TypeVar, Union
|
|
2
|
+
from typing import Annotated, Any, Generic, Literal, TypeVar, Union
|
|
3
3
|
|
|
4
4
|
from mashumaro import DataClassDictMixin
|
|
5
5
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
6
|
+
from mashumaro.types import Alias
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class Item(DataClassDictMixin, Generic[DataT]):
|
|
12
|
-
time: int
|
|
13
|
-
value: DataT
|
|
8
|
+
from pymammotion.data.mqtt.mammotion_properties import DeviceProperties
|
|
14
9
|
|
|
15
10
|
|
|
16
11
|
@dataclass
|
|
@@ -163,29 +158,29 @@ class Items(DataClassDictMixin):
|
|
|
163
158
|
|
|
164
159
|
@dataclass
|
|
165
160
|
class Params(DataClassORJSONMixin):
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
161
|
+
device_type: Annotated[Literal["LawnMower", "Tracker"], Alias("deviceType")]
|
|
162
|
+
check_failed_data: Annotated[dict[str, Any], Alias("checkFailedData")]
|
|
163
|
+
group_id_list: Annotated[list[str], Alias("groupIdList")]
|
|
164
|
+
_tenant_id: Annotated[str, Alias("_tenantId")]
|
|
165
|
+
group_id: Annotated[str, Alias("groupId")]
|
|
166
|
+
category_key: Annotated[Literal["LawnMower", "Tracker"], Alias("categoryKey")]
|
|
167
|
+
batch_id: Annotated[str, Alias("batchId")]
|
|
168
|
+
gmt_create: Annotated[int, Alias("gmtCreate")]
|
|
169
|
+
product_key: Annotated[str, Alias("productKey")]
|
|
170
|
+
generate_time: Annotated[int, Alias("generateTime")]
|
|
171
|
+
device_name: Annotated[str, Alias("deviceName")]
|
|
172
|
+
_trace_id: Annotated[str, Alias("_traceId")]
|
|
173
|
+
iot_id: Annotated[str, Alias("iotId")]
|
|
174
|
+
jmsx_delivery_count: Annotated[int, Alias("JMSXDeliveryCount")]
|
|
175
|
+
check_level: Annotated[int, Alias("checkLevel")]
|
|
181
176
|
qos: int
|
|
182
|
-
|
|
183
|
-
|
|
177
|
+
request_id: Annotated[str, Alias("requestId")]
|
|
178
|
+
_category_key: Annotated[str, Alias("_categoryKey")]
|
|
184
179
|
namespace: str
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
items: Items
|
|
188
|
-
|
|
180
|
+
tenant_id: Annotated[str, Alias("tenantId")]
|
|
181
|
+
thing_type: Annotated[Literal["DEVICE"], Alias("thingType")]
|
|
182
|
+
items: Annotated["Items", Alias("items")]
|
|
183
|
+
tenant_instance_id: Annotated[str, Alias("tenantInstanceId")]
|
|
189
184
|
|
|
190
185
|
|
|
191
186
|
@dataclass
|
|
@@ -194,3 +189,11 @@ class ThingPropertiesMessage(DataClassORJSONMixin):
|
|
|
194
189
|
id: str
|
|
195
190
|
params: Params
|
|
196
191
|
version: Literal["1.0"]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass
|
|
195
|
+
class MammotionPropertiesMessage(DataClassORJSONMixin):
|
|
196
|
+
id: str
|
|
197
|
+
version: str
|
|
198
|
+
sys: dict
|
|
199
|
+
params: DeviceProperties
|
pymammotion/data/mqtt/status.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import Literal
|
|
3
|
+
from typing import Annotated, Literal
|
|
4
4
|
|
|
5
5
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
|
6
|
+
from mashumaro.types import Alias
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
@dataclass
|
|
@@ -25,23 +26,23 @@ class Status(DataClassORJSONMixin):
|
|
|
25
26
|
|
|
26
27
|
@dataclass
|
|
27
28
|
class Params(DataClassORJSONMixin):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
group_id_list: Annotated[list[GroupIdListItem], Alias("groupIdList")]
|
|
30
|
+
net_type: Annotated[Literal["NET_WIFI", "NET_MNET"], Alias("netType")]
|
|
31
|
+
active_time: Annotated[int, Alias("activeTime")]
|
|
31
32
|
ip: str
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
aliyun_commodity_code: Annotated[Literal["iothub_senior"], Alias("aliyunCommodityCode")]
|
|
34
|
+
category_key: Annotated[Literal["LawnMower", "Tracker"], Alias("categoryKey")]
|
|
35
|
+
node_type: Annotated[Literal["DEVICE"], Alias("nodeType")]
|
|
36
|
+
product_key: Annotated[str, Alias("productKey")]
|
|
37
|
+
status_last: Annotated[int, Alias("statusLast")]
|
|
38
|
+
device_name: Annotated[str, Alias("deviceName")]
|
|
39
|
+
iot_id: Annotated[str, Alias("iotId")]
|
|
39
40
|
namespace: str
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
status: Status
|
|
41
|
+
tenant_id: Annotated[str, Alias("tenantId")]
|
|
42
|
+
thing_type: Annotated[Literal["DEVICE"], Alias("thingType")]
|
|
43
|
+
tenant_instance_id: Annotated[str, Alias("tenantInstanceId")]
|
|
44
|
+
category_id: Annotated[int, Alias("categoryId")]
|
|
45
|
+
status: Annotated[Status, Alias("status")]
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
@dataclass
|
pymammotion/http/http.py
CHANGED
|
@@ -370,7 +370,13 @@ class MammotionHTTP:
|
|
|
370
370
|
async with ClientSession(self.jwt_info.iot) as session:
|
|
371
371
|
async with session.post(
|
|
372
372
|
"/v1/mqtt/rpc/thing/service/invoke",
|
|
373
|
-
json={
|
|
373
|
+
json={
|
|
374
|
+
"args": {"content": content},
|
|
375
|
+
"deviceName": device_name,
|
|
376
|
+
"identifier": "device_protobuf_sync_service",
|
|
377
|
+
"iotId": iot_id,
|
|
378
|
+
"productKey": "",
|
|
379
|
+
},
|
|
374
380
|
headers={
|
|
375
381
|
**self._headers,
|
|
376
382
|
"Authorization": f"Bearer {self.login_info.access_token}",
|
|
@@ -289,8 +289,7 @@ class Mammotion:
|
|
|
289
289
|
if len(mammotion_http.device_records.records) != 0:
|
|
290
290
|
mammotion_cloud = MammotionCloud(
|
|
291
291
|
MammotionMQTT(
|
|
292
|
-
|
|
293
|
-
device_name=cloud_client.aep_response.data.deviceName,
|
|
292
|
+
records=mammotion_http.device_records.records,
|
|
294
293
|
mammotion_http=mammotion_http,
|
|
295
294
|
mqtt_connection=mammotion_http.mqtt_credentials,
|
|
296
295
|
),
|
|
@@ -15,8 +15,8 @@ from pymammotion import AliyunMQTT, CloudIOTGateway, MammotionMQTT
|
|
|
15
15
|
from pymammotion.aliyun.cloud_gateway import DeviceOfflineException
|
|
16
16
|
from pymammotion.aliyun.model.dev_by_account_response import Device
|
|
17
17
|
from pymammotion.data.mower_state_manager import MowerStateManager
|
|
18
|
-
from pymammotion.data.mqtt.event import ThingEventMessage
|
|
19
|
-
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
|
18
|
+
from pymammotion.data.mqtt.event import MammotionEventMessage, ThingEventMessage
|
|
19
|
+
from pymammotion.data.mqtt.properties import MammotionPropertiesMessage, ThingPropertiesMessage
|
|
20
20
|
from pymammotion.data.mqtt.status import ThingStatusMessage
|
|
21
21
|
from pymammotion.event.event import DataEvent
|
|
22
22
|
from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
|
|
@@ -112,10 +112,9 @@ class MammotionCloud:
|
|
|
112
112
|
# _LOGGER.debug("MQTT message received on topic %s: %s, iot_id: %s", topic, payload, iot_id)
|
|
113
113
|
json_str = payload.decode("utf-8")
|
|
114
114
|
dict_payload = json.loads(json_str)
|
|
115
|
-
|
|
116
|
-
await self._parse_mqtt_response(topic, dict_payload)
|
|
115
|
+
await self._parse_mqtt_response(topic, dict_payload, iot_id)
|
|
117
116
|
|
|
118
|
-
async def _parse_mqtt_response(self, topic: str, payload: dict) -> None:
|
|
117
|
+
async def _parse_mqtt_response(self, topic: str, payload: dict, iot_id: str) -> None:
|
|
119
118
|
"""Parse and handle MQTT responses based on the topic.
|
|
120
119
|
|
|
121
120
|
This function processes different types of MQTT messages received from various
|
|
@@ -150,6 +149,17 @@ class MammotionCloud:
|
|
|
150
149
|
property_event = ThingPropertiesMessage.from_dict(payload)
|
|
151
150
|
await self.mqtt_properties_event.data_event(property_event)
|
|
152
151
|
|
|
152
|
+
if topic.endswith("/thing/event/device_protobuf_msg_event/post"):
|
|
153
|
+
_LOGGER.debug("Mammotion Thing event received")
|
|
154
|
+
mammotion_event = MammotionEventMessage.from_dict(payload)
|
|
155
|
+
mammotion_event.params.iot_id = iot_id
|
|
156
|
+
await self.mqtt_message_event.data_event(mammotion_event)
|
|
157
|
+
elif topic.endswith("/thing/event/property/post"):
|
|
158
|
+
_LOGGER.debug("Mammotion Property event received")
|
|
159
|
+
mammotion_property_event = MammotionPropertiesMessage.from_dict(payload)
|
|
160
|
+
mammotion_property_event.params.iot_id = iot_id
|
|
161
|
+
await self.mqtt_properties_event.data_event(mammotion_property_event)
|
|
162
|
+
|
|
153
163
|
def _disconnect(self) -> None:
|
|
154
164
|
"""Disconnect the MQTT client."""
|
|
155
165
|
self._mqtt_client.disconnect()
|
|
@@ -229,6 +239,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
229
239
|
self.stopped = True
|
|
230
240
|
|
|
231
241
|
async def start(self) -> None:
|
|
242
|
+
"""Start the device connection."""
|
|
232
243
|
self.stopped = False
|
|
233
244
|
if not self.mqtt.is_connected():
|
|
234
245
|
loop = asyncio.get_running_loop()
|
|
@@ -277,18 +288,18 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
277
288
|
return None
|
|
278
289
|
|
|
279
290
|
async def _parse_message_properties_for_device(self, event: ThingPropertiesMessage) -> None:
|
|
280
|
-
if event.params.
|
|
291
|
+
if event.params.iot_id != self.iot_id:
|
|
281
292
|
return
|
|
282
293
|
await self.state_manager.properties(event)
|
|
283
294
|
|
|
284
295
|
async def _parse_message_status_for_device(self, status: ThingStatusMessage) -> None:
|
|
285
|
-
if status.params.
|
|
296
|
+
if status.params.iot_id != self.iot_id:
|
|
286
297
|
return
|
|
287
298
|
await self.state_manager.status(status)
|
|
288
299
|
|
|
289
300
|
async def _parse_device_event_for_device(self, status: ThingStatusMessage) -> None:
|
|
290
301
|
"""Process device event if it matches the device's IoT ID."""
|
|
291
|
-
if status.params.
|
|
302
|
+
if status.params.iot_id != self.iot_id:
|
|
292
303
|
return
|
|
293
304
|
await self.state_manager.device_event(status)
|
|
294
305
|
|
|
@@ -307,7 +318,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
307
318
|
"""
|
|
308
319
|
params = event.params
|
|
309
320
|
new_msg = LubaMsg()
|
|
310
|
-
if event.params.
|
|
321
|
+
if event.params.iot_id != self.iot_id:
|
|
311
322
|
return
|
|
312
323
|
binary_data = base64.b64decode(params.value.content)
|
|
313
324
|
try:
|
|
@@ -318,9 +329,9 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
318
329
|
|
|
319
330
|
if (
|
|
320
331
|
self._commands.get_device_product_key() == ""
|
|
321
|
-
and self._commands.get_device_name() == event.params.
|
|
332
|
+
and self._commands.get_device_name() == event.params.device_name
|
|
322
333
|
):
|
|
323
|
-
self._commands.set_device_product_key(event.params.
|
|
334
|
+
self._commands.set_device_product_key(event.params.product_key)
|
|
324
335
|
|
|
325
336
|
res = betterproto2.which_one_of(new_msg, "LubaSubMsg")
|
|
326
337
|
if res[0] == "net":
|
|
@@ -96,14 +96,14 @@ class MammotionRTKCloudDevice(MammotionRTKDevice):
|
|
|
96
96
|
|
|
97
97
|
async def _parse_message_properties_for_device(self, event: ThingPropertiesMessage) -> None:
|
|
98
98
|
"""Parse property messages for this RTK device."""
|
|
99
|
-
if event.params.
|
|
99
|
+
if event.params.iot_id != self.iot_id:
|
|
100
100
|
return
|
|
101
101
|
# RTK devices have simpler properties - update as needed
|
|
102
102
|
_LOGGER.debug("RTK properties update: %s", event)
|
|
103
103
|
|
|
104
104
|
async def _parse_message_status_for_device(self, status: ThingStatusMessage) -> None:
|
|
105
105
|
"""Parse status messages for this RTK device."""
|
|
106
|
-
if status.params.
|
|
106
|
+
if status.params.iot_id != self.iot_id:
|
|
107
107
|
return
|
|
108
108
|
# Update online status
|
|
109
109
|
self._rtk_device.online = True
|
|
@@ -11,27 +11,30 @@ from paho.mqtt.properties import Properties
|
|
|
11
11
|
from paho.mqtt.reasoncodes import ReasonCode
|
|
12
12
|
|
|
13
13
|
from pymammotion import MammotionHTTP
|
|
14
|
-
from pymammotion.http.model.http import MQTTConnection, Response
|
|
14
|
+
from pymammotion.http.model.http import DeviceRecord, MQTTConnection, Response
|
|
15
|
+
from pymammotion.utility.datatype_converter import DatatypeConverter
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class MammotionMQTT:
|
|
21
|
+
"""Mammotion MQTT Client."""
|
|
22
|
+
converter = DatatypeConverter()
|
|
23
|
+
|
|
20
24
|
def __init__(
|
|
21
|
-
self, mqtt_connection: MQTTConnection, mammotion_http: MammotionHTTP,
|
|
25
|
+
self, mqtt_connection: MQTTConnection, mammotion_http: MammotionHTTP, records: list[DeviceRecord]
|
|
22
26
|
) -> None:
|
|
23
27
|
self.on_connected: Callable[[], Awaitable[None]] | None = None
|
|
24
28
|
self.on_ready: Callable[[], Awaitable[None]] | None = None
|
|
25
29
|
self.on_error: Callable[[str], Awaitable[None]] | None = None
|
|
26
30
|
self.on_disconnected: Callable[[], Awaitable[None]] | None = None
|
|
27
|
-
self.on_message: Callable[[str,
|
|
31
|
+
self.on_message: Callable[[str, bytes, str], Awaitable[None]] | None = None
|
|
28
32
|
self.loop = asyncio.get_running_loop()
|
|
29
33
|
self.mammotion_http = mammotion_http
|
|
30
34
|
self.mqtt_connection = mqtt_connection
|
|
31
35
|
self.client = self.build(mqtt_connection)
|
|
32
36
|
|
|
33
|
-
self.
|
|
34
|
-
self.product_key = product_key
|
|
37
|
+
self.records = records
|
|
35
38
|
|
|
36
39
|
# wire callbacks from the service object if present
|
|
37
40
|
self.client.on_connect = self._on_connect
|
|
@@ -42,7 +45,8 @@ class MammotionMQTT:
|
|
|
42
45
|
|
|
43
46
|
def __del__(self) -> None:
|
|
44
47
|
if self.client.is_connected():
|
|
45
|
-
|
|
48
|
+
for record in self.records:
|
|
49
|
+
self.unsubscribe_all(record.product_key, record.device_name)
|
|
46
50
|
self.client.disconnect()
|
|
47
51
|
|
|
48
52
|
def connect_async(self) -> None:
|
|
@@ -103,14 +107,36 @@ class MammotionMQTT:
|
|
|
103
107
|
def _on_message(self, _client: mqtt.Client, _userdata: Any, message: mqtt.MQTTMessage) -> None:
|
|
104
108
|
"""Is called when message is received."""
|
|
105
109
|
logger.debug("Message on topic %s", message.topic)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
113
|
-
|
|
110
|
+
logger.debug(message)
|
|
111
|
+
|
|
112
|
+
if self.on_message is not None:
|
|
113
|
+
iot_id = None
|
|
114
|
+
# Parse the topic path to get product_key and device_name
|
|
115
|
+
topic_parts = message.topic.split("/")
|
|
116
|
+
if len(topic_parts) >= 4:
|
|
117
|
+
product_key = topic_parts[2]
|
|
118
|
+
device_name = topic_parts[3]
|
|
119
|
+
|
|
120
|
+
# Filter records to find matching device
|
|
121
|
+
filtered_records = [
|
|
122
|
+
record
|
|
123
|
+
for record in self.records
|
|
124
|
+
if record.product_key == product_key and record.device_name == device_name
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
if filtered_records:
|
|
128
|
+
iot_id = filtered_records[0].iot_id
|
|
129
|
+
payload = json.loads(message.payload.decode("utf-8"))
|
|
130
|
+
payload["iot_id"] = iot_id
|
|
131
|
+
payload["product_key"] = product_key
|
|
132
|
+
payload["device_name"] = device_name
|
|
133
|
+
message.payload = json.dumps(payload).encode("utf-8")
|
|
134
|
+
|
|
135
|
+
if iot_id:
|
|
136
|
+
future = asyncio.run_coroutine_threadsafe(
|
|
137
|
+
self.on_message(message.topic, message.payload, iot_id), self.loop
|
|
138
|
+
)
|
|
139
|
+
asyncio.wrap_future(future, loop=self.loop)
|
|
114
140
|
|
|
115
141
|
def _on_connect(
|
|
116
142
|
self,
|
|
@@ -122,11 +148,17 @@ class MammotionMQTT:
|
|
|
122
148
|
) -> None:
|
|
123
149
|
"""Handle connection event and execute callback if set."""
|
|
124
150
|
self.is_connected = True
|
|
151
|
+
for record in self.records:
|
|
152
|
+
self.subscribe_all(record.product_key, record.device_name)
|
|
125
153
|
if self.on_connected is not None:
|
|
126
|
-
self.subscribe_all(self.product_key, self.device_name)
|
|
127
154
|
future = asyncio.run_coroutine_threadsafe(self.on_connected(), self.loop)
|
|
128
155
|
asyncio.wrap_future(future, loop=self.loop)
|
|
129
156
|
|
|
157
|
+
if self.on_ready:
|
|
158
|
+
self.is_ready = True
|
|
159
|
+
future = asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop)
|
|
160
|
+
asyncio.wrap_future(future, loop=self.loop)
|
|
161
|
+
|
|
130
162
|
logger.debug("on_connect, session_flag:%s, rc:%s", session_flag, rc)
|
|
131
163
|
|
|
132
164
|
def _on_disconnect(
|
|
@@ -141,7 +173,8 @@ class MammotionMQTT:
|
|
|
141
173
|
"""Handle disconnection event and execute callback if set."""
|
|
142
174
|
self.is_connected = False
|
|
143
175
|
if self.on_disconnected is not None:
|
|
144
|
-
|
|
176
|
+
for record in self.records:
|
|
177
|
+
self.unsubscribe_all(record.product_key, record.device_name)
|
|
145
178
|
future = asyncio.run_coroutine_threadsafe(self.on_disconnected(), self.loop)
|
|
146
179
|
asyncio.wrap_future(future, loop=self.loop)
|
|
147
180
|
|
|
@@ -165,6 +198,13 @@ class MammotionMQTT:
|
|
|
165
198
|
|
|
166
199
|
async def send_cloud_command(self, iot_id: str, command: bytes) -> str:
|
|
167
200
|
"""Send command to cloud."""
|
|
168
|
-
res: Response[dict] = await self.mammotion_http.mqtt_invoke(
|
|
201
|
+
res: Response[dict] = await self.mammotion_http.mqtt_invoke(
|
|
202
|
+
self.converter.printBase64Binary(command), "", iot_id
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
logger.debug("send_cloud_command: %s", res)
|
|
206
|
+
|
|
207
|
+
if res.code == 500:
|
|
208
|
+
return res.msg
|
|
169
209
|
|
|
170
210
|
return str(res.data["result"])
|
|
@@ -2,7 +2,7 @@ import base64
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class DatatypeConverter:
|
|
5
|
-
encode_map = None
|
|
5
|
+
encode_map: list[str | int] | None = None
|
|
6
6
|
|
|
7
7
|
@staticmethod
|
|
8
8
|
def init_encode_map():
|
|
@@ -20,24 +20,25 @@ class DatatypeConverter:
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
if DatatypeConverter.encode_map is None:
|
|
23
|
-
|
|
23
|
+
c_arr: list[str | int] = [0] * 64
|
|
24
24
|
for num in range(26):
|
|
25
|
-
|
|
25
|
+
c_arr[num] = chr(num + 65)
|
|
26
26
|
for num_2 in range(26, 52):
|
|
27
|
-
|
|
27
|
+
c_arr[num_2] = chr(num_2 - 26 + 97)
|
|
28
28
|
for num_3 in range(52, 62):
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
DatatypeConverter.encode_map =
|
|
29
|
+
c_arr[num_3] = chr(num_3 - 52 + 48)
|
|
30
|
+
c_arr[62] = "+"
|
|
31
|
+
c_arr[63] = "/"
|
|
32
|
+
DatatypeConverter.encode_map = c_arr
|
|
33
33
|
return DatatypeConverter.encode_map
|
|
34
34
|
|
|
35
35
|
@staticmethod
|
|
36
|
-
def parseBase64Binary(s):
|
|
36
|
+
def parseBase64Binary(s: str) -> bytes:
|
|
37
37
|
return base64.b64decode(s)
|
|
38
38
|
|
|
39
39
|
@staticmethod
|
|
40
|
-
def printBase64Binary(bArr):
|
|
40
|
+
def printBase64Binary(bArr: bytes) -> str:
|
|
41
|
+
"""Print the Base64 str representation of a byte array."""
|
|
41
42
|
return DatatypeConverter._printBase64Binary(bArr)
|
|
42
43
|
|
|
43
44
|
@staticmethod
|
|
@@ -45,7 +46,7 @@ class DatatypeConverter:
|
|
|
45
46
|
return DatatypeConverter.encode_map[i & 63]
|
|
46
47
|
|
|
47
48
|
@staticmethod
|
|
48
|
-
def _printBase64Binary(bArr: bytes, i: int = 0, i2=None):
|
|
49
|
+
def _printBase64Binary(bArr: bytes, i: int = 0, i2=None) -> str:
|
|
49
50
|
"""Print the Base64 binary representation of a byte array.
|
|
50
51
|
|
|
51
52
|
This function takes a byte array and optional start and end indices to
|
|
@@ -68,7 +69,7 @@ class DatatypeConverter:
|
|
|
68
69
|
return "".join(cArr)
|
|
69
70
|
|
|
70
71
|
@staticmethod
|
|
71
|
-
def _printBase64Binary_core(bArr: bytes, i, i2, cArr, i3):
|
|
72
|
+
def _printBase64Binary_core(bArr: bytes, i, i2, cArr, i3) -> int:
|
|
72
73
|
"""Encode binary data into Base64 format.
|
|
73
74
|
|
|
74
75
|
This function encodes binary data into Base64 format following the
|
|
@@ -32,12 +32,12 @@ pymammotion/data/model/device.py,sha256=MeaB0ucFuNU5E4RAQ6qC8sTqdPJMw3lsMXR_YmYv
|
|
|
32
32
|
pymammotion/data/model/device_config.py,sha256=cLfvO_xs8GhEQbhTZLOfhYK5OX4T337M9OZkyQ_GNWQ,2652
|
|
33
33
|
pymammotion/data/model/device_info.py,sha256=vjqHlRbHOyEkkaIoTwAp14CZMY5R5o609SbThfOO-gg,1390
|
|
34
34
|
pymammotion/data/model/device_limits.py,sha256=m8HdxD-RaAkPm7jHYb9GLxMEH9IfzBPz0ZypmsLnId4,1946
|
|
35
|
-
pymammotion/data/model/enums.py,sha256=
|
|
35
|
+
pymammotion/data/model/enums.py,sha256=LVLP-9ypW0NxwyTeizxPVFNX3INWGfhSR9obM_vl0-M,1782
|
|
36
36
|
pymammotion/data/model/errors.py,sha256=lBHq2cE8P5fc6Q4JXgrkJXzFKTWgxsoPOyMlTaJWbWk,396
|
|
37
37
|
pymammotion/data/model/excute_boarder_params.py,sha256=9CpUqrygcle1C_1hDW-riLmm4map4ZbE842NXjcomEI,1394
|
|
38
38
|
pymammotion/data/model/execute_boarder.py,sha256=9rd_h4fbcsXxgnLOd2rO2hWyD1abnTGc47QTEpp8DD0,1103
|
|
39
39
|
pymammotion/data/model/generate_route_information.py,sha256=-_c8pk10zwRh-O2vJ0i3DDCOQbv9CRJ7YNWpfsIpajI,807
|
|
40
|
-
pymammotion/data/model/hash_list.py,sha256=
|
|
40
|
+
pymammotion/data/model/hash_list.py,sha256=jikG45FlsIBXaPzFN84WVN7Mn-c1yi3BmMC46aw2c3k,14817
|
|
41
41
|
pymammotion/data/model/location.py,sha256=PwmITejfI4pm7PI4rzqSuuHetwle6IJr_CV95435s2M,871
|
|
42
42
|
pymammotion/data/model/mowing_modes.py,sha256=4rMn1H8w2iU2aBwpmAhPh_sT81yqrocrWWUIaU7DCIc,1171
|
|
43
43
|
pymammotion/data/model/rapid_state.py,sha256=mIdhAG_LZXpVcybxqTLgLXkNOmVmDTn04B9PGIDA8Ls,1251
|
|
@@ -46,9 +46,10 @@ pymammotion/data/model/region_data.py,sha256=OP4hXYX2U1gNxU7VFsHQfQbE1Bze_nvVFQ0
|
|
|
46
46
|
pymammotion/data/model/report_info.py,sha256=3nXBFRfdKWnZj4YfBcrPwhBRpq58ICMQlrESfI1tTMg,3808
|
|
47
47
|
pymammotion/data/model/work.py,sha256=AfKMItFqnRtAlVHzKCfYY-BQy-WFDYZBzdj-9Yc03bo,655
|
|
48
48
|
pymammotion/data/mqtt/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
49
|
-
pymammotion/data/mqtt/event.py,sha256=
|
|
50
|
-
pymammotion/data/mqtt/
|
|
51
|
-
pymammotion/data/mqtt/
|
|
49
|
+
pymammotion/data/mqtt/event.py,sha256=JPzu2XCgyLwcdKsN9vqg94WMvHtsGH6vHHSFGstILjo,6911
|
|
50
|
+
pymammotion/data/mqtt/mammotion_properties.py,sha256=oMsHkMJQxLjiUu8wXtWiLV7cmUPM8sJk2GBVsBeIfNU,8528
|
|
51
|
+
pymammotion/data/mqtt/properties.py,sha256=lqd7TCQ04HVZdosCI_J_ovmF7E3VpFFWLz1sFRemIOE,5170
|
|
52
|
+
pymammotion/data/mqtt/status.py,sha256=jZ1Qx8tRhtBOguL7MOtR0jhsA1cRmVKoPJszho5A2Bs,1644
|
|
52
53
|
pymammotion/event/__init__.py,sha256=mgATR6vPHACNQ-0zH5fi7NdzeTCDV1CZyaWPmtUusi8,115
|
|
53
54
|
pymammotion/event/event.py,sha256=Z8WYxv_-5khEqKjL1w4c_Et24G1Kdm8QFuIBylD3h3U,3021
|
|
54
55
|
pymammotion/homeassistant/__init__.py,sha256=j0aQZKWR41pCDR3g1y2p_zfp033pESECcqXiefRg1DQ,107
|
|
@@ -57,7 +58,7 @@ pymammotion/homeassistant/rtk_api.py,sha256=YyTF_14cWvvT31H-LYD715W82KsWfkV1nwXs
|
|
|
57
58
|
pymammotion/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
59
|
pymammotion/http/_init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
60
|
pymammotion/http/encryption.py,sha256=lzXu3WwBdQlzjXxWnlJuRgkCrKdPbxx5drhMitVKIEk,8287
|
|
60
|
-
pymammotion/http/http.py,sha256=
|
|
61
|
+
pymammotion/http/http.py,sha256=UvHIVN4nB1Oua9otXvbWGaDfoC6-UjMzXgDxvEx7yog,20336
|
|
61
62
|
pymammotion/http/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
63
|
pymammotion/http/model/camera_stream.py,sha256=ilxQNny_w9Frwt-m8kbHinvyjDv4Bx8C2swfZ2lTEDE,600
|
|
63
64
|
pymammotion/http/model/http.py,sha256=GyWXIL4fNBssuE7WJR1IR89shWUEMbxRF3T9L24jGMM,6381
|
|
@@ -80,22 +81,22 @@ pymammotion/mammotion/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
|
|
|
80
81
|
pymammotion/mammotion/control/joystick.py,sha256=QfBVxM_gxpWsZAGO90whtgxCI2tIZ3TTad9wHIPsU9s,5640
|
|
81
82
|
pymammotion/mammotion/devices/__init__.py,sha256=YYphpxf0zr1Xc_dOBKsCzcO5zSwlewuhZNjAbmy6cHI,1012
|
|
82
83
|
pymammotion/mammotion/devices/base.py,sha256=MLLup0ZQI1GvyEF980BTtIeuayRZJijR1kvk77U1DS0,6518
|
|
83
|
-
pymammotion/mammotion/devices/mammotion.py,sha256=
|
|
84
|
+
pymammotion/mammotion/devices/mammotion.py,sha256=tI_K1P5rh3VUamMepIhWnIuWANHhBME6ViUlf8CRhzo,23767
|
|
84
85
|
pymammotion/mammotion/devices/mammotion_bluetooth.py,sha256=bwl1wphx2ev3iuDBTFTJbeujtll0L2ZBYDRzTcKBZes,19203
|
|
85
|
-
pymammotion/mammotion/devices/mammotion_cloud.py,sha256=
|
|
86
|
+
pymammotion/mammotion/devices/mammotion_cloud.py,sha256=8MosSefZtymgHhKxkp7nSHq7b5D6sNPobxoKYVMBy-I,15139
|
|
86
87
|
pymammotion/mammotion/devices/mammotion_mower_ble.py,sha256=GLc-f9TFhpGp2OYnVestzFqPSyGBMHX2YmrAnnmYu0I,1947
|
|
87
88
|
pymammotion/mammotion/devices/mammotion_mower_cloud.py,sha256=JwCQICv8S31c1ll23JIWFFJEwzbTss182MvE4ZiQxWs,1877
|
|
88
89
|
pymammotion/mammotion/devices/mower_device.py,sha256=VUgoJYlMKMOuy9j6MzxDmOIe6y7ziG-O-gUdDQPhkUM,5627
|
|
89
90
|
pymammotion/mammotion/devices/mower_manager.py,sha256=m2U90ZlNgSmdPAh8BtKtyMVt5ZTBpfxNs5RT_lsUTls,4280
|
|
90
91
|
pymammotion/mammotion/devices/rtk_ble.py,sha256=egZo4VJYS1_9r7e8f8FA21tD-bVthc-YgJbTtf0cNSI,3601
|
|
91
|
-
pymammotion/mammotion/devices/rtk_cloud.py,sha256=
|
|
92
|
+
pymammotion/mammotion/devices/rtk_cloud.py,sha256=gFzcnlSyWnyFLTYR-WOLpjIQD8Y1k844ZTkd-gdyIIk,4816
|
|
92
93
|
pymammotion/mammotion/devices/rtk_device.py,sha256=8wUCPqS6Xkox7sFj8AaXlVbzh67X9HarqldddBoWqBE,1917
|
|
93
94
|
pymammotion/mammotion/devices/rtk_manager.py,sha256=m963cKvfx2ThjYJpKb5_KrjqUPpvxK-DLVoF7TNXyP4,4392
|
|
94
95
|
pymammotion/mammotion/devices/managers/managers.py,sha256=d2z7oo2kMCsZ_YHSOBO9LazMSQxov0NNbr4EzJtxlYs,2495
|
|
95
96
|
pymammotion/mqtt/__init__.py,sha256=9nhy9GS8EbzOAPOrXHBTd_olePq8mctIkwMruDDSeWw,155
|
|
96
97
|
pymammotion/mqtt/aliyun_mqtt.py,sha256=CmGsxHfCYkhE5mJFPyV7NXcJs7srvpQo_6PVjDyK3Nk,10144
|
|
97
98
|
pymammotion/mqtt/mammotion_future.py,sha256=_OWqKOlUGl2yT1xOsXFQYpGd-1zQ63OxqXgy7KRQgYc,710
|
|
98
|
-
pymammotion/mqtt/mammotion_mqtt.py,sha256=
|
|
99
|
+
pymammotion/mqtt/mammotion_mqtt.py,sha256=Hz2lFvgUPSsz3DkLE_vh4IRnTvTOVhnzEsLm_hjofQg,8663
|
|
99
100
|
pymammotion/mqtt/mqtt_models.py,sha256=3JXP9Nals-3f27pYUqxD9WrfQHf5tpnal1j4R6vr-CM,1760
|
|
100
101
|
pymammotion/mqtt/linkkit/__init__.py,sha256=ENgc3ynd2kd9gMQR3-kgmCu6Ed9Y6XCIzU0zFReUlkk,80
|
|
101
102
|
pymammotion/mqtt/linkkit/h2client.py,sha256=w9Nvi_nY4CLD_fw-pHtYChwQf7e2TiAGeqkY_sF4cf0,19659
|
|
@@ -134,7 +135,7 @@ pymammotion/proto/mctrl_sys_pb2.pyi,sha256=N1sJJMx7qWsj1kKYoQJ4JEZanZetLZ5JThs1I
|
|
|
134
135
|
pymammotion/proto/message_pool.py,sha256=4-cRhhiM6bmfpUJZ8qxc8LEyqHBHpLCcotjbyZxl7JM,71
|
|
135
136
|
pymammotion/proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
136
137
|
pymammotion/utility/conversions.py,sha256=v3YICy0zZwwBBzrUZgabI7GRfiDBnkiAX2qdtk3NxOY,89
|
|
137
|
-
pymammotion/utility/datatype_converter.py,sha256=
|
|
138
|
+
pymammotion/utility/datatype_converter.py,sha256=A9qHBTbnq2PniAyBKxx3Qrk08aF5SIXGK1lDIY1siPU,4424
|
|
138
139
|
pymammotion/utility/device_config.py,sha256=65Jl73-dQDs4yMXwYXZW_bsgSvnwpFBZDu8OQPEIgx8,27877
|
|
139
140
|
pymammotion/utility/device_type.py,sha256=RdxBdkqzd03Q0MCCkbfqLj_CKrks8nNV4ji50UvJbH8,17024
|
|
140
141
|
pymammotion/utility/map.py,sha256=GYscVMg2cX3IPlNpCBNHDW0S55yS1WGRf1iHnNZ7TfQ,2227
|
|
@@ -144,7 +145,7 @@ pymammotion/utility/periodic.py,sha256=MbeSb9cfhxzYmdT_RiE0dZe3H9IfbQW_zSqhmSX2R
|
|
|
144
145
|
pymammotion/utility/rocker_util.py,sha256=6tX7sS87qoQC_tsxbx3NLL-HgS08wtzXiZkhDiz7uo0,7179
|
|
145
146
|
pymammotion/utility/constant/__init__.py,sha256=tcY0LDeD-qDDHx2LKt55KOyv9ZI0UfCNM6fknLCmm8s,110
|
|
146
147
|
pymammotion/utility/constant/device_constant.py,sha256=MJE1DEOg86oWNSXYGx3xxJ542KEuSJNZ0VJLN4eHbUg,8237
|
|
147
|
-
pymammotion-0.5.
|
|
148
|
-
pymammotion-0.5.
|
|
149
|
-
pymammotion-0.5.
|
|
150
|
-
pymammotion-0.5.
|
|
148
|
+
pymammotion-0.5.43.dist-info/METADATA,sha256=71E2QhJPO5sCX_c1ddqj1SHjgi7pxcc6O6Yzy90PzRU,3575
|
|
149
|
+
pymammotion-0.5.43.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
150
|
+
pymammotion-0.5.43.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
151
|
+
pymammotion-0.5.43.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|