Skip to content

Commit 512ea2f

Browse files
committed
refactor: extract get_dock_state function and deprecate StatusV2.dock_state
1 parent 7537c3c commit 512ea2f

4 files changed

Lines changed: 103 additions & 57 deletions

File tree

‎roborock/data/v1/v1_containers.py‎

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datetime
22
import logging
3+
import warnings
34
from dataclasses import dataclass, field
45
from enum import StrEnum
56
from typing import Any
@@ -73,6 +74,39 @@ class StatusField(FieldNameBase):
7374
RDT = "rdt"
7475

7576

77+
def get_dock_state(
78+
state: RoborockStateCode | int | None,
79+
battery: int | None,
80+
charge_status: RoborockChargeStatus | int | None = None,
81+
*,
82+
is_supported_valley_electricity: bool = False,
83+
) -> RoborockDockState:
84+
"""Return the dock state, accounting for off-peak charging support."""
85+
if state is None or state == RoborockStateCode.unknown:
86+
return RoborockDockState.unknown
87+
88+
# 6. DUSTING
89+
if state == RoborockStateCode.emptying_the_bin:
90+
return RoborockDockState.dusting
91+
92+
# 5. FULL
93+
if state == RoborockStateCode.charging_complete or (state == RoborockStateCode.charging and battery == 100):
94+
return RoborockDockState.full
95+
96+
# 3 & 4. CHARGING and CHARGE_WAITING
97+
if state == RoborockStateCode.charging:
98+
if is_supported_valley_electricity and charge_status == RoborockChargeStatus.charge_waiting:
99+
return RoborockDockState.off_peak_waiting
100+
return RoborockDockState.charging
101+
102+
# 2. RECHARGING
103+
if state in (RoborockStateCode.returning_home, RoborockStateCode.docking):
104+
return RoborockDockState.returning
105+
106+
# 1. IDLE (Not on dock, or doing something else)
107+
return RoborockDockState.idle
108+
109+
76110
@dataclass
77111
class StatusV2(RoborockBase):
78112
"""The result of a GET_STATUS API request."""
@@ -219,40 +253,21 @@ def dock_cool_fan_status(self) -> int | None:
219253
def dock_state(self) -> RoborockDockState:
220254
"""A synthesized, high-level dock state reflecting the UI's display.
221255
222-
This property simplifies integration by handling the complex logic
223-
of checking state, charge_status, and battery level simultaneously. It handles
224-
newer off-peak charging logic seamlessly while maintaining backwards compatibility
225-
with older devices.
256+
.. deprecated::
257+
Use StatusTrait.dock_state or get_dock_state instead.
226258
"""
227-
return self.get_dock_state()
228-
229-
def get_dock_state(self, *, is_supported_valley_electricity: bool = True) -> RoborockDockState:
230-
"""Return the dock state, accounting for off-peak charging support."""
231-
if self.state is None or self.state == RoborockStateCode.unknown:
232-
return RoborockDockState.unknown
233-
234-
# 6. DUSTING
235-
if self.state == RoborockStateCode.emptying_the_bin:
236-
return RoborockDockState.dusting
237-
238-
# 5. FULL
239-
if self.state == RoborockStateCode.charging_complete or (
240-
self.state == RoborockStateCode.charging and self.battery == 100
241-
):
242-
return RoborockDockState.full
243-
244-
# 3 & 4. CHARGING and CHARGE_WAITING
245-
if self.state == RoborockStateCode.charging:
246-
if is_supported_valley_electricity and self.charge_status == RoborockChargeStatus.charge_waiting:
247-
return RoborockDockState.off_peak_waiting
248-
return RoborockDockState.charging
249-
250-
# 2. RECHARGING
251-
if self.state in (RoborockStateCode.returning_home, RoborockStateCode.docking):
252-
return RoborockDockState.returning
253-
254-
# 1. IDLE (Not on dock, or doing something else)
255-
return RoborockDockState.idle
259+
warnings.warn(
260+
"StatusV2.dock_state is deprecated and will be removed in a future release. "
261+
"Use StatusTrait.dock_state or get_dock_state instead.",
262+
DeprecationWarning,
263+
stacklevel=2,
264+
)
265+
return get_dock_state(
266+
state=self.state,
267+
battery=self.battery,
268+
charge_status=self.charge_status,
269+
is_supported_valley_electricity=True,
270+
)
256271

257272
@property
258273
def is_battery_charging(self) -> bool:

‎roborock/devices/traits/v1/status.py‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
get_cleaning_mode_options,
1515
get_cleaning_mode_parameters,
1616
get_current_cleaning_mode,
17+
get_dock_state,
1718
get_water_mode_mapping,
1819
get_water_modes,
1920
get_water_slide_mode_name,
@@ -70,10 +71,18 @@ def __init__(self, device_feature_trait: DeviceFeaturesTrait, region: str | None
7071
@property
7172
def dock_state(self) -> RoborockDockState:
7273
"""Return the feature-aware dock state used by the Roborock app."""
73-
return self.get_dock_state(
74-
is_supported_valley_electricity=self._device_features_trait.is_supported_valley_electricity
74+
return get_dock_state(
75+
state=self.state,
76+
battery=self.battery,
77+
charge_status=self.charge_status,
78+
is_supported_valley_electricity=self._device_features_trait.is_supported_valley_electricity,
7579
)
7680

81+
@property
82+
def is_battery_charging(self) -> bool:
83+
"""Return whether the battery is actively charging."""
84+
return self.dock_state == RoborockDockState.charging
85+
7786
@cached_property
7887
def fan_speed_options(self) -> list[VacuumModes]:
7988
return get_clean_modes(self._device_features_trait)

‎tests/data/v1/test_v1_containers.py‎

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Consumable,
2424
DnDTimer,
2525
StatusV2,
26+
get_dock_state,
2627
)
2728
from tests.mock_data import (
2829
CLEAN_RECORD,
@@ -141,36 +142,54 @@ def test_current_map() -> None:
141142

142143

143144
@pytest.mark.parametrize(
144-
"state, charge_status, battery, expected_dock_state",
145+
"state, charge_status, battery, is_supported_valley_electricity, expected_dock_state",
145146
[
146-
(RoborockStateCode.emptying_the_bin, None, 50, RoborockDockState.dusting),
147-
(RoborockStateCode.charging_complete, None, 100, RoborockDockState.full),
148-
(RoborockStateCode.charging, None, 100, RoborockDockState.full),
149-
(RoborockStateCode.charging, RoborockChargeStatus.charging.value, 90, RoborockDockState.charging),
150-
(RoborockStateCode.charging, RoborockChargeStatus.charge_waiting.value, 50, RoborockDockState.off_peak_waiting),
151-
(RoborockStateCode.charging, None, 50, RoborockDockState.charging),
152-
(RoborockStateCode.returning_home, None, 20, RoborockDockState.returning),
153-
(RoborockStateCode.docking, None, 15, RoborockDockState.returning),
154-
(RoborockStateCode.cleaning, None, 80, RoborockDockState.idle),
155-
(RoborockStateCode.paused, None, 80, RoborockDockState.idle),
156-
(RoborockStateCode.unknown, None, 100, RoborockDockState.unknown),
157-
(None, None, 100, RoborockDockState.unknown),
147+
(RoborockStateCode.emptying_the_bin, None, 50, False, RoborockDockState.dusting),
148+
(RoborockStateCode.charging_complete, None, 100, False, RoborockDockState.full),
149+
(RoborockStateCode.charging, None, 100, False, RoborockDockState.full),
150+
(RoborockStateCode.charging, RoborockChargeStatus.charging.value, 90, False, RoborockDockState.charging),
151+
(
152+
RoborockStateCode.charging,
153+
RoborockChargeStatus.charge_waiting.value,
154+
50,
155+
True,
156+
RoborockDockState.off_peak_waiting,
157+
),
158+
(RoborockStateCode.charging, RoborockChargeStatus.charge_waiting.value, 50, False, RoborockDockState.charging),
159+
(RoborockStateCode.charging, None, 50, False, RoborockDockState.charging),
160+
(RoborockStateCode.returning_home, None, 20, False, RoborockDockState.returning),
161+
(RoborockStateCode.docking, None, 15, False, RoborockDockState.returning),
162+
(RoborockStateCode.cleaning, None, 80, False, RoborockDockState.idle),
163+
(RoborockStateCode.paused, None, 80, False, RoborockDockState.idle),
164+
(RoborockStateCode.unknown, None, 100, False, RoborockDockState.unknown),
165+
(None, None, 100, False, RoborockDockState.unknown),
158166
],
159167
)
160-
def test_dock_state(
168+
def test_get_dock_state(
161169
state: RoborockStateCode | None,
162170
charge_status: int | None,
163171
battery: int,
172+
is_supported_valley_electricity: bool,
164173
expected_dock_state: RoborockDockState,
165174
) -> None:
166-
"""Test that dock_state correctly synthesizes UI state."""
167-
status = copy.deepcopy(STATUS)
168-
status["state"] = state
169-
status["charge_status"] = charge_status
170-
status["battery"] = battery
175+
"""Test that get_dock_state correctly synthesizes UI state."""
176+
assert (
177+
get_dock_state(
178+
state=state,
179+
battery=battery,
180+
charge_status=charge_status,
181+
is_supported_valley_electricity=is_supported_valley_electricity,
182+
)
183+
== expected_dock_state
184+
)
171185

186+
187+
def test_status_v2_dock_state_deprecated() -> None:
188+
"""Test that accessing StatusV2.dock_state issues a DeprecationWarning."""
189+
status = copy.deepcopy(STATUS)
172190
s = StatusV2.from_dict(status)
173-
assert s.dock_state == expected_dock_state
191+
with pytest.deprecated_call():
192+
assert s.dock_state == RoborockDockState.full
174193

175194

176195
def test_status_v2() -> None:

‎tests/devices/traits/v1/test_status.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests for the StatusTrait class."""
22

33
import asyncio
4+
import warnings
45
from typing import cast
56
from unittest.mock import AsyncMock, call
67

@@ -208,8 +209,10 @@ def test_feature_aware_dock_and_charging_state(
208209
status_trait.charge_status = charge_status
209210
status_trait.battery = battery
210211

211-
assert status_trait.dock_state == expected_dock_state
212-
assert status_trait.is_battery_charging is expected_is_battery_charging
212+
with warnings.catch_warnings():
213+
warnings.simplefilter("error", DeprecationWarning)
214+
assert status_trait.dock_state == expected_dock_state
215+
assert status_trait.is_battery_charging is expected_is_battery_charging
213216

214217

215218
def test_options(status_trait: StatusTrait) -> None:

0 commit comments

Comments
 (0)