Skip to content

Commit b23c358

Browse files
authored
feat: add a new type for supported features (#431)
1 parent 04a83e8 commit b23c358

File tree

1 file changed

+139
-1
lines changed

1 file changed

+139
-1
lines changed

roborock/device_features.py

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass, field, fields
4-
from enum import IntEnum
4+
from enum import IntEnum, StrEnum
55
from typing import Any
66

77
from roborock import RoborockProductNickname
@@ -59,6 +59,121 @@ class NewFeatureStrBit(IntEnum):
5959
EGG_DANCE_MODE = 87
6060

6161

62+
class ProductFeatures(StrEnum):
63+
REMOTE_BACK = "remote_back"
64+
CLEANMODE_MAXPLUS = "cleanmode_maxplus"
65+
CLEANMODE_PURECLEANMOP = "cleanmode_purecleanmop"
66+
CLEANMODE_NONE_PURECLEANMOP_WITH_MAXPLUS = "cleanmode_none_purecleanmop_with_maxplus"
67+
MOP_ELECTRONIC_MODULE = "mop_electronic_module"
68+
MOP_SHAKE_MODULE = "mop_shake_module"
69+
MOP_SPIN_MODULE = "mop_spin_module"
70+
DEFAULT_MAP3D = "map3d"
71+
DEFAULT_CLEANMODECUSTOM = "custom_cleanmode"
72+
REALTIMEVIDEO = "realtimevideo"
73+
REALTIMEVIDEO_LIVECALL = "realtimevideo_livecall"
74+
REALTIMEVIDEO_RECORDANDSHORTCUT = "realtimevideo_livecall"
75+
CAMERA_SINGLELINE = "camera_singleline"
76+
CAMERA_DUALLINE = "camera_dualline"
77+
CAMERA_RGB = "camera_rgb"
78+
CAMERA_DOUBLERGB = "camera_doublergb"
79+
AIRECOGNITION_SETTING = "airecognition_setting"
80+
AIRECOGNITION_SCENE = "airecognition_scene"
81+
AIRECOGNITION_PET = "airecognition_pet"
82+
AIRECOGNITION_OBSTACLE = "airecognition_obstacle"
83+
84+
85+
# The following combinations are pulled directly from decompiled source code.
86+
AIRECOGNITION_OBSTACLE = [ProductFeatures.AIRECOGNITION_OBSTACLE]
87+
RGB_CAMERA_FEATURES = [
88+
ProductFeatures.CAMERA_RGB,
89+
ProductFeatures.AIRECOGNITION_SETTING,
90+
ProductFeatures.AIRECOGNITION_SCENE,
91+
ProductFeatures.AIRECOGNITION_PET,
92+
ProductFeatures.AIRECOGNITION_OBSTACLE,
93+
ProductFeatures.REALTIMEVIDEO,
94+
ProductFeatures.REALTIMEVIDEO_LIVECALL,
95+
ProductFeatures.REALTIMEVIDEO_RECORDANDSHORTCUT,
96+
]
97+
DOUBLE_RGB_CAMERA_FEATURES = [
98+
ProductFeatures.CAMERA_DOUBLERGB,
99+
ProductFeatures.AIRECOGNITION_SETTING,
100+
ProductFeatures.AIRECOGNITION_PET,
101+
ProductFeatures.AIRECOGNITION_OBSTACLE,
102+
ProductFeatures.REALTIMEVIDEO,
103+
]
104+
SINGLE_LINE_CAMERA_FEATURES = [
105+
ProductFeatures.CAMERA_SINGLELINE,
106+
ProductFeatures.AIRECOGNITION_SETTING,
107+
ProductFeatures.AIRECOGNITION_OBSTACLE,
108+
]
109+
DUAL_LINE_CAMERA_FEATURES = [
110+
ProductFeatures.CAMERA_DUALLINE,
111+
ProductFeatures.AIRECOGNITION_SETTING,
112+
ProductFeatures.AIRECOGNITION_OBSTACLE,
113+
ProductFeatures.AIRECOGNITION_PET,
114+
]
115+
116+
NEW_DEFAULT_FEATURES = [ProductFeatures.REMOTE_BACK, ProductFeatures.CLEANMODE_MAXPLUS]
117+
118+
119+
PEARL_FEATURES = NEW_DEFAULT_FEATURES + SINGLE_LINE_CAMERA_FEATURES + [ProductFeatures.MOP_SPIN_MODULE]
120+
PEARL_PLUS_FEATURES = NEW_DEFAULT_FEATURES + RGB_CAMERA_FEATURES + [ProductFeatures.MOP_SPIN_MODULE]
121+
ULTRON_FEATURES = NEW_DEFAULT_FEATURES + DUAL_LINE_CAMERA_FEATURES + [ProductFeatures.MOP_SHAKE_MODULE]
122+
ULTRONSV_FEATURES = NEW_DEFAULT_FEATURES + RGB_CAMERA_FEATURES + [ProductFeatures.MOP_SHAKE_MODULE]
123+
TANOSS_FEATURES = NEW_DEFAULT_FEATURES + [ProductFeatures.MOP_SHAKE_MODULE]
124+
TOPAZSPOWER_FEATURES = NEW_DEFAULT_FEATURES + [ProductFeatures.MOP_SHAKE_MODULE]
125+
126+
product_feature_map = {
127+
RoborockProductNickname.PEARL: PEARL_FEATURES,
128+
RoborockProductNickname.PEARLS: PEARL_FEATURES,
129+
RoborockProductNickname.PEARLPLUS: PEARL_PLUS_FEATURES,
130+
RoborockProductNickname.VIVIAN: PEARL_PLUS_FEATURES,
131+
RoborockProductNickname.CORAL: PEARL_PLUS_FEATURES,
132+
RoborockProductNickname.ULTRON: ULTRON_FEATURES,
133+
RoborockProductNickname.ULTRONE: [ProductFeatures.CLEANMODE_NONE_PURECLEANMOP_WITH_MAXPLUS],
134+
RoborockProductNickname.ULTRONSV: ULTRONSV_FEATURES,
135+
RoborockProductNickname.TOPAZSPOWER: TOPAZSPOWER_FEATURES,
136+
RoborockProductNickname.TANOSS: TANOSS_FEATURES,
137+
RoborockProductNickname.PEARLC: PEARL_FEATURES,
138+
RoborockProductNickname.PEARLPLUSS: PEARL_PLUS_FEATURES,
139+
RoborockProductNickname.PEARLSLITE: PEARL_FEATURES,
140+
RoborockProductNickname.PEARLE: PEARL_FEATURES,
141+
RoborockProductNickname.PEARLELITE: PEARL_FEATURES,
142+
RoborockProductNickname.VIVIANC: PEARL_PLUS_FEATURES,
143+
RoborockProductNickname.CORALPRO: PEARL_PLUS_FEATURES,
144+
RoborockProductNickname.ULTRONLITE: SINGLE_LINE_CAMERA_FEATURES
145+
+ [ProductFeatures.CLEANMODE_NONE_PURECLEANMOP_WITH_MAXPLUS, ProductFeatures.MOP_ELECTRONIC_MODULE],
146+
RoborockProductNickname.ULTRONSC: ULTRON_FEATURES,
147+
RoborockProductNickname.ULTRONSE: [
148+
ProductFeatures.CLEANMODE_NONE_PURECLEANMOP_WITH_MAXPLUS,
149+
ProductFeatures.MOP_ELECTRONIC_MODULE,
150+
],
151+
RoborockProductNickname.ULTRONSPLUS: ULTRON_FEATURES,
152+
RoborockProductNickname.VERDELITE: ULTRONSV_FEATURES,
153+
RoborockProductNickname.TOPAZS: NEW_DEFAULT_FEATURES + [ProductFeatures.MOP_SHAKE_MODULE],
154+
RoborockProductNickname.TOPAZSPLUS: NEW_DEFAULT_FEATURES
155+
+ DUAL_LINE_CAMERA_FEATURES
156+
+ [ProductFeatures.MOP_SHAKE_MODULE],
157+
RoborockProductNickname.TOPAZSC: TOPAZSPOWER_FEATURES + SINGLE_LINE_CAMERA_FEATURES,
158+
RoborockProductNickname.TOPAZSV: NEW_DEFAULT_FEATURES + RGB_CAMERA_FEATURES + [ProductFeatures.MOP_SHAKE_MODULE],
159+
RoborockProductNickname.TANOSSPLUS: TANOSS_FEATURES + DUAL_LINE_CAMERA_FEATURES,
160+
RoborockProductNickname.TANOSSLITE: [ProductFeatures.MOP_ELECTRONIC_MODULE],
161+
RoborockProductNickname.TANOSSC: [],
162+
RoborockProductNickname.TANOSSE: [],
163+
RoborockProductNickname.TANOSSMAX: NEW_DEFAULT_FEATURES
164+
+ DUAL_LINE_CAMERA_FEATURES
165+
+ [ProductFeatures.MOP_SHAKE_MODULE],
166+
RoborockProductNickname.TANOS: [ProductFeatures.REMOTE_BACK],
167+
RoborockProductNickname.TANOSE: [ProductFeatures.MOP_ELECTRONIC_MODULE, ProductFeatures.REMOTE_BACK],
168+
RoborockProductNickname.TANOSV: DOUBLE_RGB_CAMERA_FEATURES
169+
+ [ProductFeatures.REMOTE_BACK, ProductFeatures.MOP_ELECTRONIC_MODULE],
170+
RoborockProductNickname.RUBYPLUS: [],
171+
RoborockProductNickname.RUBYSC: [],
172+
RoborockProductNickname.RUBYSE: [],
173+
RoborockProductNickname.RUBYSLITE: [ProductFeatures.MOP_ELECTRONIC_MODULE],
174+
}
175+
176+
62177
@dataclass
63178
class DeviceFeatures:
64179
"""Represents the features supported by a Roborock device."""
@@ -290,6 +405,24 @@ class DeviceFeatures:
290405
)
291406
is_support_mop_back_pwm_set: bool = field(metadata={"model_whitelist": [RoborockProductNickname.PEARL]})
292407
is_collect_dust_mode_supported: bool = field(metadata={"model_blacklist": [RoborockProductNickname.PEARL]})
408+
is_support_water_mode: bool = field(
409+
metadata={
410+
"product_features": [
411+
ProductFeatures.MOP_ELECTRONIC_MODULE,
412+
ProductFeatures.MOP_SHAKE_MODULE,
413+
ProductFeatures.MOP_SPIN_MODULE,
414+
]
415+
}
416+
)
417+
is_pure_clean_mop_supported: bool = field(metadata={"product_features": [ProductFeatures.CLEANMODE_PURECLEANMOP]})
418+
is_new_remote_view_supported: bool = field(metadata={"product_features": [ProductFeatures.REMOTE_BACK]})
419+
is_max_plus_mode_supported: bool = field(metadata={"product_features": [ProductFeatures.CLEANMODE_MAXPLUS]})
420+
is_none_pure_clean_mop_with_max_plus: bool = field(
421+
metadata={"product_features": [ProductFeatures.CLEANMODE_NONE_PURECLEANMOP_WITH_MAXPLUS]}
422+
)
423+
is_clean_route_setting_supported: bool = field(
424+
metadata={"product_features": [ProductFeatures.MOP_SHAKE_MODULE, ProductFeatures.MOP_SPIN_MODULE]}
425+
)
293426

294427
@classmethod
295428
def from_feature_flags(
@@ -357,6 +490,11 @@ def from_feature_flags(
357490
kwargs[f.name] = True
358491
else:
359492
kwargs[f.name] = product_nickname not in blacklist
493+
elif (product_features := f.metadata.get("product_features")) is not None:
494+
if product_nickname is not None:
495+
available_features = product_feature_map.get(product_nickname, [])
496+
if any(feat in available_features for feat in product_features): # type: ignore
497+
kwargs[f.name] = True
360498

361499
return cls(**kwargs)
362500

0 commit comments

Comments
 (0)