Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional sensors and settings to Roborock vacuums #1543

Closed
wants to merge 67 commits into from
Closed
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
c808adf
Remove Fanspeed
starkillerOG Sep 30, 2022
fc8c7b4
Fix unit
starkillerOG Sep 30, 2022
a63270a
capital letters
starkillerOG Sep 30, 2022
b040a7c
disable battery by default
starkillerOG Sep 30, 2022
bc09260
Add sensors
starkillerOG Sep 30, 2022
c766026
fix typo
starkillerOG Sep 30, 2022
383de8b
adjust sensors
starkillerOG Sep 30, 2022
92dae6e
update icons
starkillerOG Sep 30, 2022
444f8ed
Add last_clean_details
starkillerOG Sep 30, 2022
2c3b4b3
Add DnD status
starkillerOG Sep 30, 2022
f7cbb95
use qualified_name for id
starkillerOG Sep 30, 2022
43e7a3e
add multi map id
starkillerOG Sep 30, 2022
33faf82
Update vacuumcontainers.py
starkillerOG Sep 30, 2022
c32b24a
Add mullti_map_id to clean history
starkillerOG Sep 30, 2022
7488ea9
fix type
starkillerOG Sep 30, 2022
32d0028
Add retrieving multi_maps_list
starkillerOG Oct 1, 2022
73fa561
add dust_collection_work_times
starkillerOG Oct 1, 2022
60e6d70
add load_multi_map
starkillerOG Oct 1, 2022
d995d26
add Multi map selector
starkillerOG Oct 1, 2022
a4e6dc4
fix typo
starkillerOG Oct 1, 2022
5cc7664
make changing multi map work in homeassistant
starkillerOG Oct 1, 2022
b3ecd75
Add Mop scrub intensity and mop route
starkillerOG Oct 1, 2022
a164846
fix circiler import
starkillerOG Oct 1, 2022
0c0a617
update icons
starkillerOG Oct 1, 2022
68a4fb4
Add auto_dust_collection
starkillerOG Oct 1, 2022
00dfae9
Last clean per floor
starkillerOG Oct 2, 2022
4d9ca0e
fixes
starkillerOG Oct 2, 2022
6c8bb0e
add entity_categories
starkillerOG Oct 2, 2022
e490cd4
add device_classes
starkillerOG Oct 2, 2022
298a485
add state_class
starkillerOG Oct 2, 2022
5fb7f9e
fix styling
starkillerOG Oct 3, 2022
f80a13e
docformatter
starkillerOG Oct 3, 2022
39d9781
docformatter correct lenght
starkillerOG Oct 3, 2022
fd49e94
fix mypy
starkillerOG Oct 3, 2022
142dfb0
fix flake8
starkillerOG Oct 3, 2022
4485c79
fix python compatibility
starkillerOG Oct 3, 2022
6f21e31
fix vacuum test
starkillerOG Oct 3, 2022
8c3eae4
fix tests
starkillerOG Oct 3, 2022
1b009d5
Change new fan mode Mopping to Off
starkillerOG Oct 7, 2022
bef9197
Merge branch 'master' into roborock_sensors
starkillerOG Oct 7, 2022
a86d7cc
Reduce device calls by caching the status
starkillerOG Oct 7, 2022
cd86f24
split out vacuum_status
starkillerOG Oct 7, 2022
46c2b80
simplify return
starkillerOG Oct 7, 2022
f53edc3
notes
starkillerOG Oct 7, 2022
28f3340
return FloorCleanDetails from last_clean_all_floor
starkillerOG Oct 8, 2022
65037f9
Use MultiMapList container
starkillerOG Oct 8, 2022
edc42d4
dynamically create FloorCleanDetail settings
starkillerOG Oct 8, 2022
6068336
fix __repr__
starkillerOG Oct 8, 2022
e47a9de
Embed CleanDetails for each floor in FloorCleanDetails container
starkillerOG Oct 8, 2022
cdc140e
Add button support
starkillerOG Oct 8, 2022
c051028
add start_dust_collection button
starkillerOG Oct 8, 2022
6ad0e45
Add stop dust collection button
starkillerOG Oct 8, 2022
14a7d6e
bind button method
starkillerOG Oct 8, 2022
2f5f3b3
fix
starkillerOG Oct 8, 2022
ee90954
fix styling
starkillerOG Oct 8, 2022
758e185
fix imports
starkillerOG Oct 8, 2022
252c1b6
fix mypy issues
starkillerOG Oct 8, 2022
de9dac9
fix styling
starkillerOG Oct 8, 2022
180dd5c
Add dock_error and dock_error_code
starkillerOG Nov 9, 2022
cc7d4ea
Merge branch 'master' into roborock_sensors
starkillerOG Nov 9, 2022
83e7e90
Update to latest miio
starkillerOG Nov 9, 2022
ac58bf3
Update to latest miio
starkillerOG Nov 9, 2022
f2fed51
Merge branch 'master' into roborock_sensors
starkillerOG Nov 9, 2022
1c69cc5
Merge branch 'master' into roborock_sensors
starkillerOG Nov 14, 2022
f1f3a62
Merge branch 'master' into roborock_sensors
starkillerOG Nov 14, 2022
998ec16
Merge branch 'master' into roborock_sensors
starkillerOG Nov 15, 2022
a5a0e3c
Merge branch 'master' into roborock_sensors
starkillerOG Dec 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions miio/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from .click_common import DeviceGroupMeta, LiteralParamType, command, format_output
from .descriptors import (
ButtonDescriptor,
EnumSettingDescriptor,
NumberSettingDescriptor,
SensorDescriptor,
SettingDescriptor,
SwitchDescriptor,
)
from .deviceinfo import DeviceInfo
Expand Down Expand Up @@ -242,9 +243,13 @@ def buttons(self) -> List[ButtonDescriptor]:
"""Return a list of button-like, clickable actions of the device."""
return []

def settings(self) -> Dict[str, SettingDescriptor]:
def settings(
self,
) -> Dict[str, Union[EnumSettingDescriptor, NumberSettingDescriptor]]:
"""Return list of settings."""
settings = self.status().settings()
settings = (
self.status().settings()
) # NOTE that this already does IO so schould be run in executer job in HA
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my note about below about doing the binding during the init. I suppose the best place to hook this is to happen is in the info() call, but I'll think on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can move all the binding/IO (status retrieving, choices list retrieving etc) to the info() call, cache it there and then return the list of sensors/buttons/settings from the cache.
If sensors/buttons/settings is accesed when cache is still empty I can call info() there.

Do you want me to move it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we could raise an error when sensors/buttons/settings is accesed when cache is still empty, maybe that is nicer since you then know that the sensors/buttons/settings will not do IO, and therefore do not have to be run in a executor job...

for setting in settings.values():
# TODO: Bind setter methods, this should probably done only once during init.
if setting.setter is None:
Expand All @@ -255,6 +260,12 @@ def settings(self) -> Dict[str, SettingDescriptor]:
)

setting.setter = getattr(self, setting.setter_name)
if (
isinstance(setting, EnumSettingDescriptor)
and setting.choices_attribute is not None
):
retrieve_choices_function = getattr(self, setting.choices_attribute)
setting.choices = retrieve_choices_function()

return settings

Expand Down
41 changes: 23 additions & 18 deletions miio/devicestatus.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ def switches(self) -> Dict[str, SwitchDescriptor]:
"""
return self._switches # type: ignore[attr-defined]

def settings(self) -> Dict[str, SettingDescriptor]:
def settings(
self,
) -> Dict[str, Union[EnumSettingDescriptor, NumberSettingDescriptor]]:
"""Return the dict of settings exposed by the status container.

You can use @setting decorator to define sensors inside your status class.
Expand Down Expand Up @@ -155,7 +157,8 @@ def sensor(name: str, *, unit: str = "", **kwargs):
"""

def decorator_sensor(func):
property_name = func.__name__
property_name = str(func.__name__)
qualified_name = str(func.__qualname__)

def _sensor_type_for_return_type(func):
rtype = get_type_hints(func).get("return")
Expand All @@ -169,8 +172,8 @@ def _sensor_type_for_return_type(func):

sensor_type = _sensor_type_for_return_type(func)
descriptor = SensorDescriptor(
id=str(property_name),
property=str(property_name),
id=qualified_name,
property=property_name,
name=name,
unit=unit,
type=sensor_type,
Expand All @@ -194,12 +197,13 @@ def switch(name: str, *, setter_name: str, **kwargs):
and can be interpreted downstream users as they wish.
"""

def decorator_sensor(func):
property_name = func.__name__
def decorator_switch(func):
property_name = str(func.__name__)
qualified_name = str(func.__qualname__)

descriptor = SwitchDescriptor(
id=str(property_name),
property=str(property_name),
id=qualified_name,
property=property_name,
name=name,
setter_name=setter_name,
extras=kwargs,
Expand All @@ -208,7 +212,7 @@ def decorator_sensor(func):

return func

return decorator_sensor
return decorator_switch


def setting(
Expand All @@ -235,15 +239,16 @@ def setting(
"""

def decorator_setting(func):
property_name = func.__name__
property_name = str(func.__name__)
qualified_name = str(func.__qualname__)

if setter is None and setter_name is None:
raise Exception("Either setter or setter_name needs to be defined")

if min_value or max_value:
descriptor = NumberSettingDescriptor(
id=str(property_name),
property=str(property_name),
id=qualified_name,
property=property_name,
name=name,
unit=unit,
setter=setter,
Expand All @@ -254,13 +259,13 @@ def decorator_setting(func):
extras=kwargs,
)
elif choices or choices_attribute:
if choices_attribute is not None:
# TODO: adding choices from attribute is a bit more complex, as it requires a way to
# construct enums pointed by the attribute
raise NotImplementedError("choices_attribute is not yet implemented")
# if choices_attribute is not None:
# TODO: adding choices from attribute is a bit more complex, as it requires a way to
# construct enums pointed by the attribute
# raise NotImplementedError("choices_attribute is not yet implemented")
descriptor = EnumSettingDescriptor(
id=str(property_name),
property=str(property_name),
id=qualified_name,
property=property_name,
name=name,
unit=unit,
setter=setter,
Expand Down
47 changes: 47 additions & 0 deletions miio/integrations/vacuum/roborock/tests/test_vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,45 @@ def __init__(self, *args, **kwargs):
1487548800,
],
]
self.dummies["dnd_timer"] = [
{
"enabled": 1,
"start_minute": 0,
"end_minute": 0,
"start_hour": 22,
"end_hour": 8,
}
]
self.dummies["multi_maps"] = [
{
"max_multi_map": 4,
"max_bak_map": 1,
"multi_map_count": 3,
"map_info": [
{
"mapFlag": 0,
"add_time": 1664448893,
"length": 10,
"name": "Downstairs",
"bak_maps": [{"mapFlag": 4, "add_time": 1663577737}],
},
{
"mapFlag": 1,
"add_time": 1663580330,
"length": 8,
"name": "Upstairs",
"bak_maps": [{"mapFlag": 5, "add_time": 1663577752}],
},
{
"mapFlag": 2,
"add_time": 1663580384,
"length": 5,
"name": "Attic",
"bak_maps": [{"mapFlag": 6, "add_time": 1663577765}],
},
],
}
]

self.return_values = {
"get_status": lambda x: [self.state],
Expand All @@ -81,8 +120,16 @@ def __init__(self, *args, **kwargs):
"app_zoned_clean": lambda x: self.change_mode("zoned clean"),
"app_charge": lambda x: self.change_mode("charge"),
"miIO.info": "dummy info",
"get_clean_record": lambda x: [[1488347071, 1488347123, 16, 0, 0, 0]],
"get_dnd_timer": lambda x: self.dummies["dnd_timer"],
"get_multi_maps_list": lambda x: self.dummies["multi_maps"],
}

self._multi_maps = None

self._floor_clean_details = {}
self._searched_clean_id = None

super().__init__(args, kwargs)

def change_mode(self, new_mode):
Expand Down
Loading