pymammotion 0.0.40__py3-none-any.whl → 0.0.41__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/__init__.py +4 -2
- pymammotion/aliyun/__init__.py +1 -0
- pymammotion/aliyun/cloud_gateway.py +74 -95
- pymammotion/aliyun/tmp_constant.py +2 -6
- pymammotion/bluetooth/ble.py +4 -12
- pymammotion/bluetooth/ble_message.py +12 -36
- pymammotion/bluetooth/data/convert.py +1 -3
- pymammotion/bluetooth/data/notifydata.py +0 -1
- pymammotion/data/model/device.py +62 -3
- pymammotion/data/model/hash_list.py +34 -14
- pymammotion/data/model/location.py +40 -0
- pymammotion/data/model/rapid_state.py +1 -5
- pymammotion/data/state_manager.py +84 -0
- pymammotion/event/event.py +18 -3
- pymammotion/http/http.py +2 -6
- pymammotion/mammotion/commands/mammotion_command.py +1 -3
- pymammotion/mammotion/commands/messages/driver.py +7 -21
- pymammotion/mammotion/commands/messages/media.py +4 -9
- pymammotion/mammotion/commands/messages/navigation.py +42 -107
- pymammotion/mammotion/commands/messages/network.py +10 -30
- pymammotion/mammotion/commands/messages/system.py +11 -26
- pymammotion/mammotion/commands/messages/video.py +1 -3
- pymammotion/mammotion/control/joystick.py +9 -33
- pymammotion/mammotion/devices/__init__.py +5 -1
- pymammotion/mammotion/devices/{luba.py → mammotion.py} +299 -110
- pymammotion/mqtt/__init__.py +5 -0
- pymammotion/mqtt/{mqtt.py → mammotion_mqtt.py} +46 -50
- pymammotion/utility/constant/device_constant.py +14 -0
- pymammotion/utility/datatype_converter.py +52 -9
- pymammotion/utility/device_type.py +129 -20
- pymammotion/utility/periodic.py +65 -0
- pymammotion/utility/rocker_util.py +63 -4
- {pymammotion-0.0.40.dist-info → pymammotion-0.0.41.dist-info}/METADATA +10 -4
- {pymammotion-0.0.40.dist-info → pymammotion-0.0.41.dist-info}/RECORD +36 -34
- {pymammotion-0.0.40.dist-info → pymammotion-0.0.41.dist-info}/WHEEL +1 -1
- pymammotion/luba/_init_.py +0 -0
- pymammotion/luba/base.py +0 -52
- {pymammotion-0.0.40.dist-info → pymammotion-0.0.41.dist-info}/LICENSE +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""MammotionMQTT."""
|
|
2
|
+
|
|
1
3
|
import hashlib
|
|
2
4
|
import hmac
|
|
3
5
|
import json
|
|
@@ -8,22 +10,18 @@ from typing import Callable, Optional, cast
|
|
|
8
10
|
from linkkit.linkkit import LinkKit
|
|
9
11
|
from paho.mqtt.client import Client, MQTTMessage, MQTTv311, connack_string
|
|
10
12
|
|
|
11
|
-
from pymammotion.
|
|
13
|
+
from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
|
|
12
14
|
from pymammotion.data.mqtt.event import ThingEventMessage
|
|
13
15
|
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
|
14
16
|
from pymammotion.data.mqtt.status import ThingStatusMessage
|
|
15
|
-
from pymammotion.luba.base import BaseLuba
|
|
16
|
-
from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
|
|
17
17
|
from pymammotion.proto import luba_msg_pb2
|
|
18
18
|
|
|
19
19
|
logger = getLogger(__name__)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
class MammotionMQTT:
|
|
23
|
+
"""MQTT client for pymammotion."""
|
|
25
24
|
|
|
26
|
-
class LubaMQTT(BaseLuba):
|
|
27
25
|
_cloud_client = None
|
|
28
26
|
|
|
29
27
|
def __init__(
|
|
@@ -35,11 +33,13 @@ class LubaMQTT(BaseLuba):
|
|
|
35
33
|
iot_token: str,
|
|
36
34
|
client_id: Optional[str] = None,
|
|
37
35
|
):
|
|
36
|
+
"""Create instance of MammotionMQTT."""
|
|
38
37
|
super().__init__()
|
|
39
38
|
|
|
40
39
|
self.on_connected: Optional[Callable[[], None]] = None
|
|
41
40
|
self.on_error: Optional[Callable[[str], None]] = None
|
|
42
41
|
self.on_disconnected: Optional[Callable[[], None]] = None
|
|
42
|
+
self.on_message: Optional[Callable[[str, str, str], None]] = None
|
|
43
43
|
|
|
44
44
|
self._product_key = product_key
|
|
45
45
|
self._device_name = device_name
|
|
@@ -50,9 +50,7 @@ class LubaMQTT(BaseLuba):
|
|
|
50
50
|
if client_id is None:
|
|
51
51
|
client_id = f"python-{device_name}"
|
|
52
52
|
self._mqtt_client_id = f"{client_id}|securemode=2,signmethod=hmacsha1|"
|
|
53
|
-
sign_content =
|
|
54
|
-
f"clientId{client_id}deviceName{device_name}productKey{product_key}"
|
|
55
|
-
)
|
|
53
|
+
sign_content = f"clientId{client_id}deviceName{device_name}productKey{product_key}"
|
|
56
54
|
self._mqtt_password = hmac.new(
|
|
57
55
|
device_secret.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha1
|
|
58
56
|
).hexdigest()
|
|
@@ -90,11 +88,13 @@ class LubaMQTT(BaseLuba):
|
|
|
90
88
|
|
|
91
89
|
# region Connection handling
|
|
92
90
|
def connect(self):
|
|
91
|
+
"""Connect to MQTT Server."""
|
|
93
92
|
logger.info("Connecting...")
|
|
94
93
|
self._client.connect(host=self._mqtt_host)
|
|
95
94
|
self._client.loop_forever()
|
|
96
95
|
|
|
97
96
|
def connect_async(self):
|
|
97
|
+
"""Connect async to MQTT Server."""
|
|
98
98
|
logger.info("Connecting...")
|
|
99
99
|
self._linkkit_client.thing_setup()
|
|
100
100
|
self._linkkit_client.connect_async()
|
|
@@ -103,14 +103,16 @@ class LubaMQTT(BaseLuba):
|
|
|
103
103
|
self._linkkit_client.start_worker_loop()
|
|
104
104
|
|
|
105
105
|
def disconnect(self):
|
|
106
|
+
"""Disconnect from MQTT Server."""
|
|
106
107
|
logger.info("Disconnecting...")
|
|
107
108
|
self._linkkit_client.disconnect()
|
|
108
109
|
self._client.disconnect()
|
|
109
110
|
self._client.loop_stop()
|
|
110
111
|
|
|
111
112
|
def _thing_on_thing_enable(self, user_data):
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
"""Is called when Thing is enabled."""
|
|
114
|
+
logger.debug("on_thing_enable")
|
|
115
|
+
# logger.debug('subscribe_topic, topic:%s' % echo_topic)
|
|
114
116
|
# self._linkkit_client.subscribe_topic(echo_topic, 0)
|
|
115
117
|
self._linkkit_client.subscribe_topic(
|
|
116
118
|
f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
|
|
@@ -118,15 +120,9 @@ class LubaMQTT(BaseLuba):
|
|
|
118
120
|
self._linkkit_client.subscribe_topic(
|
|
119
121
|
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/event/property/post_reply"
|
|
120
122
|
)
|
|
121
|
-
self._linkkit_client.subscribe_topic(
|
|
122
|
-
|
|
123
|
-
)
|
|
124
|
-
self._linkkit_client.subscribe_topic(
|
|
125
|
-
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status"
|
|
126
|
-
)
|
|
127
|
-
self._linkkit_client.subscribe_topic(
|
|
128
|
-
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties"
|
|
129
|
-
)
|
|
123
|
+
self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events")
|
|
124
|
+
self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status")
|
|
125
|
+
self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties")
|
|
130
126
|
self._linkkit_client.subscribe_topic(
|
|
131
127
|
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/model/down_raw"
|
|
132
128
|
)
|
|
@@ -144,27 +140,34 @@ class LubaMQTT(BaseLuba):
|
|
|
144
140
|
)
|
|
145
141
|
|
|
146
142
|
# self._linkkit_client.query_ota_firmware()
|
|
147
|
-
command = MammotionCommand(device_name="Luba")
|
|
148
|
-
self._cloud_client.send_cloud_command(command.get_report_cfg())
|
|
143
|
+
# command = MammotionCommand(device_name="Luba")
|
|
144
|
+
# self._cloud_client.send_cloud_command(command.get_report_cfg())
|
|
149
145
|
|
|
150
146
|
def _thing_on_topic_message(self, topic, payload, qos, user_data):
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
147
|
+
"""Is called when thing topic comes in."""
|
|
148
|
+
logger.debug(
|
|
149
|
+
"on_topic_message, receive message, topic:%s, payload:%s, qos:%d",
|
|
150
|
+
topic,
|
|
151
|
+
payload,
|
|
152
|
+
qos,
|
|
154
153
|
)
|
|
154
|
+
payload = json.loads(payload)
|
|
155
|
+
iot_id = payload.get("params", {}).get("iotId", "")
|
|
156
|
+
if iot_id != "" and self.on_message:
|
|
157
|
+
self.on_message(topic, payload, iot_id)
|
|
155
158
|
|
|
156
159
|
def _thing_on_connect(self, session_flag, rc, user_data):
|
|
157
|
-
|
|
160
|
+
"""Is called on thing connect."""
|
|
161
|
+
logger.debug("on_connect, session_flag:%d, rc:%d", session_flag, rc)
|
|
158
162
|
|
|
159
163
|
# self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/#")
|
|
160
164
|
|
|
161
165
|
def _on_connect(self, _client, _userdata, _flags: dict, rc: int):
|
|
166
|
+
"""Is called when on connect."""
|
|
162
167
|
if rc == 0:
|
|
163
|
-
logger.
|
|
168
|
+
logger.debug("Connected")
|
|
164
169
|
self._client.subscribe(f"/sys/{self._product_key}/{self._device_name}/#")
|
|
165
|
-
self._client.subscribe(
|
|
166
|
-
f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
|
|
167
|
-
)
|
|
170
|
+
self._client.subscribe(f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply")
|
|
168
171
|
|
|
169
172
|
self._client.publish(
|
|
170
173
|
f"/sys/{self._product_key}/{self._device_name}/app/up/account/bind",
|
|
@@ -186,18 +189,15 @@ class LubaMQTT(BaseLuba):
|
|
|
186
189
|
self.on_error(connack_string(rc))
|
|
187
190
|
|
|
188
191
|
def _on_disconnect(self, _client, _userdata, rc: int):
|
|
192
|
+
"""Is called on disconnect."""
|
|
189
193
|
logger.info("Disconnected")
|
|
190
194
|
logger.debug(rc)
|
|
191
195
|
if self.on_disconnected:
|
|
192
196
|
self.on_disconnected()
|
|
193
197
|
|
|
194
|
-
# endregion
|
|
195
|
-
|
|
196
198
|
def _on_message(self, _client, _userdata, message: MQTTMessage):
|
|
199
|
+
"""Is called when message is received."""
|
|
197
200
|
logger.info("Message on topic %s", message.topic)
|
|
198
|
-
# with sqlite3.connect("messages.db") as conn:
|
|
199
|
-
# conn.execute("INSERT INTO messages (topic, timestamp, payload) VALUES (?, ?, ?)",
|
|
200
|
-
# (message.topic, int(message.timestamp), message.payload.decode("utf-8")))
|
|
201
201
|
|
|
202
202
|
payload = json.loads(message.payload)
|
|
203
203
|
if message.topic.endswith("/app/down/thing/events"):
|
|
@@ -205,26 +205,22 @@ class LubaMQTT(BaseLuba):
|
|
|
205
205
|
params = event.params
|
|
206
206
|
if params.identifier == "device_protobuf_msg_event":
|
|
207
207
|
content = cast(luba_msg_pb2, params.value.content)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
and content.sys.WhichOneof("subSysMsg") == "systemRapidState"
|
|
211
|
-
):
|
|
212
|
-
state = RapidState.from_raw(content.sys.systemRapidState.data)
|
|
213
|
-
self._set_rapid_state(state)
|
|
214
|
-
else:
|
|
215
|
-
logger.info(
|
|
216
|
-
"Unhandled protobuf event: %s", content.WhichOneof("subMsg")
|
|
217
|
-
)
|
|
208
|
+
|
|
209
|
+
logger.info("Unhandled protobuf event: %s", content.WhichOneof("subMsg"))
|
|
218
210
|
elif params.identifier == "device_warning_event":
|
|
219
|
-
|
|
220
|
-
self.on_warning(params.value.code)
|
|
211
|
+
logger.debug("identifier event: %s", params.identifier)
|
|
221
212
|
else:
|
|
222
213
|
logger.info("Unhandled event: %s", params.identifier)
|
|
223
214
|
elif message.topic.endswith("/app/down/thing/status"):
|
|
224
215
|
status = ThingStatusMessage(**payload)
|
|
225
|
-
|
|
216
|
+
logger.debug(status.params.status.value)
|
|
226
217
|
elif message.topic.endswith("/app/down/thing/properties"):
|
|
227
218
|
properties = ThingPropertiesMessage(**payload)
|
|
219
|
+
logger.debug("properties: %s", properties)
|
|
228
220
|
else:
|
|
229
|
-
logger.
|
|
221
|
+
logger.debug("Unhandled topic: %s", message.topic)
|
|
230
222
|
logger.debug(payload)
|
|
223
|
+
|
|
224
|
+
def get_cloud_client(self) -> Optional[CloudIOTGateway]:
|
|
225
|
+
"""Return internal cloud client."""
|
|
226
|
+
return self._cloud_client
|
|
@@ -210,6 +210,20 @@ class WorkMode:
|
|
|
210
210
|
|
|
211
211
|
|
|
212
212
|
def device_mode(value):
|
|
213
|
+
"""Return the mode corresponding to the given value.
|
|
214
|
+
|
|
215
|
+
This function takes a value and returns the corresponding mode from a
|
|
216
|
+
predefined dictionary.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
value (int): The value for which mode needs to be determined.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
str: The mode corresponding to the input value. Returns "Invalid mode" if no
|
|
223
|
+
mode is found.
|
|
224
|
+
|
|
225
|
+
"""
|
|
226
|
+
|
|
213
227
|
modes = {
|
|
214
228
|
0: "MODE_NOT_ACTIVE",
|
|
215
229
|
1: "MODE_ONLINE",
|
|
@@ -6,6 +6,19 @@ class DatatypeConverter:
|
|
|
6
6
|
|
|
7
7
|
@staticmethod
|
|
8
8
|
def init_encode_map():
|
|
9
|
+
"""Initialize the encode map for DatatypeConverter if it is not already
|
|
10
|
+
initialized.
|
|
11
|
+
|
|
12
|
+
This function initializes the encode map for DatatypeConverter by
|
|
13
|
+
creating a list of 64 elements and populating it with characters for
|
|
14
|
+
encoding. If the encode map is already initialized, it returns the
|
|
15
|
+
existing encode map.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
list: The encode map for DatatypeConverter.
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
|
|
9
22
|
if DatatypeConverter.encode_map is None:
|
|
10
23
|
cArr = [0] * 64
|
|
11
24
|
for i in range(26):
|
|
@@ -33,6 +46,21 @@ class DatatypeConverter:
|
|
|
33
46
|
|
|
34
47
|
@staticmethod
|
|
35
48
|
def _printBase64Binary(bArr, i=0, i2=None):
|
|
49
|
+
"""Print the Base64 binary representation of a byte array.
|
|
50
|
+
|
|
51
|
+
This function takes a byte array and optional start and end indices to
|
|
52
|
+
print the Base64 binary representation.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
bArr (list): A list of bytes to be converted to Base64 binary.
|
|
56
|
+
i (int): The starting index of the byte array (default is 0).
|
|
57
|
+
i2 (int): The ending index of the byte array (default is the length of bArr).
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
str: The Base64 binary representation of the input byte array.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
|
|
36
64
|
if i2 is None:
|
|
37
65
|
i2 = len(bArr)
|
|
38
66
|
cArr = [""] * (((i2 + 2) // 3) * 4)
|
|
@@ -41,15 +69,32 @@ class DatatypeConverter:
|
|
|
41
69
|
|
|
42
70
|
@staticmethod
|
|
43
71
|
def _printBase64Binary_core(bArr, i, i2, cArr, i3):
|
|
72
|
+
"""Encode binary data into Base64 format.
|
|
73
|
+
|
|
74
|
+
This function encodes binary data into Base64 format following the
|
|
75
|
+
Base64 encoding algorithm.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
bArr (list): List of binary data to be encoded.
|
|
79
|
+
i (int): Starting index of the binary data to be encoded.
|
|
80
|
+
i2 (int): Length of binary data to be encoded.
|
|
81
|
+
cArr (list): List to store the encoded Base64 characters.
|
|
82
|
+
i3 (int): Starting index in the cArr to store the encoded characters.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
int: The index in cArr where encoding ends.
|
|
86
|
+
|
|
87
|
+
Note:
|
|
88
|
+
This function assumes that DatatypeConverter has a method 'encode' and
|
|
89
|
+
'init_encode_map' for encoding.
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
|
|
44
93
|
DatatypeConverter.init_encode_map() # Ensure encode_map is initialized
|
|
45
94
|
while i2 >= 3:
|
|
46
95
|
cArr[i3] = DatatypeConverter.encode(bArr[i] >> 2)
|
|
47
|
-
cArr[i3 + 1] = DatatypeConverter.encode(
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
cArr[i3 + 2] = DatatypeConverter.encode(
|
|
51
|
-
((bArr[i + 1] & 15) << 2) | ((bArr[i + 2] >> 6) & 3)
|
|
52
|
-
)
|
|
96
|
+
cArr[i3 + 1] = DatatypeConverter.encode(((bArr[i] & 3) << 4) | ((bArr[i + 1] >> 4) & 15))
|
|
97
|
+
cArr[i3 + 2] = DatatypeConverter.encode(((bArr[i + 1] & 15) << 2) | ((bArr[i + 2] >> 6) & 3))
|
|
53
98
|
cArr[i3 + 3] = DatatypeConverter.encode(bArr[i + 2] & 63)
|
|
54
99
|
i2 -= 3
|
|
55
100
|
i += 3
|
|
@@ -63,9 +108,7 @@ class DatatypeConverter:
|
|
|
63
108
|
|
|
64
109
|
if i2 == 2:
|
|
65
110
|
cArr[i3] = DatatypeConverter.encode(bArr[i] >> 2)
|
|
66
|
-
cArr[i3 + 1] = DatatypeConverter.encode(
|
|
67
|
-
((bArr[i] & 3) << 4) | ((bArr[i + 1] >> 4) & 15)
|
|
68
|
-
)
|
|
111
|
+
cArr[i3 + 1] = DatatypeConverter.encode(((bArr[i] & 3) << 4) | ((bArr[i + 1] >> 4) & 15))
|
|
69
112
|
cArr[i3 + 2] = DatatypeConverter.encode((bArr[i + 1] & 15) << 2)
|
|
70
113
|
cArr[i3 + 3] = "="
|
|
71
114
|
|
|
@@ -30,6 +30,19 @@ class DeviceType(Enum):
|
|
|
30
30
|
|
|
31
31
|
@staticmethod
|
|
32
32
|
def valueof(value):
|
|
33
|
+
"""Return the corresponding DeviceType based on the input value.
|
|
34
|
+
|
|
35
|
+
This function takes an integer value as input and returns the
|
|
36
|
+
corresponding DeviceType enum value.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
value (int): An integer representing the device type.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
DeviceType: The corresponding DeviceType enum value based on the input value.
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
|
|
33
46
|
if value == 0:
|
|
34
47
|
return DeviceType.RTK
|
|
35
48
|
elif value == 1:
|
|
@@ -43,6 +56,18 @@ class DeviceType(Enum):
|
|
|
43
56
|
|
|
44
57
|
@staticmethod
|
|
45
58
|
def value_of_str(device_name, product_key=""):
|
|
59
|
+
"""Determine the type of device based on the provided device name and
|
|
60
|
+
product key.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
device_name (str): The name of the device.
|
|
64
|
+
product_key (str?): The product key associated with the device. Defaults to "".
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
DeviceType: The type of device based on the provided information.
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
|
|
46
71
|
if not device_name and not product_key:
|
|
47
72
|
return DeviceType.UNKNOWN
|
|
48
73
|
|
|
@@ -50,21 +75,13 @@ class DeviceType(Enum):
|
|
|
50
75
|
substring = device_name[:3]
|
|
51
76
|
substring2 = device_name[:7]
|
|
52
77
|
|
|
53
|
-
if DeviceType.RTK.name in substring or DeviceType.contain_rtk_product_key(
|
|
54
|
-
product_key
|
|
55
|
-
):
|
|
78
|
+
if DeviceType.RTK.name in substring or DeviceType.contain_rtk_product_key(product_key):
|
|
56
79
|
return DeviceType.RTK
|
|
57
|
-
elif (
|
|
58
|
-
DeviceType.LUBA_2.name in substring2
|
|
59
|
-
or DeviceType.contain_luba_2_product_key(product_key)
|
|
60
|
-
):
|
|
80
|
+
elif DeviceType.LUBA_2.name in substring2 or DeviceType.contain_luba_2_product_key(product_key):
|
|
61
81
|
return DeviceType.LUBA_2
|
|
62
82
|
elif DeviceType.LUBA_YUKA.name in substring2:
|
|
63
83
|
return DeviceType.LUBA_YUKA
|
|
64
|
-
elif (
|
|
65
|
-
DeviceType.LUBA.name in substring2
|
|
66
|
-
or DeviceType.contain_luba_product_key(product_key)
|
|
67
|
-
):
|
|
84
|
+
elif DeviceType.LUBA.name in substring2 or DeviceType.contain_luba_product_key(product_key):
|
|
68
85
|
return DeviceType.LUBA
|
|
69
86
|
else:
|
|
70
87
|
return DeviceType.UNKNOWN
|
|
@@ -73,6 +90,22 @@ class DeviceType(Enum):
|
|
|
73
90
|
|
|
74
91
|
@staticmethod
|
|
75
92
|
def has_4g(device_name, product_key=""):
|
|
93
|
+
"""Check if the device has 4G capability based on the device name and
|
|
94
|
+
optional product key.
|
|
95
|
+
|
|
96
|
+
This function determines the device type based on the device name and
|
|
97
|
+
product key (if provided). It then checks if the device type has a value
|
|
98
|
+
greater than or equal to the 4G threshold.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
device_name (str): The name of the device.
|
|
102
|
+
product_key (str?): The product key associated with the device. Defaults to "".
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
bool: True if the device has 4G capability, False otherwise.
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
|
|
76
109
|
if not product_key:
|
|
77
110
|
device_type = DeviceType.value_of_str(device_name)
|
|
78
111
|
else:
|
|
@@ -82,6 +115,21 @@ class DeviceType(Enum):
|
|
|
82
115
|
|
|
83
116
|
@staticmethod
|
|
84
117
|
def is_luba1(device_name, product_key=""):
|
|
118
|
+
"""Check if the given device is of type LUBA.
|
|
119
|
+
|
|
120
|
+
This function determines if the device specified by 'device_name' is of
|
|
121
|
+
type LUBA. If 'product_key' is provided, it is used to further identify
|
|
122
|
+
the device type.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
device_name (str): The name of the device.
|
|
126
|
+
product_key (str?): The product key associated with the device. Defaults to "".
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
bool: True if the device is of type LUBA, False otherwise.
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
|
|
85
133
|
if not product_key:
|
|
86
134
|
device_type = DeviceType.value_of_str(device_name)
|
|
87
135
|
else:
|
|
@@ -91,6 +139,18 @@ class DeviceType(Enum):
|
|
|
91
139
|
|
|
92
140
|
@staticmethod
|
|
93
141
|
def is_luba_2(device_name, product_key=""):
|
|
142
|
+
"""Check if the device type is LUBA 2 or higher based on the device name
|
|
143
|
+
and optional product key.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
device_name (str): The name of the device.
|
|
147
|
+
product_key (str?): The product key associated with the device. Defaults to "".
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
bool: True if the device type is LUBA 2 or higher, False otherwise.
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
|
|
94
154
|
if not product_key:
|
|
95
155
|
device_type = DeviceType.value_of_str(device_name)
|
|
96
156
|
else:
|
|
@@ -100,32 +160,71 @@ class DeviceType(Enum):
|
|
|
100
160
|
|
|
101
161
|
@staticmethod
|
|
102
162
|
def is_yuka(device_name):
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
163
|
+
"""Check if the given device name corresponds to a LUBA_YUKA device type.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
device_name (str): The name of the device to be checked.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
bool: True if the device type is LUBA_YUKA, False otherwise.
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
return DeviceType.value_of_str(device_name).get_value() == DeviceType.LUBA_YUKA.get_value()
|
|
107
174
|
|
|
108
175
|
@staticmethod
|
|
109
176
|
def is_rtk(device_name, product_key=""):
|
|
177
|
+
"""Check if the device type is within the range of RTK devices.
|
|
178
|
+
|
|
179
|
+
This function determines if the device type corresponding to the given
|
|
180
|
+
device name and optional product key falls within the range of RTK
|
|
181
|
+
(Real-Time Kinematic) devices.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
device_name (str): The name of the device.
|
|
185
|
+
product_key (str?): The product key associated with the device. Defaults to "".
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
bool: True if the device type is within the RTK range, False otherwise.
|
|
189
|
+
|
|
190
|
+
"""
|
|
191
|
+
|
|
110
192
|
if not product_key:
|
|
111
193
|
device_type = DeviceType.value_of_str(device_name)
|
|
112
194
|
else:
|
|
113
195
|
device_type = DeviceType.value_of_str(device_name, product_key)
|
|
114
196
|
|
|
115
|
-
return (
|
|
116
|
-
DeviceType.RTK.get_value()
|
|
117
|
-
<= device_type.get_value()
|
|
118
|
-
< DeviceType.LUBA.get_value()
|
|
119
|
-
)
|
|
197
|
+
return DeviceType.RTK.get_value() <= device_type.get_value() < DeviceType.LUBA.get_value()
|
|
120
198
|
|
|
121
199
|
@staticmethod
|
|
122
200
|
def contain_rtk_product_key(product_key):
|
|
201
|
+
"""Check if the given product key is in a predefined list of RTK product
|
|
202
|
+
keys.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
product_key (str): The product key to be checked.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
bool: True if the product key is in the predefined list, False otherwise.
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
|
|
123
212
|
if not product_key:
|
|
124
213
|
return False
|
|
125
214
|
return product_key in ["a1qXkZ5P39W", "a1Nc68bGZzX"]
|
|
126
215
|
|
|
127
216
|
@staticmethod
|
|
128
217
|
def contain_luba_product_key(product_key):
|
|
218
|
+
"""Check if the given product key is in the list of valid product keys.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
product_key (str): The product key to be checked.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
bool: True if the product key is in the list of valid keys, False otherwise.
|
|
225
|
+
|
|
226
|
+
"""
|
|
227
|
+
|
|
129
228
|
if not product_key:
|
|
130
229
|
return False
|
|
131
230
|
return product_key in [
|
|
@@ -144,6 +243,16 @@ class DeviceType(Enum):
|
|
|
144
243
|
|
|
145
244
|
@staticmethod
|
|
146
245
|
def contain_luba_2_product_key(product_key):
|
|
246
|
+
"""Check if the given product key is present in a predefined list.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
product_key (str): The product key to be checked.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
bool: True if the product key is in the predefined list, False otherwise.
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
|
|
147
256
|
if not product_key:
|
|
148
257
|
return False
|
|
149
258
|
return product_key in ["a1iMygIwxFC", "a1LLmy1zc0j", "a1LLmy1zc0j"]
|
pymammotion/utility/periodic.py
CHANGED
|
@@ -10,12 +10,24 @@ class Periodic:
|
|
|
10
10
|
self._task = None
|
|
11
11
|
|
|
12
12
|
def start(self):
|
|
13
|
+
"""Start the task if it is not already started.
|
|
14
|
+
|
|
15
|
+
If the task is not already started, it sets the 'is_started' flag to
|
|
16
|
+
True and initiates a task to call a function periodically using asyncio.
|
|
17
|
+
"""
|
|
18
|
+
|
|
13
19
|
if not self.is_started:
|
|
14
20
|
self.is_started = True
|
|
15
21
|
# Start task to call func periodically:
|
|
16
22
|
self._task = asyncio.ensure_future(self._run())
|
|
17
23
|
|
|
18
24
|
async def stop(self):
|
|
25
|
+
"""Stop the task if it is currently running.
|
|
26
|
+
|
|
27
|
+
If the task is currently running, it will be cancelled and awaited until
|
|
28
|
+
it stops.
|
|
29
|
+
"""
|
|
30
|
+
|
|
19
31
|
if self.is_started:
|
|
20
32
|
self.is_started = False
|
|
21
33
|
# Stop task and await it stopped:
|
|
@@ -24,14 +36,67 @@ class Periodic:
|
|
|
24
36
|
await self._task
|
|
25
37
|
|
|
26
38
|
async def _run(self):
|
|
39
|
+
"""Run the specified function at regular intervals using asyncio.
|
|
40
|
+
|
|
41
|
+
This method runs the specified function at regular intervals based on
|
|
42
|
+
the time provided.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
self: The instance of the class.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
|
|
27
49
|
while True:
|
|
28
50
|
await asyncio.sleep(self.time)
|
|
29
51
|
await self.func()
|
|
30
52
|
|
|
31
53
|
|
|
32
54
|
def periodic(period):
|
|
55
|
+
"""Schedule a function to run periodically at a specified time interval.
|
|
56
|
+
|
|
57
|
+
This decorator function takes a period (in seconds) as input and returns
|
|
58
|
+
a scheduler function. The scheduler function, when applied as a
|
|
59
|
+
decorator to another function, will run the decorated function
|
|
60
|
+
periodically at the specified time interval.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
period (int): Time interval in seconds at which the decorated function should run
|
|
64
|
+
periodically.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
function: A scheduler function that can be used as a decorator to run a function
|
|
68
|
+
periodically.
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
|
|
33
72
|
def scheduler(fcn):
|
|
73
|
+
"""Schedule the execution of a given async function periodically.
|
|
74
|
+
|
|
75
|
+
This function takes an async function as input and returns a new async
|
|
76
|
+
function that will execute the input function periodically at a
|
|
77
|
+
specified interval.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
fcn (function): The async function to be scheduled.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
function: An async function that will execute the input function periodically.
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
|
|
34
87
|
async def wrapper(*args, **kwargs):
|
|
88
|
+
"""Execute the given function periodically using asyncio tasks.
|
|
89
|
+
|
|
90
|
+
This function continuously creates an asyncio task to execute the
|
|
91
|
+
provided function with the given arguments and keyword arguments. It
|
|
92
|
+
then waits for a specified period of time before creating the next task.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
*args: Variable length argument list to be passed to the function.
|
|
96
|
+
**kwargs: Arbitrary keyword arguments to be passed to the function.
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
|
|
35
100
|
while True:
|
|
36
101
|
asyncio.create_task(fcn(*args, **kwargs))
|
|
37
102
|
await asyncio.sleep(period)
|