Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make em mow #54

Merged
merged 36 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9c0f6a5
start work on mowing
mikey0000 Aug 10, 2024
9ca6997
update mtrl nav
mikey0000 Aug 11, 2024
a3e6718
fix nav commands for none luba 1s
mikey0000 Aug 12, 2024
2ec41cf
ruff format and so on
mikey0000 Aug 12, 2024
0194180
add product key to commands for checking device
mikey0000 Aug 12, 2024
7e13f96
fix blade on off
mikey0000 Aug 12, 2024
6f0b578
Try to implement MQTT response - Need helo on Lock
Aug 13, 2024
0de6ccd
update protobufs with basestation
mikey0000 Aug 13, 2024
994ceb0
adding report info model and map conversions for lla enu
mikey0000 Aug 14, 2024
79e05f6
release 0.1.7
mikey0000 Aug 14, 2024
ede5f06
try to keep last data if its missing in report cfg
mikey0000 Aug 14, 2024
39867b5
fix report info
mikey0000 Aug 14, 2024
41a24cc
Merge branch 'make-em-mow' into make-em-mow
mikey0000 Aug 15, 2024
21011b6
Add missing calls for ble sync as well as hook up callbacks, remove d…
mikey0000 Aug 17, 2024
256f822
format and run ruff
mikey0000 Aug 17, 2024
a88ef95
add cloud as part of base MammotionDevice
mikey0000 Aug 17, 2024
cf89c0b
rename http class
mikey0000 Aug 18, 2024
bc87dd4
fix up the client sesion for http
mikey0000 Aug 18, 2024
d366e23
update login to login_info
mikey0000 Aug 19, 2024
0145abd
bump version to 0.2.1
mikey0000 Aug 19, 2024
1bd8261
add start sync and sync maps calls to MammotionDevice
mikey0000 Aug 19, 2024
198ba02
refactor mammotion to handle multiple devices and match to a name
mikey0000 Aug 19, 2024
2734378
update test examples
mikey0000 Aug 19, 2024
de8d1f1
fix oops
mikey0000 Aug 21, 2024
511bfbf
fix quite a few errors and issues
mikey0000 Aug 21, 2024
3bfb15e
stability improvements
mikey0000 Aug 21, 2024
1234714
fix more issues
mikey0000 Aug 22, 2024
5e13d6d
adding movement commands
mikey0000 Aug 24, 2024
24d3bfa
fix issue with device identification messing up sync maps
mikey0000 Aug 25, 2024
a12e913
remove operation lock for mqtt
mikey0000 Aug 25, 2024
f4e852f
publish 0.2.7
mikey0000 Aug 25, 2024
2f96392
Add check and refresh token into send_cloud_command (#56)
d3dfantasy99 Aug 26, 2024
32d3ba9
add methods for getting credentials for storing in HA when reloading …
mikey0000 Aug 27, 2024
a09869b
fix queuing of requests for mqtt, sync maps now works bump version to…
mikey0000 Aug 27, 2024
f563646
don't create cloud or initiate cloud connection if there isn't creden…
mikey0000 Aug 27, 2024
1297564
bump version to 0.2.11 and catch small issue with futures queue being…
mikey0000 Aug 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added __init__.py
Empty file.
331 changes: 191 additions & 140 deletions poetry.lock

Large diffs are not rendered by default.

10 changes: 1 addition & 9 deletions pymammotion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@
from pymammotion.mammotion.devices import MammotionBaseBLEDevice
from pymammotion.mqtt import MammotionMQTT



__all__ = [
'LubaBLE',
'LubaHTTP',
'connect_http',
'MammotionBaseBLEDevice',
'MammotionMQTT'
]
__all__ = ["LubaBLE", "LubaHTTP", "connect_http", "MammotionBaseBLEDevice", "MammotionMQTT"]

logger = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion pymammotion/aliyun/dataclass/dev_by_account_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

@dataclass
class Device(DataClassORJSONMixin):
productModel: str
gmtModified: int
netType: str
nickName: str
Expand All @@ -26,6 +25,7 @@ class Device(DataClassORJSONMixin):
status: int
productImage: Optional[str] = None
categoryImage: Optional[str] = None
productModel: Optional[str] = None


@dataclass
Expand Down
8 changes: 8 additions & 0 deletions pymammotion/data/model/account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from dataclasses import dataclass


@dataclass
class Credentials:
email: str
password: str
account_id: str
35 changes: 30 additions & 5 deletions pymammotion/data/model/device.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
"""MowingDevice class to wrap around the betterproto dataclasses."""

import math
from dataclasses import dataclass

import betterproto

from pymammotion.data.model import HashList
from pymammotion.data.model.device_config import DeviceLimits
from pymammotion.data.model.location import Location
from pymammotion.data.model.report_info import ReportData
from pymammotion.proto.dev_net import DevNet
from pymammotion.proto.luba_msg import LubaMsg
from pymammotion.proto.luba_mul import SocMul
from pymammotion.proto.mctrl_driver import MctlDriver
from pymammotion.proto.mctrl_nav import MctlNav
from pymammotion.proto.mctrl_ota import MctlOta
from pymammotion.proto.mctrl_pept import MctlPept
from pymammotion.proto.mctrl_sys import MctlSys, SystemUpdateBufMsg
from pymammotion.proto.mctrl_sys import MctlSys, MowToAppInfoT, ReportInfoData, SystemUpdateBufMsg
from pymammotion.utility.map import CoordinateConverter


def parse_double(val: float, d: float):
return val / math.pow(10.0, d)


@dataclass
Expand All @@ -28,8 +36,10 @@ def __init__(self):
self.device = LubaMsg()
self.map = HashList(area={}, path={}, obstacle={})
self.location = Location()
self.report_data = ReportData()
self.err_code_list = []
self.err_code_list_time = []
self.limits = DeviceLimits(30, 70, 0.2, 0.6)

@classmethod
def from_raw(cls, raw: dict) -> "MowingDevice":
Expand All @@ -47,10 +57,10 @@ def buffer(self, buffer_list: SystemUpdateBufMsg):
match buffer_list.update_buf_data[0]:
case 1:
# 4 speed
self.location.RTK.latitude = buffer_list.update_buf_data[5]
self.location.RTK.longitude = buffer_list.update_buf_data[6]
self.location.dock.latitude = buffer_list.update_buf_data[7]
self.location.dock.longitude = buffer_list.update_buf_data[8]
self.location.RTK.latitude = parse_double(buffer_list.update_buf_data[5], 8.0)
self.location.RTK.longitude = parse_double(buffer_list.update_buf_data[6], 8.0)
self.location.dock.latitude = parse_double(buffer_list.update_buf_data[7], 4.0)
self.location.dock.longitude = parse_double(buffer_list.update_buf_data[8], 4.0)
self.location.dock.rotation = buffer_list.update_buf_data[3] + 180
case 2:
self.err_code_list.clear()
Expand Down Expand Up @@ -84,6 +94,21 @@ def buffer(self, buffer_list: SystemUpdateBufMsg):
]
)

def update_report_data(self, toapp_report_data: ReportInfoData):
coordinate_converter = CoordinateConverter(self.location.RTK.latitude, self.location.RTK.longitude)
for index, location in enumerate(toapp_report_data.locations):
if index == 0:
self.location.position_type = location.pos_type
self.location.orientation = location.real_toward / 10000
self.location.device = coordinate_converter.enu_to_lla(
parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)
)

self.report_data = self.report_data.from_dict(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE))

def mow_info(self, toapp_mow_info: MowToAppInfoT):
pass

@property
def net(self):
"""Will return a wrapped betterproto of net."""
Expand Down
9 changes: 9 additions & 0 deletions pymammotion/data/model/device_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from dataclasses import dataclass


@dataclass
class DeviceLimits:
blade_height_min: int
blade_height_max: int
working_speed_min: float
working_speed_max: float
8 changes: 5 additions & 3 deletions pymammotion/data/model/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class Point:
latitude: float
longitude: float

def __init__(self):
self.latitude = 0
self.longitude = 0
def __init__(self, latitude=0.0, longitude=0.0):
self.latitude = latitude
self.longitude = longitude


@dataclass
Expand All @@ -33,6 +33,8 @@ class Location:
device: Point
RTK: Point
dock: Dock
position_type: int
orientation: int # 360 degree rotation +-

def __init__(self):
self.device = Point()
Expand Down
9 changes: 9 additions & 0 deletions pymammotion/data/model/mowing_modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,12 @@ class MowOrder(IntEnum):

border_first = 0
grid_first = 1


class BypassStrategy(IntEnum):
"""Matches up with ultra_wave."""

direct_touch = 0
slow_touch = 1
less_touch = 2
no_touch = 3 # luba 2 yuka only or possibly value of 10
146 changes: 146 additions & 0 deletions pymammotion/data/model/report_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
from dataclasses import asdict, dataclass, field


@dataclass
class ConnectData:
connect_type: int = 0
ble_rssi: int = 0
wifi_rssi: int = 0

@classmethod
def from_dict(cls, data: dict):
return cls(
connect_type=data.get("connect_type", 0),
ble_rssi=data.get("ble_rssi", 0),
wifi_rssi=data.get("wifi_rssi", 0),
)


@dataclass
class DeviceData:
sys_status: int = 0
charge_state: int = 0
battery_val: int = 0
sensor_status: int = 0
last_status: int = 0
sys_time_stamp: str = ""

@classmethod
def from_dict(cls, data: dict):
return cls(
sys_status=data.get("sys_status", 0),
charge_state=data.get("charge_state", 0),
battery_val=data.get("battery_val", 0),
sensor_status=data.get("sensor_status", 0),
last_status=data.get("last_status", 0),
sys_time_stamp=data.get("sys_time_stamp", ""),
)


@dataclass
class RTKData:
status: int = 0
pos_level: int = 0
gps_stars: int = 0
dis_status: str = ""
co_view_stars: int = 0

@classmethod
def from_dict(cls, data: dict):
return cls(
status=data.get("status", 0),
pos_level=data.get("pos_level", 0),
gps_stars=data.get("gps_stars", 0),
dis_status=data.get("dis_status", ""),
co_view_stars=data.get("co_view_stars", 0),
)


@dataclass
class LocationData:
real_pos_x: int = 0
real_pos_y: int = 0
real_toward: int = 0
pos_type: int = 0
bol_hash: str = ""

@classmethod
def from_dict(cls, data: dict):
return cls(
real_pos_x=data.get("real_pos_x", 0),
real_pos_y=data.get("real_pos_y", 0),
real_toward=data.get("real_toward", 0),
pos_type=data.get("pos_type", 0),
bol_hash=data.get("bol_hash", ""),
)


@dataclass
class WorkData:
path: int = 0
path_hash: str = ""
progress: int = 0
area: int = 0
bp_info: int = 0
bp_hash: str = ""
bp_pos_x: int = 0
bp_pos_y: int = 0
real_path_num: str = ""
path_pos_x: int = 0
path_pos_y: int = 0
ub_zone_hash: str = ""
ub_path_hash: str = ""
init_cfg_hash: str = ""
ub_ecode_hash: str = ""
nav_run_mode: int = 0
test_mode_status: int = 0
man_run_speed: int = 0
nav_edit_status: int = 0
knife_height: int = 0

@classmethod
def from_dict(cls, data: dict):
return cls(
path=data.get("path", 0),
path_hash=data.get("path_hash", ""),
progress=data.get("progress", 0),
area=data.get("area", 0),
bp_info=data.get("bp_info", 0),
bp_hash=data.get("bp_hash", ""),
bp_pos_x=data.get("bp_pos_x", 0),
bp_pos_y=data.get("bp_pos_y", 0),
real_path_num=data.get("real_path_num", ""),
path_pos_x=data.get("path_pos_x", 0),
path_pos_y=data.get("path_pos_y", 0),
ub_zone_hash=data.get("ub_zone_hash", ""),
ub_path_hash=data.get("ub_path_hash", ""),
init_cfg_hash=data.get("init_cfg_hash", ""),
ub_ecode_hash=data.get("ub_ecode_hash", ""),
nav_run_mode=data.get("nav_run_mode", 0),
test_mode_status=data.get("test_mode_status", 0),
man_run_speed=data.get("man_run_speed", 0),
nav_edit_status=data.get("nav_edit_status", 0),
knife_height=data.get("knife_height", 0),
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update to blade height

)


@dataclass
class ReportData:
connect: ConnectData = field(default_factory=ConnectData)
dev: DeviceData = field(default_factory=DeviceData)
rtk: RTKData = field(default_factory=RTKData)
locations: list[LocationData] = field(default_factory=list)
work: WorkData = field(default_factory=WorkData)

def from_dict(self, data: dict):
locations = self.locations
if data.get("locations") is not None:
locations = [LocationData.from_dict(loc) for loc in data.get("locations", [])]

return ReportData(
connect=ConnectData.from_dict(data.get("connect", asdict(self.connect))),
dev=DeviceData.from_dict(data.get("dev", asdict(self.dev))),
rtk=RTKData.from_dict(data.get("rtk", asdict(self.rtk))),
locations=locations,
work=WorkData.from_dict(data.get("work", asdict(self.work))),
)
33 changes: 32 additions & 1 deletion pymammotion/data/mqtt/event.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from base64 import b64decode
from dataclasses import dataclass
from typing import Any, Literal, Union
from typing import Any, Literal, Optional, Union

from google.protobuf import json_format
from mashumaro.mixins.orjson import DataClassORJSONMixin
Expand Down Expand Up @@ -67,6 +67,17 @@ class GeneralParams(DataClassORJSONMixin):
tenantInstanceId: str
value: Any

# Campi opzionali
checkFailedData: Optional[dict] = None
_tenantId: Optional[str] = None
generateTime: Optional[int] = None
JMSXDeliveryCount: Optional[int] = None
qos: Optional[int] = None
requestId: Optional[str] = None
_categoryKey: Optional[str] = None
deviceType: Optional[str] = None
_traceId: Optional[str] = None


@dataclass
class DeviceProtobufMsgEventParams(GeneralParams):
Expand All @@ -88,3 +99,23 @@ class ThingEventMessage(DataClassORJSONMixin):
id: str
params: Union[DeviceProtobufMsgEventParams, DeviceWarningEventParams]
version: Literal["1.0"]

@classmethod
def from_dicts(cls, payload: dict) -> "ThingEventMessage":
"""Deserializza il payload JSON in un'istanza di ThingEventMessage."""
method = payload.get("method")
event_id = payload.get("id")
params_dict = payload.get("params", {})
version = payload.get("version")

# Determina quale classe usare per i parametri
identifier = params_dict.get("identifier")
if identifier == "device_protobuf_msg_event":
params_obj = DeviceProtobufMsgEventParams(**params_dict)
elif identifier == "device_warning_event":
params_obj = DeviceWarningEventParams(**params_dict)
else:
raise ValueError(f"Unknown identifier: {identifier}")

# Crea e restituisce l'istanza di ThingEventMessage
return cls(method=method, id=event_id, params=params_obj, version=version)
6 changes: 4 additions & 2 deletions pymammotion/data/state_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ def _update_sys_data(self, message):
case "system_update_buf":
self._device.buffer(sys_msg[1])
case "toapp_report_data":
pass
self._device.update_report_data(sys_msg[1])
case "mow_to_app_info":
self._device.mow_info(sys_msg[1])

def _update_driver_data(self, message):
pass
Expand All @@ -80,4 +82,4 @@ def _update_mul_data(self, message):
pass

def _update_ota_data(self, message):
pass
pass
4 changes: 2 additions & 2 deletions pymammotion/http/http.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import Generic, Literal, TypeVar
from typing import Generic, Literal, Optional, TypeVar

from aiohttp import ClientSession
from mashumaro import DataClassDictMixin
Expand All @@ -25,7 +25,7 @@ class Response(DataClassDictMixin, Generic[DataT]):
class LoginResponseUserInformation(DataClassORJSONMixin):
areaCode: str
domainAbbreviation: str
email: str
email: Optional[str]
userId: str
userAccount: str
authType: str
Expand Down
Loading
Loading