Skip to content

Commit 19d7674

Browse files
allenporterLash-LCopilot
authored
feat: Add from_any_optional method to RoborockModeEnum (#788)
* feat: Add `from_any_optional` method to `CodeMapping` for flexible enum resolution with corresponding tests. * refactor: simplify B01_Q10 command parsing by removing a helper function and utilizing `from_any_optional`. * chore: apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Luke Lashley <conway220@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 312b7ac commit 19d7674

File tree

3 files changed

+50
-16
lines changed

3 files changed

+50
-16
lines changed

roborock/cli.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -764,21 +764,6 @@ async def network_info(ctx, device_id: str):
764764
await _display_v1_trait(context, device_id, lambda v1: v1.network_info)
765765

766766

767-
def _parse_b01_q10_command(cmd: str) -> B01_Q10_DP:
768-
"""Parse B01_Q10 command from either enum name or value."""
769-
try:
770-
return B01_Q10_DP(int(cmd))
771-
except ValueError:
772-
try:
773-
return B01_Q10_DP.from_name(cmd)
774-
except ValueError:
775-
try:
776-
return B01_Q10_DP.from_value(cmd)
777-
except ValueError:
778-
pass
779-
raise RoborockException(f"Invalid command {cmd} for B01_Q10 device")
780-
781-
782767
@click.command()
783768
@click.option("--device_id", required=True)
784769
@click.option("--cmd", required=True)
@@ -795,7 +780,8 @@ async def command(ctx, cmd, device_id, params):
795780
if result:
796781
click.echo(dump_json(result))
797782
elif device.b01_q10_properties is not None:
798-
cmd_value = _parse_b01_q10_command(cmd)
783+
if cmd_value := B01_Q10_DP.from_any_optional(cmd) is None:
784+
raise RoborockException(f"Invalid command {cmd} for B01_Q10 device")
799785
command_trait: Trait = device.b01_q10_properties.command
800786
await command_trait.send(cmd_value, json.loads(params) if params is not None else None)
801787
click.echo("Command sent successfully; Enable debug logging (-d) to see responses.")

roborock/data/code_mappings.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,30 @@ def from_name(cls, name: str) -> Self:
100100
return member
101101
raise ValueError(f"{name} is not a valid name for {cls.__name__}")
102102

103+
@classmethod
104+
def from_any_optional(cls, value: str | int) -> Self | None:
105+
"""Resolve a string or int to an enum member.
106+
107+
Tries to look up by enum name, string value, or integer code
108+
and returns None if no match is found.
109+
"""
110+
# Try enum name lookup (e.g. "SEEK")
111+
try:
112+
return cls.from_name(str(value))
113+
except ValueError:
114+
pass
115+
# Try DP string value lookup (e.g. "dpSeek")
116+
try:
117+
return cls.from_value(str(value))
118+
except ValueError:
119+
pass
120+
# Try integer code lookup (e.g. "11")
121+
try:
122+
return cls.from_code(int(value))
123+
except (ValueError, TypeError):
124+
pass
125+
return None
126+
103127
@classmethod
104128
def keys(cls) -> list[str]:
105129
"""Returns a list of all member values."""

tests/data/test_code_mappings.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,30 @@ def test_invalid_from_value() -> None:
5252
B01_Q10_DP.from_value("invalid_value")
5353

5454

55+
@pytest.mark.parametrize(
56+
"input, expected",
57+
[
58+
("START_CLEAN", B01_Q10_DP.START_CLEAN),
59+
("start_clean", B01_Q10_DP.START_CLEAN),
60+
("dpStartClean", B01_Q10_DP.START_CLEAN),
61+
(201, B01_Q10_DP.START_CLEAN),
62+
("PAUSE", B01_Q10_DP.PAUSE),
63+
("pause", B01_Q10_DP.PAUSE),
64+
("dpPause", B01_Q10_DP.PAUSE),
65+
(204, B01_Q10_DP.PAUSE),
66+
("STOP", B01_Q10_DP.STOP),
67+
("stop", B01_Q10_DP.STOP),
68+
("dpStop", B01_Q10_DP.STOP),
69+
(206, B01_Q10_DP.STOP),
70+
("invalid_value", None),
71+
(999999, None),
72+
],
73+
)
74+
def test_from_any_optional(input: str | int, expected: B01_Q10_DP | None) -> None:
75+
"""Test from_any_optional method."""
76+
assert B01_Q10_DP.from_any_optional(input) == expected
77+
78+
5579
def test_homedata_product_unknown_category():
5680
"""Test that HomeDataProduct can handle unknown categories."""
5781
data = {

0 commit comments

Comments
 (0)