pymammotion 0.5.69__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.
Files changed (154) hide show
  1. pymammotion/__init__.py +53 -0
  2. pymammotion/agora/__init__.py +0 -0
  3. pymammotion/agora/agora_api.py +755 -0
  4. pymammotion/agora/agora_rtc_capabilities.py +748 -0
  5. pymammotion/agora/agora_websockets.py +1175 -0
  6. pymammotion/aliyun/__init__.py +1 -0
  7. pymammotion/aliyun/client.py +235 -0
  8. pymammotion/aliyun/cloud_gateway.py +982 -0
  9. pymammotion/aliyun/model/aep_response.py +21 -0
  10. pymammotion/aliyun/model/connect_response.py +51 -0
  11. pymammotion/aliyun/model/dev_by_account_response.py +195 -0
  12. pymammotion/aliyun/model/login_by_oauth_response.py +64 -0
  13. pymammotion/aliyun/model/regions_response.py +29 -0
  14. pymammotion/aliyun/model/session_by_authcode_response.py +19 -0
  15. pymammotion/aliyun/model/thing_response.py +12 -0
  16. pymammotion/aliyun/regions.py +62 -0
  17. pymammotion/aliyun/tea/core.py +297 -0
  18. pymammotion/aliyun/tmp_constant.py +171 -0
  19. pymammotion/bluetooth/__init__.py +1 -0
  20. pymammotion/bluetooth/ble.py +62 -0
  21. pymammotion/bluetooth/ble_message.py +676 -0
  22. pymammotion/bluetooth/const.py +27 -0
  23. pymammotion/bluetooth/data/__init__.py +0 -0
  24. pymammotion/bluetooth/data/convert.py +25 -0
  25. pymammotion/bluetooth/data/framectrldata.py +40 -0
  26. pymammotion/bluetooth/data/notifydata.py +62 -0
  27. pymammotion/bluetooth/model/__init__.py +0 -0
  28. pymammotion/bluetooth/model/atomic_integer.py +54 -0
  29. pymammotion/const.py +13 -0
  30. pymammotion/data/__init__.py +0 -0
  31. pymammotion/data/model/__init__.py +8 -0
  32. pymammotion/data/model/account.py +8 -0
  33. pymammotion/data/model/device.py +192 -0
  34. pymammotion/data/model/device_config.py +72 -0
  35. pymammotion/data/model/device_info.py +60 -0
  36. pymammotion/data/model/device_limits.py +49 -0
  37. pymammotion/data/model/enums.py +77 -0
  38. pymammotion/data/model/errors.py +12 -0
  39. pymammotion/data/model/events.py +14 -0
  40. pymammotion/data/model/generate_geojson.py +565 -0
  41. pymammotion/data/model/generate_route_information.py +26 -0
  42. pymammotion/data/model/hash_list.py +475 -0
  43. pymammotion/data/model/location.py +36 -0
  44. pymammotion/data/model/mowing_modes.py +77 -0
  45. pymammotion/data/model/rapid_state.py +45 -0
  46. pymammotion/data/model/raw_data.py +215 -0
  47. pymammotion/data/model/region_data.py +102 -0
  48. pymammotion/data/model/report_info.py +182 -0
  49. pymammotion/data/model/work.py +27 -0
  50. pymammotion/data/mower_state_manager.py +369 -0
  51. pymammotion/data/mqtt/__init__.py +1 -0
  52. pymammotion/data/mqtt/event.py +227 -0
  53. pymammotion/data/mqtt/mammotion_properties.py +276 -0
  54. pymammotion/data/mqtt/properties.py +203 -0
  55. pymammotion/data/mqtt/status.py +57 -0
  56. pymammotion/event/__init__.py +6 -0
  57. pymammotion/event/event.py +96 -0
  58. pymammotion/homeassistant/__init__.py +3 -0
  59. pymammotion/homeassistant/mower_api.py +514 -0
  60. pymammotion/homeassistant/rtk_api.py +54 -0
  61. pymammotion/http/__init__.py +0 -0
  62. pymammotion/http/encryption.py +220 -0
  63. pymammotion/http/http.py +673 -0
  64. pymammotion/http/model/__init__.py +0 -0
  65. pymammotion/http/model/camera_stream.py +31 -0
  66. pymammotion/http/model/http.py +249 -0
  67. pymammotion/http/model/response_factory.py +61 -0
  68. pymammotion/http/model/rtk.py +16 -0
  69. pymammotion/mammotion/__init__.py +0 -0
  70. pymammotion/mammotion/commands/__init__.py +0 -0
  71. pymammotion/mammotion/commands/abstract_message.py +24 -0
  72. pymammotion/mammotion/commands/mammotion_command.py +81 -0
  73. pymammotion/mammotion/commands/messages/__init__.py +0 -0
  74. pymammotion/mammotion/commands/messages/basestation.py +43 -0
  75. pymammotion/mammotion/commands/messages/driver.py +122 -0
  76. pymammotion/mammotion/commands/messages/media.py +87 -0
  77. pymammotion/mammotion/commands/messages/navigation.py +564 -0
  78. pymammotion/mammotion/commands/messages/network.py +205 -0
  79. pymammotion/mammotion/commands/messages/ota.py +38 -0
  80. pymammotion/mammotion/commands/messages/system.py +330 -0
  81. pymammotion/mammotion/commands/messages/video.py +33 -0
  82. pymammotion/mammotion/control/__init__.py +0 -0
  83. pymammotion/mammotion/control/joystick.py +145 -0
  84. pymammotion/mammotion/devices/__init__.py +29 -0
  85. pymammotion/mammotion/devices/base.py +163 -0
  86. pymammotion/mammotion/devices/mammotion.py +571 -0
  87. pymammotion/mammotion/devices/mammotion_bluetooth.py +496 -0
  88. pymammotion/mammotion/devices/mammotion_cloud.py +355 -0
  89. pymammotion/mammotion/devices/mammotion_mower_ble.py +48 -0
  90. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  91. pymammotion/mammotion/devices/managers/managers.py +81 -0
  92. pymammotion/mammotion/devices/mower_device.py +120 -0
  93. pymammotion/mammotion/devices/mower_manager.py +107 -0
  94. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  95. pymammotion/mammotion/devices/rtk_cloud.py +115 -0
  96. pymammotion/mammotion/devices/rtk_device.py +50 -0
  97. pymammotion/mammotion/devices/rtk_manager.py +125 -0
  98. pymammotion/mqtt/__init__.py +6 -0
  99. pymammotion/mqtt/aliyun_mqtt.py +237 -0
  100. pymammotion/mqtt/linkkit/__init__.py +5 -0
  101. pymammotion/mqtt/linkkit/h2client.py +585 -0
  102. pymammotion/mqtt/linkkit/linkkit.py +3025 -0
  103. pymammotion/mqtt/mammotion_future.py +26 -0
  104. pymammotion/mqtt/mammotion_mqtt.py +214 -0
  105. pymammotion/mqtt/mqtt_models.py +66 -0
  106. pymammotion/proto/__init__.py +4841 -0
  107. pymammotion/proto/basestation.proto +51 -0
  108. pymammotion/proto/basestation_pb2.py +35 -0
  109. pymammotion/proto/basestation_pb2.pyi +89 -0
  110. pymammotion/proto/common.proto +7 -0
  111. pymammotion/proto/common_pb2.py +25 -0
  112. pymammotion/proto/common_pb2.pyi +13 -0
  113. pymammotion/proto/dev_net.proto +321 -0
  114. pymammotion/proto/dev_net_pb2.py +111 -0
  115. pymammotion/proto/dev_net_pb2.pyi +515 -0
  116. pymammotion/proto/luba_msg.proto +76 -0
  117. pymammotion/proto/luba_msg_pb2.py +41 -0
  118. pymammotion/proto/luba_msg_pb2.pyi +97 -0
  119. pymammotion/proto/luba_mul.proto +129 -0
  120. pymammotion/proto/luba_mul_pb2.py +61 -0
  121. pymammotion/proto/luba_mul_pb2.pyi +178 -0
  122. pymammotion/proto/mctrl_driver.proto +107 -0
  123. pymammotion/proto/mctrl_driver_pb2.py +57 -0
  124. pymammotion/proto/mctrl_driver_pb2.pyi +167 -0
  125. pymammotion/proto/mctrl_nav.proto +591 -0
  126. pymammotion/proto/mctrl_nav_pb2.py +136 -0
  127. pymammotion/proto/mctrl_nav_pb2.pyi +1067 -0
  128. pymammotion/proto/mctrl_ota.proto +80 -0
  129. pymammotion/proto/mctrl_ota_pb2.py +45 -0
  130. pymammotion/proto/mctrl_ota_pb2.pyi +128 -0
  131. pymammotion/proto/mctrl_pept.proto +34 -0
  132. pymammotion/proto/mctrl_pept_pb2.py +33 -0
  133. pymammotion/proto/mctrl_pept_pb2.pyi +58 -0
  134. pymammotion/proto/mctrl_sys.proto +741 -0
  135. pymammotion/proto/mctrl_sys_pb2.py +206 -0
  136. pymammotion/proto/mctrl_sys_pb2.pyi +1213 -0
  137. pymammotion/proto/message_pool.py +3 -0
  138. pymammotion/proto/py.typed +0 -0
  139. pymammotion/py.typed +0 -0
  140. pymammotion/utility/constant/__init__.py +3 -0
  141. pymammotion/utility/constant/device_constant.py +315 -0
  142. pymammotion/utility/conversions.py +5 -0
  143. pymammotion/utility/datatype_converter.py +124 -0
  144. pymammotion/utility/device_config.py +755 -0
  145. pymammotion/utility/device_type.py +489 -0
  146. pymammotion/utility/map.py +259 -0
  147. pymammotion/utility/movement.py +18 -0
  148. pymammotion/utility/mur_mur_hash.py +159 -0
  149. pymammotion/utility/periodic.py +106 -0
  150. pymammotion/utility/rocker_util.py +194 -0
  151. pymammotion-0.5.69.dist-info/METADATA +93 -0
  152. pymammotion-0.5.69.dist-info/RECORD +154 -0
  153. pymammotion-0.5.69.dist-info/WHEEL +4 -0
  154. pymammotion-0.5.69.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,145 @@
1
+ import asyncio
2
+ from timeit import default_timer as timer
3
+
4
+ import nest_asyncio
5
+ import pyjoystick
6
+ from pyjoystick.sdl2 import Key, run_event_loop
7
+ from pyjoystick.utils import PeriodicThread
8
+
9
+ from pymammotion.event import BleNotificationEvent
10
+ from pymammotion.mammotion.devices import MammotionBaseBLEDevice
11
+ from pymammotion.utility.movement import get_percent, transform_both_speeds
12
+
13
+ bleNotificationEvt = BleNotificationEvent()
14
+
15
+ nest_asyncio.apply()
16
+
17
+
18
+ class JoystickControl:
19
+ """Joystick class for controlling Luba with a joystick"""
20
+
21
+ angular_percent = 0
22
+ linear_percent = 0
23
+ linear_speed = 0
24
+ angular_speed = 0
25
+ ignore_events = False
26
+ _blade_height = 25
27
+ worker = None
28
+
29
+ def __init__(self, luba_ble: MammotionBaseBLEDevice) -> None:
30
+ self._client = luba_ble
31
+ self._curr_time = timer()
32
+ self.stopped = False
33
+
34
+ repeater = pyjoystick.HatRepeater(first_repeat_timeout=0.2, repeat_timeout=0.03, check_timeout=0.01)
35
+
36
+ self.mngr = pyjoystick.ThreadEventManager(
37
+ event_loop=run_event_loop,
38
+ handle_key_event=self.key_received,
39
+ add_joystick=self.print_add,
40
+ remove_joystick=self.print_remove,
41
+ button_repeater=repeater,
42
+ )
43
+
44
+ self.worker = PeriodicThread(0.2, self.run_movement, name="luba-process_movements")
45
+ self.worker.alive = self.mngr.alive # stop when this event stops
46
+ self.worker.daemon = True
47
+
48
+ def _movement_finished(self) -> None:
49
+ self.ignore_events = False
50
+
51
+ def run_movement(self) -> None:
52
+ if self.linear_percent == 0.0 and self.angular_percent == 0.0:
53
+ if self.stopped:
54
+ return
55
+ self.stopped = True
56
+ self.stopped = False
57
+ (linear_speed, angular_speed) = transform_both_speeds(
58
+ self.linear_speed,
59
+ self.angular_speed,
60
+ self.linear_percent,
61
+ self.angular_percent,
62
+ )
63
+ asyncio.run(self._client.command("send_movement", linear_speed=linear_speed, angular_speed=angular_speed))
64
+
65
+ def print_add(self, joy) -> None:
66
+ print("Added", joy)
67
+
68
+ def print_remove(self, joy) -> None:
69
+ print("Removed", joy)
70
+
71
+ def key_received(self, key) -> None:
72
+ self.handle_key_received(key)
73
+
74
+ def run_controller(self) -> None:
75
+ self.mngr.start()
76
+ self.worker.start()
77
+
78
+ def handle_key_received(self, key) -> None:
79
+ # print(key, "-", key.keytype, "-", key.number, "-", key.value)
80
+
81
+ if key.keytype is Key.BUTTON and key.value == 1:
82
+ # print(key, "-", key.keytype, "-", key.number, "-", key.value)
83
+ if key.number == 0: # x
84
+ asyncio.run(self._client.command("return_to_dock"))
85
+ if key.number == 1:
86
+ asyncio.run(self._client.command("leave_dock"))
87
+ if key.number == 3:
88
+ asyncio.run(self._client.command("set_blade_control", on_off=1))
89
+ if key.number == 2:
90
+ asyncio.run(self._client.command("set_blade_control", on_off=0))
91
+ if key.number == 9:
92
+ # lower knife height
93
+ if self._blade_height > 25:
94
+ self._blade_height -= 5
95
+ asyncio.run(self._client.command("set_blade_height", height=self._blade_height))
96
+ if key.number == 10:
97
+ # raise knife height
98
+ if self._blade_height < 60:
99
+ self._blade_height += 5
100
+ asyncio.run(self._client.command("set_blade_height", height=self._blade_height))
101
+
102
+ if key.keytype is Key.AXIS:
103
+ # print(key, "-", key.keytype, "-", key.number, "-", key.value)
104
+ if key.value > 0.09 or key.value < -0.09:
105
+ match key.number:
106
+ case 1: # left (up down)
107
+ # take left right values and convert to linear movement
108
+ # -1 is forward
109
+ # 1 is back
110
+
111
+ # linear_speed==1000
112
+ # linear_speed==-1000
113
+ print("case 1")
114
+ if key.value > 0:
115
+ """Backwards."""
116
+ self.linear_speed = 270.0
117
+ self.linear_percent = get_percent(abs(key.value * 100))
118
+ else:
119
+ """Forwards."""
120
+ self.linear_speed = 90.0
121
+ self.linear_percent = get_percent(abs(key.value * 100))
122
+
123
+ case 2: # right (left right)
124
+ # take left right values and convert to angular movement
125
+ # -1 left
126
+ # 1 is right
127
+ # angular_speed==-450
128
+ # angular_speed==450
129
+ if key.value > 0:
130
+ self.angular_speed = 0.0
131
+ self.angular_percent = get_percent(abs(key.value * 100))
132
+ else:
133
+ # angle=180.0
134
+ # linear_speed=0//angular_speed=-450
135
+ self.angular_speed = 180.0
136
+ self.angular_percent = get_percent(abs(key.value * 100))
137
+
138
+ else:
139
+ match key.number:
140
+ case 1: # left (up down)
141
+ self.linear_speed = 0.0
142
+ self.linear_percent = 0.0
143
+ case 2: # right (left right)
144
+ self.angular_speed = 0.0
145
+ self.angular_percent = 0.0
@@ -0,0 +1,29 @@
1
+ """Mammotion devices module."""
2
+
3
+ from .mammotion import Mammotion, MammotionDeviceManager
4
+ from .mammotion_bluetooth import MammotionBaseBLEDevice
5
+ from .mammotion_cloud import MammotionBaseCloudDevice, MammotionCloud
6
+ from .mammotion_mower_ble import MammotionMowerBLEDevice
7
+ from .mammotion_mower_cloud import MammotionMowerCloudDevice
8
+ from .mower_device import MammotionMowerDevice
9
+ from .mower_manager import MammotionMowerDeviceManager
10
+ from .rtk_ble import MammotionRTKBLEDevice
11
+ from .rtk_cloud import MammotionRTKCloudDevice
12
+ from .rtk_device import MammotionRTKDevice
13
+ from .rtk_manager import MammotionRTKDeviceManager
14
+
15
+ __all__ = [
16
+ "Mammotion",
17
+ "MammotionDeviceManager",
18
+ "MammotionMowerDeviceManager",
19
+ "MammotionBaseBLEDevice",
20
+ "MammotionBaseCloudDevice",
21
+ "MammotionCloud",
22
+ "MammotionMowerBLEDevice",
23
+ "MammotionMowerCloudDevice",
24
+ "MammotionMowerDevice",
25
+ "MammotionRTKBLEDevice",
26
+ "MammotionRTKCloudDevice",
27
+ "MammotionRTKDevice",
28
+ "MammotionRTKDeviceManager",
29
+ ]
@@ -0,0 +1,163 @@
1
+ from abc import ABC, abstractmethod
2
+ import asyncio
3
+ import logging
4
+ from typing import Any
5
+
6
+ import betterproto2
7
+
8
+ from pymammotion.aliyun.model.dev_by_account_response import Device
9
+ from pymammotion.data.model.device import MowingDevice
10
+ from pymammotion.data.model.raw_data import RawMowerData
11
+ from pymammotion.data.mower_state_manager import MowerStateManager
12
+ from pymammotion.proto import LubaMsg
13
+
14
+ _LOGGER = logging.getLogger(__name__)
15
+
16
+
17
+ class MammotionBaseDevice(ABC):
18
+ """Base class for Mammotion devices."""
19
+
20
+ def __init__(self, state_manager: MowerStateManager, cloud_device: Device) -> None:
21
+ """Initialize MammotionBaseDevice."""
22
+ self.loop = asyncio.get_event_loop()
23
+ self._state_manager = state_manager
24
+ self._raw_data = dict()
25
+ self._raw_mower_data: RawMowerData = RawMowerData()
26
+ self._notify_future: asyncio.Future[bytes] | None = None
27
+ self._cloud_device = cloud_device
28
+
29
+ def _update_raw_data(self, data: bytes) -> None:
30
+ """Update raw and model data from notifications."""
31
+ tmp_msg = LubaMsg().parse(data)
32
+ res = betterproto2.which_one_of(tmp_msg, "LubaSubMsg")
33
+ match res[0]:
34
+ case "nav":
35
+ self._update_nav_data(tmp_msg)
36
+ case "sys":
37
+ self._update_sys_data(tmp_msg)
38
+ case "driver":
39
+ self._update_driver_data(tmp_msg)
40
+ case "net":
41
+ self._update_net_data(tmp_msg)
42
+ case "mul":
43
+ self._update_mul_data(tmp_msg)
44
+ case "ota":
45
+ self._update_ota_data(tmp_msg)
46
+
47
+ self._raw_mower_data.update_raw(self._raw_data)
48
+
49
+ def _update_nav_data(self, tmp_msg) -> None:
50
+ """Update navigation data."""
51
+ nav_sub_msg = betterproto2.which_one_of(tmp_msg.nav, "SubNavMsg")
52
+ if nav_sub_msg[1] is None:
53
+ _LOGGER.debug("Sub message was NoneType %s", nav_sub_msg[0])
54
+ return
55
+ nav = self._raw_data.get("nav", {})
56
+ if isinstance(nav_sub_msg[1], int):
57
+ nav[nav_sub_msg[0]] = nav_sub_msg[1]
58
+ else:
59
+ nav[nav_sub_msg[0]] = nav_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
60
+ self._raw_data["nav"] = nav
61
+
62
+ def _update_sys_data(self, tmp_msg: LubaMsg) -> None:
63
+ """Update system data."""
64
+ sys_sub_msg = betterproto2.which_one_of(tmp_msg.sys, "SubSysMsg")
65
+ if sys_sub_msg[1] is None:
66
+ _LOGGER.debug("Sub message was NoneType %s", sys_sub_msg[0])
67
+ return
68
+ sys = self._raw_data.get("sys", {})
69
+ sys[sys_sub_msg[0]] = sys_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
70
+ self._raw_data["sys"] = sys
71
+
72
+ def _update_driver_data(self, tmp_msg: LubaMsg) -> None:
73
+ """Update driver data."""
74
+ drv_sub_msg = betterproto2.which_one_of(tmp_msg.driver, "SubDrvMsg")
75
+ if drv_sub_msg[1] is None:
76
+ _LOGGER.debug("Sub message was NoneType %s", drv_sub_msg[0])
77
+ return
78
+ drv = self._raw_data.get("driver", {})
79
+ drv[drv_sub_msg[0]] = drv_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
80
+ self._raw_data["driver"] = drv
81
+
82
+ def _update_net_data(self, tmp_msg: LubaMsg) -> None:
83
+ """Update network data."""
84
+ net_sub_msg = betterproto2.which_one_of(tmp_msg.net, "NetSubType")
85
+ if net_sub_msg[1] is None:
86
+ _LOGGER.debug("Sub message was NoneType %s", net_sub_msg[0])
87
+ return
88
+ net = self._raw_data.get("net", {})
89
+ if isinstance(net_sub_msg[1], int):
90
+ net[net_sub_msg[0]] = net_sub_msg[1]
91
+ else:
92
+ net[net_sub_msg[0]] = net_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
93
+ self._raw_data["net"] = net
94
+
95
+ def _update_mul_data(self, tmp_msg: LubaMsg) -> None:
96
+ """Update mul data."""
97
+ mul_sub_msg = betterproto2.which_one_of(tmp_msg.mul, "SubMul")
98
+ if mul_sub_msg[1] is None:
99
+ _LOGGER.debug("Sub message was NoneType %s", mul_sub_msg[0])
100
+ return
101
+ mul = self._raw_data.get("mul", {})
102
+ mul[mul_sub_msg[0]] = mul_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
103
+ self._raw_data["mul"] = mul
104
+
105
+ def _update_ota_data(self, tmp_msg: LubaMsg) -> None:
106
+ """Update OTA data."""
107
+ ota_sub_msg = betterproto2.which_one_of(tmp_msg.ota, "SubOtaMsg")
108
+ if ota_sub_msg[1] is None:
109
+ _LOGGER.debug("Sub message was NoneType %s", ota_sub_msg[0])
110
+ return
111
+ ota = self._raw_data.get("ota", {})
112
+ ota[ota_sub_msg[0]] = ota_sub_msg[1].to_dict(casing=betterproto2.Casing.SNAKE)
113
+ self._raw_data["ota"] = ota
114
+
115
+ @property
116
+ def raw_data(self) -> dict[str, Any]:
117
+ """Get the raw data of the device."""
118
+ return self._raw_data
119
+
120
+ @property
121
+ def mower(self) -> MowingDevice:
122
+ """Get the LubaMsg of the device."""
123
+ return self._state_manager.get_device()
124
+
125
+ @abstractmethod
126
+ async def queue_command(self, key: str, **kwargs: Any) -> None:
127
+ """Queue commands to mower."""
128
+
129
+ @abstractmethod
130
+ async def _ble_sync(self) -> None:
131
+ """Send ble sync command every 3 seconds or sooner."""
132
+
133
+ @abstractmethod
134
+ def stop(self) -> None:
135
+ """Stop everything ready for destroying."""
136
+
137
+ async def async_read_settings(self) -> None:
138
+ """Read settings from device."""
139
+ # no cutting in rain nav_sys_param_cmd (id 3 context 1/0)
140
+ await self.queue_command("read_write_device", rw_id=3, context=1, rw=0)
141
+ # ??
142
+ await self.queue_command("read_write_device", rw_id=4, context=1, rw=0)
143
+ # turning mode nav_sys_param_cmd (id 6, context 1/0)
144
+ await self.queue_command("read_write_device", rw_id=6, context=1, rw=0)
145
+ # traversal mode
146
+ await self.queue_command("read_write_device", rw_id=7, context=1, rw=0)
147
+
148
+ await self.queue_command("read_and_set_sidelight", is_sidelight=True, operate=1)
149
+
150
+ await self.queue_command("read_and_set_rtk_pairing_code", op=1, cfg="")
151
+
152
+ async def async_get_errors(self) -> None:
153
+ """Error codes."""
154
+ await self.queue_command("read_write_device", rw_id=5, rw=1, context=2)
155
+ await self.queue_command("read_write_device", rw_id=5, rw=1, context=3)
156
+
157
+ async def command(self, key: str, **kwargs: Any) -> None:
158
+ """Send a command to the device."""
159
+ await self.queue_command(key, **kwargs)
160
+
161
+ @property
162
+ def state_manager(self) -> MowerStateManager:
163
+ return self._state_manager