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.

Files changed (38) hide show
  1. pymammotion/__init__.py +4 -2
  2. pymammotion/aliyun/__init__.py +1 -0
  3. pymammotion/aliyun/cloud_gateway.py +74 -95
  4. pymammotion/aliyun/tmp_constant.py +2 -6
  5. pymammotion/bluetooth/ble.py +4 -12
  6. pymammotion/bluetooth/ble_message.py +12 -36
  7. pymammotion/bluetooth/data/convert.py +1 -3
  8. pymammotion/bluetooth/data/notifydata.py +0 -1
  9. pymammotion/data/model/device.py +62 -3
  10. pymammotion/data/model/hash_list.py +34 -14
  11. pymammotion/data/model/location.py +40 -0
  12. pymammotion/data/model/rapid_state.py +1 -5
  13. pymammotion/data/state_manager.py +84 -0
  14. pymammotion/event/event.py +18 -3
  15. pymammotion/http/http.py +2 -6
  16. pymammotion/mammotion/commands/mammotion_command.py +1 -3
  17. pymammotion/mammotion/commands/messages/driver.py +7 -21
  18. pymammotion/mammotion/commands/messages/media.py +4 -9
  19. pymammotion/mammotion/commands/messages/navigation.py +42 -107
  20. pymammotion/mammotion/commands/messages/network.py +10 -30
  21. pymammotion/mammotion/commands/messages/system.py +11 -26
  22. pymammotion/mammotion/commands/messages/video.py +1 -3
  23. pymammotion/mammotion/control/joystick.py +9 -33
  24. pymammotion/mammotion/devices/__init__.py +5 -1
  25. pymammotion/mammotion/devices/{luba.py → mammotion.py} +299 -110
  26. pymammotion/mqtt/__init__.py +5 -0
  27. pymammotion/mqtt/{mqtt.py → mammotion_mqtt.py} +46 -50
  28. pymammotion/utility/constant/device_constant.py +14 -0
  29. pymammotion/utility/datatype_converter.py +52 -9
  30. pymammotion/utility/device_type.py +129 -20
  31. pymammotion/utility/periodic.py +65 -0
  32. pymammotion/utility/rocker_util.py +63 -4
  33. {pymammotion-0.0.40.dist-info → pymammotion-0.0.41.dist-info}/METADATA +10 -4
  34. {pymammotion-0.0.40.dist-info → pymammotion-0.0.41.dist-info}/RECORD +36 -34
  35. {pymammotion-0.0.40.dist-info → pymammotion-0.0.41.dist-info}/WHEEL +1 -1
  36. pymammotion/luba/_init_.py +0 -0
  37. pymammotion/luba/base.py +0 -52
  38. {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.data.model import RapidState
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
- # with sqlite3.connect("messages.db") as conn:
23
- # conn.execute("CREATE TABLE IF NOT EXISTS messages (topic TEXT, timestamp INTEGER, payload TEXT)")
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
- print("on_thing_enable")
113
- # print('subscribe_topic, topic:%s' % echo_topic)
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
- f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events"
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
- print(
152
- "on_topic_message, receive message, topic:%s, payload:%s, qos:%d"
153
- % (topic, payload, qos)
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
- print("on_connect, session_flag:%d, rc:%d" % (session_flag, rc))
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.info("Connected")
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
- if (
209
- content.WhichOneof("subMsg") == "sys"
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
- if self.on_warning:
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
- self._set_status(status.params.status.value)
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.info("Unhandled topic: %s", message.topic)
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
- ((bArr[i] & 3) << 4) | ((bArr[i + 1] >> 4) & 15)
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
- return (
104
- DeviceType.value_of_str(device_name).get_value()
105
- == DeviceType.LUBA_YUKA.get_value()
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"]
@@ -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)