22
33import typing
44from enum import StrEnum
5+ from typing import TypeVar
56
67from ...exceptions import RoborockUnsupportedFeature
78from ..code_mappings import RoborockModeEnum
@@ -70,7 +71,16 @@ class WashTowelModes(RoborockModeEnum):
7071 SUPER_DEEP = ("super_deep" , 8 )
7172
7273
73- class CleaningModes (StrEnum ):
74+ class CleaningMode (StrEnum ):
75+ """High-level cleaning intent derived from the lower-level motor settings.
76+
77+ Prefer this abstraction when you want to present or switch between the
78+ user-facing cleaning behaviors exposed by the app. The lower-level
79+ `VacuumModes`, `WaterModes`, and `CleanRoutes` enums are still useful for
80+ advanced tuning, but most integrations should treat them as implementation
81+ details of a single high-level cleaning mode.
82+ """
83+
7484 VACUUM = "vacuum"
7585 VAC_AND_MOP = "vac_and_mop"
7686 MOP = "mop"
@@ -88,6 +98,8 @@ class CleaningModes(StrEnum):
8898 250 : WaterModes .PURE_WATER_FLOW_END ,
8999}
90100
101+ ModeEnumT = TypeVar ("ModeEnumT" , bound = RoborockModeEnum )
102+
91103
92104def get_wash_towel_modes (features : DeviceFeatures ) -> list [WashTowelModes ]:
93105 """Get the valid wash towel modes for the device"""
@@ -184,18 +196,25 @@ def get_water_mode_mapping(features: DeviceFeatures) -> dict[int, str]:
184196 return {mode .code : mode .value for mode in get_water_modes (features )}
185197
186198
187- def get_cleaning_mode_options (features : DeviceFeatures ) -> list [CleaningModes ]:
188- """Get the supported high-level cleaning modes for the device."""
199+ def get_cleaning_mode_options (features : DeviceFeatures ) -> list [CleaningMode ]:
200+ """Return the supported high-level cleaning modes for the device.
201+
202+ These options are the preferred user-facing choices because they bundle the
203+ correct fan, water, and mop-route settings together for the device. Callers
204+ should generally present these instead of mixing lower-level mode enums
205+ unless they explicitly need fine-grained control.
206+ """
189207 if not features .is_support_water_mode :
190208 return []
191209
192- options = [CleaningModes .VACUUM , CleaningModes .VAC_AND_MOP ]
210+ supported_water_modes = get_water_modes (features )
211+ options = [CleaningMode .VACUUM , CleaningMode .VAC_AND_MOP ]
193212 if features .is_pure_clean_mop_supported :
194- options .append (CleaningModes .MOP )
195- if features .is_customized_clean_supported :
196- options .append (CleaningModes .CUSTOM )
197- if features .is_smart_clean_mode_set_supported :
198- options .append (CleaningModes .SMART_MODE )
213+ options .append (CleaningMode .MOP )
214+ if features .is_customized_clean_supported and WaterModes . CUSTOMIZED in supported_water_modes :
215+ options .append (CleaningMode .CUSTOM )
216+ if features .is_smart_clean_mode_set_supported and WaterModes . SMART_MODE in supported_water_modes :
217+ options .append (CleaningMode .SMART_MODE )
199218 return options
200219
201220
@@ -207,75 +226,107 @@ def get_mop_only_vacuum_mode(features: DeviceFeatures) -> VacuumModes:
207226 return VacuumModes .OFF
208227
209228
210- def _get_default_mopping_water_code (features : DeviceFeatures ) -> int :
211- """Pick a sensible default water code when mopping for the device."""
229+ def _get_default_mopping_water_mode (features : DeviceFeatures ) -> WaterModes :
230+ """Pick a sensible default water mode when mopping for the device."""
212231 # Water-slide devices use a disjoint set of water codes; pick a mid-flow
213232 # slide code instead of the standard 202, which they don't accept.
214233 if features .is_water_slide_mode_supported :
215- return WaterModes .PURE_WATER_FLOW_MIDDLE . code
216- return WaterModes .STANDARD . code
234+ return WaterModes .PURE_WATER_FLOW_MIDDLE
235+ return WaterModes .STANDARD
217236
218237
219- def _get_clean_motor_mode_params (mode : CleaningModes , features : DeviceFeatures ) -> tuple [int , int , int ]:
220- """Return (fan_power, water_box_mode, mop_mode) codes for the high-level mode."""
221- if mode == CleaningModes .VACUUM :
222- return (VacuumModes .BALANCED .code , WaterModes .OFF .code , CleanRoutes .STANDARD .code )
223- if mode == CleaningModes .VAC_AND_MOP :
224- return (VacuumModes .BALANCED .code , _get_default_mopping_water_code (features ), CleanRoutes .STANDARD .code )
225- if mode == CleaningModes .MOP :
238+ def _get_clean_motor_mode_params (
239+ mode : CleaningMode ,
240+ features : DeviceFeatures ,
241+ ) -> tuple [VacuumModes , WaterModes , CleanRoutes ]:
242+ """Return (fan_power, water_box_mode, mop_mode) enums for the high-level mode."""
243+ if mode == CleaningMode .VACUUM :
244+ return (VacuumModes .BALANCED , WaterModes .OFF , CleanRoutes .STANDARD )
245+ if mode == CleaningMode .VAC_AND_MOP :
246+ return (VacuumModes .BALANCED , _get_default_mopping_water_mode (features ), CleanRoutes .STANDARD )
247+ if mode == CleaningMode .MOP :
226248 return (
227- get_mop_only_vacuum_mode (features ). code ,
228- _get_default_mopping_water_code (features ),
229- CleanRoutes .STANDARD . code ,
249+ get_mop_only_vacuum_mode (features ),
250+ _get_default_mopping_water_mode (features ),
251+ CleanRoutes .STANDARD ,
230252 )
231- if mode == CleaningModes .CUSTOM :
232- return (VacuumModes .CUSTOMIZED . code , WaterModes .CUSTOMIZED . code , CleanRoutes .CUSTOMIZED . code )
233- if mode == CleaningModes .SMART_MODE :
234- return (VacuumModes .SMART_MODE . code , WaterModes .SMART_MODE . code , CleanRoutes .SMART_MODE . code )
253+ if mode == CleaningMode .CUSTOM :
254+ return (VacuumModes .CUSTOMIZED , WaterModes .CUSTOMIZED , CleanRoutes .CUSTOMIZED )
255+ if mode == CleaningMode .SMART_MODE :
256+ return (VacuumModes .SMART_MODE , WaterModes .SMART_MODE , CleanRoutes .SMART_MODE )
235257 raise RoborockUnsupportedFeature (f"Cleaning mode { mode .value !r} is not supported" )
236258
237259
238- def get_cleaning_mode_parameters (cleaning_mode : str | CleaningModes , features : DeviceFeatures ) -> list [dict [str , int ]]:
239- """Get the RPC payload for switching the high-level cleaning mode."""
260+ def resolve_cleaning_mode (cleaning_mode : str | CleaningMode ) -> CleaningMode :
261+ """Resolve a string or enum into a CleaningMode value."""
262+ if isinstance (cleaning_mode , CleaningMode ):
263+ return cleaning_mode
240264 try :
241- mode = CleaningModes (cleaning_mode )
265+ return CleaningMode (cleaning_mode )
242266 except ValueError as err :
243267 raise RoborockUnsupportedFeature (f"Cleaning mode { cleaning_mode !r} is not supported" ) from err
244- if mode not in get_cleaning_mode_options (features ):
245- raise RoborockUnsupportedFeature (f"Cleaning mode { mode .value !r} is not supported" )
246268
247- fan_power , water_box_mode , mop_mode = _get_clean_motor_mode_params (mode , features )
248- params : dict [str , int ] = {"fan_power" : fan_power , "water_box_mode" : water_box_mode }
269+
270+ def get_cleaning_mode_parameters (cleaning_mode : CleaningMode , features : DeviceFeatures ) -> list [dict [str , int ]]:
271+ """Get the RPC payload for switching the high-level cleaning mode."""
272+ if cleaning_mode not in get_cleaning_mode_options (features ):
273+ raise RoborockUnsupportedFeature (f"Cleaning mode { cleaning_mode .value !r} is not supported" )
274+
275+ fan_power , water_box_mode , mop_mode = _get_clean_motor_mode_params (cleaning_mode , features )
276+ params : dict [str , int ] = {"fan_power" : fan_power .code , "water_box_mode" : water_box_mode .code }
249277 if features .is_clean_route_setting_supported :
250- params ["mop_mode" ] = mop_mode
278+ params ["mop_mode" ] = mop_mode . code
251279 return [params ]
252280
253281
282+ def _resolve_mode_code (value : int | ModeEnumT | None , mode_cls : type [ModeEnumT ]) -> ModeEnumT | None :
283+ """Resolve a raw code or enum into a RoborockModeEnum."""
284+ if value is None :
285+ return None
286+ if isinstance (value , mode_cls ):
287+ return value
288+ return mode_cls .from_code_optional (int (value ))
289+
290+
291+ def _resolve_clean_mode (value : int | VacuumModes | None , features : DeviceFeatures ) -> VacuumModes | None :
292+ """Resolve a vacuum mode code, accounting for feature-specific code aliases."""
293+ if value is None or isinstance (value , VacuumModes ):
294+ return value
295+ if value == VacuumModes .OFF .code :
296+ if features .is_pure_clean_mop_supported :
297+ return get_mop_only_vacuum_mode (features )
298+ return VacuumModes .GENTLE
299+ return VacuumModes .from_code_optional (value )
300+
301+
254302def get_current_cleaning_mode (
255- clean_mode : int | None ,
256- water_mode : int | None ,
257- mop_mode : int | None ,
303+ clean_mode : int | VacuumModes | None ,
304+ water_mode : int | WaterModes | None ,
305+ mop_mode : int | CleanRoutes | None ,
258306 features : DeviceFeatures ,
259- ) -> CleaningModes | None :
307+ ) -> CleaningMode | None :
260308 """Classify the current high-level cleaning mode from individual mode codes."""
261309 if not features .is_support_water_mode :
262310 return None
263- if clean_mode is None or water_mode is None :
311+ clean_mode_enum = _resolve_clean_mode (clean_mode , features )
312+ water_mode_enum = _resolve_mode_code (water_mode , WaterModes )
313+ mop_mode_enum = _resolve_mode_code (mop_mode , CleanRoutes )
314+ if clean_mode_enum is None or water_mode_enum is None :
264315 return None
265316
266- if is_smart_mode_set (water_mode , clean_mode , mop_mode ):
267- return CleaningModes .SMART_MODE
268- if is_mode_customized (clean_mode , water_mode , mop_mode ):
269- return CleaningModes .CUSTOM
270- if water_mode != WaterModes .OFF . code :
317+ if is_smart_mode_set (water_mode_enum , clean_mode_enum , mop_mode_enum ):
318+ return CleaningMode .SMART_MODE
319+ if is_mode_customized (clean_mode_enum , water_mode_enum , mop_mode_enum ):
320+ return CleaningMode .CUSTOM
321+ if water_mode_enum != WaterModes .OFF :
271322 try :
272- if clean_mode == get_mop_only_vacuum_mode (features ). code :
273- return CleaningModes .MOP
323+ if clean_mode_enum == get_mop_only_vacuum_mode (features ):
324+ return CleaningMode .MOP
274325 except RoborockUnsupportedFeature :
275326 pass
276- if water_mode == WaterModes .OFF . code :
277- return CleaningModes .VACUUM
278- return CleaningModes .VAC_AND_MOP
327+ if water_mode_enum == WaterModes .OFF :
328+ return CleaningMode .VACUUM
329+ return CleaningMode .VAC_AND_MOP
279330
280331
281332def is_mode_customized (
0 commit comments