pymammotion 0.2.1__py3-none-any.whl → 0.2.3__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/aliyun/cloud_gateway.py +4 -0
- pymammotion/mammotion/devices/mammotion.py +132 -52
- {pymammotion-0.2.1.dist-info → pymammotion-0.2.3.dist-info}/METADATA +1 -1
- {pymammotion-0.2.1.dist-info → pymammotion-0.2.3.dist-info}/RECORD +6 -6
- {pymammotion-0.2.1.dist-info → pymammotion-0.2.3.dist-info}/LICENSE +0 -0
- {pymammotion-0.2.1.dist-info → pymammotion-0.2.3.dist-info}/WHEEL +0 -0
|
@@ -26,6 +26,7 @@ from bleak_retry_connector import (
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
|
|
29
|
+
from pymammotion.aliyun.dataclass.dev_by_account_response import Device
|
|
29
30
|
from pymammotion.bluetooth import BleMessage
|
|
30
31
|
from pymammotion.const import MAMMOTION_DOMAIN
|
|
31
32
|
from pymammotion.data.model import RegionData
|
|
@@ -33,7 +34,7 @@ from pymammotion.data.model.account import Credentials
|
|
|
33
34
|
from pymammotion.data.model.device import MowingDevice
|
|
34
35
|
from pymammotion.data.mqtt.event import ThingEventMessage
|
|
35
36
|
from pymammotion.data.state_manager import StateManager
|
|
36
|
-
from pymammotion.http.http import
|
|
37
|
+
from pymammotion.http.http import connect_http
|
|
37
38
|
from pymammotion.mammotion.commands.mammotion_command import MammotionCommand
|
|
38
39
|
from pymammotion.mqtt import MammotionMQTT
|
|
39
40
|
from pymammotion.proto.luba_msg import LubaMsg
|
|
@@ -125,23 +126,82 @@ class ConnectionPreference(Enum):
|
|
|
125
126
|
WIFI = 1
|
|
126
127
|
BLUETOOTH = 2
|
|
127
128
|
|
|
129
|
+
class MammotionMixedDeviceManager:
|
|
130
|
+
_ble_device: MammotionBaseBLEDevice | None = None
|
|
131
|
+
_cloud_device: MammotionBaseCloudDevice | None = None
|
|
132
|
+
_mowing_state: MowingDevice = MowingDevice()
|
|
133
|
+
|
|
134
|
+
def __init__(self, name: str, cloud_device: Device | None = None,
|
|
135
|
+
ble_device: BLEDevice | None = None, mqtt: MammotionMQTT | None = None) -> None:
|
|
136
|
+
self.name = name
|
|
137
|
+
self.add_ble(ble_device)
|
|
138
|
+
self.add_cloud(cloud_device, mqtt)
|
|
139
|
+
|
|
140
|
+
def mower_state(self):
|
|
141
|
+
return self._mowing_state
|
|
142
|
+
|
|
143
|
+
def ble(self) -> MammotionBaseBLEDevice | None:
|
|
144
|
+
return self._ble_device
|
|
145
|
+
|
|
146
|
+
def cloud(self) -> MammotionBaseCloudDevice | None:
|
|
147
|
+
return self._cloud_device
|
|
148
|
+
|
|
149
|
+
def add_ble(self, ble_device: BLEDevice) -> None:
|
|
150
|
+
if ble_device is not None:
|
|
151
|
+
self._ble_device = MammotionBaseBLEDevice(self._mowing_state, ble_device)
|
|
152
|
+
|
|
153
|
+
def add_cloud(self, cloud_device: Device | None = None, mqtt: MammotionMQTT | None = None) -> None:
|
|
154
|
+
if cloud_device is not None:
|
|
155
|
+
self._cloud_device = MammotionBaseCloudDevice(
|
|
156
|
+
mqtt_client=mqtt,
|
|
157
|
+
cloud_device=cloud_device,
|
|
158
|
+
mowing_state=self._mowing_state)
|
|
159
|
+
|
|
160
|
+
def replace_cloud(self, cloud_device:MammotionBaseCloudDevice) -> None:
|
|
161
|
+
self._cloud_device = cloud_device
|
|
162
|
+
|
|
163
|
+
def replace_ble(self, ble_device:MammotionBaseBLEDevice) -> None:
|
|
164
|
+
self._ble_device = ble_device
|
|
165
|
+
|
|
166
|
+
def has_cloud(self) -> bool:
|
|
167
|
+
return self._cloud_device is not None
|
|
168
|
+
|
|
169
|
+
def has_ble(self) -> bool:
|
|
170
|
+
return self._ble_device is not None
|
|
171
|
+
|
|
128
172
|
|
|
129
|
-
class
|
|
173
|
+
class MammotionDevices:
|
|
174
|
+
|
|
175
|
+
devices = dict[str, MammotionMixedDeviceManager] = {}
|
|
176
|
+
|
|
177
|
+
def add_device(self, mammotion_device: MammotionMixedDeviceManager) -> None:
|
|
178
|
+
exists: MammotionMixedDeviceManager | None = self.devices.get(mammotion_device.name)
|
|
179
|
+
if not exists:
|
|
180
|
+
self.devices[mammotion_device.name] = mammotion_device
|
|
181
|
+
if mammotion_device.has_cloud():
|
|
182
|
+
exists.replace_cloud(mammotion_device.cloud())
|
|
183
|
+
if mammotion_device.has_ble():
|
|
184
|
+
exists.replace_ble(mammotion_device.ble())
|
|
185
|
+
|
|
186
|
+
def get_device(self, mammotion_device_name: str) -> MammotionMixedDeviceManager:
|
|
187
|
+
return self.devices.get(mammotion_device_name)
|
|
188
|
+
|
|
189
|
+
class Mammotion:
|
|
130
190
|
"""Represents a Mammotion device."""
|
|
131
191
|
|
|
132
|
-
|
|
133
|
-
_cloud_device: MammotionBaseCloudDevice | None = None
|
|
134
|
-
_devices_list = []
|
|
192
|
+
devices = MammotionDevices()
|
|
135
193
|
|
|
136
194
|
def __init__(
|
|
137
195
|
self,
|
|
138
196
|
ble_device: BLEDevice,
|
|
139
197
|
cloud_credentials: Credentials | None = None,
|
|
140
|
-
preference: ConnectionPreference = ConnectionPreference.
|
|
198
|
+
preference: ConnectionPreference = ConnectionPreference.BLUETOOTH,
|
|
141
199
|
) -> None:
|
|
142
200
|
"""Initialize MammotionDevice."""
|
|
143
201
|
if ble_device:
|
|
144
|
-
self.
|
|
202
|
+
self.devices.add_device(MammotionMixedDeviceManager(name=ble_device.name, ble_device=ble_device))
|
|
203
|
+
|
|
204
|
+
if preference:
|
|
145
205
|
self._preference = preference
|
|
146
206
|
|
|
147
207
|
if cloud_credentials:
|
|
@@ -160,16 +220,10 @@ class MammotionDevice:
|
|
|
160
220
|
_mammotion_mqtt._cloud_client = cloud_client
|
|
161
221
|
_mammotion_mqtt.connect_async()
|
|
162
222
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
mqtt_client=_mammotion_mqtt,
|
|
168
|
-
iot_id=device.iotId,
|
|
169
|
-
device_name=device.deviceName,
|
|
170
|
-
nick_name=device.nickName
|
|
171
|
-
)
|
|
172
|
-
self._devices_list.append(dev)
|
|
223
|
+
for device in cloud_client.listing_dev_by_account_response.data.data:
|
|
224
|
+
if device.deviceName.startswith(("Luba-", "Yuka-")):
|
|
225
|
+
self.devices.add_device(MammotionMixedDeviceManager(name=device.deviceName, cloud_device=device, mqtt=_mammotion_mqtt))
|
|
226
|
+
|
|
173
227
|
|
|
174
228
|
@staticmethod
|
|
175
229
|
async def login(account: str, password: str) -> CloudIOTGateway:
|
|
@@ -189,25 +243,51 @@ class MammotionDevice:
|
|
|
189
243
|
cloud_client.list_binding_by_account()
|
|
190
244
|
return cloud_client
|
|
191
245
|
|
|
246
|
+
def get_device_by_name(self, name: str) -> MammotionMixedDeviceManager:
|
|
247
|
+
return self.devices.get_device(name)
|
|
192
248
|
|
|
193
|
-
async def send_command(self, key: str):
|
|
249
|
+
async def send_command(self, name: str, key: str):
|
|
194
250
|
"""Send a command to the device."""
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
251
|
+
device = self.get_device_by_name(name)
|
|
252
|
+
if device:
|
|
253
|
+
if self._preference is ConnectionPreference.BLUETOOTH:
|
|
254
|
+
return await device.ble().command(key)
|
|
255
|
+
if self._preference is ConnectionPreference.WIFI:
|
|
256
|
+
return await device.cloud().command(key)
|
|
257
|
+
# TODO work with both with EITHER
|
|
258
|
+
|
|
259
|
+
async def send_command_with_args(self,name: str, key: str, kwargs):
|
|
202
260
|
"""Send a command with args to the device."""
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
261
|
+
device = self.get_device_by_name(name)
|
|
262
|
+
if device:
|
|
263
|
+
if self._preference is ConnectionPreference.BLUETOOTH:
|
|
264
|
+
return await device.ble().command(key, **kwargs)
|
|
265
|
+
if self._preference is ConnectionPreference.WIFI:
|
|
266
|
+
return await device.cloud().command(key, **kwargs)
|
|
267
|
+
# TODO work with both with EITHER
|
|
268
|
+
|
|
269
|
+
async def start_sync(self,name:str, retry: int):
|
|
270
|
+
device = self.get_device_by_name(name)
|
|
271
|
+
if device:
|
|
272
|
+
if self._preference is ConnectionPreference.BLUETOOTH:
|
|
273
|
+
return await device.ble().start_sync(retry)
|
|
274
|
+
if self._preference is ConnectionPreference.WIFI:
|
|
275
|
+
return await device.cloud().start_sync(retry)
|
|
276
|
+
# TODO work with both with EITHER
|
|
277
|
+
|
|
278
|
+
async def start_map_sync(self, name:str):
|
|
279
|
+
device = self.get_device_by_name(name)
|
|
280
|
+
if device:
|
|
281
|
+
if self._preference is ConnectionPreference.BLUETOOTH:
|
|
282
|
+
return await device.ble().start_map_sync()
|
|
283
|
+
if self._preference is ConnectionPreference.WIFI:
|
|
284
|
+
return await device.cloud().start_map_sync()
|
|
285
|
+
# TODO work with both with EITHER
|
|
286
|
+
|
|
287
|
+
def mower(self, name: str):
|
|
288
|
+
device = self.get_device_by_name(name)
|
|
289
|
+
if device:
|
|
290
|
+
return device.mower_state
|
|
211
291
|
|
|
212
292
|
def has_field(message: betterproto.Message) -> bool:
|
|
213
293
|
"""Check if the message has any fields serialized on wire."""
|
|
@@ -217,15 +297,15 @@ def has_field(message: betterproto.Message) -> bool:
|
|
|
217
297
|
class MammotionBaseDevice:
|
|
218
298
|
"""Base class for Mammotion devices."""
|
|
219
299
|
|
|
220
|
-
|
|
300
|
+
_mower: MowingDevice
|
|
221
301
|
_state_manager: StateManager
|
|
222
302
|
|
|
223
|
-
def __init__(self) -> None:
|
|
303
|
+
def __init__(self, device: MowingDevice) -> None:
|
|
224
304
|
"""Initialize MammotionBaseDevice."""
|
|
225
305
|
self.loop = asyncio.get_event_loop()
|
|
226
306
|
self._raw_data = LubaMsg().to_dict(casing=betterproto.Casing.SNAKE)
|
|
227
|
-
self.
|
|
228
|
-
self._state_manager = StateManager(self.
|
|
307
|
+
self._mower = device
|
|
308
|
+
self._state_manager = StateManager(self._mower)
|
|
229
309
|
|
|
230
310
|
self._state_manager.gethash_ack_callback.add_subscribers(self.datahash_response)
|
|
231
311
|
self._state_manager.get_commondata_ack_callback.add_subscribers(self.commdata_response)
|
|
@@ -248,7 +328,7 @@ class MammotionBaseDevice:
|
|
|
248
328
|
if total_frame == current_frame:
|
|
249
329
|
# get next in hash ack list
|
|
250
330
|
|
|
251
|
-
data_hash = find_next_integer(self.
|
|
331
|
+
data_hash = find_next_integer(self._mower.nav.toapp_gethash_ack.data_couple, common_data.hash)
|
|
252
332
|
if data_hash is None:
|
|
253
333
|
return
|
|
254
334
|
result_hash = 0
|
|
@@ -285,7 +365,7 @@ class MammotionBaseDevice:
|
|
|
285
365
|
case "ota":
|
|
286
366
|
self._update_ota_data(tmp_msg)
|
|
287
367
|
|
|
288
|
-
self.
|
|
368
|
+
self._mower.update_raw(self._raw_data)
|
|
289
369
|
|
|
290
370
|
def _update_nav_data(self, tmp_msg):
|
|
291
371
|
"""Update navigation data."""
|
|
@@ -359,9 +439,9 @@ class MammotionBaseDevice:
|
|
|
359
439
|
return self._raw_data
|
|
360
440
|
|
|
361
441
|
@property
|
|
362
|
-
def
|
|
442
|
+
def mower(self) -> MowingDevice:
|
|
363
443
|
"""Get the LubaMsg of the device."""
|
|
364
|
-
return self.
|
|
444
|
+
return self._mower
|
|
365
445
|
|
|
366
446
|
@abstractmethod
|
|
367
447
|
async def _send_command(self, key: str, retry: int | None = None) -> bytes | None:
|
|
@@ -394,7 +474,7 @@ class MammotionBaseDevice:
|
|
|
394
474
|
await self._send_command_with_args("get_hash_response", total_frame=1, current_frame=1)
|
|
395
475
|
|
|
396
476
|
await self._send_command_with_args(
|
|
397
|
-
"get_area_name_list", device_id=self.
|
|
477
|
+
"get_area_name_list", device_id=self._mower.device.net.toapp_wifi_iot_status.devicename
|
|
398
478
|
)
|
|
399
479
|
|
|
400
480
|
# sub_cmd 3 is job hashes??
|
|
@@ -410,13 +490,14 @@ class MammotionBaseDevice:
|
|
|
410
490
|
class MammotionBaseBLEDevice(MammotionBaseDevice):
|
|
411
491
|
"""Base class for Mammotion BLE devices."""
|
|
412
492
|
|
|
413
|
-
def __init__(self, device: BLEDevice, interface: int = 0, **kwargs: Any) -> None:
|
|
493
|
+
def __init__(self, mowing_state: MowingDevice, device: BLEDevice, interface: int = 0, **kwargs: Any) -> None:
|
|
414
494
|
"""Initialize MammotionBaseBLEDevice."""
|
|
415
|
-
super().__init__()
|
|
495
|
+
super().__init__(mowing_state)
|
|
416
496
|
self._ble_sync_task = None
|
|
417
497
|
self._prev_notification = None
|
|
418
498
|
self._interface = f"hci{interface}"
|
|
419
499
|
self._device = device
|
|
500
|
+
self._mower = mowing_state
|
|
420
501
|
self._client: BleakClientWithServiceCache | None = None
|
|
421
502
|
self._read_char: BleakGATTCharacteristic | None = None
|
|
422
503
|
self._write_char: BleakGATTCharacteristic | None = None
|
|
@@ -791,20 +872,19 @@ class MammotionBaseCloudDevice(MammotionBaseDevice):
|
|
|
791
872
|
def __init__(
|
|
792
873
|
self,
|
|
793
874
|
mqtt_client: MammotionMQTT,
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
nick_name: str,
|
|
797
|
-
**kwargs: Any,
|
|
875
|
+
cloud_device: Device,
|
|
876
|
+
mowing_state: MowingDevice
|
|
798
877
|
) -> None:
|
|
799
878
|
"""Initialize MammotionBaseCloudDevice."""
|
|
800
|
-
super().__init__()
|
|
879
|
+
super().__init__(mowing_state)
|
|
801
880
|
self._ble_sync_task = None
|
|
802
881
|
self.is_ready = False
|
|
803
882
|
self._mqtt_client = mqtt_client
|
|
804
|
-
self.iot_id =
|
|
805
|
-
self.
|
|
883
|
+
self.iot_id = cloud_device.iotId
|
|
884
|
+
self.device = cloud_device
|
|
885
|
+
self._mower = mowing_state
|
|
806
886
|
self._command_futures = {}
|
|
807
|
-
self._commands: MammotionCommand = MammotionCommand(
|
|
887
|
+
self._commands: MammotionCommand = MammotionCommand(cloud_device.deviceName)
|
|
808
888
|
self.currentID = ""
|
|
809
889
|
self.on_ready_callback: Optional[Callable[[], None]] = None
|
|
810
890
|
self._operation_lock = asyncio.Lock()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
pymammotion/__init__.py,sha256=kmnjdt3AEMejIz5JK7h1tTJj5ZriAgKwZBa3ScA4-Ao,1516
|
|
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=XbFMWjjvqbsics04EvU-dGfTvlRLALvy075Q53foEzU,18423
|
|
4
4
|
pymammotion/aliyun/cloud_service.py,sha256=YWcKuKK6iRWy5mTnBYgHxcCusiRGGzQt3spSf7dGDss,2183
|
|
5
5
|
pymammotion/aliyun/dataclass/aep_response.py,sha256=8f6GIP58ve8gd6AL3HBoXxsy0n2q4ygWvjELGnoOnVc,452
|
|
6
6
|
pymammotion/aliyun/dataclass/connect_response.py,sha256=Yz-fEbDzgGPTo5Of2oAjmFkSv08T7ze80pQU4k-gKIU,824
|
|
@@ -58,7 +58,7 @@ pymammotion/mammotion/commands/messages/video.py,sha256=_8lJsU4sLm2CGnc7RDkueA0A
|
|
|
58
58
|
pymammotion/mammotion/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
59
|
pymammotion/mammotion/control/joystick.py,sha256=EWV20MMzQuhbLlNlXbsyZKSEpeM7x1CQL7saU4Pn0-g,6165
|
|
60
60
|
pymammotion/mammotion/devices/__init__.py,sha256=T72jt0ejtMjo1rPmn_FeMF3pmp0LLeRRpc9WcDKEYYY,126
|
|
61
|
-
pymammotion/mammotion/devices/mammotion.py,sha256=
|
|
61
|
+
pymammotion/mammotion/devices/mammotion.py,sha256=bEQJO5f3fc7eZK3D4tGVJvITfqk3L-zc8fGlBOP3qlY,42130
|
|
62
62
|
pymammotion/mqtt/__init__.py,sha256=Ocs5e-HLJvTuDpVXyECEsWIvwsUaxzj7lZ9mSYutNDY,105
|
|
63
63
|
pymammotion/mqtt/mammotion_mqtt.py,sha256=VU0c8X1DrZSZDLCVAsCaEbLX2zcyrUbEAYaPzOXALNo,7523
|
|
64
64
|
pymammotion/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -110,7 +110,7 @@ pymammotion/utility/device_type.py,sha256=KYawu2glZMVlPmxRbA4kVFujXz3miHp3rJiOWR
|
|
|
110
110
|
pymammotion/utility/map.py,sha256=aoi-Luzuph02hKynTofMoq3mnPstanx75MDAVv49CuY,2211
|
|
111
111
|
pymammotion/utility/periodic.py,sha256=9wJMfwXPlx6Mbp3Fws7LLTI34ZDKphH1bva_Ggyk32g,3281
|
|
112
112
|
pymammotion/utility/rocker_util.py,sha256=syPL0QN4zMzHiTIkUKS7RXBBptjdbkfNlPddwUD5V3A,7171
|
|
113
|
-
pymammotion-0.2.
|
|
114
|
-
pymammotion-0.2.
|
|
115
|
-
pymammotion-0.2.
|
|
116
|
-
pymammotion-0.2.
|
|
113
|
+
pymammotion-0.2.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
114
|
+
pymammotion-0.2.3.dist-info/METADATA,sha256=U-xH1iYBmX2wi09t5a6sgLKdD5VFwcJo-KcWwqli9vw,3922
|
|
115
|
+
pymammotion-0.2.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
116
|
+
pymammotion-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|