Skip to content

Commit

Permalink
Roborock: various improvements to exposed sensors (#1914)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
SLaks authored Mar 20, 2024
1 parent 8230bd4 commit 8643a57
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 12 deletions.
6 changes: 4 additions & 2 deletions miio/integrations/roborock/vacuum/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@
ROCKROBO_Q_REVO,
]

MODELS_WITH_MOP = [ROCKROBO_S7, ROCKROBO_S7_MAXV, ROCKROBO_Q_REVO]


class RoborockVacuum(Device):
"""Main class for roborock vacuums (roborock.vacuum.*)."""
Expand Down Expand Up @@ -981,7 +983,7 @@ def set_mop_mode(self, mop_mode: MopMode):
@command()
def mop_intensity(self) -> MopIntensity:
"""Get mop scrub intensity setting."""
if self.model not in [ROCKROBO_S7, ROCKROBO_S7_MAXV]:
if self.model not in MODELS_WITH_MOP:
raise UnsupportedFeatureException(
"Mop scrub intensity not supported by %s", self.model
)
Expand All @@ -991,7 +993,7 @@ def mop_intensity(self) -> MopIntensity:
@command(click.argument("mop_intensity", type=EnumType(MopIntensity)))
def set_mop_intensity(self, mop_intensity: MopIntensity):
"""Set mop scrub intensity setting."""
if self.model not in [ROCKROBO_S7, ROCKROBO_S7_MAXV]:
if self.model not in MODELS_WITH_MOP:
raise UnsupportedFeatureException(
"Mop scrub intensity not supported by %s", self.model
)
Expand Down
50 changes: 40 additions & 10 deletions miio/integrations/roborock/vacuum/vacuumcontainers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime, time, timedelta
from enum import IntEnum
from typing import Any, Dict, List, Optional, Union
from urllib import parse

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

self._map_name_dict = {}
for map in self.data["map_info"]:
self._map_name_dict[map["name"]] = map["mapFlag"]
self._map_name_dict[parse.unquote(map["name"])] = map["mapFlag"]

@property
def map_count(self) -> int:
Expand Down Expand Up @@ -185,7 +186,12 @@ def state_code(self) -> int:
return int(self.data["state"])

@property
@sensor("State", entity_category="diagnostic")
@sensor(
"State",
device_class="enum",
entity_category="diagnostic",
options=list(STATE_CODE_TO_STRING.values()),
)
def state(self) -> str:
"""Human readable state description, see also :func:`state_code`."""
return STATE_CODE_TO_STRING.get(
Expand All @@ -198,6 +204,14 @@ def vacuum_state(self) -> VacuumState:
"""Return vacuum state."""
return STATE_CODE_TO_VACUUMSTATE.get(self.state_code, VacuumState.Unknown)

@property
@sensor("Cleaning Progress", icon="mdi:progress-check", unit="%")
def clean_percent(self) -> Optional[int]:
"""Return progress of the current clean."""
if "clean_percent" in self.data:
return int(self.data["clean_percent"])
return None

@property
@sensor(
"Error code",
Expand All @@ -214,6 +228,8 @@ def error_code(self) -> int:
"Error string",
id=VacuumId.ErrorMessage,
icon="mdi:alert",
device_class="enum",
options=list(ERROR_CODES.values()),
entity_category="diagnostic",
enabled_default=False,
)
Expand Down Expand Up @@ -241,6 +257,8 @@ def dock_error_code(self) -> Optional[int]:
@sensor(
"Dock error string",
icon="mdi:alert",
device_class="enum",
options=list(dock_error_codes.values()),
entity_category="diagnostic",
enabled_default=False,
)
Expand All @@ -254,14 +272,14 @@ def dock_error(self) -> Optional[str]:
return "Definition missing for dock error %s" % self.dock_error_code

@property
@sensor("Battery", unit="%", id=VacuumId.Battery)
@sensor("Battery", unit="%", device_class="battery", id=VacuumId.Battery)
def battery(self) -> int:
"""Remaining battery in percentage."""
return int(self.data["battery"])

@property
@setting(
"Fanspeed",
"Fan speed",
unit="%",
setter_name="set_fan_speed",
min_value=0,
Expand Down Expand Up @@ -326,7 +344,12 @@ def clean_time(self) -> timedelta:
return pretty_seconds(self.data["clean_time"])

@property
@sensor("Current clean area", unit="m²", icon="mdi:texture-box")
@sensor(
"Current clean area",
unit="m²",
icon="mdi:texture-box",
suggested_display_precision=2,
)
def clean_area(self) -> float:
"""Cleaned area in m2."""
return pretty_area(self.data["clean_area"])
Expand Down Expand Up @@ -397,7 +420,7 @@ def is_water_box_carriage_attached(self) -> Optional[bool]:
return None

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

@property
@sensor(
"Error", icon="mdi:alert", entity_category="diagnostic", enabled_default=False
"Error",
entity_category="diagnostic",
device_class="problem",
enabled_default=False,
)
def got_error(self) -> bool:
"""True if an error has occurred."""
Expand All @@ -432,6 +458,7 @@ def got_error(self) -> bool:
icon="mdi:tumble-dryer",
entity_category="diagnostic",
enabled_default=False,
device_class="heat",
)
def is_mop_drying(self) -> Optional[bool]:
"""Return if mop drying is running."""
Expand All @@ -444,6 +471,7 @@ def is_mop_drying(self) -> Optional[bool]:
"Dryer remaining seconds",
unit="s",
entity_category="diagnostic",
device_class="duration",
enabled_default=False,
)
def mop_dryer_remaining_seconds(self) -> Optional[timedelta]:
Expand Down Expand Up @@ -496,6 +524,7 @@ def total_duration(self) -> timedelta:
unit="m²",
icon="mdi:texture-box",
entity_category="diagnostic",
suggested_display_precision=2,
)
def total_area(self) -> float:
"""Total cleaned area."""
Expand Down Expand Up @@ -592,6 +621,7 @@ def duration(self) -> timedelta:
unit="m²",
icon="mdi:texture-box",
entity_category="diagnostic",
suggested_display_precision=2,
)
def area(self) -> float:
"""Total cleaned area."""
Expand Down Expand Up @@ -792,15 +822,15 @@ def __init__(self, data: Dict[str, Any]):
self.data = data

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

@property
@sensor(
"Do not disturb start",
icon="mdi:minus-circle-off",
icon="mdi:bell-cancel",
device_class="timestamp",
entity_category="diagnostic",
enabled_default=False,
Expand All @@ -812,7 +842,7 @@ def start(self) -> time:
@property
@sensor(
"Do not disturb end",
icon="mdi:minus-circle-off",
icon="mdi:bell-ring",
device_class="timestamp",
entity_category="diagnostic",
enabled_default=False,
Expand Down

0 comments on commit 8643a57

Please sign in to comment.