pymammotion 0.2.34__py3-none-any.whl → 0.2.36__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymammotion/aliyun/cloud_gateway.py +1 -0
- pymammotion/aliyun/dataclass/session_by_authcode_response.py +3 -3
- pymammotion/data/model/device_config.py +2 -2
- pymammotion/data/model/hash_list.py +2 -1
- pymammotion/data/mqtt/event.py +2 -5
- pymammotion/data/state_manager.py +7 -5
- pymammotion/mammotion/commands/messages/system.py +53 -41
- pymammotion/mammotion/devices/base.py +7 -6
- pymammotion/mammotion/devices/mammotion.py +76 -35
- pymammotion/mammotion/devices/mammotion_bluetooth.py +1 -0
- pymammotion/mammotion/devices/mammotion_cloud.py +8 -3
- pymammotion/utility/device_type.py +0 -1
- {pymammotion-0.2.34.dist-info → pymammotion-0.2.36.dist-info}/METADATA +1 -1
- {pymammotion-0.2.34.dist-info → pymammotion-0.2.36.dist-info}/RECORD +16 -16
- {pymammotion-0.2.34.dist-info → pymammotion-0.2.36.dist-info}/LICENSE +0 -0
- {pymammotion-0.2.34.dist-info → pymammotion-0.2.36.dist-info}/WHEEL +0 -0
@@ -586,6 +586,7 @@ class CloudIOTGateway:
|
|
586
586
|
request=request,
|
587
587
|
version="1.0",
|
588
588
|
)
|
589
|
+
logger.debug(self.converter.printBase64Binary(command))
|
589
590
|
|
590
591
|
# send request
|
591
592
|
response = client.do_request("/thing/service/invoke", "https", "POST", None, body, RuntimeOptions())
|
@@ -1,5 +1,5 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
|
-
from
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import Optional
|
3
3
|
|
4
4
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
5
5
|
|
@@ -16,4 +16,4 @@ class SessionOauthToken(DataClassORJSONMixin):
|
|
16
16
|
@dataclass
|
17
17
|
class SessionByAuthCodeResponse(DataClassORJSONMixin):
|
18
18
|
code: int
|
19
|
-
data: SessionOauthToken
|
19
|
+
data: Optional[SessionOauthToken]
|
@@ -43,7 +43,7 @@ def create_path_order(operation_mode: OperationSettings, device_name: str) -> st
|
|
43
43
|
bArr = bytearray(8)
|
44
44
|
bArr[0] = operation_mode.border_mode
|
45
45
|
bArr[1] = operation_mode.obstacle_laps
|
46
|
-
bArr[3] = operation_mode.start_progress
|
46
|
+
bArr[3] = int(operation_mode.start_progress)
|
47
47
|
bArr[2] = 0
|
48
48
|
|
49
49
|
if not DeviceType.is_luba1(device_name):
|
@@ -54,7 +54,7 @@ def create_path_order(operation_mode: OperationSettings, device_name: str) -> st
|
|
54
54
|
i = 0
|
55
55
|
bArr[5] = i
|
56
56
|
if operation_mode.is_dump:
|
57
|
-
b = operation_mode.collect_grass_frequency
|
57
|
+
b = int(operation_mode.collect_grass_frequency)
|
58
58
|
else:
|
59
59
|
b = 10
|
60
60
|
bArr[6] = b
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from dataclasses import dataclass, field
|
2
2
|
from enum import IntEnum
|
3
3
|
|
4
|
-
from pymammotion.proto.mctrl_nav import
|
4
|
+
from pymammotion.proto.mctrl_nav import AreaHashName, NavGetCommDataAck
|
5
5
|
|
6
6
|
|
7
7
|
class PathType(IntEnum):
|
@@ -24,6 +24,7 @@ class HashList:
|
|
24
24
|
[hashID, FrameList].
|
25
25
|
hashlist for all our hashIDs for verification
|
26
26
|
"""
|
27
|
+
|
27
28
|
area: dict # type 0
|
28
29
|
path: dict # type 2
|
29
30
|
obstacle: dict # type 1
|
pymammotion/data/mqtt/event.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
import base64
|
2
1
|
from base64 import b64decode
|
3
2
|
from dataclasses import dataclass
|
4
|
-
from typing import Any, Literal, Optional, Union
|
3
|
+
from typing import Any, Literal, Optional, Union
|
5
4
|
|
6
5
|
from google.protobuf import json_format
|
7
6
|
from mashumaro.mixins.orjson import DataClassORJSONMixin
|
@@ -40,8 +39,6 @@ class DeviceProtobufMsgEventValue(DataClassORJSONMixin):
|
|
40
39
|
content: str
|
41
40
|
|
42
41
|
|
43
|
-
|
44
|
-
|
45
42
|
@dataclass
|
46
43
|
class DeviceWarningEventValue(DataClassORJSONMixin):
|
47
44
|
# TODO: enum for error codes
|
@@ -64,7 +61,7 @@ class DeviceNotificationEventCode(DataClassORJSONMixin):
|
|
64
61
|
|
65
62
|
@dataclass
|
66
63
|
class DeviceNotificationEventValue(DataClassORJSONMixin):
|
67
|
-
data: str
|
64
|
+
data: str # parsed to DeviceNotificationEventCode
|
68
65
|
|
69
66
|
|
70
67
|
@dataclass
|
@@ -1,12 +1,12 @@
|
|
1
1
|
"""Manage state from notifications into MowingDevice."""
|
2
2
|
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Awaitable, Callable, Optional
|
4
4
|
|
5
5
|
import betterproto
|
6
6
|
|
7
7
|
from pymammotion.data.model.device import MowingDevice
|
8
8
|
from pymammotion.proto.luba_msg import LubaMsg
|
9
|
-
from pymammotion.proto.mctrl_nav import NavGetCommDataAck, NavGetHashListAck
|
9
|
+
from pymammotion.proto.mctrl_nav import AppGetAllAreaHashName, NavGetCommDataAck, NavGetHashListAck
|
10
10
|
|
11
11
|
|
12
12
|
class StateManager:
|
@@ -19,6 +19,7 @@ class StateManager:
|
|
19
19
|
self.gethash_ack_callback: Optional[Callable[[NavGetHashListAck], Awaitable[None]]] = None
|
20
20
|
self.get_commondata_ack_callback: Optional[Callable[[NavGetCommDataAck], Awaitable[None]]] = None
|
21
21
|
self.on_notification_callback: Optional[Callable[[], Awaitable[None]]] = None
|
22
|
+
self.queue_command_callback: Optional[Callable[[str, dict[str, Any]], Awaitable[bytes]]] = None
|
22
23
|
|
23
24
|
def get_device(self) -> MowingDevice:
|
24
25
|
"""Get device."""
|
@@ -36,7 +37,7 @@ class StateManager:
|
|
36
37
|
case "nav":
|
37
38
|
await self._update_nav_data(message)
|
38
39
|
case "sys":
|
39
|
-
self._update_sys_data(message)
|
40
|
+
await self._update_sys_data(message)
|
40
41
|
case "driver":
|
41
42
|
self._update_driver_data(message)
|
42
43
|
case "net":
|
@@ -66,8 +67,7 @@ class StateManager:
|
|
66
67
|
hash_names: AppGetAllAreaHashName = nav_msg[1]
|
67
68
|
self._device.map.area_name = hash_names.hashnames
|
68
69
|
|
69
|
-
|
70
|
-
def _update_sys_data(self, message) -> None:
|
70
|
+
async def _update_sys_data(self, message) -> None:
|
71
71
|
"""Update system."""
|
72
72
|
sys_msg = betterproto.which_one_of(message.sys, "SubSysMsg")
|
73
73
|
match sys_msg[0]:
|
@@ -75,6 +75,8 @@ class StateManager:
|
|
75
75
|
self._device.buffer(sys_msg[1])
|
76
76
|
case "toapp_report_data":
|
77
77
|
self._device.update_report_data(sys_msg[1])
|
78
|
+
if self.queue_command_callback:
|
79
|
+
await self.queue_command_callback("get_report_cfg", stop=True)
|
78
80
|
case "mow_to_app_info":
|
79
81
|
self._device.mow_info(sys_msg[1])
|
80
82
|
case "system_tard_state_tunnel":
|
@@ -4,8 +4,20 @@ from abc import ABC
|
|
4
4
|
|
5
5
|
from pymammotion.mammotion.commands.abstract_message import AbstractMessage
|
6
6
|
from pymammotion.mammotion.commands.messages.navigation import MessageNavigation
|
7
|
-
from pymammotion.proto import
|
8
|
-
from pymammotion.proto.mctrl_sys import
|
7
|
+
from pymammotion.proto.luba_msg import LubaMsg, MsgAttr, MsgCmdType, MsgDevice
|
8
|
+
from pymammotion.proto.mctrl_sys import (
|
9
|
+
DeviceProductTypeInfoT,
|
10
|
+
LoraCfgReq,
|
11
|
+
MctlSys,
|
12
|
+
MCtrlSimulationCmdData,
|
13
|
+
ReportInfoCfg,
|
14
|
+
RptAct,
|
15
|
+
RptInfoType,
|
16
|
+
SysCommCmd,
|
17
|
+
SysKnifeControl,
|
18
|
+
SysSetDateTime,
|
19
|
+
TimeCtrlLight,
|
20
|
+
)
|
9
21
|
from pymammotion.utility.device_type import DeviceType
|
10
22
|
|
11
23
|
|
@@ -13,37 +25,35 @@ class MessageSystem(AbstractMessage, ABC):
|
|
13
25
|
messageNavigation: MessageNavigation = MessageNavigation()
|
14
26
|
|
15
27
|
def send_order_msg_sys(self, sys):
|
16
|
-
luba_msg =
|
17
|
-
msgtype=
|
18
|
-
sender=
|
19
|
-
rcver=
|
28
|
+
luba_msg = LubaMsg(
|
29
|
+
msgtype=MsgCmdType.MSG_CMD_TYPE_EMBED_SYS,
|
30
|
+
sender=MsgDevice.DEV_MOBILEAPP,
|
31
|
+
rcver=MsgDevice.DEV_MAINCTL,
|
20
32
|
sys=sys,
|
21
33
|
)
|
22
34
|
|
23
35
|
return luba_msg.SerializeToString()
|
24
36
|
|
25
37
|
def reset_system(self):
|
26
|
-
build =
|
38
|
+
build = MctlSys(todev_reset_system=1)
|
27
39
|
print("Send command - send factory reset")
|
28
40
|
return self.send_order_msg_sys(build)
|
29
41
|
|
30
42
|
def set_blade_control(self, on_off: int):
|
31
|
-
mctlsys =
|
32
|
-
sys_knife_control =
|
43
|
+
mctlsys = MctlSys()
|
44
|
+
sys_knife_control = SysKnifeControl()
|
33
45
|
sys_knife_control.knife_status = on_off
|
34
|
-
mctlsys.todev_knife_ctrl
|
46
|
+
mctlsys.todev_knife_ctrl = sys_knife_control
|
35
47
|
|
36
48
|
return self.send_order_msg_sys(mctlsys)
|
37
49
|
|
38
50
|
def get_device_product_model(self):
|
39
|
-
return self.send_order_msg_sys(
|
40
|
-
mctrl_sys_pb2.MctlSys(device_product_type_info=mctrl_sys_pb2.device_product_type_info_t())
|
41
|
-
)
|
51
|
+
return self.send_order_msg_sys(MctlSys(device_product_type_info=DeviceProductTypeInfoT()))
|
42
52
|
|
43
53
|
def read_and_set_sidelight(self, is_sidelight: bool, operate: int):
|
44
54
|
"""Read state of sidelight as well as set it."""
|
45
55
|
if is_sidelight:
|
46
|
-
build =
|
56
|
+
build = TimeCtrlLight(
|
47
57
|
operate=operate,
|
48
58
|
enable=0,
|
49
59
|
action=0,
|
@@ -53,7 +63,7 @@ class MessageSystem(AbstractMessage, ABC):
|
|
53
63
|
end_min=0,
|
54
64
|
)
|
55
65
|
else:
|
56
|
-
build =
|
66
|
+
build = TimeCtrlLight(
|
57
67
|
operate=operate,
|
58
68
|
enable=1,
|
59
69
|
action=0,
|
@@ -64,16 +74,16 @@ class MessageSystem(AbstractMessage, ABC):
|
|
64
74
|
)
|
65
75
|
print(f"Send read and write sidelight command is_sidelight:{
|
66
76
|
is_sidelight}, operate:{operate}")
|
67
|
-
build2 =
|
77
|
+
build2 = MctlSys(todev_time_ctrl_light=build)
|
68
78
|
print(f"Send command - send read and write sidelight command is_sidelight:{
|
69
79
|
is_sidelight}, operate:{operate}, timeCtrlLight:{build}")
|
70
80
|
return self.send_order_msg_sys(build2)
|
71
81
|
|
72
82
|
def test_tool_order_to_sys(self, sub_cmd: int, param_id: int, param_value: list[int]):
|
73
|
-
build =
|
83
|
+
build = MCtrlSimulationCmdData(sub_cmd=sub_cmd, param_id=param_id, param_value=param_value)
|
74
84
|
print(f"Send tool test command: subCmd={sub_cmd}, param_id:{
|
75
85
|
param_id}, param_value={param_value}")
|
76
|
-
build2 =
|
86
|
+
build2 = MctlSys(simulation_cmd=build)
|
77
87
|
print(f"Send tool test command: subCmd={sub_cmd}, param_id:{
|
78
88
|
param_id}, param_value={param_value}")
|
79
89
|
return self.send_order_msg_sys(build2)
|
@@ -81,15 +91,13 @@ class MessageSystem(AbstractMessage, ABC):
|
|
81
91
|
def read_and_set_rtk_paring_code(self, op: int, cgf: str):
|
82
92
|
print(f"Send read and write base station configuration quality op:{
|
83
93
|
op}, cgf:{cgf}")
|
84
|
-
return self.send_order_msg_sys(
|
85
|
-
mctrl_sys_pb2.MctlSys(todev_lora_cfg_req=mctrl_sys_pb2.LoraCfgReq(op=op, cfg=cgf))
|
86
|
-
)
|
94
|
+
return self.send_order_msg_sys(MctlSys(todev_lora_cfg_req=LoraCfgReq(op=op, cfg=cgf)))
|
87
95
|
|
88
96
|
def allpowerfull_rw(self, id: int, context: int, rw: int):
|
89
97
|
if (id == 6 or id == 3 or id == 7) and DeviceType.is_luba_2(self.get_device_name()):
|
90
98
|
self.messageNavigation.allpowerfull_rw_adapter_x3(id, context, rw)
|
91
99
|
return
|
92
|
-
build =
|
100
|
+
build = MctlSys(bidire_comm_cmd=SysCommCmd(id=id, context=context, rw=rw))
|
93
101
|
print(f"Send command - 9 general read and write command id={id}, context={context}, rw={rw}")
|
94
102
|
if id == 5:
|
95
103
|
# This logic doesnt make snese, but its what they had so..
|
@@ -98,7 +106,7 @@ class MessageSystem(AbstractMessage, ABC):
|
|
98
106
|
|
99
107
|
# Commented out as not needed and too many refs to try fix up
|
100
108
|
# def factory_test_order(self, test_id: int, test_duration: int, expect: str):
|
101
|
-
# new_builder =
|
109
|
+
# new_builder = mow_to_app_qctools_info_t.Builder()
|
102
110
|
# print(f"Factory tool print, expect={expect}")
|
103
111
|
# if not expect:
|
104
112
|
# build = new_builder.set_type_value(
|
@@ -108,7 +116,7 @@ class MessageSystem(AbstractMessage, ABC):
|
|
108
116
|
# json_array = json.loads(expect)
|
109
117
|
# z2 = True
|
110
118
|
# for i in range(len(json_array)):
|
111
|
-
# new_builder2 =
|
119
|
+
# new_builder2 = QCAppTestExcept.Builder()
|
112
120
|
# json_object = json_array[i]
|
113
121
|
# if "except_type" in json_object:
|
114
122
|
# string = json_object["except_type"]
|
@@ -116,7 +124,7 @@ class MessageSystem(AbstractMessage, ABC):
|
|
116
124
|
# json_array2 = json_object["conditions"]
|
117
125
|
# for i2 in range(len(json_array2)):
|
118
126
|
# json_object2 = json_array2[i2]
|
119
|
-
# new_builder3 =
|
127
|
+
# new_builder3 = QCAppTestConditions.Builder()
|
120
128
|
# if "cond_type" in json_object2:
|
121
129
|
# new_builder3.set_cond_type(
|
122
130
|
# json_object2["cond_type"])
|
@@ -151,7 +159,7 @@ class MessageSystem(AbstractMessage, ABC):
|
|
151
159
|
# test_id).set_time_of_duration(test_duration).build()
|
152
160
|
# print(f"Factory tool print, mow_to_app_qctools_info_t={
|
153
161
|
# build.except_count}, mow_to_app_qctools_info_t22={build.except_list}")
|
154
|
-
# build2 =
|
162
|
+
# build2 = MctlSys(mow_to_app_qctools_info=build)
|
155
163
|
# print(f"Send command - factory tool test command testId={
|
156
164
|
# test_id}, testDuration={test_duration}", "Factory tool print222", True)
|
157
165
|
# return self.send_order_msg_sys(build2)
|
@@ -169,8 +177,8 @@ class MessageSystem(AbstractMessage, ABC):
|
|
169
177
|
i9 = 1 if calendar.dst() else 0
|
170
178
|
print(f"Print time zone, time zone={
|
171
179
|
i8}, daylight saving time={i9} week={i4}")
|
172
|
-
build =
|
173
|
-
todev_data_time=
|
180
|
+
build = MctlSys(
|
181
|
+
todev_data_time=SysSetDateTime(
|
174
182
|
year=i,
|
175
183
|
month=i2,
|
176
184
|
date=i3,
|
@@ -191,21 +199,21 @@ class MessageSystem(AbstractMessage, ABC):
|
|
191
199
|
return self.send_order_msg_sys(build)
|
192
200
|
|
193
201
|
def get_device_version_info(self):
|
194
|
-
return self.send_order_msg_sys(
|
202
|
+
return self.send_order_msg_sys(MctlSys(todev_get_dev_fw_info=1))
|
195
203
|
|
196
204
|
# === sendOrderMsg_Sys2 ===
|
197
205
|
|
198
206
|
def request_iot_sys(
|
199
207
|
self,
|
200
|
-
rpt_act:
|
208
|
+
rpt_act: RptAct,
|
201
209
|
rpt_info_type: list[RptInfoType | str] | None,
|
202
210
|
timeout: int,
|
203
211
|
period: int,
|
204
212
|
no_change_period: int,
|
205
213
|
count: int,
|
206
214
|
) -> bytes:
|
207
|
-
build =
|
208
|
-
todev_report_cfg=
|
215
|
+
build = MctlSys(
|
216
|
+
todev_report_cfg=ReportInfoCfg(
|
209
217
|
act=rpt_act,
|
210
218
|
sub=rpt_info_type,
|
211
219
|
timeout=timeout,
|
@@ -218,9 +226,12 @@ class MessageSystem(AbstractMessage, ABC):
|
|
218
226
|
build.todev_report_cfg.act} {build}")
|
219
227
|
return self.send_order_msg_sys(build)
|
220
228
|
|
221
|
-
def get_report_cfg(
|
222
|
-
|
223
|
-
|
229
|
+
def get_report_cfg(
|
230
|
+
self, timeout: int = 10000, period: int = 1000, no_change_period: int = 1000, stop: bool = False
|
231
|
+
):
|
232
|
+
mctlsys = MctlSys(
|
233
|
+
todev_report_cfg=ReportInfoCfg(
|
234
|
+
act=RptAct.RPT_STOP if stop else RptAct.RPT_START,
|
224
235
|
timeout=timeout,
|
225
236
|
period=period,
|
226
237
|
no_change_period=no_change_period,
|
@@ -233,17 +244,18 @@ class MessageSystem(AbstractMessage, ABC):
|
|
233
244
|
mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_LOCAL.value)
|
234
245
|
mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_WORK.value)
|
235
246
|
mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_DEV_STA.value)
|
247
|
+
mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_MAINTAIN.value)
|
236
248
|
mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_POINT.value)
|
237
249
|
mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VIO.value)
|
238
250
|
mctlsys.todev_report_cfg.sub.append(RptInfoType.RIT_VISION_STATISTIC.value)
|
239
251
|
|
240
|
-
lubaMsg =
|
241
|
-
lubaMsg.msgtype =
|
242
|
-
lubaMsg.sender =
|
243
|
-
lubaMsg.rcver =
|
244
|
-
lubaMsg.msgattr =
|
252
|
+
lubaMsg = LubaMsg()
|
253
|
+
lubaMsg.msgtype = MsgCmdType.MSG_CMD_TYPE_EMBED_SYS
|
254
|
+
lubaMsg.sender = MsgDevice.DEV_MOBILEAPP
|
255
|
+
lubaMsg.rcver = MsgDevice.DEV_MAINCTL
|
256
|
+
lubaMsg.msgattr = MsgAttr.MSG_ATTR_REQ
|
245
257
|
lubaMsg.seqs = 1
|
246
258
|
lubaMsg.version = 1
|
247
259
|
lubaMsg.subtype = 1
|
248
|
-
lubaMsg.sys
|
260
|
+
lubaMsg.sys = mctlsys
|
249
261
|
return lubaMsg.SerializeToString()
|
@@ -18,7 +18,7 @@ from pymammotion.utility.movement import get_percent, transform_both_speeds
|
|
18
18
|
_LOGGER = logging.getLogger(__name__)
|
19
19
|
|
20
20
|
|
21
|
-
def find_next_integer(lst: list[int], current_hash:
|
21
|
+
def find_next_integer(lst: list[int], current_hash: int) -> int | None:
|
22
22
|
try:
|
23
23
|
# Find the index of the current integer
|
24
24
|
current_index = lst.index(current_hash)
|
@@ -54,6 +54,9 @@ class MammotionBaseDevice:
|
|
54
54
|
def set_notification_callback(self, func: Callable[[], Awaitable[None]]) -> None:
|
55
55
|
self._state_manager.on_notification_callback = func
|
56
56
|
|
57
|
+
def set_queue_callback(self, func: Callable[[str, dict[str, Any]], Awaitable[bytes]]) -> None:
|
58
|
+
self._state_manager.queue_command_callback = func
|
59
|
+
|
57
60
|
async def datahash_response(self, hash_ack: NavGetHashListAck) -> None:
|
58
61
|
"""Handle datahash responses."""
|
59
62
|
await self.queue_command("synchronize_hash_data", hash_num=hash_ack.data_couple[0])
|
@@ -206,8 +209,9 @@ class MammotionBaseDevice:
|
|
206
209
|
await self.queue_command("get_area_name_list", device_id=self._cloud_device.deviceName)
|
207
210
|
if has_field(self._mower.net.toapp_wifi_iot_status):
|
208
211
|
if not DeviceType.is_luba1(self._mower.net.toapp_wifi_iot_status.devicename):
|
209
|
-
await self.queue_command(
|
210
|
-
|
212
|
+
await self.queue_command(
|
213
|
+
"get_area_name_list", device_id=self._mower.net.toapp_wifi_iot_status.devicename
|
214
|
+
)
|
211
215
|
except Exception:
|
212
216
|
"""Do nothing for now."""
|
213
217
|
|
@@ -220,9 +224,6 @@ class MammotionBaseDevice:
|
|
220
224
|
for data_hash in self.mower.nav.toapp_gethash_ack.data_couple:
|
221
225
|
await self.queue_command("synchronize_hash_data", hash_num=data_hash)
|
222
226
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
227
|
# sub_cmd 3 is job hashes??
|
227
228
|
# sub_cmd 4 is dump location (yuka)
|
228
229
|
# jobs list
|
@@ -37,7 +37,8 @@ class ConnectionPreference(Enum):
|
|
37
37
|
class MammotionMixedDeviceManager:
|
38
38
|
_ble_device: MammotionBaseBLEDevice | None = None
|
39
39
|
_cloud_device: MammotionBaseCloudDevice | None = None
|
40
|
-
_mowing_state: MowingDevice
|
40
|
+
_mowing_state: MowingDevice
|
41
|
+
preference: ConnectionPreference
|
41
42
|
|
42
43
|
def __init__(
|
43
44
|
self,
|
@@ -45,10 +46,13 @@ class MammotionMixedDeviceManager:
|
|
45
46
|
cloud_device: Device | None = None,
|
46
47
|
ble_device: BLEDevice | None = None,
|
47
48
|
mqtt: MammotionCloud | None = None,
|
49
|
+
preference: ConnectionPreference = ConnectionPreference.BLUETOOTH,
|
48
50
|
) -> None:
|
49
51
|
self.name = name
|
52
|
+
self._mowing_state = MowingDevice()
|
50
53
|
self.add_ble(ble_device)
|
51
54
|
self.add_cloud(cloud_device, mqtt)
|
55
|
+
self.preference = preference
|
52
56
|
|
53
57
|
def mower_state(self):
|
54
58
|
return self._mowing_state
|
@@ -101,19 +105,30 @@ class MammotionDevices:
|
|
101
105
|
def get_device(self, mammotion_device_name: str) -> MammotionMixedDeviceManager:
|
102
106
|
return self.devices.get(mammotion_device_name)
|
103
107
|
|
108
|
+
def remove_device(self, name) -> None:
|
109
|
+
device_for_removal = self.devices.pop(name)
|
110
|
+
if device_for_removal.has_cloud():
|
111
|
+
should_disconnect = {
|
112
|
+
device
|
113
|
+
for key, device in self.devices.items()
|
114
|
+
if device.cloud() is not None and device.cloud()._mqtt == device_for_removal.cloud()._mqtt
|
115
|
+
}
|
116
|
+
if len(should_disconnect) == 0:
|
117
|
+
device_for_removal.cloud()._mqtt.disconnect()
|
118
|
+
|
104
119
|
|
105
120
|
async def create_devices(
|
106
121
|
ble_device: BLEDevice,
|
107
122
|
cloud_credentials: Credentials | None = None,
|
108
123
|
preference: ConnectionPreference = ConnectionPreference.BLUETOOTH,
|
109
124
|
):
|
110
|
-
mammotion = Mammotion(
|
125
|
+
mammotion = Mammotion()
|
126
|
+
mammotion.add_ble_device(ble_device, preference)
|
111
127
|
|
112
128
|
if cloud_credentials and preference == ConnectionPreference.EITHER or preference == ConnectionPreference.WIFI:
|
113
|
-
|
129
|
+
await mammotion.login_and_initiate_cloud(
|
114
130
|
cloud_credentials.account_id or cloud_credentials.email, cloud_credentials.password
|
115
131
|
)
|
116
|
-
await mammotion.initiate_cloud_connection(cloud_client)
|
117
132
|
|
118
133
|
return mammotion
|
119
134
|
|
@@ -123,26 +138,44 @@ class Mammotion:
|
|
123
138
|
"""Represents a Mammotion account and its devices."""
|
124
139
|
|
125
140
|
devices = MammotionDevices()
|
126
|
-
|
127
|
-
mqtt: MammotionCloud | None = None
|
141
|
+
mqtt_list: dict[str, MammotionCloud] = dict()
|
128
142
|
|
129
|
-
|
130
|
-
|
131
|
-
|
143
|
+
_instance = None
|
144
|
+
|
145
|
+
def __new__(cls, *args, **kwargs):
|
146
|
+
if not cls._instance:
|
147
|
+
cls._instance = super().__new__(cls)
|
148
|
+
return cls._instance
|
149
|
+
|
150
|
+
def __init__(self) -> None:
|
132
151
|
"""Initialize MammotionDevice."""
|
133
|
-
|
134
|
-
self.devices.add_device(MammotionMixedDeviceManager(name=ble_device.name, ble_device=ble_device))
|
152
|
+
self._login_lock = asyncio.Lock()
|
135
153
|
|
136
|
-
|
137
|
-
|
154
|
+
def add_ble_device(self, ble_device: BLEDevice, preference: ConnectionPreference = ConnectionPreference.BLUETOOTH) -> None:
|
155
|
+
if ble_device:
|
156
|
+
self.devices.add_device(
|
157
|
+
MammotionMixedDeviceManager(name=ble_device.name, ble_device=ble_device, preference=preference)
|
158
|
+
)
|
138
159
|
|
139
|
-
async def
|
140
|
-
|
141
|
-
|
160
|
+
async def login_and_initiate_cloud(self, account, password, force: bool = False) -> None:
|
161
|
+
async with self._login_lock:
|
162
|
+
exists: MammotionCloud | None = self.mqtt_list.get(account)
|
163
|
+
if not exists or force:
|
164
|
+
cloud_client = await self.login(account, password)
|
165
|
+
else:
|
166
|
+
cloud_client = exists.cloud_client
|
167
|
+
|
168
|
+
await self.initiate_cloud_connection(account, cloud_client)
|
169
|
+
|
170
|
+
async def initiate_cloud_connection(self, account: str, cloud_client: CloudIOTGateway) -> None:
|
171
|
+
if self.mqtt_list.get(account) is not None:
|
172
|
+
if self.mqtt_list.get(account).is_connected:
|
173
|
+
# we might have removed a device so readd
|
174
|
+
self.add_cloud_devices(self.mqtt_list.get(account))
|
142
175
|
return
|
143
176
|
|
144
177
|
self.cloud_client = cloud_client
|
145
|
-
self.
|
178
|
+
self.mqtt_list[account] = MammotionCloud(
|
146
179
|
MammotionMQTT(
|
147
180
|
region_id=cloud_client.region_response.data.regionId,
|
148
181
|
product_key=cloud_client.aep_response.data.productKey,
|
@@ -151,24 +184,30 @@ class Mammotion:
|
|
151
184
|
iot_token=cloud_client.session_by_authcode_response.data.iotToken,
|
152
185
|
client_id=cloud_client.client_id,
|
153
186
|
cloud_client=cloud_client,
|
154
|
-
)
|
187
|
+
),
|
188
|
+
cloud_client,
|
155
189
|
)
|
156
190
|
|
157
191
|
loop = asyncio.get_running_loop()
|
158
|
-
await loop.run_in_executor(None, self.
|
192
|
+
await loop.run_in_executor(None, self.mqtt_list[account].connect_async)
|
159
193
|
|
160
|
-
|
194
|
+
def add_cloud_devices(self, mqtt_client: MammotionCloud) -> None:
|
195
|
+
for device in mqtt_client.cloud_client.devices_by_account_response.data.data:
|
161
196
|
mower_device = self.devices.get_device(device.deviceName)
|
162
197
|
if device.deviceName.startswith(("Luba-", "Yuka-")) and mower_device is None:
|
163
198
|
self.devices.add_device(
|
164
|
-
MammotionMixedDeviceManager(
|
199
|
+
MammotionMixedDeviceManager(
|
200
|
+
name=device.deviceName,
|
201
|
+
cloud_device=device,
|
202
|
+
mqtt=mqtt_client,
|
203
|
+
preference=ConnectionPreference.WIFI,
|
204
|
+
)
|
165
205
|
)
|
166
206
|
elif device.deviceName.startswith(("Luba-", "Yuka-")) and mower_device:
|
167
207
|
if mower_device.cloud() is None:
|
168
|
-
mower_device.add_cloud(cloud_device=device, mqtt=
|
169
|
-
|
170
|
-
|
171
|
-
|
208
|
+
mower_device.add_cloud(cloud_device=device, mqtt=mqtt_client)
|
209
|
+
elif mqtt_client != mower_device.cloud().mqtt:
|
210
|
+
mower_device.replace_mqtt(mqtt_client)
|
172
211
|
|
173
212
|
def set_disconnect_strategy(self, disconnect: bool) -> None:
|
174
213
|
for device_name, device in self.devices.devices:
|
@@ -176,8 +215,7 @@ class Mammotion:
|
|
176
215
|
ble_device: MammotionBaseBLEDevice = device.ble()
|
177
216
|
ble_device.set_disconnect_strategy(disconnect)
|
178
217
|
|
179
|
-
|
180
|
-
async def login(account: str, password: str) -> CloudIOTGateway:
|
218
|
+
async def login(self, account: str, password: str) -> CloudIOTGateway:
|
181
219
|
"""Login to mammotion cloud."""
|
182
220
|
cloud_client = CloudIOTGateway()
|
183
221
|
async with ClientSession(MAMMOTION_DOMAIN) as session:
|
@@ -198,6 +236,9 @@ class Mammotion:
|
|
198
236
|
await loop.run_in_executor(None, cloud_client.list_binding_by_account)
|
199
237
|
return cloud_client
|
200
238
|
|
239
|
+
def remove_device(self, name: str) -> None:
|
240
|
+
self.devices.remove_device(name)
|
241
|
+
|
201
242
|
def get_device_by_name(self, name: str) -> MammotionMixedDeviceManager:
|
202
243
|
return self.devices.get_device(name)
|
203
244
|
|
@@ -205,9 +246,9 @@ class Mammotion:
|
|
205
246
|
"""Send a command to the device."""
|
206
247
|
device = self.get_device_by_name(name)
|
207
248
|
if device:
|
208
|
-
if
|
249
|
+
if device.preference is ConnectionPreference.BLUETOOTH:
|
209
250
|
return await device.ble().command(key)
|
210
|
-
if
|
251
|
+
if device.preference is ConnectionPreference.WIFI:
|
211
252
|
return await device.cloud().command(key)
|
212
253
|
# TODO work with both with EITHER
|
213
254
|
|
@@ -215,27 +256,27 @@ class Mammotion:
|
|
215
256
|
"""Send a command with args to the device."""
|
216
257
|
device = self.get_device_by_name(name)
|
217
258
|
if device:
|
218
|
-
if
|
259
|
+
if device.preference is ConnectionPreference.BLUETOOTH:
|
219
260
|
return await device.ble().command(key, **kwargs)
|
220
|
-
if
|
261
|
+
if device.preference is ConnectionPreference.WIFI:
|
221
262
|
return await device.cloud().command(key, **kwargs)
|
222
263
|
# TODO work with both with EITHER
|
223
264
|
|
224
265
|
async def start_sync(self, name: str, retry: int):
|
225
266
|
device = self.get_device_by_name(name)
|
226
267
|
if device:
|
227
|
-
if
|
268
|
+
if device.preference is ConnectionPreference.BLUETOOTH:
|
228
269
|
return await device.ble().start_sync(retry)
|
229
|
-
if
|
270
|
+
if device.preference is ConnectionPreference.WIFI:
|
230
271
|
return await device.cloud().start_sync(retry)
|
231
272
|
# TODO work with both with EITHER
|
232
273
|
|
233
274
|
async def start_map_sync(self, name: str):
|
234
275
|
device = self.get_device_by_name(name)
|
235
276
|
if device:
|
236
|
-
if
|
277
|
+
if device.preference is ConnectionPreference.BLUETOOTH:
|
237
278
|
return await device.ble().start_map_sync()
|
238
|
-
if
|
279
|
+
if device.preference is ConnectionPreference.WIFI:
|
239
280
|
return await device.cloud().start_map_sync()
|
240
281
|
# TODO work with both with EITHER
|
241
282
|
|
@@ -88,6 +88,7 @@ class MammotionBaseBLEDevice(MammotionBaseDevice):
|
|
88
88
|
self._connect_lock = asyncio.Lock()
|
89
89
|
self._operation_lock = asyncio.Lock()
|
90
90
|
self._key: str | None = None
|
91
|
+
self.set_queue_callback(self.queue_command)
|
91
92
|
|
92
93
|
def update_device(self, device: BLEDevice) -> None:
|
93
94
|
"""Update the BLE device."""
|
@@ -7,7 +7,7 @@ from typing import Any, Awaitable, Callable, Optional, cast
|
|
7
7
|
|
8
8
|
import betterproto
|
9
9
|
|
10
|
-
from pymammotion import MammotionMQTT
|
10
|
+
from pymammotion import CloudIOTGateway, MammotionMQTT
|
11
11
|
from pymammotion.aliyun.dataclass.dev_by_account_response import Device
|
12
12
|
from pymammotion.data.model.device import MowingDevice
|
13
13
|
from pymammotion.data.mqtt.event import ThingEventMessage
|
@@ -24,7 +24,8 @@ _LOGGER = logging.getLogger(__name__)
|
|
24
24
|
class MammotionCloud:
|
25
25
|
"""Per account MQTT cloud."""
|
26
26
|
|
27
|
-
def __init__(self, mqtt_client: MammotionMQTT) -> None:
|
27
|
+
def __init__(self, mqtt_client: MammotionMQTT, cloud_client: CloudIOTGateway) -> None:
|
28
|
+
self.cloud_client = cloud_client
|
28
29
|
self.loop = asyncio.get_event_loop()
|
29
30
|
self._ble_sync_task = None
|
30
31
|
self.is_ready = False
|
@@ -155,6 +156,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
155
156
|
self.currentID = ""
|
156
157
|
self._mqtt.mqtt_message_event.add_subscribers(self._parse_message_for_device)
|
157
158
|
self._mqtt.on_ready_event.add_subscribers(self.on_ready)
|
159
|
+
self.set_queue_callback(self.queue_command)
|
158
160
|
|
159
161
|
if self._mqtt.is_ready:
|
160
162
|
self.run_periodic_sync_task()
|
@@ -186,7 +188,6 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
186
188
|
160, lambda: asyncio.ensure_future(self.run_periodic_sync_task())
|
187
189
|
)
|
188
190
|
|
189
|
-
|
190
191
|
async def queue_command(self, key: str, **kwargs: Any) -> bytes:
|
191
192
|
# Create a future to hold the result
|
192
193
|
_LOGGER.debug("Queueing command: %s", key)
|
@@ -245,3 +246,7 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
245
246
|
if not fut.fut.cancelled():
|
246
247
|
fut.resolve(cast(bytes, binary_data))
|
247
248
|
await self._state_manager.notification(new_msg)
|
249
|
+
|
250
|
+
@property
|
251
|
+
def mqtt(self):
|
252
|
+
return self._mqtt
|
@@ -95,7 +95,6 @@ class DeviceType(Enum):
|
|
95
95
|
elif DeviceType.LUBA.get_name() in substring2 or DeviceType.contain_luba_product_key(product_key):
|
96
96
|
return DeviceType.LUBA
|
97
97
|
else:
|
98
|
-
print("unknown device type")
|
99
98
|
return DeviceType.UNKNOWN
|
100
99
|
except Exception:
|
101
100
|
return DeviceType.UNKNOWN
|
@@ -1,13 +1,13 @@
|
|
1
1
|
pymammotion/__init__.py,sha256=jHCQrpJaG1jAoID9T4RT3g4JsZc0JpJqIcqjnA7cXd0,1605
|
2
2
|
pymammotion/aliyun/__init__.py,sha256=T1lkX7TRYiL4nqYanG4l4MImV-SlavSbuooC-W-uUGw,29
|
3
|
-
pymammotion/aliyun/cloud_gateway.py,sha256=
|
3
|
+
pymammotion/aliyun/cloud_gateway.py,sha256=X6Y9yyIXCwkPwgRqsBESUI5v6j-CKlJGJaEnMINyp8c,22780
|
4
4
|
pymammotion/aliyun/cloud_service.py,sha256=px7dUKow5Z7VyebjYzuKkzkm77XbUXYiFiYO_2e-UQ0,2207
|
5
5
|
pymammotion/aliyun/dataclass/aep_response.py,sha256=8f6GIP58ve8gd6AL3HBoXxsy0n2q4ygWvjELGnoOnVc,452
|
6
6
|
pymammotion/aliyun/dataclass/connect_response.py,sha256=Yz-fEbDzgGPTo5Of2oAjmFkSv08T7ze80pQU4k-gKIU,824
|
7
7
|
pymammotion/aliyun/dataclass/dev_by_account_response.py,sha256=gskum11h9HPf4lKjLJKVrsxRl5BHaHJP2TPrI09SUYs,1032
|
8
8
|
pymammotion/aliyun/dataclass/login_by_oauth_response.py,sha256=IXSLZ6XnOliOnyXo5Bh0ErqFjA11puACh_9NH0sSJGQ,1262
|
9
9
|
pymammotion/aliyun/dataclass/regions_response.py,sha256=CVPpdFhDD6_emWHyLRzOdp2j3HLPtP8tlNyzGnr8AcI,690
|
10
|
-
pymammotion/aliyun/dataclass/session_by_authcode_response.py,sha256=
|
10
|
+
pymammotion/aliyun/dataclass/session_by_authcode_response.py,sha256=FVqT2EmePt4ze83sndkw45GHtMsx12nsMU7Nljd35RM,412
|
11
11
|
pymammotion/aliyun/tmp_constant.py,sha256=M4Hq_lrGB3LZdX6R2XohRPFoK1NDnNV-pTJwJcJ9838,6650
|
12
12
|
pymammotion/bluetooth/__init__.py,sha256=LAl8jqZ1fPh-3mLmViNQsP3s814C1vsocYUa6oSaXt0,36
|
13
13
|
pymammotion/bluetooth/ble.py,sha256=YfkfEK3TLJ8BaidjAXfUVFv8reLCu6U_lYa3Bo0pddw,2449
|
@@ -22,12 +22,12 @@ pymammotion/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
|
|
22
22
|
pymammotion/data/model/__init__.py,sha256=aSyroxYQQS-WMRi6WmWm2js4wLa9nmsi160gx9tts4o,323
|
23
23
|
pymammotion/data/model/account.py,sha256=vJM-KTf2q6eBfVC-UlNHBSmJvqHiCawZ40vnuhXhaz8,140
|
24
24
|
pymammotion/data/model/device.py,sha256=ejIMloTHlJcBi-qNFewTDt1K0pPJYpgjtJqqqhUqR1g,11182
|
25
|
-
pymammotion/data/model/device_config.py,sha256=
|
25
|
+
pymammotion/data/model/device_config.py,sha256=Ez55DiYV7QTxgSk6R6XjUSZ7XdTGf8w38VaFsAOBNv8,2637
|
26
26
|
pymammotion/data/model/enums.py,sha256=EpKmO8yVUZyEnTY4yH0DMMVKYNQM42zpW1maUu0i3IE,1582
|
27
27
|
pymammotion/data/model/excute_boarder_params.py,sha256=9CpUqrygcle1C_1hDW-riLmm4map4ZbE842NXjcomEI,1394
|
28
28
|
pymammotion/data/model/execute_boarder.py,sha256=9rd_h4fbcsXxgnLOd2rO2hWyD1abnTGc47QTEpp8DD0,1103
|
29
29
|
pymammotion/data/model/generate_route_information.py,sha256=MkUBoqGtCAKmiVQ4Q1pEoDVHZs5uLIo7vhfWT4nGbtY,801
|
30
|
-
pymammotion/data/model/hash_list.py,sha256=
|
30
|
+
pymammotion/data/model/hash_list.py,sha256=fszjpbt0e7DNqibQnPaYCTiuK39gwtNVjBF0hUyMeeE,3025
|
31
31
|
pymammotion/data/model/location.py,sha256=H1h4Rhr0z_mDplNf1CP_ZCA3zM4_FJ_nMqzkbaoh7To,787
|
32
32
|
pymammotion/data/model/mowing_modes.py,sha256=5TrHSijUyPtIDWpNtgzx_vFQukRJWRz4gIrUaXggKPw,827
|
33
33
|
pymammotion/data/model/plan.py,sha256=mcadkSL7fQXy0iJ0q786I3GEQY4i6kmQXfW6Ri69lcQ,2906
|
@@ -35,10 +35,10 @@ pymammotion/data/model/rapid_state.py,sha256=BdJFD_DlhrVneg-PqEruqCoMty-CR7q_9Qn
|
|
35
35
|
pymammotion/data/model/region_data.py,sha256=FLuL6kA7lbbh_idRh1eT9EosDqh4SpAqzpqHuQRDM88,2888
|
36
36
|
pymammotion/data/model/report_info.py,sha256=gBSOmylSUdsYyIDsRms0L0nhQBx4V4LO-4tERvFpqXU,4475
|
37
37
|
pymammotion/data/mqtt/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
38
|
-
pymammotion/data/mqtt/event.py,sha256=
|
38
|
+
pymammotion/data/mqtt/event.py,sha256=GKh4NdcaeeaG5pPktzy-eguFKK74Mfxpl0eIAkKU1Vc,4567
|
39
39
|
pymammotion/data/mqtt/properties.py,sha256=HkBPghr26L9_b4QaOi1DtPgb0UoPIOGSe9wb3kgnM6Y,2815
|
40
40
|
pymammotion/data/mqtt/status.py,sha256=zqnlo-MzejEQZszl0i0Wucoc3E76x6UtI9JLxoBnu54,1067
|
41
|
-
pymammotion/data/state_manager.py,sha256=
|
41
|
+
pymammotion/data/state_manager.py,sha256=ZIOB1Th7yDAgCJk17rd-kXjwD50GIqQMsNgf1iFldUs,3615
|
42
42
|
pymammotion/event/__init__.py,sha256=mgATR6vPHACNQ-0zH5fi7NdzeTCDV1CZyaWPmtUusi8,115
|
43
43
|
pymammotion/event/event.py,sha256=UzYnxV5DfvMDK3E06UvSzvzuBbaXOOUwO6xYt_zn9To,2034
|
44
44
|
pymammotion/http/_init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -54,15 +54,15 @@ pymammotion/mammotion/commands/messages/media.py,sha256=ps0l06CXy5Ej--gTNCsyKttw
|
|
54
54
|
pymammotion/mammotion/commands/messages/navigation.py,sha256=4rXBL-mViWc38K6x1w5O-GjwV8UWS5xZXkf4aHYjs8A,23684
|
55
55
|
pymammotion/mammotion/commands/messages/network.py,sha256=gD7NKVKg8U2KNbPvgOxvTJXbznWdpdPQo9jBsQSx4OI,8027
|
56
56
|
pymammotion/mammotion/commands/messages/ota.py,sha256=XkeuWBZtpYMMBze6r8UN7dJXbe2FxUNGNnjwBpXJKM0,1240
|
57
|
-
pymammotion/mammotion/commands/messages/system.py,sha256=
|
57
|
+
pymammotion/mammotion/commands/messages/system.py,sha256=4ijnn0VPHOIizgjqDGpeD3jVetEscnSDt2E4tIBwkoQ,10888
|
58
58
|
pymammotion/mammotion/commands/messages/video.py,sha256=_8lJsU4sLm2CGnc7RDkueA0A51Ysui6x7SqFnhX8O2g,1007
|
59
59
|
pymammotion/mammotion/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
60
|
pymammotion/mammotion/control/joystick.py,sha256=QfBVxM_gxpWsZAGO90whtgxCI2tIZ3TTad9wHIPsU9s,5640
|
61
61
|
pymammotion/mammotion/devices/__init__.py,sha256=f2qQFPgLGmV85W2hSlMUh5BYuht9o_Ar_JEAAMD4fsE,102
|
62
|
-
pymammotion/mammotion/devices/base.py,sha256=
|
63
|
-
pymammotion/mammotion/devices/mammotion.py,sha256=
|
64
|
-
pymammotion/mammotion/devices/mammotion_bluetooth.py,sha256=
|
65
|
-
pymammotion/mammotion/devices/mammotion_cloud.py,sha256=
|
62
|
+
pymammotion/mammotion/devices/base.py,sha256=5k82KHwW2JqZXW-MBYe8SkedZw_bQ2gqShX5RQd_Yio,11504
|
63
|
+
pymammotion/mammotion/devices/mammotion.py,sha256=uiKtzLHfVPyk-cdm5gFraI9nkrIX3ZkdZPE7xEyuoTQ,11597
|
64
|
+
pymammotion/mammotion/devices/mammotion_bluetooth.py,sha256=sgGeyQeAeA3lQodcalRYS4nDNAzjfFs9SddIB1kadvw,17355
|
65
|
+
pymammotion/mammotion/devices/mammotion_cloud.py,sha256=dk49VY9yHO3d-Nb17y-D4YIURs2FTLMzWHUYrxBYhtw,10116
|
66
66
|
pymammotion/mqtt/__init__.py,sha256=Ocs5e-HLJvTuDpVXyECEsWIvwsUaxzj7lZ9mSYutNDY,105
|
67
67
|
pymammotion/mqtt/mammotion_future.py,sha256=_OWqKOlUGl2yT1xOsXFQYpGd-1zQ63OxqXgy7KRQgYc,710
|
68
68
|
pymammotion/mqtt/mammotion_mqtt.py,sha256=QZuqp2G5nywCFSwGNSayPI5JVL789yOxyr0UM0G7wzg,8295
|
@@ -112,12 +112,12 @@ pymammotion/utility/constant/__init__.py,sha256=tcY0LDeD-qDDHx2LKt55KOyv9ZI0UfCN
|
|
112
112
|
pymammotion/utility/constant/device_constant.py,sha256=rAEK60F52VyJL31uLnq0Y60D-0VK5gVm59yi9kBfndM,7123
|
113
113
|
pymammotion/utility/conversions.py,sha256=v3YICy0zZwwBBzrUZgabI7GRfiDBnkiAX2qdtk3NxOY,89
|
114
114
|
pymammotion/utility/datatype_converter.py,sha256=SPM_HuaaD_XOawlqEnA8qlRRZXGba3WjA8kGOZgeBlQ,4284
|
115
|
-
pymammotion/utility/device_type.py,sha256=
|
115
|
+
pymammotion/utility/device_type.py,sha256=xOgfIhOkzgcAtoKtlhlB1q8FpiKe1rVVV5BvN7K7zYc,9433
|
116
116
|
pymammotion/utility/map.py,sha256=GYscVMg2cX3IPlNpCBNHDW0S55yS1WGRf1iHnNZ7TfQ,2227
|
117
117
|
pymammotion/utility/movement.py,sha256=N75oAoAgFydqoaOedYIxGUHmuTCtPzAOtb-d_29tpfI,615
|
118
118
|
pymammotion/utility/periodic.py,sha256=MbeSb9cfhxzYmdT_RiE0dZe3H9IfbQW_zSqhmSX2RUc,3321
|
119
119
|
pymammotion/utility/rocker_util.py,sha256=6tX7sS87qoQC_tsxbx3NLL-HgS08wtzXiZkhDiz7uo0,7179
|
120
|
-
pymammotion-0.2.
|
121
|
-
pymammotion-0.2.
|
122
|
-
pymammotion-0.2.
|
123
|
-
pymammotion-0.2.
|
120
|
+
pymammotion-0.2.36.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
121
|
+
pymammotion-0.2.36.dist-info/METADATA,sha256=oKsEdniFXVqz9dbL-obKVKoZWsKWlS0xDGjaob8n3Lc,4052
|
122
|
+
pymammotion-0.2.36.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
123
|
+
pymammotion-0.2.36.dist-info/RECORD,,
|
File without changes
|
File without changes
|