Skip to content

Commit 0d3b205

Browse files
committed
fix: Handle AppInitStatus with omitted new_feature_info_str
The approach taken here is: (1) add an example of this response type to the json examples to exercise the decoder. (This succeeds already, but is just used to record the payload) (2) Add a test for passing the decoded data to the dataclass. In the future this can be improved with trait tests that use these payloads, but this is a way to do it incrementally for now.
1 parent cd7ef7c commit 0d3b205

File tree

6 files changed

+131
-3
lines changed

6 files changed

+131
-3
lines changed

roborock/data/v1/v1_containers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ class AppInitStatus(RoborockBase):
585585
local_info: AppInitStatusLocalInfo
586586
feature_info: list[int]
587587
new_feature_info: int
588-
new_feature_info_str: str
588+
new_feature_info_str: str | None = None
589589
new_feature_info_2: int | None = None
590590
carriage_type: int | None = None
591591
dsp_version: str | None = None

roborock/devices/traits/v1/device_features.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def _parse_response(self, response: common.V1ResponseData) -> DeviceFeatures:
4444
app_status = AppInitStatus.from_dict(response[0])
4545
return DeviceFeatures.from_feature_flags(
4646
new_feature_info=app_status.new_feature_info,
47-
new_feature_info_str=app_status.new_feature_info_str,
47+
new_feature_info_str=app_status.new_feature_info_str or "",
4848
feature_info=app_status.feature_info,
4949
product_nickname=self._nickname,
5050
)

tests/protocols/__snapshots__/test_v1_protocol.ambr

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,71 @@
11
# serializer version: 1
2+
# name: test_decode_rpc_payload[app_get_init_status2]
3+
23607
4+
# ---
5+
# name: test_decode_rpc_payload[app_get_init_status2].1
6+
'''
7+
[
8+
{
9+
"local_info": {
10+
"name": "custom_A.03.0096_FCC",
11+
"bom": "A.03.0096",
12+
"location": "us",
13+
"language": "en",
14+
"wifiplan": "US",
15+
"timezone": "US/Pacific",
16+
"logserver": "awsusor0.fds.api.xiaomi.com",
17+
"featureset": 1
18+
},
19+
"feature_info": [
20+
111,
21+
112,
22+
113,
23+
114,
24+
115,
25+
116,
26+
117,
27+
118,
28+
119,
29+
120,
30+
121,
31+
122,
32+
123,
33+
124,
34+
125
35+
],
36+
"new_feature_info": 10738169343,
37+
"status_info": {
38+
"state": 8,
39+
"battery": 100,
40+
"clean_time": 251,
41+
"clean_area": 3847500,
42+
"error_code": 0,
43+
"in_cleaning": 0,
44+
"in_returning": 0,
45+
"in_fresh_state": 1,
46+
"lab_status": 3,
47+
"water_box_status": 0,
48+
"map_status": 7,
49+
"is_locating": 0,
50+
"lock_status": 0,
51+
"water_box_mode": 203,
52+
"distance_off": 0,
53+
"water_box_carriage_status": 0,
54+
"mop_forbidden_enable": 0,
55+
"camera_status": 3495,
56+
"is_exploring": 0,
57+
"home_sec_status": 0,
58+
"home_sec_enable_password": 1,
59+
"adbumper_status": [
60+
0,
61+
0,
62+
0
63+
]
64+
}
65+
}
66+
]
67+
'''
68+
# ---
269
# name: test_decode_rpc_payload[app_get_init_status]
370
20001
471
# ---
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"t":1765918422,"dps":{"102":"{\"id\":23607,\"result\":[{\"local_info\":{\"name\":\"custom_A.03.0096_FCC\",\"bom\":\"A.03.0096\",\"location\":\"us\",\"language\":\"en\",\"wifiplan\":\"US\",\"timezone\":\"US/Pacific\",\"logserver\":\"awsusor0.fds.api.xiaomi.com\",\"featureset\":1},\"feature_info\":[111,112,113,114,115,116,117,118,119,120,121,122,123,124,125],\"new_feature_info\":10738169343,\"status_info\":{\"state\":8,\"battery\":100,\"clean_time\":251,\"clean_area\":3847500,\"error_code\":0,\"in_cleaning\":0,\"in_returning\":0,\"in_fresh_state\":1,\"lab_status\":3,\"water_box_status\":0,\"map_status\":7,\"is_locating\":0,\"lock_status\":0,\"water_box_mode\":203,\"distance_off\":0,\"water_box_carriage_status\":0,\"mop_forbidden_enable\":0,\"camera_status\":3495,\"is_exploring\":0,\"home_sec_status\":0,\"home_sec_enable_password\":1,\"adbumper_status\":[0,0,0]}}]}"}}

tests/test_containers.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from syrupy import SnapshotAssertion
99

1010
from roborock import CleanRecord, CleanSummary, Consumable, DnDTimer, HomeData, S7MaxVStatus, UserData
11-
from roborock.data import HomeDataDevice, RoborockBase, RoborockCategory
11+
from roborock.data import AppInitStatus, HomeDataDevice, RoborockBase, RoborockCategory
1212
from roborock.data.b01_q7 import (
1313
B01Fault,
1414
B01Props,
@@ -600,3 +600,49 @@ def test_offline_device() -> None:
600600
assert device.time_zone_id == "Europe/Moscow"
601601
assert not device.online
602602
assert device.fv is None
603+
604+
605+
def test_partial_app_init_status() -> None:
606+
"""Test that a partial AppInitStatus response is handled correctly."""
607+
app_init_status = AppInitStatus.from_dict(
608+
{
609+
"local_info": {
610+
"name": "custom_A.03.0096_FCC",
611+
"bom": "A.03.0096",
612+
"location": "us",
613+
"language": "en",
614+
"wifiplan": "US",
615+
"timezone": "US/Pacific",
616+
"logserver": "awsusor0.fds.api.xiaomi.com",
617+
"featureset": 1,
618+
},
619+
"feature_info": [111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125],
620+
"new_feature_info": 10738169343,
621+
"status_info": {
622+
"state": 8,
623+
"battery": 100,
624+
"clean_time": 251,
625+
"clean_area": 3847500,
626+
"error_code": 0,
627+
"in_cleaning": 0,
628+
"in_returning": 0,
629+
"in_fresh_state": 1,
630+
"lab_status": 3,
631+
"water_box_status": 0,
632+
"map_status": 7,
633+
"is_locating": 0,
634+
"lock_status": 0,
635+
"water_box_mode": 203,
636+
"distance_off": 0,
637+
"water_box_carriage_status": 0,
638+
"mop_forbidden_enable": 0,
639+
"camera_status": 3495,
640+
"is_exploring": 0,
641+
"home_sec_status": 0,
642+
"home_sec_enable_password": 1,
643+
"adbumper_status": [0, 0, 0],
644+
},
645+
}
646+
)
647+
assert app_init_status.local_info.name == "custom_A.03.0096_FCC"
648+
assert app_init_status.feature_info == [111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125]

tests/test_supported_features.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,17 @@ def test_device_feature_serialization(snapshot: SnapshotAssertion) -> None:
5959
serialized = device_features.as_dict()
6060
deserialized = DeviceFeatures.from_dict(serialized)
6161
assert deserialized == device_features
62+
63+
64+
def test_new_feature_str_missing():
65+
"""Ensure that DeviceFeatures missing fields can still be created."""
66+
device_features = DeviceFeatures.from_feature_flags(
67+
new_feature_info=0,
68+
new_feature_info_str="",
69+
feature_info=[],
70+
product_nickname=None,
71+
)
72+
# Check arbitrary fields that are false by default
73+
assert not device_features.is_dust_collection_setting_supported
74+
assert not device_features.is_hot_wash_towel_supported
75+
assert not device_features.is_show_clean_finish_reason_supported

0 commit comments

Comments
 (0)