pymammotion 0.2.27__tar.gz → 0.2.29__tar.gz
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.
- {pymammotion-0.2.27 → pymammotion-0.2.29}/PKG-INFO +1 -1
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/__init__.py +2 -2
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/cloud_gateway.py +32 -16
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/dataclass/dev_by_account_response.py +0 -1
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/dataclass/session_by_authcode_response.py +1 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/ble.py +1 -1
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/account.py +1 -1
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/device.py +10 -5
- pymammotion-0.2.29/pymammotion/data/model/device_config.py +80 -0
- pymammotion-0.2.29/pymammotion/data/model/generate_route_information.py +27 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/hash_list.py +3 -6
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/location.py +1 -1
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/mowing_modes.py +8 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/mqtt/event.py +7 -2
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/state_manager.py +4 -3
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/http/http.py +10 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/control/joystick.py +1 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/devices/mammotion.py +63 -71
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mqtt/mammotion_future.py +1 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mqtt/mammotion_mqtt.py +6 -9
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/conversions.py +1 -1
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/device_type.py +36 -7
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/movement.py +2 -1
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pyproject.toml +3 -3
- pymammotion-0.2.27/pymammotion/data/model/device_config.py +0 -9
- pymammotion-0.2.27/pymammotion/data/model/generate_route_information.py +0 -133
- {pymammotion-0.2.27 → pymammotion-0.2.29}/LICENSE +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/README.md +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/cloud_service.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/dataclass/aep_response.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/dataclass/connect_response.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/dataclass/login_by_oauth_response.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/dataclass/regions_response.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/aliyun/tmp_constant.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/ble_message.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/const.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/data/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/data/convert.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/data/framectrldata.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/bluetooth/data/notifydata.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/const.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/enums.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/excute_boarder_params.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/execute_boarder.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/plan.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/rapid_state.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/region_data.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/model/report_info.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/mqtt/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/mqtt/properties.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/data/mqtt/status.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/event/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/event/event.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/http/_init_.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/abstract_message.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/mammotion_command.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/driver.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/media.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/navigation.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/network.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/ota.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/system.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/commands/messages/video.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/control/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mammotion/devices/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/mqtt/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/basestation.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/basestation.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/basestation_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/basestation_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/common.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/common.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/common_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/common_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/dev_net.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/dev_net.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/dev_net_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/dev_net_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_msg.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_msg.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_msg_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_msg_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_mul.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_mul.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_mul_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/luba_mul_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_driver.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_driver.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_driver_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_driver_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_nav.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_nav.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_nav_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_nav_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_ota.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_ota.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_ota_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_ota_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_pept.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_pept.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_pept_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_pept_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_sys.proto +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_sys.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_sys_pb2.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/proto/mctrl_sys_pb2.pyi +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/py.typed +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/constant/__init__.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/constant/device_constant.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/datatype_converter.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/map.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/periodic.py +0 -0
- {pymammotion-0.2.27 → pymammotion-0.2.29}/pymammotion/utility/rocker_util.py +0 -0
@@ -9,14 +9,14 @@ import logging
|
|
9
9
|
import os
|
10
10
|
|
11
11
|
# works outside HA on its own
|
12
|
-
from pymammotion.bluetooth.ble import
|
12
|
+
from pymammotion.bluetooth.ble import MammotionBLE
|
13
13
|
from pymammotion.http.http import MammotionHTTP, connect_http
|
14
14
|
|
15
15
|
# TODO make a working device that will work outside HA too.
|
16
16
|
from pymammotion.mammotion.devices import MammotionBaseBLEDevice
|
17
17
|
from pymammotion.mqtt import MammotionMQTT
|
18
18
|
|
19
|
-
__all__ = ["
|
19
|
+
__all__ = ["MammotionBLE", "MammotionHTTP", "connect_http", "MammotionBaseBLEDevice", "MammotionMQTT"]
|
20
20
|
|
21
21
|
logger = logging.getLogger(__name__)
|
22
22
|
|
@@ -49,15 +49,19 @@ MOVE_HEADERS = (
|
|
49
49
|
class SetupException(Exception):
|
50
50
|
pass
|
51
51
|
|
52
|
+
|
52
53
|
class AuthRefreshException(Exception):
|
53
54
|
"""Raise exception when library cannot refresh token."""
|
54
55
|
|
56
|
+
|
55
57
|
class DeviceOfflineException(Exception):
|
56
58
|
"""Raise exception when device is offline."""
|
57
59
|
|
60
|
+
|
58
61
|
class LoginException(Exception):
|
59
62
|
"""Raise exception when library cannot log in."""
|
60
63
|
|
64
|
+
|
61
65
|
class CloudIOTGateway:
|
62
66
|
"""Class for interacting with Aliyun Cloud IoT Gateway."""
|
63
67
|
|
@@ -72,11 +76,19 @@ class CloudIOTGateway:
|
|
72
76
|
_devices_by_account_response: ListingDevByAccountResponse | None = None
|
73
77
|
_region_response = None
|
74
78
|
|
75
|
-
_iot_token_issued_at
|
79
|
+
_iot_token_issued_at: int = None
|
76
80
|
|
77
81
|
converter = DatatypeConverter()
|
78
82
|
|
79
|
-
def __init__(
|
83
|
+
def __init__(
|
84
|
+
self,
|
85
|
+
connect_response: ConnectResponse | None = None,
|
86
|
+
login_by_oauth_response: LoginByOAuthResponse | None = None,
|
87
|
+
aep_response: AepResponse | None = None,
|
88
|
+
session_by_authcode_response: SessionByAuthCodeResponse | None = None,
|
89
|
+
region_response: RegionResponse | None = None,
|
90
|
+
dev_by_account: ListingDevByAccountResponse | None = None,
|
91
|
+
):
|
80
92
|
"""Initialize the CloudIOTGateway."""
|
81
93
|
self.mammotion_http: MammotionHTTP | None = None
|
82
94
|
self._app_key = APP_KEY
|
@@ -376,11 +388,8 @@ class CloudIOTGateway:
|
|
376
388
|
async with session.post(
|
377
389
|
f"https://{region_url}/api/prd/loginbyoauth.json",
|
378
390
|
headers=headers,
|
379
|
-
data={
|
380
|
-
'loginByOauthRequest': json.dumps(_bodyParam, separators=(",", ":"))
|
381
|
-
}
|
391
|
+
data={"loginByOauthRequest": json.dumps(_bodyParam, separators=(",", ":"))},
|
382
392
|
) as resp:
|
383
|
-
|
384
393
|
data = await resp.json()
|
385
394
|
logger.debug(data)
|
386
395
|
if resp.status == 200:
|
@@ -441,7 +450,7 @@ class CloudIOTGateway:
|
|
441
450
|
raise Exception("Error in creating session: " + response_body_str)
|
442
451
|
|
443
452
|
self._session_by_authcode_response = session_by_auth
|
444
|
-
self._iot_token_issued_at
|
453
|
+
self._iot_token_issued_at = int(time.time())
|
445
454
|
|
446
455
|
return response.body
|
447
456
|
|
@@ -491,18 +500,23 @@ class CloudIOTGateway:
|
|
491
500
|
response_body_dict = json.loads(response_body_str)
|
492
501
|
|
493
502
|
if int(response_body_dict.get("code")) != 200:
|
494
|
-
|
503
|
+
logger.error(response_body_dict)
|
504
|
+
raise Exception("Error check or refresh token: " + response_body_dict.__str__())
|
495
505
|
|
496
506
|
session = SessionByAuthCodeResponse.from_dict(response_body_dict)
|
497
507
|
session_data = session.data
|
498
508
|
|
499
|
-
if
|
509
|
+
if (
|
510
|
+
session_data.identityId is None
|
511
|
+
or session_data.refreshTokenExpire is None
|
512
|
+
or session_data.iotToken is None
|
513
|
+
or session_data.iotTokenExpire is None
|
514
|
+
or session_data.refreshToken is None
|
515
|
+
):
|
500
516
|
raise Exception("Error check or refresh token: Parameters not correct")
|
501
517
|
|
502
518
|
self._session_by_authcode_response = session
|
503
|
-
self._iot_token_issued_at
|
504
|
-
|
505
|
-
|
519
|
+
self._iot_token_issued_at = int(time.time())
|
506
520
|
|
507
521
|
def list_binding_by_account(self) -> ListingDevByAccountResponse:
|
508
522
|
"""List bindings by account."""
|
@@ -550,14 +564,16 @@ class CloudIOTGateway:
|
|
550
564
|
"""Send a cloud command to the specified IoT device."""
|
551
565
|
|
552
566
|
"""Check if iotToken is expired"""
|
553
|
-
if self._iot_token_issued_at + self._session_by_authcode_response.data.iotTokenExpire <= (
|
567
|
+
if self._iot_token_issued_at + self._session_by_authcode_response.data.iotTokenExpire <= (
|
568
|
+
int(time.time()) + (5 * 3600)
|
569
|
+
):
|
554
570
|
"""Token expired - Try to refresh - Check if refreshToken is not expired"""
|
555
|
-
if self._iot_token_issued_at + self._session_by_authcode_response.data.refreshTokenExpire > (
|
571
|
+
if self._iot_token_issued_at + self._session_by_authcode_response.data.refreshTokenExpire > (
|
572
|
+
int(time.time())
|
573
|
+
):
|
556
574
|
self.check_or_refresh_session()
|
557
575
|
else:
|
558
576
|
raise AuthRefreshException("Refresh token expired. Please re-login")
|
559
|
-
|
560
|
-
|
561
577
|
|
562
578
|
config = Config(
|
563
579
|
app_key=self._app_key,
|
@@ -16,8 +16,13 @@ from pymammotion.proto.mctrl_driver import MctlDriver
|
|
16
16
|
from pymammotion.proto.mctrl_nav import MctlNav
|
17
17
|
from pymammotion.proto.mctrl_ota import MctlOta
|
18
18
|
from pymammotion.proto.mctrl_pept import MctlPept
|
19
|
-
from pymammotion.proto.mctrl_sys import
|
20
|
-
|
19
|
+
from pymammotion.proto.mctrl_sys import (
|
20
|
+
MctlSys,
|
21
|
+
MowToAppInfoT,
|
22
|
+
ReportInfoData,
|
23
|
+
SystemUpdateBufMsg,
|
24
|
+
SystemRapidStateTunnelMsg,
|
25
|
+
)
|
21
26
|
from pymammotion.utility.constant import WorkMode
|
22
27
|
from pymammotion.utility.conversions import parse_double
|
23
28
|
from pymammotion.utility.map import CoordinateConverter
|
@@ -105,9 +110,9 @@ class MowingDevice:
|
|
105
110
|
parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)
|
106
111
|
)
|
107
112
|
if location.zone_hash:
|
108
|
-
self.location.work_zone =
|
109
|
-
|
110
|
-
|
113
|
+
self.location.work_zone = (
|
114
|
+
location.zone_hash if self.report_data.dev.sys_status == WorkMode.MODE_WORKING else 0
|
115
|
+
)
|
111
116
|
|
112
117
|
self.report_data = self.report_data.from_dict(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE))
|
113
118
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
|
3
|
+
from pymammotion.utility.device_type import DeviceType
|
4
|
+
|
5
|
+
|
6
|
+
@dataclass
|
7
|
+
class DeviceLimits:
|
8
|
+
blade_height_min: int
|
9
|
+
blade_height_max: int
|
10
|
+
working_speed_min: float
|
11
|
+
working_speed_max: float
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class OperationSettings:
|
16
|
+
"""Operation settings for a device."""
|
17
|
+
|
18
|
+
is_mow: bool = True
|
19
|
+
is_dump: bool = True
|
20
|
+
is_edge: bool = False
|
21
|
+
collect_grass_frequency: int = 10
|
22
|
+
job_mode: int = 0 # taskMode
|
23
|
+
job_version: int = 0
|
24
|
+
job_id: int = 0
|
25
|
+
speed: float = 0.3
|
26
|
+
ultra_wave: int = 2 # touch no touch etc
|
27
|
+
channel_mode: int = 0 # line mode is grid single double or single2
|
28
|
+
channel_width: int = 25
|
29
|
+
rain_tactics: int = 0
|
30
|
+
blade_height: int = 0
|
31
|
+
path_order: str = ""
|
32
|
+
toward: int = 0 # is just angle
|
33
|
+
toward_included_angle: int = 0
|
34
|
+
toward_mode: int = 0 # angle type relative etc
|
35
|
+
border_mode: int = 1 # border laps
|
36
|
+
obstacle_laps: int = 1
|
37
|
+
start_progress: int = 0
|
38
|
+
|
39
|
+
|
40
|
+
def create_path_order(operation_mode: OperationSettings, device_name: str) -> str:
|
41
|
+
i = 8
|
42
|
+
bArr = bytearray(8)
|
43
|
+
bArr[0] = operation_mode.border_mode
|
44
|
+
bArr[1] = operation_mode.obstacle_laps
|
45
|
+
bArr[3] = operation_mode.start_progress
|
46
|
+
bArr[2] = 0
|
47
|
+
|
48
|
+
if not DeviceType.is_luba1(device_name):
|
49
|
+
bArr[4] = 0
|
50
|
+
if DeviceType.is_yuka(device_name):
|
51
|
+
i = calculate_yuka_mode(operation_mode)
|
52
|
+
elif not DeviceType.is_luba_2(device_name):
|
53
|
+
i = 0
|
54
|
+
bArr[5] = i
|
55
|
+
if operation_mode.is_dump:
|
56
|
+
b = operation_mode.collect_grass_frequency
|
57
|
+
else:
|
58
|
+
b = 10
|
59
|
+
bArr[6] = b
|
60
|
+
if DeviceType.is_luba1(device_name):
|
61
|
+
bArr[4] = operation_mode.toward_mode
|
62
|
+
return str(bArr, "UTF-8")
|
63
|
+
|
64
|
+
|
65
|
+
def calculate_yuka_mode(operation_mode: OperationSettings):
|
66
|
+
if operation_mode.is_mow and operation_mode.is_dump and operation_mode.is_edge:
|
67
|
+
return 14
|
68
|
+
if operation_mode.is_mow and operation_mode.is_dump and not operation_mode.is_edge:
|
69
|
+
return 12
|
70
|
+
if operation_mode.is_mow and not operation_mode.is_dump and operation_mode.is_edge:
|
71
|
+
return 10
|
72
|
+
if operation_mode.is_mow and not operation_mode.is_dump and not operation_mode.is_edge:
|
73
|
+
return 8
|
74
|
+
if not operation_mode.is_mow and operation_mode.is_dump and operation_mode.is_edge:
|
75
|
+
return 6
|
76
|
+
if not operation_mode.is_mow and not operation_mode.is_dump and operation_mode.is_edge:
|
77
|
+
return 2
|
78
|
+
if not operation_mode.is_mow and operation_mode.is_dump and not operation_mode.is_edge:
|
79
|
+
return 4
|
80
|
+
return 0
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import logging
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import List
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class GenerateRouteInformation:
|
10
|
+
"""Creates a model for generating route information and mowing plan before starting a job."""
|
11
|
+
|
12
|
+
one_hashs: list[int] = list
|
13
|
+
job_mode: int = 0 # taskMode
|
14
|
+
job_version: int = 0
|
15
|
+
job_id: int = 0
|
16
|
+
speed: float = 0.3
|
17
|
+
ultra_wave: int = 2 # touch no touch etc
|
18
|
+
channel_mode: int = 0 # line mode is grid single double or single2
|
19
|
+
channel_width: int = 25
|
20
|
+
rain_tactics: int = 0
|
21
|
+
blade_height: int = 0
|
22
|
+
path_order: str = ""
|
23
|
+
toward: int = 0 # is just angle
|
24
|
+
toward_included_angle: int = 0
|
25
|
+
toward_mode: int = 0 # angle type relative etc
|
26
|
+
edge_mode: int = 1 # border laps
|
27
|
+
obstacle_laps: int = 1
|
@@ -6,10 +6,12 @@ from pymammotion.proto.mctrl_nav import NavGetCommDataAck
|
|
6
6
|
|
7
7
|
class PathType(IntEnum):
|
8
8
|
"""Path types for common data."""
|
9
|
+
|
9
10
|
AREA = 0
|
10
11
|
OBSTACLE = 1
|
11
12
|
PATH = 2
|
12
13
|
|
14
|
+
|
13
15
|
@dataclass
|
14
16
|
class FrameList:
|
15
17
|
total_frame: int
|
@@ -34,7 +36,6 @@ class HashList:
|
|
34
36
|
self.path = {hash_id: frames for hash_id, frames in self.path.items() if hash_id in hashlist}
|
35
37
|
self.obstacle = {hash_id: frames for hash_id, frames in self.obstacle.items() if hash_id in hashlist}
|
36
38
|
|
37
|
-
|
38
39
|
def missing_frame(self, hash_data: NavGetCommDataAck) -> list[int]:
|
39
40
|
if hash_data.type == PathType.AREA:
|
40
41
|
return self._find_missing_frames(self.area.get(hash_data.hash))
|
@@ -45,8 +46,6 @@ class HashList:
|
|
45
46
|
if hash_data.type == PathType.PATH:
|
46
47
|
return self._find_missing_frames(self.path.get(hash_data.hash))
|
47
48
|
|
48
|
-
|
49
|
-
|
50
49
|
def update(self, hash_data: NavGetCommDataAck) -> bool:
|
51
50
|
"""Update the map data."""
|
52
51
|
if hash_data.type == PathType.AREA:
|
@@ -58,18 +57,16 @@ class HashList:
|
|
58
57
|
if hash_data.type == PathType.PATH:
|
59
58
|
return self._add_hash_data(self.path, hash_data)
|
60
59
|
|
61
|
-
|
62
60
|
@staticmethod
|
63
61
|
def _find_missing_frames(frame_list: FrameList) -> list[int]:
|
64
62
|
if frame_list.total_frame == len(frame_list.data):
|
65
63
|
return []
|
66
|
-
number_list = list(range(1, frame_list.total_frame+1))
|
64
|
+
number_list = list(range(1, frame_list.total_frame + 1))
|
67
65
|
|
68
66
|
current_frames = {frame.current_frame for frame in frame_list.data}
|
69
67
|
missing_numbers = [num for num in number_list if num not in current_frames]
|
70
68
|
return missing_numbers
|
71
69
|
|
72
|
-
|
73
70
|
@staticmethod
|
74
71
|
def _add_hash_data(hash_dict: dict, hash_data: NavGetCommDataAck) -> bool:
|
75
72
|
if hash_dict.get(hash_data.hash) is None:
|
@@ -44,3 +44,11 @@ class BypassStrategy(IntEnum):
|
|
44
44
|
slow_touch = 1
|
45
45
|
less_touch = 2
|
46
46
|
no_touch = 3 # luba 2 yuka only or possibly value of 10
|
47
|
+
|
48
|
+
|
49
|
+
class PathAngleSetting(IntEnum):
|
50
|
+
"""Path Angle type."""
|
51
|
+
|
52
|
+
relative_angle = 0
|
53
|
+
absolute_angle = 1
|
54
|
+
random_angle = 2 # Luba Pro / Luba 2 Yuka only
|
@@ -45,23 +45,25 @@ class DeviceWarningEventValue(DataClassORJSONMixin):
|
|
45
45
|
# (see resources/res/values-en-rUS/strings.xml in APK)
|
46
46
|
code: int
|
47
47
|
|
48
|
+
|
48
49
|
@dataclass
|
49
50
|
class DeviceConfigurationRequestValue(DataClassORJSONMixin):
|
50
51
|
code: int
|
51
52
|
bizId: str
|
52
53
|
params: str
|
53
54
|
|
55
|
+
|
54
56
|
@dataclass
|
55
57
|
class DeviceNotificationEventCode(DataClassORJSONMixin):
|
56
58
|
localTime: int
|
57
59
|
code: str
|
58
60
|
|
61
|
+
|
59
62
|
@dataclass
|
60
63
|
class DeviceNotificationEventValue(DataClassORJSONMixin):
|
61
64
|
data: DeviceNotificationEventCode
|
62
65
|
|
63
66
|
|
64
|
-
|
65
67
|
@dataclass
|
66
68
|
class GeneralParams(DataClassORJSONMixin):
|
67
69
|
groupIdList: list[str]
|
@@ -100,6 +102,7 @@ class DeviceProtobufMsgEventParams(GeneralParams):
|
|
100
102
|
type: Literal["info"]
|
101
103
|
value: DeviceProtobufMsgEventValue
|
102
104
|
|
105
|
+
|
103
106
|
@dataclass
|
104
107
|
class DeviceNotificationEventParams(GeneralParams):
|
105
108
|
"""Device notification event.
|
@@ -111,12 +114,14 @@ class DeviceNotificationEventParams(GeneralParams):
|
|
111
114
|
type: Literal["info"]
|
112
115
|
value: DeviceNotificationEventValue
|
113
116
|
|
117
|
+
|
114
118
|
@dataclass
|
115
119
|
class DeviceWarningEventParams(GeneralParams):
|
116
120
|
identifier: Literal["device_warning_event"]
|
117
121
|
type: Literal["alert"]
|
118
122
|
value: DeviceWarningEventValue
|
119
123
|
|
124
|
+
|
120
125
|
@dataclass
|
121
126
|
class DeviceConfigurationRequestEvent(GeneralParams):
|
122
127
|
type: Literal["info"]
|
@@ -127,7 +132,7 @@ class DeviceConfigurationRequestEvent(GeneralParams):
|
|
127
132
|
class ThingEventMessage(DataClassORJSONMixin):
|
128
133
|
method: Literal["thing.events", "thing.properties"]
|
129
134
|
id: str
|
130
|
-
params: Union[DeviceProtobufMsgEventParams, DeviceWarningEventParams,
|
135
|
+
params: Union[DeviceProtobufMsgEventParams, DeviceWarningEventParams, dict]
|
131
136
|
version: Literal["1.0"]
|
132
137
|
|
133
138
|
@classmethod
|
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Manage state from notifications into MowingDevice."""
|
2
|
+
|
2
3
|
from typing import Optional, Callable, Awaitable
|
3
4
|
|
4
5
|
import betterproto
|
@@ -15,9 +16,9 @@ class StateManager:
|
|
15
16
|
|
16
17
|
def __init__(self, device: MowingDevice):
|
17
18
|
self._device = device
|
18
|
-
self.gethash_ack_callback: Optional[Callable[[NavGetHashListAck],Awaitable[None]]] = None
|
19
|
-
self.get_commondata_ack_callback: Optional[Callable[[NavGetCommDataAck],Awaitable[None]]] = None
|
20
|
-
self.on_notification_callback: Optional[Callable[[],Awaitable[None]]] = None
|
19
|
+
self.gethash_ack_callback: Optional[Callable[[NavGetHashListAck], Awaitable[None]]] = None
|
20
|
+
self.get_commondata_ack_callback: Optional[Callable[[NavGetCommDataAck], Awaitable[None]]] = None
|
21
|
+
self.on_notification_callback: Optional[Callable[[], Awaitable[None]]] = None
|
21
22
|
|
22
23
|
def get_device(self) -> MowingDevice:
|
23
24
|
"""Get device."""
|
@@ -54,6 +54,16 @@ class MammotionHTTP:
|
|
54
54
|
self.msg = response.msg
|
55
55
|
self.code = response.code
|
56
56
|
|
57
|
+
async def get_all_error_codes(self):
|
58
|
+
async with ClientSession() as session:
|
59
|
+
async with session.post(
|
60
|
+
"code/record/export-data",
|
61
|
+
headers=self._headers,
|
62
|
+
) as resp:
|
63
|
+
if resp.status == 200:
|
64
|
+
data = await resp.json()
|
65
|
+
print(resp)
|
66
|
+
|
57
67
|
@classmethod
|
58
68
|
async def login(cls, session: ClientSession, username: str, password: str) -> Response[LoginResponseData]:
|
59
69
|
async with session.post(
|