Skip to content

Commit 02d6bea

Browse files
committed
feat(ecovacs): allow raw_get_positions on lawn mowers
Restricted to vacuums until now. GOAT mowers return the same deebotPos/chargePos payload plus extra RTK fields visible in the official app (rover/reference station satellite counts, common usable count, signal strength). Allowing the service on lawn_mower entities makes the payload inspectable so deebot-client can parse those fields and this integration can surface them as sensors in follow-up changes. Translation key vacuum_raw_get_positions_not_supported is renamed to raw_get_positions_not_supported so it can be reused on the mower path.
1 parent 9c9b626 commit 02d6bea

6 files changed

Lines changed: 53 additions & 8 deletions

File tree

homeassistant/components/ecovacs/lawn_mower.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Ecovacs mower entity."""
22

33
import logging
4+
from typing import Any
45

56
from deebot_client.capabilities import Capabilities, DeviceType
67
from deebot_client.device import Device
@@ -14,9 +15,11 @@
1415
LawnMowerEntityFeature,
1516
)
1617
from homeassistant.core import HomeAssistant
18+
from homeassistant.exceptions import ServiceValidationError
1719
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1820

1921
from . import EcovacsConfigEntry
22+
from .const import DOMAIN
2023
from .entity import EcovacsEntity
2124

2225
_LOGGER = logging.getLogger(__name__)
@@ -92,3 +95,27 @@ async def async_pause(self) -> None:
9295
async def async_dock(self) -> None:
9396
"""Parks the mower until next schedule."""
9497
await self._device.execute_command(self._capability.charge.execute())
98+
99+
async def async_raw_get_positions(
100+
self,
101+
) -> dict[str, Any]:
102+
"""Get the raw positions response for the mower and its charging dock.
103+
104+
Mirrors the modern vacuum implementation so service
105+
`ecovacs.raw_get_positions` works on `lawn_mower.*` ecovacs entities.
106+
Useful for inspecting additional payload fields the cloud may return
107+
for RTK GOAT mowers (e.g. satellite counts, fix quality, signal
108+
strength), which can then be parsed and exposed by deebot-client and
109+
this integration in follow-up changes.
110+
"""
111+
_LOGGER.debug("async_raw_get_positions")
112+
113+
if not (map_cap := self._capability.map) or not (
114+
position_commands := map_cap.position.get
115+
):
116+
raise ServiceValidationError(
117+
translation_domain=DOMAIN,
118+
translation_key="raw_get_positions_not_supported",
119+
)
120+
121+
return await self._device.execute_command(position_commands[0])

homeassistant/components/ecovacs/services.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Ecovacs services."""
22

3+
from homeassistant.components.lawn_mower import DOMAIN as LAWN_MOWER_DOMAIN
34
from homeassistant.components.vacuum import DOMAIN as VACUUM_DOMAIN
45
from homeassistant.core import HomeAssistant, SupportsResponse, callback
56
from homeassistant.helpers import service
@@ -23,3 +24,17 @@ def async_setup_services(hass: HomeAssistant) -> None:
2324
func="async_raw_get_positions",
2425
supports_response=SupportsResponse.ONLY,
2526
)
27+
28+
# Lawn Mower Services (Ecovacs GOAT family — same raw position payload as
29+
# vacuums; exposed here so users can capture the response and inspect
30+
# additional fields the cloud may return, e.g. RTK status / satellite count
31+
# for GOAT mowers).
32+
service.async_register_platform_entity_service(
33+
hass,
34+
DOMAIN,
35+
SERVICE_RAW_GET_POSITIONS,
36+
entity_domain=LAWN_MOWER_DOMAIN,
37+
schema=None,
38+
func="async_raw_get_positions",
39+
supports_response=SupportsResponse.ONLY,
40+
)
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
raw_get_positions:
22
target:
33
entity:
4-
domain: vacuum
5-
integration: ecovacs
4+
- domain: vacuum
5+
integration: ecovacs
6+
- domain: lawn_mower
7+
integration: ecovacs

homeassistant/components/ecovacs/strings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@
291291
}
292292
},
293293
"exceptions": {
294-
"vacuum_raw_get_positions_not_supported": {
294+
"raw_get_positions_not_supported": {
295295
"message": "Retrieving the positions of the chargers and the device itself is not supported"
296296
},
297297
"vacuum_send_command_params_dict": {

homeassistant/components/ecovacs/vacuum.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ async def async_raw_get_positions(
182182
"""Get bot and chargers positions."""
183183
raise ServiceValidationError(
184184
translation_domain=DOMAIN,
185-
translation_key="vacuum_raw_get_positions_not_supported",
185+
translation_key="raw_get_positions_not_supported",
186186
)
187187

188188

@@ -391,7 +391,7 @@ async def async_raw_get_positions(
391391
):
392392
raise ServiceValidationError(
393393
translation_domain=DOMAIN,
394-
translation_key="vacuum_raw_get_positions_not_supported",
394+
translation_key="raw_get_positions_not_supported",
395395
)
396396

397397
return await self._device.execute_command(position_commands[0])

tests/components/ecovacs/test_services.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,18 @@ def mock_device_execute_response(data: dict[str, Any]) -> Generator[dict[str, An
6666
("device_fixture", "entity_id"),
6767
[
6868
("yna5x1", "vacuum.ozmo_950"),
69+
("5xu9h3", "lawn_mower.goat_g1"),
6970
],
70-
ids=["yna5x1"],
71+
ids=["yna5x1", "5xu9h3"],
7172
)
7273
async def test_get_positions_service(
7374
hass: HomeAssistant,
7475
mock_device_execute_response: dict[str, Any],
7576
entity_id: str,
7677
) -> None:
7778
"""Test that get_positions service response snapshots match."""
78-
vacuum = hass.states.get(entity_id)
79-
assert vacuum
79+
entity = hass.states.get(entity_id)
80+
assert entity
8081

8182
assert await hass.services.async_call(
8283
DOMAIN,

0 commit comments

Comments
 (0)