pymammotion 0.3.7__py3-none-any.whl → 0.4.0a0__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.
pymammotion/__init__.py CHANGED
@@ -12,7 +12,7 @@ from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
12
12
 
13
13
  # works outside HA on its own
14
14
  from pymammotion.bluetooth.ble import MammotionBLE
15
- from pymammotion.http.http import MammotionHTTP, connect_http
15
+ from pymammotion.http.http import MammotionHTTP
16
16
 
17
17
  # TODO make a working device that will work outside HA too.
18
18
  from pymammotion.mqtt import MammotionMQTT
@@ -20,7 +20,7 @@ from pymammotion.mqtt import MammotionMQTT
20
20
  logger = logging.getLogger(__name__)
21
21
 
22
22
 
23
- __all__ = ["MammotionBLE", "MammotionHTTP", "connect_http", "MammotionMQTT", "logger"]
23
+ __all__ = ["MammotionBLE", "MammotionHTTP", "MammotionMQTT", "logger"]
24
24
 
25
25
 
26
26
  # TODO provide interface to pick between mqtt/cloud/bluetooth
@@ -7,21 +7,12 @@ import betterproto
7
7
  from mashumaro.mixins.orjson import DataClassORJSONMixin
8
8
 
9
9
  from pymammotion.data.model import HashList, RapidState
10
- from pymammotion.data.model.device_config import DeviceLimits
11
10
  from pymammotion.data.model.device_info import MowerInfo
12
11
  from pymammotion.data.model.location import Location
13
12
  from pymammotion.data.model.report_info import ReportData
14
13
  from pymammotion.data.mqtt.properties import ThingPropertiesMessage
15
14
  from pymammotion.http.model.http import ErrorInfo
16
- from pymammotion.proto.dev_net import DevNet
17
- from pymammotion.proto.luba_msg import LubaMsg
18
- from pymammotion.proto.luba_mul import SocMul
19
- from pymammotion.proto.mctrl_driver import MctlDriver
20
- from pymammotion.proto.mctrl_nav import MctlNav
21
- from pymammotion.proto.mctrl_ota import MctlOta
22
- from pymammotion.proto.mctrl_pept import MctlPept
23
15
  from pymammotion.proto.mctrl_sys import (
24
- MctlSys,
25
16
  MowToAppInfoT,
26
17
  ReportInfoData,
27
18
  SystemRapidStateTunnelMsg,
@@ -44,21 +35,8 @@ class MowingDevice(DataClassORJSONMixin):
44
35
  report_data: ReportData = field(default_factory=ReportData)
45
36
  err_code_list: list = field(default_factory=list)
46
37
  err_code_list_time: Optional[list] = field(default_factory=list)
47
- limits: DeviceLimits = field(default_factory=DeviceLimits)
48
- device: Optional[LubaMsg] = field(default_factory=LubaMsg)
49
38
  error_codes: dict[str, ErrorInfo] = field(default_factory=dict)
50
39
 
51
- @classmethod
52
- def from_raw(cls, raw: dict) -> "MowingDevice":
53
- """Take in raw data to hold in the betterproto dataclass."""
54
- mowing_device = MowingDevice()
55
- mowing_device.device = LubaMsg(**raw)
56
- return mowing_device
57
-
58
- def update_raw(self, raw: dict) -> None:
59
- """Update the raw LubaMsg data."""
60
- self.device = LubaMsg(**raw)
61
-
62
40
  def buffer(self, buffer_list: SystemUpdateBufMsg) -> None:
63
41
  """Update the device based on which buffer we are reading from."""
64
42
  match buffer_list.update_buf_data[0]:
@@ -135,199 +113,3 @@ class MowingDevice(DataClassORJSONMixin):
135
113
 
136
114
  def report_missing_data(self) -> None:
137
115
  """Report missing data so we can refetch it."""
138
-
139
- @property
140
- def net(self):
141
- """Will return a wrapped betterproto of net."""
142
- return DevNetData(net=self.device.net)
143
-
144
- @property
145
- def sys(self):
146
- """Will return a wrapped betterproto of sys."""
147
- return SysData(sys=self.device.sys)
148
-
149
- @property
150
- def nav(self):
151
- """Will return a wrapped betterproto of nav."""
152
- return NavData(nav=self.device.nav)
153
-
154
- @property
155
- def driver(self):
156
- """Will return a wrapped betterproto of driver."""
157
- return DriverData(driver=self.device.driver)
158
-
159
- @property
160
- def mul(self):
161
- """Will return a wrapped betterproto of mul."""
162
- return MulData(mul=self.device.mul)
163
-
164
- @property
165
- def ota(self):
166
- """Will return a wrapped betterproto of ota."""
167
- return OtaData(ota=self.device.ota)
168
-
169
- @property
170
- def pept(self):
171
- """Will return a wrapped betterproto of pept."""
172
- return PeptData(pept=self.device.pept)
173
-
174
-
175
- @dataclass
176
- class DevNetData(DataClassORJSONMixin):
177
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
178
-
179
- net: dict
180
-
181
- def __init__(self, net: DevNet) -> None:
182
- if isinstance(net, dict):
183
- self.net = net
184
- else:
185
- self.net = net.to_dict()
186
-
187
- def __getattr__(self, item):
188
- """Intercept call to get net in dict and return a betterproto dataclass."""
189
- if self.net.get(item) is None:
190
- return DevNet().__getattribute__(item)
191
-
192
- if not isinstance(self.net.get(item), dict):
193
- return self.net.get(item)
194
-
195
- return DevNet().__getattribute__(item).from_dict(value=self.net.get(item))
196
-
197
-
198
- @dataclass
199
- class SysData(DataClassORJSONMixin):
200
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
201
-
202
- sys: dict
203
-
204
- def __init__(self, sys: MctlSys) -> None:
205
- if isinstance(sys, dict):
206
- self.sys = sys
207
- else:
208
- self.sys = sys.to_dict()
209
-
210
- def __getattr__(self, item: str):
211
- """Intercept call to get sys in dict and return a betterproto dataclass."""
212
- if self.sys.get(item) is None:
213
- return MctlSys().__getattribute__(item)
214
-
215
- if not isinstance(self.sys.get(item), dict):
216
- return self.sys.get(item)
217
-
218
- return MctlSys().__getattribute__(item).from_dict(value=self.sys.get(item))
219
-
220
-
221
- @dataclass
222
- class NavData(DataClassORJSONMixin):
223
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
224
-
225
- nav: dict
226
-
227
- def __init__(self, nav: MctlNav) -> None:
228
- if isinstance(nav, dict):
229
- self.nav = nav
230
- else:
231
- self.nav = nav.to_dict()
232
-
233
- def __getattr__(self, item: str):
234
- """Intercept call to get nav in dict and return a betterproto dataclass."""
235
- if self.nav.get(item) is None:
236
- return MctlNav().__getattribute__(item)
237
-
238
- if not isinstance(self.nav.get(item), dict):
239
- return self.nav.get(item)
240
-
241
- return MctlNav().__getattribute__(item).from_dict(value=self.nav.get(item))
242
-
243
-
244
- @dataclass
245
- class DriverData(DataClassORJSONMixin):
246
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
247
-
248
- driver: dict
249
-
250
- def __init__(self, driver: MctlDriver) -> None:
251
- if isinstance(driver, dict):
252
- self.driver = driver
253
- else:
254
- self.driver = driver.to_dict()
255
-
256
- def __getattr__(self, item: str):
257
- """Intercept call to get driver in dict and return a betterproto dataclass."""
258
- if self.driver.get(item) is None:
259
- return MctlDriver().__getattribute__(item)
260
-
261
- if not isinstance(self.driver.get(item), dict):
262
- return self.driver.get(item)
263
-
264
- return MctlDriver().__getattribute__(item).from_dict(value=self.driver.get(item))
265
-
266
-
267
- @dataclass
268
- class MulData(DataClassORJSONMixin):
269
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
270
-
271
- mul: dict
272
-
273
- def __init__(self, mul: SocMul) -> None:
274
- if isinstance(mul, dict):
275
- self.mul = mul
276
- else:
277
- self.mul = mul.to_dict()
278
-
279
- def __getattr__(self, item: str):
280
- """Intercept call to get mul in dict and return a betterproto dataclass."""
281
- if self.mul.get(item) is None:
282
- return SocMul().__getattribute__(item)
283
-
284
- if not isinstance(self.mul.get(item), dict):
285
- return self.mul.get(item)
286
-
287
- return SocMul().__getattribute__(item).from_dict(value=self.mul.get(item))
288
-
289
-
290
- @dataclass
291
- class OtaData(DataClassORJSONMixin):
292
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
293
-
294
- ota: dict
295
-
296
- def __init__(self, ota: MctlOta) -> None:
297
- if isinstance(ota, dict):
298
- self.ota = ota
299
- else:
300
- self.ota = ota.to_dict()
301
-
302
- def __getattr__(self, item: str):
303
- """Intercept call to get ota in dict and return a betterproto dataclass."""
304
- if self.ota.get(item) is None:
305
- return MctlOta().__getattribute__(item)
306
-
307
- if not isinstance(self.ota.get(item), dict):
308
- return self.ota.get(item)
309
-
310
- return MctlOta().__getattribute__(item).from_dict(value=self.ota.get(item))
311
-
312
-
313
- @dataclass
314
- class PeptData(DataClassORJSONMixin):
315
- """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
316
-
317
- pept: dict
318
-
319
- def __init__(self, pept: MctlPept) -> None:
320
- if isinstance(pept, dict):
321
- self.pept = pept
322
- else:
323
- self.pept = pept.to_dict()
324
-
325
- def __getattr__(self, item: str):
326
- """Intercept call to get pept in dict and return a betterproto dataclass."""
327
- if self.pept.get(item) is None:
328
- return MctlPept().__getattribute__(item)
329
-
330
- if not isinstance(self.pept.get(item), dict):
331
- return self.pept.get(item)
332
-
333
- return MctlPept().__getattribute__(item).from_dict(value=self.pept.get(item))
@@ -5,16 +5,6 @@ from mashumaro.mixins.orjson import DataClassORJSONMixin
5
5
  from pymammotion.utility.device_type import DeviceType
6
6
 
7
7
 
8
- @dataclass
9
- class DeviceLimits(DataClassORJSONMixin):
10
- blade_height_min: int = 30
11
- blade_height_max: int = 100
12
- working_speed_min: float = 0.2
13
- working_speed_max: float = 1.2
14
- working_path_min: int = 15
15
- working_path_max: int = 35
16
-
17
-
18
8
  @dataclass
19
9
  class OperationSettings(DataClassORJSONMixin):
20
10
  """Operation settings for a device."""
@@ -0,0 +1,49 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class RangeLimit:
6
+ min: float
7
+ max: float
8
+
9
+
10
+ @dataclass
11
+ class DeviceLimits:
12
+ cutter_height: RangeLimit = RangeLimit(min=30, max=100)
13
+ working_speed: RangeLimit = RangeLimit(min=0.2, max=1.2)
14
+ working_path: RangeLimit = RangeLimit(min=15, max=35)
15
+ work_area_num_max: int = 60
16
+ display_image_type: int = 0
17
+
18
+ def to_dict(self) -> dict:
19
+ """Convert the device limits to a dictionary format."""
20
+ return {
21
+ "cutter_height": {"min": self.cutter_height.min, "max": self.cutter_height.max},
22
+ "working_speed": {"min": self.working_speed.min, "max": self.working_speed.max},
23
+ "working_path": {"min": self.working_path.min, "max": self.working_path.max},
24
+ "work_area_num_max": self.work_area_num_max,
25
+ "display_image_type": self.display_image_type,
26
+ }
27
+
28
+ @classmethod
29
+ def from_dict(cls, data: dict) -> "DeviceLimits":
30
+ """Create a DeviceLimits instance from a dictionary."""
31
+ return cls(
32
+ cutter_height=RangeLimit(min=data["cutter_height"]["min"], max=data["cutter_height"]["max"]),
33
+ working_speed=RangeLimit(min=data["working_speed"]["min"], max=data["working_speed"]["max"]),
34
+ working_path=RangeLimit(min=data["working_path"]["min"], max=data["working_path"]["max"]),
35
+ work_area_num_max=data["work_area_num_max"],
36
+ display_image_type=data["display_image_type"],
37
+ )
38
+
39
+ def validate(self) -> bool:
40
+ """Validate that all ranges are logical (min <= max)."""
41
+ return all(
42
+ [
43
+ self.cutter_height.min <= self.cutter_height.max,
44
+ self.working_speed.min <= self.working_speed.max,
45
+ self.working_path.min <= self.working_path.max,
46
+ self.work_area_num_max > 0,
47
+ self.display_image_type in (0, 1),
48
+ ]
49
+ )
@@ -0,0 +1,223 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Optional
3
+
4
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
5
+
6
+ from pymammotion.proto.dev_net import DevNet
7
+ from pymammotion.proto.luba_msg import LubaMsg
8
+ from pymammotion.proto.luba_mul import SocMul
9
+ from pymammotion.proto.mctrl_driver import MctlDriver
10
+ from pymammotion.proto.mctrl_nav import MctlNav
11
+ from pymammotion.proto.mctrl_ota import MctlOta
12
+ from pymammotion.proto.mctrl_pept import MctlPept
13
+ from pymammotion.proto.mctrl_sys import MctlSys
14
+
15
+
16
+ @dataclass
17
+ class RawMowerData:
18
+ raw: Optional[LubaMsg] = field(default_factory=LubaMsg)
19
+
20
+ @classmethod
21
+ def from_raw(cls, raw: dict) -> "RawMowerData":
22
+ """Take in raw data to hold in the betterproto dataclass."""
23
+ return RawMowerData(raw=LubaMsg(**raw))
24
+
25
+ def update_raw(self, raw: dict) -> None:
26
+ """Update the raw LubaMsg data."""
27
+ self.raw = LubaMsg(**raw)
28
+
29
+ @property
30
+ def net(self):
31
+ """Will return a wrapped betterproto of net."""
32
+ return DevNetData(net=self.raw.net)
33
+
34
+ @property
35
+ def sys(self):
36
+ """Will return a wrapped betterproto of sys."""
37
+ return SysData(sys=self.raw.sys)
38
+
39
+ @property
40
+ def nav(self):
41
+ """Will return a wrapped betterproto of nav."""
42
+ return NavData(nav=self.raw.nav)
43
+
44
+ @property
45
+ def driver(self):
46
+ """Will return a wrapped betterproto of driver."""
47
+ return DriverData(driver=self.raw.driver)
48
+
49
+ @property
50
+ def mul(self):
51
+ """Will return a wrapped betterproto of mul."""
52
+ return MulData(mul=self.raw.mul)
53
+
54
+ @property
55
+ def ota(self):
56
+ """Will return a wrapped betterproto of ota."""
57
+ return OtaData(ota=self.raw.ota)
58
+
59
+ @property
60
+ def pept(self):
61
+ """Will return a wrapped betterproto of pept."""
62
+ return PeptData(pept=self.raw.pept)
63
+
64
+
65
+ @dataclass
66
+ class DevNetData(DataClassORJSONMixin):
67
+ """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
68
+
69
+ net: dict
70
+
71
+ def __init__(self, net: DevNet) -> None:
72
+ if isinstance(net, dict):
73
+ self.net = net
74
+ else:
75
+ self.net = net.to_dict()
76
+
77
+ def __getattr__(self, item):
78
+ """Intercept call to get net in dict and return a betterproto dataclass."""
79
+ if self.net.get(item) is None:
80
+ return DevNet().__getattribute__(item)
81
+
82
+ if not isinstance(self.net.get(item), dict):
83
+ return self.net.get(item)
84
+
85
+ return DevNet().__getattribute__(item).from_dict(value=self.net.get(item))
86
+
87
+
88
+ @dataclass
89
+ class SysData(DataClassORJSONMixin):
90
+ """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
91
+
92
+ sys: dict
93
+
94
+ def __init__(self, sys: MctlSys) -> None:
95
+ if isinstance(sys, dict):
96
+ self.sys = sys
97
+ else:
98
+ self.sys = sys.to_dict()
99
+
100
+ def __getattr__(self, item: str):
101
+ """Intercept call to get sys in dict and return a betterproto dataclass."""
102
+ if self.sys.get(item) is None:
103
+ return MctlSys().__getattribute__(item)
104
+
105
+ if not isinstance(self.sys.get(item), dict):
106
+ return self.sys.get(item)
107
+
108
+ return MctlSys().__getattribute__(item).from_dict(value=self.sys.get(item))
109
+
110
+
111
+ @dataclass
112
+ class NavData(DataClassORJSONMixin):
113
+ """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
114
+
115
+ nav: dict
116
+
117
+ def __init__(self, nav: MctlNav) -> None:
118
+ if isinstance(nav, dict):
119
+ self.nav = nav
120
+ else:
121
+ self.nav = nav.to_dict()
122
+
123
+ def __getattr__(self, item: str):
124
+ """Intercept call to get nav in dict and return a betterproto dataclass."""
125
+ if self.nav.get(item) is None:
126
+ return MctlNav().__getattribute__(item)
127
+
128
+ if not isinstance(self.nav.get(item), dict):
129
+ return self.nav.get(item)
130
+
131
+ return MctlNav().__getattribute__(item).from_dict(value=self.nav.get(item))
132
+
133
+
134
+ @dataclass
135
+ class DriverData(DataClassORJSONMixin):
136
+ """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
137
+
138
+ driver: dict
139
+
140
+ def __init__(self, driver: MctlDriver) -> None:
141
+ if isinstance(driver, dict):
142
+ self.driver = driver
143
+ else:
144
+ self.driver = driver.to_dict()
145
+
146
+ def __getattr__(self, item: str):
147
+ """Intercept call to get driver in dict and return a betterproto dataclass."""
148
+ if self.driver.get(item) is None:
149
+ return MctlDriver().__getattribute__(item)
150
+
151
+ if not isinstance(self.driver.get(item), dict):
152
+ return self.driver.get(item)
153
+
154
+ return MctlDriver().__getattribute__(item).from_dict(value=self.driver.get(item))
155
+
156
+
157
+ @dataclass
158
+ class MulData(DataClassORJSONMixin):
159
+ """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
160
+
161
+ mul: dict
162
+
163
+ def __init__(self, mul: SocMul) -> None:
164
+ if isinstance(mul, dict):
165
+ self.mul = mul
166
+ else:
167
+ self.mul = mul.to_dict()
168
+
169
+ def __getattr__(self, item: str):
170
+ """Intercept call to get mul in dict and return a betterproto dataclass."""
171
+ if self.mul.get(item) is None:
172
+ return SocMul().__getattribute__(item)
173
+
174
+ if not isinstance(self.mul.get(item), dict):
175
+ return self.mul.get(item)
176
+
177
+ return SocMul().__getattribute__(item).from_dict(value=self.mul.get(item))
178
+
179
+
180
+ @dataclass
181
+ class OtaData(DataClassORJSONMixin):
182
+ """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
183
+
184
+ ota: dict
185
+
186
+ def __init__(self, ota: MctlOta) -> None:
187
+ if isinstance(ota, dict):
188
+ self.ota = ota
189
+ else:
190
+ self.ota = ota.to_dict()
191
+
192
+ def __getattr__(self, item: str):
193
+ """Intercept call to get ota in dict and return a betterproto dataclass."""
194
+ if self.ota.get(item) is None:
195
+ return MctlOta().__getattribute__(item)
196
+
197
+ if not isinstance(self.ota.get(item), dict):
198
+ return self.ota.get(item)
199
+
200
+ return MctlOta().__getattribute__(item).from_dict(value=self.ota.get(item))
201
+
202
+
203
+ @dataclass
204
+ class PeptData(DataClassORJSONMixin):
205
+ """Wrapping class around LubaMsg to return a dataclass from the raw dict."""
206
+
207
+ pept: dict
208
+
209
+ def __init__(self, pept: MctlPept) -> None:
210
+ if isinstance(pept, dict):
211
+ self.pept = pept
212
+ else:
213
+ self.pept = pept.to_dict()
214
+
215
+ def __getattr__(self, item: str):
216
+ """Intercept call to get pept in dict and return a betterproto dataclass."""
217
+ if self.pept.get(item) is None:
218
+ return MctlPept().__getattribute__(item)
219
+
220
+ if not isinstance(self.pept.get(item), dict):
221
+ return self.pept.get(item)
222
+
223
+ return MctlPept().__getattribute__(item).from_dict(value=self.pept.get(item))