Skip to content

Commit e6f20cc

Browse files
authored
feat: update schema requirements in StatusV2 and Consumable (#806)
* feat: update schema requirements in StatusV2 Update the StatusV2 to check the schema for supported fields. * fix: update error_code and other fields to use _requires_schema_code for schema validation * feat: Add consumable fields * refactor: remove redundant default=None arguments from _requires_schema_code calls in v1_containers
1 parent d8ccc2a commit e6f20cc

File tree

3 files changed

+86
-21
lines changed

3 files changed

+86
-21
lines changed

roborock/data/v1/v1_containers.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class StatusField(FieldNameBase):
113113
WATER_BOX_MODE = "water_box_mode"
114114
CHARGE_STATUS = "charge_status"
115115
DRY_STATUS = "dry_status"
116+
ERROR_CODE = "error_code"
116117

117118

118119
def _requires_schema_code(requires_schema_code: str, default=None) -> Any:
@@ -125,11 +126,11 @@ class Status(RoborockBase):
125126

126127
msg_ver: int | None = None
127128
msg_seq: int | None = None
128-
state: RoborockStateCode | None = _requires_schema_code("state", default=None)
129-
battery: int | None = _requires_schema_code("battery", default=None)
129+
state: RoborockStateCode | None = _requires_schema_code("state")
130+
battery: int | None = _requires_schema_code("battery")
130131
clean_time: int | None = None
131132
clean_area: int | None = None
132-
error_code: RoborockErrorCode | None = None
133+
error_code: RoborockErrorCode | None = _requires_schema_code("error_code")
133134
map_present: int | None = None
134135
in_cleaning: RoborockInCleaning | None = None
135136
in_returning: int | None = None
@@ -139,12 +140,12 @@ class Status(RoborockBase):
139140
back_type: int | None = None
140141
wash_phase: int | None = None
141142
wash_ready: int | None = None
142-
fan_power: RoborockFanPowerCode | None = _requires_schema_code("fan_power", default=None)
143+
fan_power: RoborockFanPowerCode | None = _requires_schema_code("fan_power")
143144
dnd_enabled: int | None = None
144145
map_status: int | None = None
145146
is_locating: int | None = None
146147
lock_status: int | None = None
147-
water_box_mode: RoborockMopIntensityCode | None = _requires_schema_code("water_box_mode", default=None)
148+
water_box_mode: RoborockMopIntensityCode | None = _requires_schema_code("water_box_mode")
148149
water_box_carriage_status: int | None = None
149150
mop_forbidden_enable: int | None = None
150151
camera_status: int | None = None
@@ -162,13 +163,13 @@ class Status(RoborockBase):
162163
collision_avoid_status: int | None = None
163164
switch_map_mode: int | None = None
164165
dock_error_status: RoborockDockErrorCode | None = None
165-
charge_status: int | None = _requires_schema_code("charge_status", default=None)
166+
charge_status: int | None = _requires_schema_code("charge_status")
166167
unsave_map_reason: int | None = None
167168
unsave_map_flag: int | None = None
168169
wash_status: int | None = None
169170
distance_off: int | None = None
170171
in_warmup: int | None = None
171-
dry_status: int | None = _requires_schema_code("drying_status", default=None)
172+
dry_status: int | None = _requires_schema_code("drying_status")
172173
rdt: int | None = None
173174
clean_percent: int | None = None
174175
rss: int | None = None
@@ -293,11 +294,11 @@ class StatusV2(RoborockBase):
293294

294295
msg_ver: int | None = None
295296
msg_seq: int | None = None
296-
state: RoborockStateCode | None = None
297-
battery: int | None = None
297+
state: RoborockStateCode | None = _requires_schema_code("state")
298+
battery: int | None = _requires_schema_code("battery")
298299
clean_time: int | None = None
299300
clean_area: int | None = None
300-
error_code: RoborockErrorCode | None = None
301+
error_code: RoborockErrorCode | None = _requires_schema_code("error_code")
301302
map_present: int | None = None
302303
in_cleaning: RoborockInCleaning | None = None
303304
in_returning: int | None = None
@@ -307,12 +308,12 @@ class StatusV2(RoborockBase):
307308
back_type: int | None = None
308309
wash_phase: int | None = None
309310
wash_ready: int | None = None
310-
fan_power: int | None = None
311+
fan_power: int | None = _requires_schema_code("fan_power")
311312
dnd_enabled: int | None = None
312313
map_status: int | None = None
313314
is_locating: int | None = None
314315
lock_status: int | None = None
315-
water_box_mode: int | None = None
316+
water_box_mode: int | None = _requires_schema_code("water_box_mode")
316317
water_box_carriage_status: int | None = None
317318
mop_forbidden_enable: int | None = None
318319
camera_status: int | None = None
@@ -329,14 +330,14 @@ class StatusV2(RoborockBase):
329330
debug_mode: int | None = None
330331
collision_avoid_status: int | None = None
331332
switch_map_mode: int | None = None
332-
dock_error_status: RoborockDockErrorCode | None = None
333-
charge_status: int | None = None
333+
dock_error_status: RoborockDockErrorCode | None = _requires_schema_code("dock_error_status")
334+
charge_status: int | None = _requires_schema_code("charge_status")
334335
unsave_map_reason: int | None = None
335336
unsave_map_flag: int | None = None
336337
wash_status: int | None = None
337338
distance_off: int | None = None
338339
in_warmup: int | None = None
339-
dry_status: int | None = None
340+
dry_status: int | None = _requires_schema_code("drying_status")
340341
rdt: int | None = None
341342
clean_percent: int | None = None
342343
rss: int | None = None
@@ -624,11 +625,27 @@ class CleanSummaryWithDetail(CleanSummary):
624625
last_clean_record: CleanRecord | None = None
625626

626627

628+
class ConsumableField(FieldNameBase):
629+
"""An enum that represents a field in the `Consumable` class.
630+
631+
This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait`
632+
to understand if a feature is supported by the device using `is_field_supported`.
633+
634+
The enum values are names of fields in the `Consumable` class. Each field is
635+
annotated with `requires_schema_code` metadata to map the field to a schema
636+
code in the product schema, which may have a different name than the field/attribute name.
637+
"""
638+
639+
MAIN_BRUSH_WORK_TIME = "main_brush_work_time"
640+
SIDE_BRUSH_WORK_TIME = "side_brush_work_time"
641+
FILTER_WORK_TIME = "filter_work_time"
642+
643+
627644
@dataclass
628645
class Consumable(RoborockBase):
629-
main_brush_work_time: int | None = None
630-
side_brush_work_time: int | None = None
631-
filter_work_time: int | None = None
646+
main_brush_work_time: int | None = field(metadata={"requires_schema_code": "main_brush_life"}, default=None)
647+
side_brush_work_time: int | None = field(metadata={"requires_schema_code": "side_brush_life"}, default=None)
648+
filter_work_time: int | None = field(metadata={"requires_schema_code": "filter_life"}, default=None)
632649
filter_element_work_time: int | None = None
633650
sensor_dirty_time: int | None = None
634651
strainer_work_times: int | None = None

tests/devices/traits/v1/__snapshots__/test_device_features.ambr

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
'battery': True,
55
'charge_status': True,
66
'dry_status': True,
7+
'error_code': True,
78
'fan_power': True,
89
'state': True,
910
'water_box_mode': True,
@@ -14,6 +15,7 @@
1415
'battery': True,
1516
'charge_status': True,
1617
'dry_status': True,
18+
'error_code': True,
1719
'fan_power': True,
1820
'state': True,
1921
'water_box_mode': True,
@@ -24,8 +26,30 @@
2426
'battery': True,
2527
'charge_status': True,
2628
'dry_status': True,
29+
'error_code': True,
2730
'fan_power': True,
2831
'state': True,
2932
'water_box_mode': True,
3033
})
3134
# ---
35+
# name: test_is_consumable_field_supported[home_data_device_s5e.json]
36+
dict({
37+
'filter_work_time': True,
38+
'main_brush_work_time': True,
39+
'side_brush_work_time': True,
40+
})
41+
# ---
42+
# name: test_is_consumable_field_supported[home_data_device_s7_maxv.json]
43+
dict({
44+
'filter_work_time': True,
45+
'main_brush_work_time': True,
46+
'side_brush_work_time': True,
47+
})
48+
# ---
49+
# name: test_is_consumable_field_supported[home_data_device_saros_10r.json]
50+
dict({
51+
'filter_work_time': True,
52+
'main_brush_work_time': True,
53+
'side_brush_work_time': True,
54+
})
55+
# ---

tests/devices/traits/v1/test_device_features.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
from syrupy import SnapshotAssertion
55

66
from roborock.data import HomeDataDevice
7-
from roborock.data.v1.v1_containers import StatusField
7+
from roborock.data.v1.v1_containers import ConsumableField, StatusField
88
from roborock.devices.device import RoborockDevice
9+
from roborock.devices.traits.v1.consumeable import ConsumableTrait
910
from roborock.devices.traits.v1.status import StatusTrait
1011
from tests import mock_data
1112

@@ -29,5 +30,28 @@ async def test_is_attribute_supported(
2930
assert device.v1_properties.device_features is not None
3031
device_features_trait = device.v1_properties.device_features
3132

32-
is_supported = {field.value: device_features_trait.is_field_supported(StatusTrait, field) for field in StatusField}
33-
assert is_supported == snapshot
33+
is_v1_supported = {
34+
field.value: device_features_trait.is_field_supported(StatusTrait, field) for field in StatusField
35+
}
36+
assert is_v1_supported == snapshot
37+
38+
39+
@pytest.mark.parametrize(
40+
("device_info"),
41+
V1_DEVICES.values(),
42+
ids=list(V1_DEVICES.keys()),
43+
)
44+
async def test_is_consumable_field_supported(
45+
device_info: HomeDataDevice,
46+
device: RoborockDevice,
47+
snapshot: SnapshotAssertion,
48+
) -> None:
49+
"""Test if a field is supported."""
50+
assert device.v1_properties is not None
51+
assert device.v1_properties.device_features is not None
52+
device_features_trait = device.v1_properties.device_features
53+
54+
is_v1_supported = {
55+
field.value: device_features_trait.is_field_supported(ConsumableTrait, field) for field in ConsumableField
56+
}
57+
assert is_v1_supported == snapshot

0 commit comments

Comments
 (0)