pymammotion 0.5.34__py3-none-any.whl → 0.5.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 (47) hide show
  1. pymammotion/__init__.py +3 -3
  2. pymammotion/aliyun/cloud_gateway.py +106 -18
  3. pymammotion/aliyun/model/dev_by_account_response.py +198 -20
  4. pymammotion/data/model/device.py +1 -0
  5. pymammotion/data/model/device_config.py +1 -1
  6. pymammotion/data/model/enums.py +3 -1
  7. pymammotion/data/model/generate_route_information.py +2 -2
  8. pymammotion/data/model/hash_list.py +113 -33
  9. pymammotion/data/model/region_data.py +4 -4
  10. pymammotion/data/{state_manager.py → mower_state_manager.py} +17 -7
  11. pymammotion/homeassistant/__init__.py +3 -0
  12. pymammotion/homeassistant/mower_api.py +446 -0
  13. pymammotion/homeassistant/rtk_api.py +54 -0
  14. pymammotion/http/http.py +115 -4
  15. pymammotion/http/model/http.py +77 -2
  16. pymammotion/http/model/response_factory.py +10 -4
  17. pymammotion/mammotion/commands/mammotion_command.py +6 -0
  18. pymammotion/mammotion/commands/messages/navigation.py +10 -6
  19. pymammotion/mammotion/devices/__init__.py +27 -3
  20. pymammotion/mammotion/devices/base.py +16 -138
  21. pymammotion/mammotion/devices/mammotion.py +361 -204
  22. pymammotion/mammotion/devices/mammotion_bluetooth.py +7 -5
  23. pymammotion/mammotion/devices/mammotion_cloud.py +22 -74
  24. pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
  25. pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
  26. pymammotion/mammotion/devices/managers/managers.py +81 -0
  27. pymammotion/mammotion/devices/mower_device.py +121 -0
  28. pymammotion/mammotion/devices/mower_manager.py +107 -0
  29. pymammotion/mammotion/devices/rtk_ble.py +89 -0
  30. pymammotion/mammotion/devices/rtk_cloud.py +113 -0
  31. pymammotion/mammotion/devices/rtk_device.py +50 -0
  32. pymammotion/mammotion/devices/rtk_manager.py +122 -0
  33. pymammotion/mqtt/__init__.py +2 -1
  34. pymammotion/mqtt/aliyun_mqtt.py +232 -0
  35. pymammotion/mqtt/mammotion_mqtt.py +132 -194
  36. pymammotion/mqtt/mqtt_models.py +66 -0
  37. pymammotion/proto/__init__.py +1 -1
  38. pymammotion/proto/mctrl_nav.proto +1 -1
  39. pymammotion/proto/mctrl_nav_pb2.py +1 -1
  40. pymammotion/proto/mctrl_nav_pb2.pyi +4 -4
  41. pymammotion/proto/mctrl_sys.proto +1 -1
  42. pymammotion/utility/device_type.py +88 -3
  43. pymammotion/utility/mur_mur_hash.py +132 -87
  44. {pymammotion-0.5.34.dist-info → pymammotion-0.5.41.dist-info}/METADATA +25 -31
  45. {pymammotion-0.5.34.dist-info → pymammotion-0.5.41.dist-info}/RECORD +54 -40
  46. {pymammotion-0.5.34.dist-info → pymammotion-0.5.41.dist-info}/WHEEL +1 -1
  47. {pymammotion-0.5.34.dist-info → pymammotion-0.5.41.dist-info/licenses}/LICENSE +0 -0
pymammotion/__init__.py CHANGED
@@ -15,12 +15,12 @@ from pymammotion.bluetooth.ble import MammotionBLE
15
15
  from pymammotion.http.http import MammotionHTTP
16
16
 
17
17
  # TODO make a working device that will work outside HA too.
18
- from pymammotion.mqtt import MammotionMQTT
18
+ from pymammotion.mqtt import AliyunMQTT, MammotionMQTT
19
19
 
20
20
  logger = logging.getLogger(__name__)
21
21
 
22
22
 
23
- __all__ = ["MammotionBLE", "MammotionHTTP", "MammotionMQTT", "logger"]
23
+ __all__ = ["MammotionBLE", "MammotionHTTP", "AliyunMQTT", "MammotionMQTT", "logger"]
24
24
 
25
25
 
26
26
  # TODO provide interface to pick between mqtt/cloud/bluetooth
@@ -37,7 +37,7 @@ if __name__ == "__main__":
37
37
  REGION = os.environ.get("REGION")
38
38
  mammotion_http = MammotionHTTP()
39
39
  cloud_client = CloudIOTGateway(mammotion_http)
40
- luba = MammotionMQTT(
40
+ luba = AliyunMQTT(
41
41
  iot_token=IOT_TOKEN or "",
42
42
  region_id=REGION or "",
43
43
  product_key=PRODUCT_KEY or "",
@@ -22,7 +22,7 @@ from Tea.exceptions import UnretryableException
22
22
  from pymammotion.aliyun.client import Client
23
23
  from pymammotion.aliyun.model.aep_response import AepResponse
24
24
  from pymammotion.aliyun.model.connect_response import ConnectResponse
25
- from pymammotion.aliyun.model.dev_by_account_response import ListingDevByAccountResponse
25
+ from pymammotion.aliyun.model.dev_by_account_response import ListingDevAccountResponse
26
26
  from pymammotion.aliyun.model.login_by_oauth_response import LoginByOAuthResponse
27
27
  from pymammotion.aliyun.model.regions_response import RegionResponse
28
28
  from pymammotion.aliyun.model.session_by_authcode_response import SessionByAuthCodeResponse
@@ -103,6 +103,9 @@ class CheckSessionException(Exception):
103
103
  """Raise exception when checking session results in a failure."""
104
104
 
105
105
 
106
+ EXPIRED_CREDENTIAL_EXCEPTIONS = (CheckSessionException, SetupException)
107
+
108
+
106
109
  class CloudIOTGateway:
107
110
  """Class for interacting with Aliyun Cloud IoT Gateway."""
108
111
 
@@ -120,7 +123,7 @@ class CloudIOTGateway:
120
123
  aep_response: AepResponse | None = None,
121
124
  session_by_authcode_response: SessionByAuthCodeResponse | None = None,
122
125
  region_response: RegionResponse | None = None,
123
- dev_by_account: ListingDevByAccountResponse | None = None,
126
+ dev_by_account: ListingDevAccountResponse | None = None,
124
127
  ) -> None:
125
128
  """Initialize the CloudIOTGateway."""
126
129
  self.mammotion_http: MammotionHTTP = mammotion_http
@@ -146,7 +149,7 @@ class CloudIOTGateway:
146
149
  )
147
150
 
148
151
  @staticmethod
149
- def generate_random_string(length: int):
152
+ def generate_random_string(length: int) -> str:
150
153
  """Generate a random string of specified length."""
151
154
  characters = string.ascii_letters + string.digits
152
155
  return "".join(random.choice(characters) for _ in range(length))
@@ -165,7 +168,7 @@ class CloudIOTGateway:
165
168
  logger.error("Couldn't decode message %s", response_body_str)
166
169
  return {"code": 22000}
167
170
 
168
- def sign(self, data):
171
+ def sign(self, data: dict) -> str:
169
172
  """Generate signature for the given data."""
170
173
  keys = ["appKey", "clientId", "deviceSn", "timestamp"]
171
174
  concatenated_str = ""
@@ -180,7 +183,7 @@ class CloudIOTGateway:
180
183
  hashlib.sha1,
181
184
  ).hexdigest()
182
185
 
183
- async def get_region(self, country_code: str):
186
+ async def get_region(self, country_code: str) -> RegionResponse:
184
187
  """Get the region based on country code and auth code."""
185
188
  auth_code = self.mammotion_http.login_info.authorization_code
186
189
 
@@ -244,7 +247,7 @@ class CloudIOTGateway:
244
247
 
245
248
  return response.body
246
249
 
247
- async def aep_handle(self):
250
+ async def aep_handle(self) -> AepResponse:
248
251
  """Handle AEP authentication."""
249
252
  aep_domain = self.domain
250
253
 
@@ -300,9 +303,9 @@ class CloudIOTGateway:
300
303
 
301
304
  logger.debug(response_body_dict)
302
305
 
303
- return response.body
306
+ return self._aep_response
304
307
 
305
- async def connect(self):
308
+ async def connect(self) -> ConnectResponse:
306
309
  """Connect to the Aliyun Cloud IoT Gateway."""
307
310
  region_url = "sdk.openaccount.aliyun.com"
308
311
  time_now = time.time()
@@ -449,7 +452,7 @@ class CloudIOTGateway:
449
452
  return self._login_by_oauth_response
450
453
  raise LoginException(data)
451
454
 
452
- async def session_by_auth_code(self):
455
+ async def session_by_auth_code(self) -> SessionByAuthCodeResponse:
453
456
  """Create a session by auth code."""
454
457
  config = Config(
455
458
  app_key=self._app_key,
@@ -608,7 +611,8 @@ class CloudIOTGateway:
608
611
  session_data = session.data
609
612
 
610
613
  if (
611
- session_data.identityId is None
614
+ session_data is None
615
+ or session_data.identityId is None
612
616
  or session_data.refreshTokenExpire is None
613
617
  or session_data.iotToken is None
614
618
  or session_data.iotTokenExpire is None
@@ -619,7 +623,7 @@ class CloudIOTGateway:
619
623
  self._session_by_authcode_response = session
620
624
  self._iot_token_issued_at = int(time.time())
621
625
 
622
- async def list_binding_by_account(self) -> ListingDevByAccountResponse:
626
+ async def list_binding_by_account(self) -> ListingDevAccountResponse:
623
627
  """List bindings by account."""
624
628
  config = Config(
625
629
  app_key=self._app_key,
@@ -658,9 +662,9 @@ class CloudIOTGateway:
658
662
  response_body_dict = self.parse_json_response(response_body_str)
659
663
 
660
664
  if int(response_body_dict.get("code")) != 200:
661
- raise Exception("Error in creating session: " + response_body_dict["msg"])
665
+ raise Exception("Error in creating session: " + response_body_dict["message"])
662
666
 
663
- self._devices_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict)
667
+ self._devices_by_account_response = ListingDevAccountResponse.from_dict(response_body_dict)
664
668
  return self._devices_by_account_response
665
669
 
666
670
  async def list_binding_by_dev(self, iot_id: str):
@@ -698,10 +702,94 @@ class CloudIOTGateway:
698
702
  # Load the JSON string into a dictionary
699
703
  response_body_dict = self.parse_json_response(response_body_str)
700
704
 
705
+ if int(response_body_dict.get("code")) != 200:
706
+ raise Exception("Error in getting shared device list: " + response_body_dict["msg"])
707
+
708
+ self._devices_by_account_response = ListingDevAccountResponse.from_dict(response_body_dict)
709
+ return self._devices_by_account_response
710
+
711
+ async def confirm_share(self, record_list: list[str]) -> bool:
712
+ config = Config(
713
+ app_key=self._app_key,
714
+ app_secret=self._app_secret,
715
+ domain=self._region_response.data.apiGatewayEndpoint,
716
+ )
717
+
718
+ client = Client(config)
719
+
720
+ # build request
721
+ request = CommonParams(
722
+ api_ver="1.0.7",
723
+ language="en-US",
724
+ iot_token=self._session_by_authcode_response.data.iotToken,
725
+ )
726
+ body = IoTApiRequest(
727
+ id=str(uuid.uuid4()),
728
+ params={"agree": 1, "recordIdList": record_list},
729
+ request=request,
730
+ version="1.0",
731
+ )
732
+
733
+ # send request
734
+ response = await client.async_do_request("/uc/confirmShare", "https", "POST", None, body, RuntimeOptions())
735
+ logger.debug(response.status_message)
736
+ logger.debug(response.headers)
737
+ logger.debug(response.status_code)
738
+ logger.debug(response.body)
739
+
740
+ # Decode the response body
741
+ response_body_str = response.body.decode("utf-8")
742
+
743
+ # Load the JSON string into a dictionary
744
+ response_body_dict = self.parse_json_response(response_body_str)
745
+
746
+ if int(response_body_dict.get("code")) != 200:
747
+ raise Exception("Error in accepting share: " + response_body_dict["msg"])
748
+
749
+ return True
750
+
751
+ async def get_shared_notice_list(self):
752
+ ### status 0 accepted status -1 ready to be accepted 3 expired
753
+ config = Config(
754
+ app_key=self._app_key,
755
+ app_secret=self._app_secret,
756
+ domain=self._region_response.data.apiGatewayEndpoint,
757
+ )
758
+
759
+ client = Client(config)
760
+
761
+ # build request
762
+ request = CommonParams(
763
+ api_ver="1.0.9",
764
+ language="en-US",
765
+ iot_token=self._session_by_authcode_response.data.iotToken,
766
+ )
767
+ body = IoTApiRequest(
768
+ id=str(uuid.uuid4()),
769
+ params={"pageSize": 100, "pageNo": 1},
770
+ request=request,
771
+ version="1.0",
772
+ )
773
+
774
+ # send request
775
+ response = await client.async_do_request(
776
+ "/uc/getShareNoticeList", "https", "POST", None, body, RuntimeOptions()
777
+ )
778
+ logger.debug(response.status_message)
779
+ logger.debug(response.headers)
780
+ logger.debug(response.status_code)
781
+ logger.debug(response.body)
782
+
783
+ # Decode the response body
784
+ response_body_str = response.body.decode("utf-8")
785
+
786
+ # Load the JSON string into a dictionary
787
+ response_body_dict = self.parse_json_response(response_body_str)
788
+
701
789
  if int(response_body_dict.get("code")) != 200:
702
790
  raise Exception("Error in creating session: " + response_body_dict["msg"])
703
791
 
704
- self._devices_by_account_response = ListingDevByAccountResponse.from_dict(response_body_dict)
792
+ self._devices_by_account_response = ListingDevAccountResponse.from_dict(response_body_dict)
705
793
  return self._devices_by_account_response
706
794
 
707
795
  async def send_cloud_command(self, iot_id: str, command: bytes) -> str:
@@ -867,11 +955,11 @@ class CloudIOTGateway:
867
955
  self.mammotion_http = mammotion_http
868
956
 
869
957
  @property
870
- def region_response(self) -> RegionResponse:
958
+ def region_response(self) -> RegionResponse | None:
871
959
  return self._region_response
872
960
 
873
961
  @property
874
- def aep_response(self) -> AepResponse:
962
+ def aep_response(self) -> AepResponse | None:
875
963
  return self._aep_response
876
964
 
877
965
  @property
@@ -883,9 +971,9 @@ class CloudIOTGateway:
883
971
  return self._client_id
884
972
 
885
973
  @property
886
- def login_by_oauth_response(self) -> LoginByOAuthResponse:
974
+ def login_by_oauth_response(self) -> LoginByOAuthResponse | None:
887
975
  return self._login_by_oauth_response
888
976
 
889
977
  @property
890
- def connect_response(self) -> ConnectResponse:
978
+ def connect_response(self) -> ConnectResponse | None:
891
979
  return self._connect_response
@@ -6,30 +6,208 @@ from mashumaro.mixins.orjson import DataClassORJSONMixin
6
6
 
7
7
  @dataclass
8
8
  class Device(DataClassORJSONMixin):
9
- gmtModified: int
10
- netType: str
11
- categoryKey: str
12
- productKey: str
13
- nodeType: str
14
- isEdgeGateway: bool
15
- deviceName: str
16
- categoryName: str
17
- identityAlias: str
18
- productName: str
19
- iotId: str
20
- bindTime: int
21
- owned: int
22
- identityId: str
23
- thingType: str
9
+ """Unified device model supporting both Device and ShareNotification data"""
10
+
11
+ # Core device fields (from Device model)
12
+ gmt_modified: int
13
+ node_type: str
14
+ device_name: str
15
+ product_name: str
24
16
  status: int
25
- nickName: str | None = None
17
+ identity_id: str
18
+
19
+ # Required fields from original Device model
20
+ net_type: str
21
+ category_key: str
22
+ product_key: str
23
+ is_edge_gateway: bool
24
+ category_name: str
25
+ identity_alias: str
26
+ iot_id: str
27
+ bind_time: int
28
+ owned: int
29
+ thing_type: str
30
+
31
+ # Optional fields (common to both or nullable)
32
+ nick_name: str | None = None
26
33
  description: str | None = None
27
- productImage: str | None = None
28
- categoryImage: str | None = None
29
- productModel: str | None = None
34
+ product_image: str | None = None
35
+ category_image: str | None = None
36
+ product_model: str | None = None
37
+
38
+ # Optional fields from ShareNotification only
39
+ target_id: str | None = None
40
+ receiver_identity_id: str | None = None
41
+ target_type: str | None = None
42
+ gmt_create: int | None = None
43
+ batch_id: str | None = None
44
+ record_id: str | None = None
45
+ initiator_identity_id: str | None = None
46
+ is_receiver: int | None = None
47
+ initiator_alias: str | None = None
48
+ receiver_alias: str | None = None
30
49
 
31
50
  class Config(BaseConfig):
32
51
  omit_default = True
52
+ serialize_by_alias = True
53
+ aliases = {
54
+ # Original Device model aliases
55
+ "gmt_modified": "gmtModified",
56
+ "net_type": "netType",
57
+ "category_key": "categoryKey",
58
+ "product_key": "productKey",
59
+ "node_type": "nodeType",
60
+ "is_edge_gateway": "isEdgeGateway",
61
+ "device_name": "deviceName",
62
+ "category_name": "categoryName",
63
+ "identity_alias": "identityAlias",
64
+ "product_name": "productName",
65
+ "iot_id": "iotId",
66
+ "bind_time": "bindTime",
67
+ "identity_id": "identityId",
68
+ "thing_type": "thingType",
69
+ "nick_name": "nickName",
70
+ "product_image": "productImage",
71
+ "category_image": "categoryImage",
72
+ "product_model": "productModel",
73
+ # ShareNotification specific aliases
74
+ "target_id": "targetId",
75
+ "receiver_identity_id": "receiverIdentityId",
76
+ "target_type": "targetType",
77
+ "gmt_create": "gmtCreate",
78
+ "batch_id": "batchId",
79
+ "record_id": "recordId",
80
+ "initiator_identity_id": "initiatorIdentityId",
81
+ "is_receiver": "isReceiver",
82
+ "initiator_alias": "initiatorAlias",
83
+ "receiver_alias": "receiverAlias",
84
+ }
85
+
86
+
87
+ # # Alternative: Keep them separate but with a common base class
88
+ # @dataclass
89
+ # class BaseDevice(DataClassORJSONMixin):
90
+ # """Base device model with common fields"""
91
+ #
92
+ # gmt_modified: int
93
+ # node_type: str
94
+ # device_name: str
95
+ # product_name: str
96
+ # status: int
97
+ # product_image: Optional[str] = None
98
+ # category_image: Optional[str] = None
99
+ # description: Optional[str] = None
100
+ #
101
+ # class Config(BaseConfig):
102
+ # omit_default = True
103
+ # serialize_by_alias = True
104
+ # aliases = {
105
+ # "gmt_modified": "gmtModified",
106
+ # "node_type": "nodeType",
107
+ # "device_name": "deviceName",
108
+ # "product_name": "productName",
109
+ # "product_image": "productImage",
110
+ # "category_image": "categoryImage",
111
+ # }
112
+ #
113
+ #
114
+ # @dataclass
115
+ # class Device(BaseDevice):
116
+ # """Full device model"""
117
+ #
118
+ # net_type: str
119
+ # category_key: str
120
+ # product_key: str
121
+ # is_edge_gateway: bool
122
+ # category_name: str
123
+ # identity_alias: str
124
+ # iot_id: str
125
+ # bind_time: int
126
+ # owned: int
127
+ # identity_id: str
128
+ # thing_type: str
129
+ # nick_name: Optional[str] = None
130
+ # product_model: Optional[str] = None
131
+ #
132
+ # class Config(BaseConfig):
133
+ # omit_default = True
134
+ # serialize_by_alias = True
135
+ # aliases = {
136
+ # **BaseDevice.Config.aliases,
137
+ # "net_type": "netType",
138
+ # "category_key": "categoryKey",
139
+ # "product_key": "productKey",
140
+ # "is_edge_gateway": "isEdgeGateway",
141
+ # "category_name": "categoryName",
142
+ # "identity_alias": "identityAlias",
143
+ # "iot_id": "iotId",
144
+ # "bind_time": "bindTime",
145
+ # "identity_id": "identityId",
146
+ # "thing_type": "thingType",
147
+ # "nick_name": "nickName",
148
+ # "product_model": "productModel",
149
+ # }
150
+ #
151
+ #
152
+ # @dataclass
153
+ # class ShareNotification(BaseDevice):
154
+ # """Share notification model extending base device"""
155
+ #
156
+ # target_id: str
157
+ # receiver_identity_id: str
158
+ # target_type: str
159
+ # gmt_create: int
160
+ # batch_id: str
161
+ # record_id: str
162
+ # initiator_identity_id: str
163
+ # is_receiver: int
164
+ # initiator_alias: str
165
+ # receiver_alias: str
166
+ #
167
+ # # Optional fields that Device has but ShareNotification might not
168
+ # net_type: Optional[str] = None
169
+ # category_key: Optional[str] = None
170
+ # product_key: Optional[str] = None
171
+ # is_edge_gateway: Optional[bool] = None
172
+ # category_name: Optional[str] = None
173
+ # identity_alias: Optional[str] = None
174
+ # iot_id: Optional[str] = None
175
+ # bind_time: Optional[int] = None
176
+ # owned: Optional[int] = None
177
+ # identity_id: Optional[str] = None
178
+ # thing_type: Optional[str] = None
179
+ # nick_name: Optional[str] = None
180
+ # product_model: Optional[str] = None
181
+ #
182
+ # class Config(BaseConfig):
183
+ # omit_default = True
184
+ # serialize_by_alias = True
185
+ # aliases = {
186
+ # **BaseDevice.Config.aliases,
187
+ # "target_id": "targetId",
188
+ # "receiver_identity_id": "receiverIdentityId",
189
+ # "target_type": "targetType",
190
+ # "gmt_create": "gmtCreate",
191
+ # "batch_id": "batchId",
192
+ # "record_id": "recordId",
193
+ # "initiator_identity_id": "initiatorIdentityId",
194
+ # "is_receiver": "isReceiver",
195
+ # "initiator_alias": "initiatorAlias",
196
+ # "receiver_alias": "receiverAlias",
197
+ # # Device fields that might be present
198
+ # "net_type": "netType",
199
+ # "category_key": "categoryKey",
200
+ # "product_key": "productKey",
201
+ # "is_edge_gateway": "isEdgeGateway",
202
+ # "category_name": "categoryName",
203
+ # "identity_alias": "identityAlias",
204
+ # "iot_id": "iotId",
205
+ # "bind_time": "bindTime",
206
+ # "identity_id": "identityId",
207
+ # "thing_type": "thingType",
208
+ # "nick_name": "nickName",
209
+ # "product_model": "productModel",
210
+ # }
33
211
 
34
212
 
35
213
  @dataclass
@@ -41,7 +219,7 @@ class Data(DataClassORJSONMixin):
41
219
 
42
220
 
43
221
  @dataclass
44
- class ListingDevByAccountResponse(DataClassORJSONMixin):
222
+ class ListingDevAccountResponse(DataClassORJSONMixin):
45
223
  code: int
46
224
  data: Data | None
47
225
  id: str | None = None
@@ -96,6 +96,7 @@ class MowingDevice(DataClassORJSONMixin):
96
96
  self.location.device = coordinate_converter.enu_to_lla(
97
97
  parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)
98
98
  )
99
+ self.map.invalidate_maps(location.bol_hash)
99
100
  if location.zone_hash:
100
101
  self.location.work_zone = (
101
102
  location.zone_hash if self.report_data.dev.sys_status == WorkMode.MODE_WORKING else 0
@@ -30,7 +30,7 @@ class OperationSettings(DataClassORJSONMixin):
30
30
  obstacle_laps: int = 1
31
31
  mowing_laps: int = 1 # border laps
32
32
  start_progress: int = 0
33
- areas: list[int] = field(default_factory=list)
33
+ areas: set[int] = field(default_factory=set)
34
34
 
35
35
 
36
36
  def create_path_order(operation_mode: OperationSettings, device_name: str) -> str:
@@ -4,9 +4,11 @@ from enum import Enum
4
4
  class ConnectionPreference(Enum):
5
5
  """Enum for connection preference."""
6
6
 
7
- EITHER = 0
7
+ ANY = 0
8
8
  WIFI = 1
9
9
  BLUETOOTH = 2
10
+ PREFER_WIFI = 3
11
+ PREFER_BLUETOOTH = 4
10
12
 
11
13
 
12
14
  class PositionMode(Enum):
@@ -1,4 +1,4 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import dataclass, field
2
2
  import logging
3
3
 
4
4
  logger = logging.getLogger(__name__)
@@ -8,7 +8,7 @@ logger = logging.getLogger(__name__)
8
8
  class GenerateRouteInformation:
9
9
  """Creates a model for generating route information and mowing plan before starting a job."""
10
10
 
11
- one_hashs: list[int] = list
11
+ one_hashs: list[int] = field(default_factory=list)
12
12
  job_mode: int = 4 # taskMode
13
13
  job_version: int = 0
14
14
  job_id: int = 0