Skip to content

Commit

Permalink
Merge pull request mikey0000#136 from mikey0000/dev
Browse files Browse the repository at this point in the history
Add Mowing Service with Customizable Options
  • Loading branch information
mikey0000 authored Sep 19, 2024
2 parents 0ee2a91 + 26fc3cb commit 90a5bd3
Show file tree
Hide file tree
Showing 7 changed files with 505 additions and 24 deletions.
4 changes: 4 additions & 0 deletions custom_components/mammotion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
CONF_SESSION_DATA,
CONF_USE_WIFI,
DEFAULT_RETRY_COUNT,
DOMAIN,
)
from .coordinator import MammotionDataUpdateCoordinator

Expand Down Expand Up @@ -78,6 +79,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: MammotionConfigEntry) ->
entry.runtime_data = mammotion_coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

# need to register service for triggering tasks
# hass.services.async_register(DOMAIN, SERVICE_START_tASK, async_start_mowing)

return True


Expand Down
29 changes: 15 additions & 14 deletions custom_components/mammotion/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,22 +266,23 @@ async def async_send_command(self, command: str, **kwargs: Any) -> None:
translation_domain=DOMAIN, translation_key="command_failed"
) from exc

async def async_plan_route(self) -> None:
async def async_plan_route(self, operation_settings: OperationSettings) -> None:
"""Plan mow."""
route_information = GenerateRouteInformation(
one_hashs=self._operation_settings.areas,
rain_tactics=self._operation_settings.rain_tactics,
speed=self._operation_settings.speed,
ultra_wave=self._operation_settings.ultra_wave, # touch no touch etc
toward=self._operation_settings.toward, # is just angle
toward_included_angle=self._operation_settings.toward_included_angle, # angle type relative etc
blade_height=self._operation_settings.blade_height,
channel_mode=self._operation_settings.channel_mode, # line mode is grid single double or single2
channel_width=self._operation_settings.channel_width,
job_mode=self._operation_settings.job_mode, # taskMode
edge_mode=self._operation_settings.border_mode, # border laps
path_order=create_path_order(self._operation_settings, self.device_name),
obstacle_laps=self._operation_settings.obstacle_laps,
one_hashs=operation_settings.areas,
rain_tactics=operation_settings.rain_tactics,
speed=operation_settings.speed,
ultra_wave=operation_settings.ultra_wave, # touch no touch etc
toward=operation_settings.toward, # is just angle
toward_included_angle=operation_settings.toward_included_angle, # angle relative to grid??
toward_mode=operation_settings.toward_mode,
blade_height=operation_settings.blade_height,
channel_mode=operation_settings.channel_mode, # line mode is grid single double or single2
channel_width=operation_settings.channel_width,
job_mode=operation_settings.job_mode, # taskMode
edge_mode=operation_settings.border_mode, # border laps
path_order=create_path_order(operation_settings, self.device_name),
obstacle_laps=operation_settings.obstacle_laps,
)

await self.async_send_command(
Expand Down
97 changes: 88 additions & 9 deletions custom_components/mammotion/lawn_mower.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

from __future__ import annotations

from typing import Any

import voluptuous as vol
from homeassistant.components.lawn_mower import (
LawnMowerActivity,
LawnMowerEntity,
LawnMowerEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from pymammotion.data.model.device_config import OperationSettings
from pymammotion.data.model.report_info import ReportData
from pymammotion.proto import has_field
from pymammotion.proto.luba_msg import RptDevStatus
Expand All @@ -20,6 +26,61 @@
from .coordinator import MammotionDataUpdateCoordinator
from .entity import MammotionBaseEntity

SERVICE_START_MOWING = "start_mow"

START_MOW_SCHEMA = {
vol.Optional("is_mow", default=True): cv.boolean,
vol.Optional("is_dump", default=True): cv.boolean,
vol.Optional("is_edge", default=False): cv.boolean,
vol.Optional("collect_grass_frequency", default=10): vol.All(
vol.Coerce(int), vol.Range(min=5, max=100)
),
vol.Optional("job_mode", default=0): vol.Coerce(int),
vol.Optional("job_version", default=0): vol.Coerce(int),
vol.Optional("job_id", default=0): vol.Coerce(int),
vol.Optional("speed", default=0.3): vol.All(
vol.Coerce(float), vol.Range(min=0.2, max=1.2)
),
vol.Optional("ultra_wave", default=2): vol.In([0, 1, 2, 10]),
vol.Optional("channel_mode", default=0): vol.In([0, 1, 2, 3]),
vol.Optional("channel_width", default=25): vol.All(
vol.Coerce(int), vol.Range(min=20, max=35)
),
vol.Optional("rain_tactics", default=1): vol.In([0, 1]),
vol.Optional("blade_height", default=25): vol.All(
vol.Coerce(int), vol.Range(min=15, max=100)
),
vol.Optional("path_order", default=0): vol.In([0, 1]),
vol.Optional("toward", default=0): vol.All(
vol.Coerce(int), vol.Range(min=-180, max=180)
),
vol.Optional("toward_included_angle", default=0): vol.All(
vol.Coerce(int), vol.Range(min=-180, max=180)
),
vol.Optional("toward_mode", default=0): vol.In([0, 1, 2]),
vol.Optional("border_mode", default=1): vol.In([0, 1, 2, 3, 4]),
vol.Optional("obstacle_laps", default=1): vol.In([0, 1, 2, 3, 4]),
vol.Optional("start_progress", default=0): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
),
vol.Required("areas"): vol.All(
cv.ensure_list, [cv.entity_id]
), # This assumes `areas` are entity IDs from the integration
}


def get_entity_attribute(hass, entity_id, attribute_name):
# Get the state object of the entity
entity = hass.states.get(entity_id)

# Check if the entity exists and has attributes
if entity and attribute_name in entity.attributes:
# Return the specific attribute
return entity.attributes[attribute_name]
else:
# Return None if the entity or attribute does not exist
return None


async def async_setup_entry(
hass: HomeAssistant,
Expand All @@ -30,6 +91,12 @@ async def async_setup_entry(
coordinator = entry.runtime_data
async_add_entities([MammotionLawnMowerEntity(coordinator)])

platform = entity_platform.async_get_current_platform()

platform.async_register_entity_service(
SERVICE_START_MOWING, START_MOW_SCHEMA, "async_start_mowing"
)


class MammotionLawnMowerEntity(MammotionBaseEntity, LawnMowerEntity):
"""Representation of a Mammotion lawn mower."""
Expand Down Expand Up @@ -81,8 +148,24 @@ def activity(self) -> LawnMowerActivity | None:
return LawnMowerActivity.DOCKED
return None

async def async_start_mowing(self) -> None:
async def async_start_mowing(self, **kwargs: Any) -> None:
"""Start mowing."""
if kwargs:
entity_ids = kwargs.get("areas", [])

attributes = [
get_entity_attribute(self.hass, entity_id, "hash")
for entity_id in entity_ids
if get_entity_attribute(self.hass, entity_id, "hash") is not None
]
kwargs["areas"] = attributes
operational_settings = OperationSettings.from_dict(kwargs)
LOGGER.debug(kwargs)
await self.coordinator.async_plan_route(operational_settings)
await self.coordinator.async_send_command("start_job")
await self.coordinator.async_request_iot_sync()
return

# check if job in progress
#
if self.rpt_dev_status is None:
Expand All @@ -91,9 +174,9 @@ async def async_start_mowing(self) -> None:
)
work_area = self.report_data.work.area >> 16

if work_area > 0 and (
self.rpt_dev_status.sys_status == WorkMode.MODE_PAUSE
or self.rpt_dev_status.sys_status == WorkMode.MODE_READY
if work_area > 0 and self.rpt_dev_status.sys_status in (
WorkMode.MODE_PAUSE,
WorkMode.MODE_READY,
):
try:
await self.coordinator.async_send_command("resume_execute_task")
Expand All @@ -103,17 +186,13 @@ async def async_start_mowing(self) -> None:
translation_domain=DOMAIN, translation_key="resume_failed"
) from exc
try:
await self.coordinator.async_plan_route()
await self.coordinator.async_plan_route(self.coordinator.operation_settings)
await self.coordinator.async_send_command("start_job")
await self.coordinator.async_request_iot_sync()
except COMMAND_EXCEPTIONS as exc:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="start_failed"
) from exc
finally:
self.coordinator.async_set_updated_data(
self.coordinator.manager.mower(self.coordinator.device_name)
)

async def async_dock(self) -> None:
"""Start docking."""
Expand Down
Loading

0 comments on commit 90a5bd3

Please sign in to comment.