Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Keep the following in mind:

- **Credentials**: Never hard-code passwords, API keys, or serial numbers in code.
Use environment variables or a secrets manager. The RSA public key extracted from
the APK is not sensitive and can be committed; private keys and passwords must not be.
the public RSA key is not sensitive and can be committed; private keys and passwords must not be.

- **Network exposure**: The Yarbo EMQX broker listens on port 1883 (plaintext) on
the local network. Do **not** expose this port to the internet. Restrict access
Expand Down
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Contributing to python-yarbo

Thanks for your interest in contributing! python-yarbo is a community library
built on reverse-engineered protocol knowledge — every contribution matters.
built on community protocol documentation — every contribution matters.

## Table of Contents

Expand Down Expand Up @@ -149,13 +149,13 @@ pytest --cov=yarbo --cov-report=term-missing tests/

## Protocol Knowledge

python-yarbo is built on reverse-engineered protocol knowledge. If you discover
python-yarbo is a community project. If you discover
new commands, topics, or payload formats, please:

1. Document them in the related issue or PR
2. Add them to `src/yarbo/const.py` (topics) or as methods on the client
3. Reference the source of discovery (packet capture, APK analysis, etc.)
4. Cross-reference with `yarbo-reversing/docs/COMMAND_CATALOGUE.md`
3. Reference the source of discovery (hardware testing, protocol observation, etc.)
4. Cross-reference with existing protocol documentation

### Key protocol facts

Expand All @@ -165,4 +165,4 @@ new commands, topics, or payload formats, please:
- Light keys: `led_head`, `led_left_w`, `led_right_w`, `body_left_r`, `body_right_r`, `tail_left_r`, `tail_right_r`
- Values are integers 0–255, **not** booleans

See [`yarbo-reversing`](https://github.com/markus-lassfolk/yarbo-reversing) for full protocol docs.
See [protocol documentation](docs/index.md) for full protocol docs.
20 changes: 7 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Python library for **local and cloud control** of [Yarbo](https://yarbo.com/) robot
mowers and snow blowers — built from reverse-engineered protocol knowledge.
mowers and snow blowers — a community-developed project.

> **Status**: Alpha (0.1.0) — local MQTT control is functional and confirmed working
> on hardware. Cloud API is partially functional (JWT auth migration in progress).
Expand Down Expand Up @@ -117,7 +117,7 @@ async def main():
async with YarboCloudClient(
username="your@email.com",
password="yourpassword",
rsa_key_path="/path/to/rsa_public_key.pem", # from APK
rsa_key_path="/path/to/rsa_public_key.pem", # see keys/README.md
) as client:
robots = await client.list_robots()
for robot in robots:
Expand Down Expand Up @@ -225,8 +225,7 @@ Recommendations:

## Protocol Notes

This library was built from reverse-engineering the Yarbo Flutter app and
live packet captures. Key protocol facts:
Key protocol facts (community-observed):

- **MQTT broker**: Local EMQX (port 1883). Use `yarbo discover` (scans host networks) or
`yarbo discover --subnet <CIDR>` to find Rover/DC endpoints; IPs are DHCP-assigned.
Expand All @@ -239,7 +238,7 @@ live packet captures. Key protocol facts:
`BatteryMSG.capacity`, `StateMSG.working_state`, `RTKMSG.heading`,
`CombinedOdom.x/y/phi`
- **Not yet implemented**: Local REST API (port 8088) and TCP JSON (port 22220)
are documented in `yarbo-reversing` but not implemented here
are not yet implemented here

## Debug and troubleshooting

Expand Down Expand Up @@ -299,17 +298,13 @@ yarbo status --broker 192.168.1.1 --sn ABC123 --report-mqtt

From Python you can capture and send a dump yourself using `report_mqtt_dump_to_glitchtip` from `yarbo.error_reporting`, and `client.get_captured_mqtt()` when the client was created with `mqtt_capture_max > 0` (see `YarboLocalClient` and `MqttTransport` parameters).

See [`yarbo-reversing`](https://github.com/markus-lassfolk/yarbo-reversing) for:
- Full [command catalogue](https://github.com/markus-lassfolk/yarbo-reversing/blob/main/docs/COMMAND_CATALOGUE.md)
- [Light control protocol](https://github.com/markus-lassfolk/yarbo-reversing/blob/main/docs/LIGHT_CTRL_PROTOCOL.md)
- [API endpoints](https://github.com/markus-lassfolk/yarbo-reversing/blob/main/docs/API_ENDPOINTS.md)
- [MQTT protocol reference](https://github.com/markus-lassfolk/yarbo-reversing/blob/main/docs/MQTT_PROTOCOL.md)
See [protocol documentation](docs/index.md) for additional protocol details.

## Related Projects

| Project | Description |
|---------|-------------|
| [`yarbo-reversing`](https://github.com/markus-lassfolk/yarbo-reversing) | Protocol RE: Frida scripts, MITM setup, APK tools |
| [`home-assistant-yarbo`](https://github.com/markus-lassfolk/home-assistant-yarbo) | Home Assistant integration (coming soon) |
| [`PSYarbo`](https://github.com/markus-lassfolk/PSYarbo) | PowerShell module (same protocol, same architecture) |

## License
Expand All @@ -318,5 +313,4 @@ MIT — see [LICENSE](LICENSE).

## Disclaimer

This library was built by reverse engineering. It is not affiliated with or endorsed by
Yarbo. Use at your own risk. Do not expose your robot's MQTT broker to the internet.
This is a community project, not affiliated with or endorsed by Yarbo. Use at your own risk. Do not expose your robot's MQTT broker to the internet.
5 changes: 2 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ Python library for local and cloud control of Yarbo robot mowers via MQTT.
## Navigation

- [API Reference](api.md)
- [Protocol Details](https://github.com/markus-lassfolk/yarbo-reversing/blob/main/docs/COMMAND_CATALOGUE.md)
- [Light Control Protocol](https://github.com/markus-lassfolk/yarbo-reversing/blob/main/docs/LIGHT_CTRL_PROTOCOL.md)
- [API Endpoints](https://github.com/markus-lassfolk/yarbo-reversing/blob/main/docs/API_ENDPOINTS.md)
- [GitHub Repository](https://github.com/markus-lassfolk/python-yarbo)
- [PSYarbo PowerShell module](https://github.com/markus-lassfolk/PSYarbo)
- [CHANGELOG](../CHANGELOG.md)
- [CONTRIBUTING](../CONTRIBUTING.md)

Expand Down
5 changes: 2 additions & 3 deletions examples/cloud_login.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

Requires:
pip install "python-yarbo[cloud]"
RSA public key from the Yarbo APK (assets/rsa_key/rsa_public_key.pem)
See: https://github.com/markus-lassfolk/yarbo-reversing
RSA public key PEM file (see src/yarbo/keys/README.md for how to obtain it)

Usage:
python examples/cloud_login.py --username you@example.com --password secret \\
Expand Down Expand Up @@ -72,7 +71,7 @@ async def main(username: str, password: str, key_path: str | None) -> None:
ap.add_argument(
"--key",
default=os.environ.get("YARBO_RSA_KEY_PATH"),
help="Path to RSA public key PEM (from APK)",
help="Path to RSA public key PEM (see keys/README.md)",
)
args = ap.parse_args()

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Repository = "https://github.com/markus-lassfolk/python-yarbo"
"Bug Tracker" = "https://github.com/markus-lassfolk/python-yarbo/issues"
Documentation = "https://github.com/markus-lassfolk/python-yarbo#readme"
Changelog = "https://github.com/markus-lassfolk/python-yarbo/blob/main/CHANGELOG.md"
"yarbo-reversing" = "https://github.com/markus-lassfolk/yarbo-reversing"
"Protocol Documentation" = "https://github.com/markus-lassfolk/python-yarbo/blob/main/docs/index.md"

[tool.hatch.build.targets.wheel]
packages = ["src/yarbo"]
Expand Down
4 changes: 2 additions & 2 deletions src/yarbo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
yarbo — Python library for local and cloud control of Yarbo robot mowers.

Yarbo makes autonomous snow blowers and lawn mowers controlled via local MQTT.
This library was built from reverse-engineering the Yarbo Flutter app and
probing the protocol with live hardware captures.
This is a community-developed library for the Yarbo protocol, built through
hardware testing and protocol documentation.

Quick start (async)::

Expand Down
2 changes: 1 addition & 1 deletion src/yarbo/_codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
The robot firmware checks the firmware version (>= 3.9.0, MIN_ZIP_MQTT_VERSION)
before decompressing. All current firmware versions use zlib compression.

Reference: Blutter ASM analysis of the Flutter app's MqttPublish class.
Reference: Community protocol documentation for the Yarbo MQTT interface.
"""

from __future__ import annotations
Expand Down
11 changes: 5 additions & 6 deletions src/yarbo/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
before transmission (despite OAEP references in the Dart code — confirmed by
live testing that PKCS1v15 is the actual padding used).

The RSA public key is bundled in the Yarbo app APK at:
The RSA public key can be obtained from the Yarbo app and placed at:
``assets/rsa_key/rsa_public_key.pem``
Comment on lines +9 to 10

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

The module docstring says the RSA public key can be “placed at assets/rsa_key/rsa_public_key.pem”, but that path is inside the APK; this library actually looks for the vendored key at src/yarbo/keys/rsa_public_key.pem (or a user-supplied rsa_key_path). Reword this to clarify where the key is found in the app package vs where users should place it for this library.

Suggested change
The RSA public key can be obtained from the Yarbo app and placed at:
``assets/rsa_key/rsa_public_key.pem``
The RSA public key used by the Yarbo mobile app is stored inside the APK at:
``assets/rsa_key/rsa_public_key.pem``
For this Python library, the default RSA public key is loaded from the vendored
file in the package (e.g. ``yarbo/keys/rsa_public_key.pem``). Callers may
override this by passing an explicit ``rsa_key_path`` to ``YarboAuth``.

Copilot uses AI. Check for mistakes.

References:
yarbo-reversing/yarbo/auth.py — original synchronous implementation
yarbo-reversing/docs/API_ENDPOINTS.md — endpoint documentation
Protocol documentation (API endpoint reference)
"""

from __future__ import annotations
Expand Down Expand Up @@ -45,7 +44,7 @@ class YarboAuth:
base_url: REST API gateway base URL.
username: User email address.
password: Plaintext password (encrypted before transmission).
rsa_key_path: Path to the RSA public key PEM extracted from the APK.
rsa_key_path: Path to the RSA public key PEM.
If not provided, falls back to the vendored key in the
package (if available).
session: Existing ``aiohttp.ClientSession`` to reuse.
Expand Down Expand Up @@ -83,7 +82,7 @@ def _default_key_path() -> Path:
.. warning::
The vendored key at ``src/yarbo/keys/rsa_public_key.pem`` is a
**placeholder** — cloud auth will fail until it is replaced with
the real key extracted from the Yarbo APK.
the real key (see keys/README.md for instructions).

See ``src/yarbo/keys/README.md`` for extraction instructions, or
supply the real key path via ``rsa_key_path`` at construction time.
Expand Down Expand Up @@ -181,7 +180,7 @@ def _load_public_key(self) -> object:
except FileNotFoundError as exc:
raise YarboAuthError(
f"RSA public key not found at {self._key_path}. "
"Extract it from the Yarbo APK: assets/rsa_key/rsa_public_key.pem"
"Obtain the key and place it at: assets/rsa_key/rsa_public_key.pem"

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

The FileNotFoundError message instructs users to place the key at assets/rsa_key/rsa_public_key.pem, which is an APK-internal location and doesn’t match the default search path (src/yarbo/keys/rsa_public_key.pem) or the rsa_key_path parameter. Update the error to point to src/yarbo/keys/README.md and/or the expected on-disk path so users can recover quickly.

Suggested change
"Obtain the key and place it at: assets/rsa_key/rsa_public_key.pem"
"Ensure the key exists on disk (default: src/yarbo/keys/rsa_public_key.pem) "
"and see src/yarbo/keys/README.md for setup instructions."

Copilot uses AI. Check for mistakes.
) from exc
except ImportError as exc:
raise YarboAuthError(
Expand Down
7 changes: 3 additions & 4 deletions src/yarbo/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
The Yarbo backend is actively migrating from plain JWT Bearer tokens to
AWS SigV4 (IAM/Cognito) auth. Many endpoints that previously accepted JWT
now return 403. Endpoints marked ✅ work with Bearer auth.
See yarbo-reversing/yarbo/client.py for a full status inventory.
See the protocol documentation for a full endpoint status inventory.

References:
yarbo-reversing/yarbo/client.py — synchronous reference implementation
yarbo-reversing/docs/API_ENDPOINTS.md — endpoint documentation
Protocol documentation (API endpoint reference)
"""

from __future__ import annotations
Expand Down Expand Up @@ -51,7 +50,7 @@ class YarboCloudClient:
username: User email address.
password: Plaintext password.
base_url: Override the REST gateway base URL.
rsa_key_path: Path to the RSA public key PEM (extracted from APK).
rsa_key_path: Path to the RSA public key PEM (see keys/README.md).
"""

def __init__(
Expand Down
2 changes: 1 addition & 1 deletion src/yarbo/cloud_mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
Transport: TLS 1.2+, server certificate validated when ``tls_ca_certs`` is supplied.

References:
yarbo-reversing/docs/MQTT_PROTOCOL.md — protocol reference
Protocol documentation (MQTT protocol reference)
Tencent TDMQ MQTT documentation — broker configuration
"""

Expand Down
11 changes: 5 additions & 6 deletions src/yarbo/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
All topic templates, port numbers, and default values used by the
local and cloud MQTT transports.

Discovery sources:
- Blutter ASM analysis of the Flutter app (libapp.so)
- Live packet captures on the local EMQX broker (use discover() for broker IP)
- yarbo-reversing/docs/COMMAND_CATALOGUE.md
Protocol sources:
- Protocol observations on the local EMQX broker (use discover() for broker IP)
- Protocol documentation (command catalogue)

Transport support matrix
------------------------
Expand Down Expand Up @@ -82,11 +81,11 @@
TOPIC_LEAF_LOG_FEEDBACK = "log_feedback"
TOPIC_LEAF_A_PROPERTY_1 = "a_property_1_feedback"

# Live-confirmed telemetry leaves (zlib JSON ~1-2 Hz)
# Observed telemetry leaves (zlib JSON ~1-2 Hz)
TOPIC_LEAF_DEVICE_MSG = "DeviceMSG"
"""Full telemetry payload: BatteryMSG, StateMSG, RTKMSG, CombinedOdom, etc."""

# Live-confirmed heartbeat leaf (plain JSON ~1 Hz)
# Observed heartbeat leaf (plain JSON ~1 Hz)
TOPIC_LEAF_HEART_BEAT = "heart_beat"
"""Heartbeat: plain JSON ``{"working_state": 0|1}``.
NOTE: ``heart_beat`` is NOT zlib-compressed — the codec's plain-JSON
Expand Down
4 changes: 1 addition & 3 deletions src/yarbo/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
DC is recommended when both are present (stays connected via HaLow when Rover leaves WiFi).

Reference:
yarbo-reversing/yarbo/mqtt.py — MQTT_BROKER constant
yarbo-reversing/scripts/local_ctrl.py — DEFAULT_BROKER
docs/BROKER_ROLES.md — MAC↔role reference
Protocol documentation (MQTT broker details and MAC/role reference)
"""

from __future__ import annotations
Expand Down
15 changes: 2 additions & 13 deletions src/yarbo/keys/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,10 @@ Cloud login (`YarboCloudClient`) requires the real key to encrypt the password.

## How to get the real key

The actual public key is bundled inside the Yarbo Android APK at:
The actual public key can be obtained from the Yarbo app package and placed at:

```
assets/rsa_key/rsa_public_key.pem
```

### Extract from APK

```bash
# 1. Download the Yarbo APK (from Google Play via apkpure or your device)
# 2. Unzip it (APKs are ZIP archives)
unzip yarbo.apk -d yarbo_unpacked

# 3. Copy the key
cp yarbo_unpacked/assets/rsa_key/rsa_public_key.pem /path/to/python-yarbo/src/yarbo/keys/
src/yarbo/keys/rsa_public_key.pem
```

### Supply at runtime (alternative)
Expand Down
Loading
Loading