Skip to content

Commit aab737c

Browse files
authored
Merge branch 'main' into dynamic_status_trait
2 parents 168123e + 07cef03 commit aab737c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2160
-162
lines changed

CHANGELOG.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,143 @@
22

33
<!-- version list -->
44

5+
## v4.4.0 (2026-01-12)
6+
7+
### Features
8+
9+
- Iterate possible iot domains on 3030 error
10+
([#733](https://github.com/Python-roborock/python-roborock/pull/733),
11+
[`f2e1d51`](https://github.com/Python-roborock/python-roborock/commit/f2e1d5156dd905e296d5ed38605d4fd6f97bfbb4))
12+
13+
14+
## v4.3.0 (2026-01-10)
15+
16+
### Chores
17+
18+
- Add function to create field metadata
19+
([#740](https://github.com/Python-roborock/python-roborock/pull/740),
20+
[`bdc1591`](https://github.com/Python-roborock/python-roborock/commit/bdc159192cfb2afa02199171288a20b228abb7f6))
21+
22+
- Simplify supported_schema_codes
23+
([#740](https://github.com/Python-roborock/python-roborock/pull/740),
24+
[`bdc1591`](https://github.com/Python-roborock/python-roborock/commit/bdc159192cfb2afa02199171288a20b228abb7f6))
25+
26+
- Update pydoc for DeviceFeaturesTrait
27+
([#740](https://github.com/Python-roborock/python-roborock/pull/740),
28+
[`bdc1591`](https://github.com/Python-roborock/python-roborock/commit/bdc159192cfb2afa02199171288a20b228abb7f6))
29+
30+
- Update test descrition ([#740](https://github.com/Python-roborock/python-roborock/pull/740),
31+
[`bdc1591`](https://github.com/Python-roborock/python-roborock/commit/bdc159192cfb2afa02199171288a20b228abb7f6))
32+
33+
- Update to use StrEnum ([#740](https://github.com/Python-roborock/python-roborock/pull/740),
34+
[`bdc1591`](https://github.com/Python-roborock/python-roborock/commit/bdc159192cfb2afa02199171288a20b228abb7f6))
35+
36+
### Features
37+
38+
- Add an approach for determining if a dataclass field is supported
39+
([#740](https://github.com/Python-roborock/python-roborock/pull/740),
40+
[`bdc1591`](https://github.com/Python-roborock/python-roborock/commit/bdc159192cfb2afa02199171288a20b228abb7f6))
41+
42+
43+
## v4.2.2 (2026-01-09)
44+
45+
### Bug Fixes
46+
47+
- Decrease home data rate limits
48+
([#741](https://github.com/Python-roborock/python-roborock/pull/741),
49+
[`29eb984`](https://github.com/Python-roborock/python-roborock/commit/29eb984d22494b08f26ec6e220b7c823b67d3242))
50+
51+
### Chores
52+
53+
- Add additional Home data to diagnostics
54+
([#723](https://github.com/Python-roborock/python-roborock/pull/723),
55+
[`c29dfc8`](https://github.com/Python-roborock/python-roborock/commit/c29dfc81f4de1bb293b2918482cf681197ef3698))
56+
57+
- Add CONTRIBUTING.md ([#734](https://github.com/Python-roborock/python-roborock/pull/734),
58+
[`881b7d6`](https://github.com/Python-roborock/python-roborock/commit/881b7d687789c57eec20bf9011a195b4befff129))
59+
60+
- Add CONTRIBUTINGmd ([#734](https://github.com/Python-roborock/python-roborock/pull/734),
61+
[`881b7d6`](https://github.com/Python-roborock/python-roborock/commit/881b7d687789c57eec20bf9011a195b4befff129))
62+
63+
- Add s5e device and product data examples
64+
([#737](https://github.com/Python-roborock/python-roborock/pull/737),
65+
[`586bb3f`](https://github.com/Python-roborock/python-roborock/commit/586bb3f77e4655d4aae2d201746980b1c227160d))
66+
67+
- Add Saros 10R API response data
68+
([#726](https://github.com/Python-roborock/python-roborock/pull/726),
69+
[`fafc8d8`](https://github.com/Python-roborock/python-roborock/commit/fafc8d86833a2aac3ee69c7a1f353f83551eeb6f))
70+
71+
- Fix diagnostic lint issues ([#723](https://github.com/Python-roborock/python-roborock/pull/723),
72+
[`c29dfc8`](https://github.com/Python-roborock/python-roborock/commit/c29dfc81f4de1bb293b2918482cf681197ef3698))
73+
74+
- Fix mock data lint ([#726](https://github.com/Python-roborock/python-roborock/pull/726),
75+
[`fafc8d8`](https://github.com/Python-roborock/python-roborock/commit/fafc8d86833a2aac3ee69c7a1f353f83551eeb6f))
76+
77+
- Fix schema redaction ([#723](https://github.com/Python-roborock/python-roborock/pull/723),
78+
[`c29dfc8`](https://github.com/Python-roborock/python-roborock/commit/c29dfc81f4de1bb293b2918482cf681197ef3698))
79+
80+
- Improve redaction logic to support more complex paths
81+
([#723](https://github.com/Python-roborock/python-roborock/pull/723),
82+
[`c29dfc8`](https://github.com/Python-roborock/python-roborock/commit/c29dfc81f4de1bb293b2918482cf681197ef3698))
83+
84+
- Remove duplicate data in test_q7_device
85+
([#736](https://github.com/Python-roborock/python-roborock/pull/736),
86+
[`cd6cbbe`](https://github.com/Python-roborock/python-roborock/commit/cd6cbbe1be22a619a88d76783c60c936dbbc744d))
87+
88+
- Update device snapshots and lint errors
89+
([#723](https://github.com/Python-roborock/python-roborock/pull/723),
90+
[`c29dfc8`](https://github.com/Python-roborock/python-roborock/commit/c29dfc81f4de1bb293b2918482cf681197ef3698))
91+
92+
- Update e2e tests for q7 to use different product data
93+
([#736](https://github.com/Python-roborock/python-roborock/pull/736),
94+
[`cd6cbbe`](https://github.com/Python-roborock/python-roborock/commit/cd6cbbe1be22a619a88d76783c60c936dbbc744d))
95+
96+
- Update end to end q7 tests ([#736](https://github.com/Python-roborock/python-roborock/pull/736),
97+
[`cd6cbbe`](https://github.com/Python-roborock/python-roborock/commit/cd6cbbe1be22a619a88d76783c60c936dbbc744d))
98+
99+
- Update steps to activate virtual environment
100+
([#734](https://github.com/Python-roborock/python-roborock/pull/734),
101+
[`881b7d6`](https://github.com/Python-roborock/python-roborock/commit/881b7d687789c57eec20bf9011a195b4befff129))
102+
103+
- Use built-in as_dict method for creating diagnostic data
104+
([#723](https://github.com/Python-roborock/python-roborock/pull/723),
105+
[`c29dfc8`](https://github.com/Python-roborock/python-roborock/commit/c29dfc81f4de1bb293b2918482cf681197ef3698))
106+
107+
108+
## v4.2.1 (2026-01-05)
109+
110+
### Bug Fixes
111+
112+
- Bump aiomqtt ([#730](https://github.com/Python-roborock/python-roborock/pull/730),
113+
[`21af4f3`](https://github.com/Python-roborock/python-roborock/commit/21af4f30412d96eb5ac53f372b74b0e03ca6580e))
114+
115+
### Chores
116+
117+
- Add a01 and b01 q7 byte level tests
118+
([#724](https://github.com/Python-roborock/python-roborock/pull/724),
119+
[`f20ade9`](https://github.com/Python-roborock/python-roborock/commit/f20ade97843241aa286405c4eacbb9f1939cbdf3))
120+
121+
- Add docs for v1 device features
122+
([#727](https://github.com/Python-roborock/python-roborock/pull/727),
123+
[`f031acf`](https://github.com/Python-roborock/python-roborock/commit/f031acffa2381c2eb9e4af6fbf7967ae22b1d7dc))
124+
125+
- Documentation cleanup and updates
126+
([#725](https://github.com/Python-roborock/python-roborock/pull/725),
127+
[`bbeb0d9`](https://github.com/Python-roborock/python-roborock/commit/bbeb0d95e11274bd024cfac23988f01acf814888))
128+
129+
- Remove empty line in device features documentation
130+
([#727](https://github.com/Python-roborock/python-roborock/pull/727),
131+
[`f031acf`](https://github.com/Python-roborock/python-roborock/commit/f031acffa2381c2eb9e4af6fbf7967ae22b1d7dc))
132+
133+
- Remove some information from the summart
134+
([#727](https://github.com/Python-roborock/python-roborock/pull/727),
135+
[`f031acf`](https://github.com/Python-roborock/python-roborock/commit/f031acffa2381c2eb9e4af6fbf7967ae22b1d7dc))
136+
137+
- Restructure the channel modules
138+
([#728](https://github.com/Python-roborock/python-roborock/pull/728),
139+
[`9fcc0a8`](https://github.com/Python-roborock/python-roborock/commit/9fcc0a8ca075097b7d903a57cc0fc33ed149bd97))
140+
141+
5142
## v4.2.0 (2025-12-30)
6143

7144
### Chores

CONTRIBUTING.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Contributing to python-roborock
2+
3+
Thank you for your interest in contributing to `python-roborock`! We welcome contributions from the community.
4+
5+
## Getting Started
6+
7+
1. **Fork the repository** on GitHub.
8+
2. **Clone your fork** locally:
9+
```bash
10+
git clone https://github.com/your-username/python-roborock.git
11+
cd python-roborock
12+
```
13+
3. **Set up your environment**. This project typically tries to stay on the most recent python versions (e.g. latest 2 or 3 versions). We use `uv` for dependency management:
14+
```bash
15+
# Create virtual environment and install dependencies
16+
uv venv
17+
uv sync
18+
```
19+
4. **Activate the virtual environment**. This is required for running `pre-commit` hooks and `pytest`:
20+
```bash
21+
source .venv/bin/activate
22+
```
23+
24+
5. **Install pre-commit hooks**. This ensures your code meets our quality standards. Once installed, these hooks run automatically on staged files when you commit:
25+
```bash
26+
pre-commit install
27+
```
28+
29+
## Development Workflow
30+
31+
### Code Style
32+
33+
We use several tools to enforce code quality and consistency. These are configured via `pre-commit` and generally run automatically.
34+
35+
* **Ruff**: Used for linting and formatting.
36+
* **Mypy**: Used for static type checking.
37+
* **Codespell**: Checks for common misspellings.
38+
39+
You can verify your changes manually before committing (checks all files):
40+
41+
```bash
42+
# Run all pre-commit hooks
43+
pre-commit run --all-files
44+
```
45+
46+
### Testing
47+
48+
We use `pytest` for testing. Please ensure all tests pass and add new tests for your changes.
49+
50+
```bash
51+
# Run tests
52+
pytest
53+
```
54+
55+
## Pull Requests
56+
57+
1. **Create a branch** for your changes.
58+
2. **Make your changes**. Keep your changes focused and atomic.
59+
3. **Commit your changes**.
60+
* **Important**: We use [Conventional Commits](https://www.conventionalcommits.org/). Please format your commit messages accordingly (e.g., `feat: add new vacuum model`, `fix: handle connection timeout`). This is required for our automated release process.
61+
* Allowed types: `chore`, `docs`, `feat`, `fix`, `refactor`.
62+
4. **Push to your fork** and submit a **Pull Request**.
63+
64+
## Adding New Devices or Features
65+
66+
If you are adding support for a new device or feature, please follow these steps:
67+
68+
1. **Update Device Info**: Use the CLI to discover and fetch device features.
69+
```bash
70+
roborock get-device-info
71+
```
72+
Arguments and output will be printed to the console. **Manually copy the YAML output** from this command into the `device_info.yaml` file.
73+
74+
2. **Add Test Data**:
75+
* **Home API Data**: Capture device information from Home API responses and save as `tests/testdata/home_data_<device>.json`. This helps test device discovery and initialization.
76+
* **Protocol/Feature Data**: Capture actual device responses or protocol data. You can often see these messages in the DEBUG logs when interacting with the device. Create JSON files in `tests/protocols/testdata/` that reflect these responses. This ensures protocol parsing works correctly against real-world data.
77+
78+
## Code of Conduct
79+
80+
Please be respectful and considerate in your interactions.

docs/DEVICES.md

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -564,30 +564,40 @@ The new design:
564564
```
565565
roborock/
566566
├── devices/ # Device management and channels
567-
│ ├── device_manager.py # High-level device lifecycle
568-
│ ├── channel.py # Base Channel interface
569-
│ ├── mqtt_channel.py # MQTT channel implementation
570-
│ ├── local_channel.py # Local TCP channel implementation
571-
│ ├── v1_channel.py # V1 protocol channel with RPC strategies
572-
│ ├── a01_channel.py # A01 protocol helpers
573-
│ ├── b01_q7_channel.py # B01 Q7 protocol helpers
574-
│ └── traits/ # Device-specific command traits
575-
│ └── v1/ # V1 device traits
576-
│ ├── __init__.py # Trait initialization
577-
│ ├── clean.py # Cleaning commands
578-
│ ├── map.py # Map management
567+
│ ├── device_manager.py # High-level device lifecycle
568+
│ ├── transport/ # Module for network connections to devices
569+
│ | ├── channel.py # Base Channel interface
570+
│ | ├── mqtt_channel.py # MQTT channel implementation
571+
│ | ├── local_channel.py # Local TCP channel implementation
572+
│ | └── ...
573+
│ ├── rpc/ # Application-level protocol/device-specific glue
574+
│ | ├── v1_channel.py # V1 protocol channel with RPC strategies
575+
│ | ├── a01_channel.py # A01 protocol helpers
576+
│ | ├── b01_q7_channel.py # B01 Q7 protocol helpers
577+
│ | ├── b01_q10_channel.py # B01 Q10 protocol helpers
578+
│ | └── ...
579+
│ └── traits/ # High-level device-specific command traits
580+
│ └── v1/ # V1 device traits
581+
│ ├── __init__.py # Trait initialization
582+
│ ├── clean.py # Cleaning commands
583+
│ ├── map.py # Map management
579584
│ └── ...
580-
├── mqtt/ # MQTT session management
585+
├── mqtt/ # MQTT session management
581586
│ ├── session.py # Base session interface
582587
│ └── roborock_session.py # MQTT session with idle timeout
583-
├── protocols/ # Protocol encoders/decoders
588+
├── protocols/ # Low level protocol encoders/decoders
584589
│ ├── v1_protocol.py # V1 JSON RPC protocol
585590
│ ├── a01_protocol.py # A01 protocol
586591
│ ├── b01_q7_protocol.py # B01 Q7 protocol
587592
│ └── ...
588-
└── data/ # Data containers and mappings
593+
└── data/ # Data containers and mappings
589594
├── containers.py # Status, HomeData, etc.
590-
└── v1/ # V1-specific data structures
595+
├── v1/ # V1-specific data structures
596+
├── dyad/ # Dyad-specific data structures
597+
├── zeo/ # Zeo-specific data structures
598+
├── b01_q7/ # B01 Q7-specific data structures
599+
├── b01_q10/ # B01 Q10-specific data structures
600+
└── ...
591601
```
592602

593603
### Threading Model

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "python-roborock"
3-
version = "4.2.0"
3+
version = "4.4.0"
44
description = "A package to control Roborock vacuums."
55
authors = [{ name = "humbertogontijo", email = "humbertogontijo@users.noreply.github.com" }, {name="Lash-L"}, {name="allenporter"}]
66
requires-python = ">=3.11, <4"
@@ -27,7 +27,7 @@ dependencies = [
2727
"construct>=2.10.57,<3",
2828
"vacuum-map-parser-roborock",
2929
"pyrate-limiter>=3.7.0,<4",
30-
"aiomqtt>=2.3.2,<3",
30+
"aiomqtt>=2.5.0,<3",
3131
"click-shell~=2.1",
3232
]
3333

roborock/data/containers.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,13 @@ def summary_info(self) -> str:
232232
"""Return a string with key product information for logging purposes."""
233233
return f"{self.name} (model={self.model}, category={self.category})"
234234

235+
@cached_property
236+
def supported_schema_codes(self) -> set[str]:
237+
"""Return a set of fields that are supported by the device."""
238+
if self.schema is None:
239+
return set()
240+
return {schema.code for schema in self.schema if schema.code is not None}
241+
235242

236243
@dataclass
237244
class HomeDataDevice(RoborockBase):

roborock/data/v1/v1_containers.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import datetime
22
import logging
3-
from dataclasses import dataclass
3+
from dataclasses import dataclass, field
4+
from enum import StrEnum
45
from typing import Any
56

67
from roborock.const import (
@@ -91,14 +92,41 @@
9192
_LOGGER = logging.getLogger(__name__)
9293

9394

95+
class FieldNameBase(StrEnum):
96+
"""A base enum class that represents a field name in a RoborockBase dataclass."""
97+
98+
99+
class StatusField(FieldNameBase):
100+
"""An enum that represents a field in the `Status` class.
101+
102+
This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait`
103+
to understand if a feature is supported by the device using `is_field_supported`.
104+
105+
The enum values are names of fields in the `Status` class. Each field is
106+
annotated with `requires_schema_code` metadata to map the field to a schema
107+
code in the product schema, which may have a different name than the field/attribute name.
108+
"""
109+
110+
STATE = "state"
111+
BATTERY = "battery"
112+
FAN_POWER = "fan_power"
113+
WATER_BOX_MODE = "water_box_mode"
114+
CHARGE_STATUS = "charge_status"
115+
DRY_STATUS = "dry_status"
116+
117+
118+
def _requires_schema_code(requires_schema_code: str, default=None) -> Any:
119+
return field(metadata={"requires_schema_code": requires_schema_code}, default=default)
120+
121+
94122
@dataclass
95123
class Status(RoborockBase):
96124
"""This status will be depreciated in favor of StatusV2."""
97125

98126
msg_ver: int | None = None
99127
msg_seq: int | None = None
100-
state: RoborockStateCode | None = None
101-
battery: int | None = None
128+
state: RoborockStateCode | None = _requires_schema_code("state", default=None)
129+
battery: int | None = _requires_schema_code("battery", default=None)
102130
clean_time: int | None = None
103131
clean_area: int | None = None
104132
error_code: RoborockErrorCode | None = None
@@ -111,12 +139,12 @@ class Status(RoborockBase):
111139
back_type: int | None = None
112140
wash_phase: int | None = None
113141
wash_ready: int | None = None
114-
fan_power: RoborockFanPowerCode | None = None
142+
fan_power: RoborockFanPowerCode | None = _requires_schema_code("fan_power", default=None)
115143
dnd_enabled: int | None = None
116144
map_status: int | None = None
117145
is_locating: int | None = None
118146
lock_status: int | None = None
119-
water_box_mode: RoborockMopIntensityCode | None = None
147+
water_box_mode: RoborockMopIntensityCode | None = _requires_schema_code("water_box_mode", default=None)
120148
water_box_carriage_status: int | None = None
121149
mop_forbidden_enable: int | None = None
122150
camera_status: int | None = None
@@ -134,13 +162,13 @@ class Status(RoborockBase):
134162
collision_avoid_status: int | None = None
135163
switch_map_mode: int | None = None
136164
dock_error_status: RoborockDockErrorCode | None = None
137-
charge_status: int | None = None
165+
charge_status: int | None = _requires_schema_code("charge_status", default=None)
138166
unsave_map_reason: int | None = None
139167
unsave_map_flag: int | None = None
140168
wash_status: int | None = None
141169
distance_off: int | None = None
142170
in_warmup: int | None = None
143-
dry_status: int | None = None
171+
dry_status: int | None = _requires_schema_code("drying_status", default=None)
144172
rdt: int | None = None
145173
clean_percent: int | None = None
146174
rss: int | None = None

0 commit comments

Comments
 (0)