Skip to content

Commit 8643a57

Browse files
authored
Roborock: various improvements to exposed sensors (#1914)
* Add `clean_percent` * Expose `options`, `device_class`, `suggested_display_precision` on specific sensors for better homeassistant integration * Also, add q revo to the list of devices that have a mop
1 parent 8230bd4 commit 8643a57

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

miio/integrations/roborock/vacuum/vacuum.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@
127127
ROCKROBO_Q_REVO,
128128
]
129129

130+
MODELS_WITH_MOP = [ROCKROBO_S7, ROCKROBO_S7_MAXV, ROCKROBO_Q_REVO]
131+
130132

131133
class RoborockVacuum(Device):
132134
"""Main class for roborock vacuums (roborock.vacuum.*)."""
@@ -981,7 +983,7 @@ def set_mop_mode(self, mop_mode: MopMode):
981983
@command()
982984
def mop_intensity(self) -> MopIntensity:
983985
"""Get mop scrub intensity setting."""
984-
if self.model not in [ROCKROBO_S7, ROCKROBO_S7_MAXV]:
986+
if self.model not in MODELS_WITH_MOP:
985987
raise UnsupportedFeatureException(
986988
"Mop scrub intensity not supported by %s", self.model
987989
)
@@ -991,7 +993,7 @@ def mop_intensity(self) -> MopIntensity:
991993
@command(click.argument("mop_intensity", type=EnumType(MopIntensity)))
992994
def set_mop_intensity(self, mop_intensity: MopIntensity):
993995
"""Set mop scrub intensity setting."""
994-
if self.model not in [ROCKROBO_S7, ROCKROBO_S7_MAXV]:
996+
if self.model not in MODELS_WITH_MOP:
995997
raise UnsupportedFeatureException(
996998
"Mop scrub intensity not supported by %s", self.model
997999
)

miio/integrations/roborock/vacuum/vacuumcontainers.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from datetime import datetime, time, timedelta
33
from enum import IntEnum
44
from typing import Any, Dict, List, Optional, Union
5+
from urllib import parse
56

67
from croniter import croniter
78
from pytz import BaseTzInfo
@@ -110,7 +111,7 @@ def __init__(self, data: Dict[str, Any]) -> None:
110111

111112
self._map_name_dict = {}
112113
for map in self.data["map_info"]:
113-
self._map_name_dict[map["name"]] = map["mapFlag"]
114+
self._map_name_dict[parse.unquote(map["name"])] = map["mapFlag"]
114115

115116
@property
116117
def map_count(self) -> int:
@@ -185,7 +186,12 @@ def state_code(self) -> int:
185186
return int(self.data["state"])
186187

187188
@property
188-
@sensor("State", entity_category="diagnostic")
189+
@sensor(
190+
"State",
191+
device_class="enum",
192+
entity_category="diagnostic",
193+
options=list(STATE_CODE_TO_STRING.values()),
194+
)
189195
def state(self) -> str:
190196
"""Human readable state description, see also :func:`state_code`."""
191197
return STATE_CODE_TO_STRING.get(
@@ -198,6 +204,14 @@ def vacuum_state(self) -> VacuumState:
198204
"""Return vacuum state."""
199205
return STATE_CODE_TO_VACUUMSTATE.get(self.state_code, VacuumState.Unknown)
200206

207+
@property
208+
@sensor("Cleaning Progress", icon="mdi:progress-check", unit="%")
209+
def clean_percent(self) -> Optional[int]:
210+
"""Return progress of the current clean."""
211+
if "clean_percent" in self.data:
212+
return int(self.data["clean_percent"])
213+
return None
214+
201215
@property
202216
@sensor(
203217
"Error code",
@@ -214,6 +228,8 @@ def error_code(self) -> int:
214228
"Error string",
215229
id=VacuumId.ErrorMessage,
216230
icon="mdi:alert",
231+
device_class="enum",
232+
options=list(ERROR_CODES.values()),
217233
entity_category="diagnostic",
218234
enabled_default=False,
219235
)
@@ -241,6 +257,8 @@ def dock_error_code(self) -> Optional[int]:
241257
@sensor(
242258
"Dock error string",
243259
icon="mdi:alert",
260+
device_class="enum",
261+
options=list(dock_error_codes.values()),
244262
entity_category="diagnostic",
245263
enabled_default=False,
246264
)
@@ -254,14 +272,14 @@ def dock_error(self) -> Optional[str]:
254272
return "Definition missing for dock error %s" % self.dock_error_code
255273

256274
@property
257-
@sensor("Battery", unit="%", id=VacuumId.Battery)
275+
@sensor("Battery", unit="%", device_class="battery", id=VacuumId.Battery)
258276
def battery(self) -> int:
259277
"""Remaining battery in percentage."""
260278
return int(self.data["battery"])
261279

262280
@property
263281
@setting(
264-
"Fanspeed",
282+
"Fan speed",
265283
unit="%",
266284
setter_name="set_fan_speed",
267285
min_value=0,
@@ -326,7 +344,12 @@ def clean_time(self) -> timedelta:
326344
return pretty_seconds(self.data["clean_time"])
327345

328346
@property
329-
@sensor("Current clean area", unit="m²", icon="mdi:texture-box")
347+
@sensor(
348+
"Current clean area",
349+
unit="m²",
350+
icon="mdi:texture-box",
351+
suggested_display_precision=2,
352+
)
330353
def clean_area(self) -> float:
331354
"""Cleaned area in m2."""
332355
return pretty_area(self.data["clean_area"])
@@ -397,7 +420,7 @@ def is_water_box_carriage_attached(self) -> Optional[bool]:
397420
return None
398421

399422
@property
400-
@sensor("Water level low", icon="mdi:water-alert-outline")
423+
@sensor("Water level low", device_class="problem", icon="mdi:water-alert-outline")
401424
def is_water_shortage(self) -> Optional[bool]:
402425
"""Returns True if water is low in the tank, None if sensor not present."""
403426
if "water_shortage_status" in self.data:
@@ -420,7 +443,10 @@ def auto_dust_collection(self) -> Optional[bool]:
420443

421444
@property
422445
@sensor(
423-
"Error", icon="mdi:alert", entity_category="diagnostic", enabled_default=False
446+
"Error",
447+
entity_category="diagnostic",
448+
device_class="problem",
449+
enabled_default=False,
424450
)
425451
def got_error(self) -> bool:
426452
"""True if an error has occurred."""
@@ -432,6 +458,7 @@ def got_error(self) -> bool:
432458
icon="mdi:tumble-dryer",
433459
entity_category="diagnostic",
434460
enabled_default=False,
461+
device_class="heat",
435462
)
436463
def is_mop_drying(self) -> Optional[bool]:
437464
"""Return if mop drying is running."""
@@ -444,6 +471,7 @@ def is_mop_drying(self) -> Optional[bool]:
444471
"Dryer remaining seconds",
445472
unit="s",
446473
entity_category="diagnostic",
474+
device_class="duration",
447475
enabled_default=False,
448476
)
449477
def mop_dryer_remaining_seconds(self) -> Optional[timedelta]:
@@ -496,6 +524,7 @@ def total_duration(self) -> timedelta:
496524
unit="m²",
497525
icon="mdi:texture-box",
498526
entity_category="diagnostic",
527+
suggested_display_precision=2,
499528
)
500529
def total_area(self) -> float:
501530
"""Total cleaned area."""
@@ -592,6 +621,7 @@ def duration(self) -> timedelta:
592621
unit="m²",
593622
icon="mdi:texture-box",
594623
entity_category="diagnostic",
624+
suggested_display_precision=2,
595625
)
596626
def area(self) -> float:
597627
"""Total cleaned area."""
@@ -792,15 +822,15 @@ def __init__(self, data: Dict[str, Any]):
792822
self.data = data
793823

794824
@property
795-
@sensor("Do not disturb", icon="mdi:minus-circle-off", entity_category="diagnostic")
825+
@sensor("Do not disturb", icon="mdi:bell-cancel", entity_category="diagnostic")
796826
def enabled(self) -> bool:
797827
"""True if DnD is enabled."""
798828
return bool(self.data["enabled"])
799829

800830
@property
801831
@sensor(
802832
"Do not disturb start",
803-
icon="mdi:minus-circle-off",
833+
icon="mdi:bell-cancel",
804834
device_class="timestamp",
805835
entity_category="diagnostic",
806836
enabled_default=False,
@@ -812,7 +842,7 @@ def start(self) -> time:
812842
@property
813843
@sensor(
814844
"Do not disturb end",
815-
icon="mdi:minus-circle-off",
845+
icon="mdi:bell-ring",
816846
device_class="timestamp",
817847
entity_category="diagnostic",
818848
enabled_default=False,

0 commit comments

Comments
 (0)