pymammotion 0.5.22__py3-none-any.whl → 0.5.24__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.

@@ -81,7 +81,7 @@ class Client:
81
81
  "maxAttempts": UtilClient.default_number(runtime.max_attempts, 3),
82
82
  },
83
83
  "backoff": {
84
- "policy": UtilClient.default_string(runtime.backoff_policy, "no"),
84
+ "policy": UtilClient.default_string(runtime.backoff_policy, "yes"),
85
85
  "period": UtilClient.default_number(runtime.backoff_period, 1),
86
86
  },
87
87
  "ignoreSSL": runtime.ignore_ssl,
@@ -171,7 +171,7 @@ class Client:
171
171
  "maxAttempts": UtilClient.default_number(runtime.max_attempts, 3),
172
172
  },
173
173
  "backoff": {
174
- "policy": UtilClient.default_string(runtime.backoff_policy, "no"),
174
+ "policy": UtilClient.default_string(runtime.backoff_policy, "yes"),
175
175
  "period": UtilClient.default_number(runtime.backoff_period, 1),
176
176
  },
177
177
  "ignoreSSL": runtime.ignore_ssl,
@@ -1,5 +1,6 @@
1
1
  """Module for interacting with Aliyun Cloud IoT Gateway."""
2
2
 
3
+ import asyncio
3
4
  import base64
4
5
  import hashlib
5
6
  import hmac
@@ -86,6 +87,14 @@ class GatewayTimeoutException(Exception):
86
87
  self.iot_id = args[1]
87
88
 
88
89
 
90
+ class TooManyRequestsException(Exception):
91
+ """Raise exception when the gateway times out."""
92
+
93
+ def __init__(self, *args: object) -> None:
94
+ super().__init__(args)
95
+ self.iot_id = args[1]
96
+
97
+
89
98
  class LoginException(Exception):
90
99
  """Raise exception when library cannot log in."""
91
100
 
@@ -118,7 +127,7 @@ class CloudIOTGateway:
118
127
  self._app_key = APP_KEY
119
128
  self._app_secret = APP_SECRET
120
129
  self.domain = ALIYUN_DOMAIN
121
-
130
+ self.message_delay = 1
122
131
  self._client_id = self.generate_hardware_string(8) # 8 characters
123
132
  self._device_sn = self.generate_hardware_string(32) # 32 characters
124
133
  self._utdid = self.generate_hardware_string(32) # 32 characters
@@ -730,7 +739,6 @@ class CloudIOTGateway:
730
739
  )
731
740
 
732
741
  client = Client(config)
733
-
734
742
  # build request
735
743
  request = CommonParams(
736
744
  api_ver="1.0.5",
@@ -762,6 +770,14 @@ class CloudIOTGateway:
762
770
  logger.debug(response.body)
763
771
  logger.debug(iot_id)
764
772
 
773
+ if response.status_code == 429:
774
+ logger.debug("too many requests.")
775
+ if self.message_delay > 8:
776
+ raise TooManyRequestsException(response.status_message, iot_id)
777
+ asyncio.get_event_loop().call_later(self.message_delay, self.send_cloud_command, iot_id, command)
778
+ self.message_delay = self.message_delay * 2
779
+ return message_id
780
+
765
781
  response_body_str = response.body.decode("utf-8")
766
782
  response_body_dict = self.parse_json_response(response_body_str)
767
783
 
@@ -785,6 +801,9 @@ class CloudIOTGateway:
785
801
  if response_body_dict.get("code") == 6205:
786
802
  raise DeviceOfflineException(response_body_dict.get("code"), iot_id)
787
803
 
804
+ if self.message_delay != 1:
805
+ self.message_delay = 1
806
+
788
807
  return message_id
789
808
 
790
809
  async def get_device_properties(self, iot_id: str) -> ThingPropertiesResponse:
@@ -1,8 +1,9 @@
1
1
  """Manage state from notifications into MowingDevice."""
2
2
 
3
+ from collections.abc import Awaitable, Callable
3
4
  from datetime import UTC, datetime
4
5
  import logging
5
- from typing import Any, Awaitable, Callable
6
+ from typing import Any
6
7
 
7
8
  import betterproto2
8
9
 
@@ -42,6 +43,7 @@ class StateManager:
42
43
  """Manage state."""
43
44
 
44
45
  def __init__(self, device: MowingDevice) -> None:
46
+ """Initialize state manager with a device."""
45
47
  self._device: MowingDevice = device
46
48
  self.last_updated_at = datetime.now(UTC)
47
49
  self.preference = ConnectionPreference.WIFI
@@ -74,11 +76,13 @@ class StateManager:
74
76
  self._device = device
75
77
 
76
78
  async def properties(self, thing_properties: ThingPropertiesMessage) -> None:
79
+ """Update device properties and invoke callback."""
77
80
  # TODO update device based off thing properties
78
81
  self._device.mqtt_properties = thing_properties
79
82
  await self.on_properties_callback(thing_properties)
80
83
 
81
84
  async def status(self, thing_status: ThingStatusMessage) -> None:
85
+ """Update device status and invoke callback."""
82
86
  if not self._device.online:
83
87
  self._device.online = True
84
88
  self._device.status_properties = thing_status
@@ -93,19 +97,23 @@ class StateManager:
93
97
 
94
98
  @property
95
99
  def online(self) -> bool:
100
+ """Return online status."""
96
101
  return self._device.online
97
102
 
98
103
  @online.setter
99
104
  def online(self, value: bool) -> None:
105
+ """Set online status."""
100
106
  self._device.online = value
101
107
 
102
108
  async def gethash_ack_callback(self, msg: NavGetHashListAck) -> None:
109
+ """Dispatch hash list acknowledgment to available callback."""
103
110
  if self.cloud_gethash_ack_callback:
104
111
  await self.cloud_gethash_ack_callback(msg)
105
112
  elif self.ble_gethash_ack_callback:
106
113
  await self.ble_gethash_ack_callback(msg)
107
114
 
108
115
  async def on_notification_callback(self, res: tuple[str, Any | None]) -> None:
116
+ """Dispatch notification to available callback."""
109
117
  if self.cloud_on_notification_callback:
110
118
  await self.cloud_on_notification_callback.data_event(res)
111
119
  elif self.ble_on_notification_callback:
@@ -134,6 +142,7 @@ class StateManager:
134
142
  await self.ble_get_commondata_ack_callback(comm_data)
135
143
 
136
144
  async def get_plan_callback(self, planjob: NavPlanJobSet) -> None:
145
+ """Dispatch plan job to available callback."""
137
146
  if self.cloud_get_plan_callback:
138
147
  await self.cloud_get_plan_callback(planjob)
139
148
  elif self.ble_get_plan_callback:
@@ -244,9 +253,10 @@ class StateManager:
244
253
  self._device.mower_state.swversion = device_fw_info.version
245
254
 
246
255
  def _update_driver_data(self, message) -> None:
247
- pass
256
+ """Update driver data."""
248
257
 
249
258
  def _update_net_data(self, message) -> None:
259
+ """Update network data."""
250
260
  net_msg = betterproto2.which_one_of(message.net, "NetSubType")
251
261
  match net_msg[0]:
252
262
  case "toapp_wifi_iot_status":
@@ -264,15 +274,21 @@ class StateManager:
264
274
 
265
275
  def _update_mul_data(self, message) -> None:
266
276
  """Media and video states."""
267
- mul_msg = betterproto2.which_one_of(message.net, "SubMul")
277
+ mul_msg = betterproto2.which_one_of(message.mul, "SubMul")
268
278
  match mul_msg[0]:
269
279
  case "Getlamprsp":
270
280
  lamp_resp: Getlamprsp = mul_msg[1]
271
281
  self._device.mower_state.lamp_info.lamp_bright = lamp_resp.lamp_bright
272
- if lamp_resp.get_ids == 1126:
273
- self._device.mower_state.lamp_info.manual_light = bool(lamp_resp.lamp_manual_ctrl.value)
282
+ if lamp_resp.get_ids in (1126, 1127):
283
+ self._device.mower_state.lamp_info.lamp_bright = lamp_resp.lamp_bright
284
+ self._device.mower_state.lamp_info.manual_light = bool(lamp_resp.lamp_manual_ctrl.value) or bool(
285
+ lamp_resp.lamp_bright
286
+ )
274
287
  if lamp_resp.get_ids == 1123:
275
- self._device.mower_state.lamp_info.night_light = bool(lamp_resp.lamp_ctrl.value)
288
+ self._device.mower_state.lamp_info.lamp_bright = lamp_resp.lamp_bright
289
+ self._device.mower_state.lamp_info.night_light = bool(lamp_resp.lamp_ctrl.value) or bool(
290
+ lamp_resp.lamp_bright
291
+ )
276
292
 
277
293
  def _update_ota_data(self, message) -> None:
278
- pass
294
+ """Update OTA data."""
@@ -5,6 +5,8 @@ import time
5
5
 
6
6
  from pymammotion.mammotion.commands.abstract_message import AbstractMessage
7
7
  from pymammotion.proto import (
8
+ AppGetCutterWorkMode,
9
+ AppSetCutterWorkMode,
8
10
  DrvKnifeHeight,
9
11
  DrvMotionCtrl,
10
12
  DrvMowCtrlByHand,
@@ -23,6 +25,7 @@ logger = getLogger(__name__)
23
25
 
24
26
  class MessageDriver(AbstractMessage, ABC):
25
27
  def send_order_msg_driver(self, driver) -> bytes:
28
+ """Build and serialize a driver command message."""
26
29
  return LubaMsg(
27
30
  msgtype=MsgCmdType.EMBED_DRIVER,
28
31
  sender=MsgDevice.DEV_MOBILEAPP,
@@ -36,23 +39,42 @@ class MessageDriver(AbstractMessage, ABC):
36
39
  ).SerializeToString()
37
40
 
38
41
  def set_blade_height(self, height: int) -> bytes:
42
+ """Set mower blade height."""
39
43
  logger.debug(f"Send knife height height={height}")
40
44
  build = MctlDriver(todev_knife_height_set=DrvKnifeHeight(knife_height=height))
41
45
  logger.debug(f"Send command--Knife motor height setting height={height}")
42
46
  return self.send_order_msg_driver(build)
43
47
 
44
48
  def set_speed(self, speed: float) -> bytes:
49
+ """Set the device speed."""
45
50
  logger.debug(f"{self.get_device_name()} set speed, {speed}")
46
51
  build = MctlDriver(bidire_speed_read_set=DrvSrSpeed(speed=speed, rw=1))
47
52
  logger.debug(f"Send command--Speed setting speed={speed}")
48
53
  return self.send_order_msg_driver(build)
49
54
 
55
+ def get_cutter_mode(self) -> bytes:
56
+ """Request the current cutter mode."""
57
+ build = MctlDriver(current_cutter_mode=AppGetCutterWorkMode())
58
+ return self.send_order_msg_driver(build)
59
+
60
+ def set_cutter_mode(self, cutter_mode: int) -> bytes:
61
+ """Set blade speed."""
62
+ """
63
+ 1 slow
64
+ 0 normal
65
+ 2 fast
66
+ """
67
+ build = MctlDriver(cutter_mode_ctrl_by_hand=AppSetCutterWorkMode(cutter_mode=cutter_mode))
68
+ return self.send_order_msg_driver(build)
69
+
50
70
  def syn_nav_star_point_data(self, sat_system: int) -> bytes:
71
+ """Synchronize navigation satellite frequency points."""
51
72
  build = MctlDriver(rtk_sys_mask_query=RtkSysMaskQueryT(sat_system=sat_system))
52
73
  logger.debug(f"Send command--Navigation satellite frequency point synchronization={sat_system}")
53
74
  return self.send_order_msg_driver(build)
54
75
 
55
76
  def set_nav_star_point(self, cmd_req: str) -> bytes:
77
+ """Configure navigation satellite frequency points."""
56
78
  build = MctlDriver(rtk_cfg_req=RtkCfgReqT(cmd_req=cmd_req, cmd_length=len(cmd_req) - 1))
57
79
  logger.debug(f"Send command--Navigation satellite frequency point setting={cmd_req}")
58
80
  logger.debug(
@@ -61,6 +83,7 @@ class MessageDriver(AbstractMessage, ABC):
61
83
  return self.send_order_msg_driver(build)
62
84
 
63
85
  def get_speed(self) -> bytes:
86
+ """Request the current speed value."""
64
87
  build = MctlDriver(bidire_speed_read_set=DrvSrSpeed(rw=0))
65
88
  logger.debug("Send command--Get speed value")
66
89
  return self.send_order_msg_driver(build)
@@ -72,6 +95,7 @@ class MessageDriver(AbstractMessage, ABC):
72
95
  cut_knife_height: int,
73
96
  max_run_speed: float,
74
97
  ) -> bytes:
98
+ """Send manual mowing control command."""
75
99
  build = MctlDriver(
76
100
  mow_ctrl_by_hand=DrvMowCtrlByHand(
77
101
  main_ctrl=main_ctrl,
@@ -88,6 +112,7 @@ class MessageDriver(AbstractMessage, ABC):
88
112
  return self.send_order_msg_driver(build)
89
113
 
90
114
  def send_movement(self, linear_speed: int, angular_speed: int) -> bytes:
115
+ """Send motion command with linear and angular speeds."""
91
116
  logger.debug(f"Control command print, linearSpeed={
92
117
  linear_speed} // angularSpeed={angular_speed}")
93
118
  return self.send_order_msg_driver(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pymammotion
3
- Version: 0.5.22
3
+ Version: 0.5.24
4
4
  Summary:
5
5
  License: GPL-3.0
6
6
  Author: Michael Arthur
@@ -1,7 +1,7 @@
1
1
  pymammotion/__init__.py,sha256=H-U94bNLp0LPC6hkRopVEUlUsZSR97n7WfKGPjK1GMg,1638
2
2
  pymammotion/aliyun/__init__.py,sha256=T1lkX7TRYiL4nqYanG4l4MImV-SlavSbuooC-W-uUGw,29
3
- pymammotion/aliyun/client.py,sha256=GCpAnLOVWW_W0c8UvdRpWVzZH5oLto8JZnb82stJi-8,9662
4
- pymammotion/aliyun/cloud_gateway.py,sha256=Qhzs3UtkAnVQljf4qtFiOdd4bz04oHzJT-ZPvnM2CdQ,31250
3
+ pymammotion/aliyun/client.py,sha256=z-dCR6cD5-8MuxjFaXC5U8IsiKcsGQ4EzzexG7X78B8,9664
4
+ pymammotion/aliyun/cloud_gateway.py,sha256=rnOV5v6V1jIbZ8nYXTwvJTEfbAIrmbNBPG8eIbqVyZ0,31977
5
5
  pymammotion/aliyun/model/aep_response.py,sha256=EY4uMTJ4F9rvbcXnAOc5YKi7q__9kIVgfDwfyr65Gk0,421
6
6
  pymammotion/aliyun/model/connect_response.py,sha256=Yz-fEbDzgGPTo5Of2oAjmFkSv08T7ze80pQU4k-gKIU,824
7
7
  pymammotion/aliyun/model/dev_by_account_response.py,sha256=P9yYy4Z2tLkJSqXA_5XGaCUliSSVa5ILl7VoMtL_tCA,977
@@ -47,7 +47,7 @@ pymammotion/data/mqtt/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdr
47
47
  pymammotion/data/mqtt/event.py,sha256=pj65y93ACcTzUIJkHZC6s_pRFBYaNMSU1wyYRhiGvw4,5571
48
48
  pymammotion/data/mqtt/properties.py,sha256=laud9rE-JqBHWKZ44Dov2yMkp3Ld8ghpBwlZ4_3g9uQ,4321
49
49
  pymammotion/data/mqtt/status.py,sha256=qwZPevIzScePA_25nbRWMN7IhU-MhhtIl7fcZKAJfIc,1102
50
- pymammotion/data/state_manager.py,sha256=3EXOul7lorlKSnaSWqswBEXIlY9KC2D33VGNj_BBybE,12696
50
+ pymammotion/data/state_manager.py,sha256=i9TSTGQeDtUCgUWuunQI_tbm76x0F5L8CkCZ0E_ixms,13565
51
51
  pymammotion/event/__init__.py,sha256=mgATR6vPHACNQ-0zH5fi7NdzeTCDV1CZyaWPmtUusi8,115
52
52
  pymammotion/event/event.py,sha256=Z8WYxv_-5khEqKjL1w4c_Et24G1Kdm8QFuIBylD3h3U,3021
53
53
  pymammotion/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -65,7 +65,7 @@ pymammotion/mammotion/commands/abstract_message.py,sha256=M4qL-Yw5g3fTgS9jB9LUSg
65
65
  pymammotion/mammotion/commands/mammotion_command.py,sha256=AMOx6nEbPa0HijbENomPLLuJummS1q-23BzgetBAuOI,2991
66
66
  pymammotion/mammotion/commands/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
67
  pymammotion/mammotion/commands/messages/basestation.py,sha256=a1LEF4C25ynILeQLx-FOOU4d-N_vMkXSh_etojJjIDM,1442
68
- pymammotion/mammotion/commands/messages/driver.py,sha256=LAYGvAcDDXwwliQ1OfRwNBk1VpIdpURo1TlA64zpLA4,3701
68
+ pymammotion/mammotion/commands/messages/driver.py,sha256=tJUM1WhlWFs92PbZW5QSG-TMo6nemAUkcUDzI_6gETU,4695
69
69
  pymammotion/mammotion/commands/messages/media.py,sha256=KJyMzSZkwQrHWjAWfAI88Y7-jmfN3-WYtY193KaomT4,2930
70
70
  pymammotion/mammotion/commands/messages/navigation.py,sha256=yI-B5HHGuZJXn9iYrxhYZ5BArde6wB_j9bUc5IcSIJE,23261
71
71
  pymammotion/mammotion/commands/messages/network.py,sha256=7K6aqt29ymTSUG0iEv4kIRD0Dv6rfHNctE00UvuMpG4,7264
@@ -130,7 +130,7 @@ pymammotion/utility/movement.py,sha256=N75oAoAgFydqoaOedYIxGUHmuTCtPzAOtb-d_29tp
130
130
  pymammotion/utility/mur_mur_hash.py,sha256=xEfOZVbqRawJj66eLgtnZ85OauDR47oIPr29OHelzPI,4468
131
131
  pymammotion/utility/periodic.py,sha256=MbeSb9cfhxzYmdT_RiE0dZe3H9IfbQW_zSqhmSX2RUc,3321
132
132
  pymammotion/utility/rocker_util.py,sha256=6tX7sS87qoQC_tsxbx3NLL-HgS08wtzXiZkhDiz7uo0,7179
133
- pymammotion-0.5.22.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
134
- pymammotion-0.5.22.dist-info/METADATA,sha256=Kk3wTPnCon-TxjgqtRIJvn8XXBu4YBcVxcuv2HwfY3E,3872
135
- pymammotion-0.5.22.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
136
- pymammotion-0.5.22.dist-info/RECORD,,
133
+ pymammotion-0.5.24.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
134
+ pymammotion-0.5.24.dist-info/METADATA,sha256=Wj7OG5shjN_rQ6QoBC3LgH-wnAr0PsLIjEhj6uQ5Sb4,3872
135
+ pymammotion-0.5.24.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
136
+ pymammotion-0.5.24.dist-info/RECORD,,