From 4e63252bf9fe76edecb132f52f9028309d9f8c58 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 13 Mar 2022 10:48:53 +0100 Subject: [PATCH 01/15] Added Xiaomi Vaccum Mop 2 Ultra and Pro+ --- .../vacuum/dreame/dreamevacuum_miot.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index b36b3d9e0..a7466be83 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -18,6 +18,8 @@ DREAME_F9 = "dreame.vacuum.p2008" DREAME_D9 = "dreame.vacuum.p2009" DREAME_Z10_PRO = "dreame.vacuum.p2028" +DREAME_MOP_2_PRO_PLUS = "dreame.vacuum.p2041o" +DREAME_MOP_2_ULTRA = "dreame.vacuum.p2150a" _DREAME_1C_MAPPING: MiotMapping = { @@ -70,6 +72,8 @@ # https://home.miot-spec.com/spec/dreame.vacuum.p2008 # https://home.miot-spec.com/spec/dreame.vacuum.p2009 # https://home.miot-spec.com/spec/dreame.vacuum.p2028 + # https://home.miot-spec.com/spec/dreame.vacuum.p2041o + # https://home.miot-spec.com/spec/dreame.vacuum.p2150a "battery_level": {"siid": 3, "piid": 1}, "charging_state": {"siid": 3, "piid": 2}, "device_fault": {"siid": 2, "piid": 2}, @@ -115,6 +119,8 @@ DREAME_F9: _DREAME_F9_MAPPING, DREAME_D9: _DREAME_F9_MAPPING, DREAME_Z10_PRO: _DREAME_F9_MAPPING, + DREAME_MOP_2_PRO_PLUS: _DREAME_F9_MAPPING, + DREAME_MOP_2_ULTRA: _DREAME_F9_MAPPING, } @@ -184,7 +190,7 @@ def _get_cleaning_mode_enum_class(model): """Return cleaning mode enum class for model if found or None.""" if model == DREAME_1C: return CleaningModeDreame1C - elif model in [DREAME_F9, DREAME_D9, DREAME_Z10_PRO]: + elif model in (DREAME_F9, DREAME_D9, DREAME_Z10_PRO, DREAME_MOP_2_PRO_PLUS, DREAME_MOP_2_ULTRA): return CleaningModeDreameF9 @@ -387,6 +393,14 @@ def is_water_box_carriage_attached(self) -> Optional[bool]: class DreameVacuum(MiotDevice): + _supported_models = [ + DREAME_1C, + DREAME_D9, + DREAME_F9, + DREAME_Z10_PRO, + DREAME_MOP_2_PRO_PLUS, + DREAME_MOP_2_ULTRA, + ] _mappings = MIOT_MAPPING @command( From 6e8f278ad7232ac02eac039e0ee894a9e86529c6 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 13 Mar 2022 11:01:58 +0100 Subject: [PATCH 02/15] Updated automatic formatting using black --- miio/integrations/vacuum/dreame/dreamevacuum_miot.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index a7466be83..0591891ac 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -190,7 +190,13 @@ def _get_cleaning_mode_enum_class(model): """Return cleaning mode enum class for model if found or None.""" if model == DREAME_1C: return CleaningModeDreame1C - elif model in (DREAME_F9, DREAME_D9, DREAME_Z10_PRO, DREAME_MOP_2_PRO_PLUS, DREAME_MOP_2_ULTRA): + elif model in ( + DREAME_F9, + DREAME_D9, + DREAME_Z10_PRO, + DREAME_MOP_2_PRO_PLUS, + DREAME_MOP_2_ULTRA, + ): return CleaningModeDreameF9 From e21387c96646c5670c827042a3d27fe5d98a6caa Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 13 Mar 2022 11:40:00 +0100 Subject: [PATCH 03/15] Removed duplicated supported models --- miio/integrations/vacuum/dreame/dreamevacuum_miot.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index 0591891ac..c8e04f85d 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -399,14 +399,6 @@ def is_water_box_carriage_attached(self) -> Optional[bool]: class DreameVacuum(MiotDevice): - _supported_models = [ - DREAME_1C, - DREAME_D9, - DREAME_F9, - DREAME_Z10_PRO, - DREAME_MOP_2_PRO_PLUS, - DREAME_MOP_2_ULTRA, - ] _mappings = MIOT_MAPPING @command( From 469cc89c9ffcf9381575280a2632217c328ade12 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 13 Mar 2022 13:08:21 +0100 Subject: [PATCH 04/15] Extended dreame test to test all supported models --- .../vacuum/dreame/tests/test_dreamevacuum_miot.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py index 2ee44b61b..88ae14600 100644 --- a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py @@ -15,6 +15,7 @@ FaultStatus, OperatingMode, WaterFlow, + MIOT_MAPPING ) _INITIAL_STATE_1C = { @@ -248,3 +249,13 @@ def test_waterflow_presets(self): def test_waterflow(self): value = self.device.waterflow() assert value == {"Medium": 2} + + +@pytest.mark.parametrize( + "model", MIOT_MAPPING.keys() +) +def test_dreame_models(model: str): + vac = DreameVacuum(model=model) + # test _get_cleaning_mode_enum_class returns non-empty mapping + fp = vac.fan_speed_presets() + assert (fp is not None) and (len(fp) > 0) From 2db536cd23a94b1fda31ffe07c9f37d3a53a275b Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 13 Mar 2022 18:10:24 +0100 Subject: [PATCH 05/15] Added test for invalid dreame model --- miio/integrations/vacuum/dreame/dreamevacuum_miot.py | 1 + .../vacuum/dreame/tests/test_dreamevacuum_miot.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index c8e04f85d..280c25b83 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -198,6 +198,7 @@ def _get_cleaning_mode_enum_class(model): DREAME_MOP_2_ULTRA, ): return CleaningModeDreameF9 + return None class DreameVacuumStatus(DeviceStatusContainer): diff --git a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py index 88ae14600..442e38af2 100644 --- a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py @@ -259,3 +259,9 @@ def test_dreame_models(model: str): # test _get_cleaning_mode_enum_class returns non-empty mapping fp = vac.fan_speed_presets() assert (fp is not None) and (len(fp) > 0) + + +def test_invalid_dreame_model(): + vac = DreameVacuum(model='model.invalid') + fp = vac.fan_speed_presets() + assert fp == {} From 05a6ea15cf854cb472d48f509c82e07cff444f49 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 13 Mar 2022 18:14:50 +0100 Subject: [PATCH 06/15] Formatting by black --- .../vacuum/dreame/tests/test_dreamevacuum_miot.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py index 442e38af2..2fafc6ca4 100644 --- a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py @@ -15,7 +15,7 @@ FaultStatus, OperatingMode, WaterFlow, - MIOT_MAPPING + MIOT_MAPPING, ) _INITIAL_STATE_1C = { @@ -251,9 +251,7 @@ def test_waterflow(self): assert value == {"Medium": 2} -@pytest.mark.parametrize( - "model", MIOT_MAPPING.keys() -) +@pytest.mark.parametrize("model", MIOT_MAPPING.keys()) def test_dreame_models(model: str): vac = DreameVacuum(model=model) # test _get_cleaning_mode_enum_class returns non-empty mapping @@ -262,6 +260,6 @@ def test_dreame_models(model: str): def test_invalid_dreame_model(): - vac = DreameVacuum(model='model.invalid') + vac = DreameVacuum(model="model.invalid") fp = vac.fan_speed_presets() assert fp == {} From 2db26f4a946afa02b15cd41610e091f8cc8be2cf Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 13 Mar 2022 18:21:59 +0100 Subject: [PATCH 07/15] import isort --- miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py index 2fafc6ca4..5a2ab0650 100644 --- a/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/tests/test_dreamevacuum_miot.py @@ -8,6 +8,7 @@ from ..dreamevacuum_miot import ( DREAME_1C, DREAME_F9, + MIOT_MAPPING, ChargingState, CleaningModeDreame1C, CleaningModeDreameF9, @@ -15,7 +16,6 @@ FaultStatus, OperatingMode, WaterFlow, - MIOT_MAPPING, ) _INITIAL_STATE_1C = { From 7723bfabe160becdfc9f1e8a44ee03a7556d5abb Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Mon, 14 Mar 2022 19:38:42 +0100 Subject: [PATCH 08/15] Updated readme with newly supported models --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index cd9eb212f..bcc4b4f29 100644 --- a/README.rst +++ b/README.rst @@ -154,6 +154,7 @@ Supported devices - Xiaomi Walkingpad A1 (ksmb.walkingpad.v3) - Xiaomi Smart Pet Water Dispenser (mmgg.pet_waterer.s1, s4) - Xiaomi Mi Smart Humidifer S (jsqs, jsq5) +- Xiaomi Mi Robot Vacuum Mop 2 (Pro+, Ultra) *Feel free to create a pull request to add support for new devices as From 2dd5e61ac0a356689e1ba5001902f1b71dceaadc Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 20 Mar 2022 18:45:34 +0100 Subject: [PATCH 09/15] Added support for Vacuum interface: VaccumDevice and VacuumMiotDevice --- miio/integrations/vacuum/__init__.py | 2 ++ .../vacuum/dreame/dreamevacuum_miot.py | 6 ++-- miio/integrations/vacuum/mijia/g1vacuum.py | 6 ++-- miio/integrations/vacuum/roborock/vacuum.py | 5 +-- .../vacuum/roidmi/roidmivacuum_miot.py | 6 ++-- miio/integrations/vacuum/vacuum_device.py | 34 +++++++++++++++++++ miio/integrations/vacuum/viomi/viomivacuum.py | 6 ++-- miio/tests/test_device.py | 7 ++-- miio/tests/test_miotdevice.py | 2 ++ 9 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 miio/integrations/vacuum/vacuum_device.py diff --git a/miio/integrations/vacuum/__init__.py b/miio/integrations/vacuum/__init__.py index e69de29bb..0491f3d0f 100644 --- a/miio/integrations/vacuum/__init__.py +++ b/miio/integrations/vacuum/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from .vacuum_device import VacuumDevice, VacuumMiotDevice diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index 280c25b83..8ff3789a6 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -9,7 +9,9 @@ from miio.click_common import command, format_output from miio.exceptions import DeviceException from miio.miot_device import DeviceStatus as DeviceStatusContainer -from miio.miot_device import MiotDevice, MiotMapping +from miio.miot_device import MiotMapping + +from ..vacuum_device import VacuumMiotDevice _LOGGER = logging.getLogger(__name__) @@ -399,7 +401,7 @@ def is_water_box_carriage_attached(self) -> Optional[bool]: return None -class DreameVacuum(MiotDevice): +class DreameVacuum(VacuumMiotDevice): _mappings = MIOT_MAPPING @command( diff --git a/miio/integrations/vacuum/mijia/g1vacuum.py b/miio/integrations/vacuum/mijia/g1vacuum.py index 344be83f6..b6952b991 100644 --- a/miio/integrations/vacuum/mijia/g1vacuum.py +++ b/miio/integrations/vacuum/mijia/g1vacuum.py @@ -5,7 +5,9 @@ import click from miio.click_common import EnumType, command, format_output -from miio.miot_device import DeviceStatus, MiotDevice +from miio.miot_device import DeviceStatus + +from ..vacuum_device import VacuumMiotDevice _LOGGER = logging.getLogger(__name__) MIJIA_VACUUM_V1 = "mijia.vacuum.v1" @@ -274,7 +276,7 @@ def total_clean_time(self) -> timedelta: return timedelta(hours=self.data["total_clean_area"]) -class G1Vacuum(MiotDevice): +class G1Vacuum(VacuumMiotDevice): """Support for G1 vacuum (G1, mijia.vacuum.v2).""" _mappings = MIOT_MAPPING diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index a28a5755a..ce569dea4 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -20,9 +20,10 @@ LiteralParamType, command, ) -from miio.device import Device, DeviceInfo +from miio.device import DeviceInfo from miio.exceptions import DeviceException, DeviceInfoUnavailableException +from ..vacuum_device import VacuumDevice from .vacuumcontainers import ( CarpetModeStatus, CleaningDetails, @@ -167,7 +168,7 @@ class CarpetCleaningMode(enum.Enum): ] -class RoborockVacuum(Device): +class RoborockVacuum(VacuumDevice): """Main class for roborock vacuums (roborock.vacuum.*).""" _supported_models = SUPPORTED_MODELS diff --git a/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py b/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py index d96275879..cbad94eb4 100644 --- a/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py +++ b/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py @@ -11,7 +11,9 @@ from miio.click_common import EnumType, command from miio.integrations.vacuum.roborock.vacuumcontainers import DNDStatus -from miio.miot_device import DeviceStatus, MiotDevice, MiotMapping +from miio.miot_device import DeviceStatus, MiotMapping + +from ..vacuum_device import VacuumMiotDevice _LOGGER = logging.getLogger(__name__) @@ -534,7 +536,7 @@ def sensor_dirty_left(self) -> timedelta: return timedelta(minutes=self.data["sensor_dirty_time_left_minutes"]) -class RoidmiVacuumMiot(MiotDevice): +class RoidmiVacuumMiot(VacuumMiotDevice): """Interface for Vacuum Eve Plus (roidmi.vacuum.v60)""" _mappings = _MAPPINGS diff --git a/miio/integrations/vacuum/vacuum_device.py b/miio/integrations/vacuum/vacuum_device.py new file mode 100644 index 000000000..ad585b96b --- /dev/null +++ b/miio/integrations/vacuum/vacuum_device.py @@ -0,0 +1,34 @@ +"""`VacuumDevice` is abstract class with shared API for all vacuum devices +`VacuumMiotDevice` is abstract class with shared API for all vacuum MIOT devices.""" +from abc import abstractmethod + +from miio.device import Device +from miio.miot_device import MiotDevice + + +class VacuumDevice(Device): + """Vacuum API interface.""" + + @abstractmethod + def home(self): + """Return to home.""" + pass + + @abstractmethod + def start(self): + """Start cleaning.""" + pass + + @abstractmethod + def stop(self): + """Validate that Stop cleaning.""" + pass + + +class VacuumMiotDevice(VacuumDevice, MiotDevice): + """Vacuum API Interface for Miot devices.""" + + @abstractmethod + def start(self): + """Start cleaning.""" + pass diff --git a/miio/integrations/vacuum/viomi/viomivacuum.py b/miio/integrations/vacuum/viomi/viomivacuum.py index 28d622eb5..e5137be6f 100644 --- a/miio/integrations/vacuum/viomi/viomivacuum.py +++ b/miio/integrations/vacuum/viomi/viomivacuum.py @@ -52,7 +52,7 @@ import click from miio.click_common import EnumType, command, format_output -from miio.device import Device, DeviceStatus +from miio.device import DeviceStatus from miio.exceptions import DeviceException from miio.integrations.vacuum.roborock.vacuumcontainers import ( ConsumableStatus, @@ -60,6 +60,8 @@ ) from miio.utils import pretty_seconds +from ..vacuum_device import VacuumDevice + _LOGGER = logging.getLogger(__name__) SUPPORTED_MODELS = [ @@ -482,7 +484,7 @@ def _get_rooms_from_schedules(schedules: List[str]) -> Tuple[bool, Dict]: return scheduled_found, rooms -class ViomiVacuum(Device): +class ViomiVacuum(VacuumDevice): """Interface for Viomi vacuums (viomi.vacuum.v7).""" _supported_models = SUPPORTED_MODELS diff --git a/miio/tests/test_device.py b/miio/tests/test_device.py index 1221dc364..3c5d72fdf 100644 --- a/miio/tests/test_device.py +++ b/miio/tests/test_device.py @@ -4,8 +4,12 @@ from miio import Device, MiotDevice, RoborockVacuum from miio.exceptions import DeviceInfoUnavailableException, PayloadDecodeException +from miio.integrations.vacuum import VacuumDevice, VacuumMiotDevice DEVICE_CLASSES = Device.__subclasses__() + MiotDevice.__subclasses__() # type: ignore +DEVICE_CLASSES.remove(MiotDevice) +DEVICE_CLASSES.remove(VacuumDevice) +DEVICE_CLASSES.remove(VacuumMiotDevice) @pytest.mark.parametrize("max_properties", [None, 1, 15]) @@ -119,7 +123,4 @@ def test_device_ctor_model(cls): @pytest.mark.parametrize("cls", DEVICE_CLASSES) def test_device_supported_models(cls): """Make sure that every device subclass has a non-empty supported models.""" - if cls.__name__ == "MiotDevice": # skip miotdevice - return - assert cls.supported_models diff --git a/miio/tests/test_miotdevice.py b/miio/tests/test_miotdevice.py index 52787076a..85c50b8e1 100644 --- a/miio/tests/test_miotdevice.py +++ b/miio/tests/test_miotdevice.py @@ -1,12 +1,14 @@ import pytest from miio import Huizuo, MiotDevice +from miio.integrations.vacuum import VacuumMiotDevice from miio.miot_device import MiotValueType MIOT_DEVICES = MiotDevice.__subclasses__() # TODO: huizuo needs to be refactored to use _mappings, # until then, just disable the tests on it. MIOT_DEVICES.remove(Huizuo) # type: ignore +MIOT_DEVICES.remove(VacuumMiotDevice) # type: ignore @pytest.fixture(scope="module") From dc1ccfadc11da451d150fd5a47dc996ccdc06f71 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 20 Mar 2022 18:53:56 +0100 Subject: [PATCH 10/15] Fixed isort --- miio/integrations/vacuum/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/integrations/vacuum/__init__.py b/miio/integrations/vacuum/__init__.py index 1467dfa1b..35f546193 100644 --- a/miio/integrations/vacuum/__init__.py +++ b/miio/integrations/vacuum/__init__.py @@ -1,7 +1,7 @@ # flake8: noqa -from .vacuum_device import VacuumDevice, VacuumMiotDevice from .dreame import * from .mijia import * from .roborock import * from .roidmi import * +from .vacuum_device import VacuumDevice, VacuumMiotDevice from .viomi import * From 5ba53743c7a3fac8439e04170a99862431f11422 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Sun, 20 Mar 2022 19:05:21 +0100 Subject: [PATCH 11/15] Removing unnecessary pass --- miio/integrations/vacuum/vacuum_device.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/miio/integrations/vacuum/vacuum_device.py b/miio/integrations/vacuum/vacuum_device.py index ad585b96b..ee4b9dc06 100644 --- a/miio/integrations/vacuum/vacuum_device.py +++ b/miio/integrations/vacuum/vacuum_device.py @@ -12,17 +12,14 @@ class VacuumDevice(Device): @abstractmethod def home(self): """Return to home.""" - pass @abstractmethod def start(self): """Start cleaning.""" - pass @abstractmethod def stop(self): """Validate that Stop cleaning.""" - pass class VacuumMiotDevice(VacuumDevice, MiotDevice): @@ -31,4 +28,3 @@ class VacuumMiotDevice(VacuumDevice, MiotDevice): @abstractmethod def start(self): """Start cleaning.""" - pass From 866e85629143f03ef1f58ab4191a25b5f7c8e822 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Thu, 7 Apr 2022 18:21:09 +0200 Subject: [PATCH 12/15] Feedback from PR: VaccumDevice renamed to VacuumInterface --- miio/integrations/vacuum/__init__.py | 1 - .../vacuum/dreame/dreamevacuum_miot.py | 7 ++--- miio/integrations/vacuum/mijia/g1vacuum.py | 7 ++--- miio/integrations/vacuum/roborock/vacuum.py | 4 +-- .../vacuum/roidmi/roidmivacuum_miot.py | 7 ++--- miio/integrations/vacuum/vacuum_device.py | 30 ------------------- miio/integrations/vacuum/viomi/viomivacuum.py | 5 ++-- miio/interfaces/__init__.py | 0 miio/interfaces/vacuuminterface.py | 25 ++++++++++++++++ miio/tests/test_device.py | 5 ++-- 10 files changed, 40 insertions(+), 51 deletions(-) delete mode 100644 miio/integrations/vacuum/vacuum_device.py create mode 100644 miio/interfaces/__init__.py create mode 100644 miio/interfaces/vacuuminterface.py diff --git a/miio/integrations/vacuum/__init__.py b/miio/integrations/vacuum/__init__.py index 35f546193..0718196e4 100644 --- a/miio/integrations/vacuum/__init__.py +++ b/miio/integrations/vacuum/__init__.py @@ -3,5 +3,4 @@ from .mijia import * from .roborock import * from .roidmi import * -from .vacuum_device import VacuumDevice, VacuumMiotDevice from .viomi import * diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index 8ff3789a6..517053908 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -8,10 +8,9 @@ from miio.click_common import command, format_output from miio.exceptions import DeviceException +from miio.interfaces.vacuuminterface import VacuumInterface from miio.miot_device import DeviceStatus as DeviceStatusContainer -from miio.miot_device import MiotMapping - -from ..vacuum_device import VacuumMiotDevice +from miio.miot_device import MiotDevice, MiotMapping _LOGGER = logging.getLogger(__name__) @@ -401,7 +400,7 @@ def is_water_box_carriage_attached(self) -> Optional[bool]: return None -class DreameVacuum(VacuumMiotDevice): +class DreameVacuum(VacuumInterface, MiotDevice): _mappings = MIOT_MAPPING @command( diff --git a/miio/integrations/vacuum/mijia/g1vacuum.py b/miio/integrations/vacuum/mijia/g1vacuum.py index b6952b991..e0fd3c9cf 100644 --- a/miio/integrations/vacuum/mijia/g1vacuum.py +++ b/miio/integrations/vacuum/mijia/g1vacuum.py @@ -5,9 +5,8 @@ import click from miio.click_common import EnumType, command, format_output -from miio.miot_device import DeviceStatus - -from ..vacuum_device import VacuumMiotDevice +from miio.interfaces.vacuuminterface import VacuumInterface +from miio.miot_device import DeviceStatus, MiotDevice _LOGGER = logging.getLogger(__name__) MIJIA_VACUUM_V1 = "mijia.vacuum.v1" @@ -276,7 +275,7 @@ def total_clean_time(self) -> timedelta: return timedelta(hours=self.data["total_clean_area"]) -class G1Vacuum(VacuumMiotDevice): +class G1Vacuum(MiotDevice, VacuumInterface): """Support for G1 vacuum (G1, mijia.vacuum.v2).""" _mappings = MIOT_MAPPING diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index ce569dea4..d3fc93d16 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -22,8 +22,8 @@ ) from miio.device import DeviceInfo from miio.exceptions import DeviceException, DeviceInfoUnavailableException +from miio.interfaces.vacuuminterface import VacuumInterface -from ..vacuum_device import VacuumDevice from .vacuumcontainers import ( CarpetModeStatus, CleaningDetails, @@ -168,7 +168,7 @@ class CarpetCleaningMode(enum.Enum): ] -class RoborockVacuum(VacuumDevice): +class RoborockVacuum(VacuumInterface): """Main class for roborock vacuums (roborock.vacuum.*).""" _supported_models = SUPPORTED_MODELS diff --git a/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py b/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py index cbad94eb4..eb8a7942f 100644 --- a/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py +++ b/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py @@ -11,9 +11,8 @@ from miio.click_common import EnumType, command from miio.integrations.vacuum.roborock.vacuumcontainers import DNDStatus -from miio.miot_device import DeviceStatus, MiotMapping - -from ..vacuum_device import VacuumMiotDevice +from miio.interfaces.vacuuminterface import VacuumInterface +from miio.miot_device import DeviceStatus, MiotDevice, MiotMapping _LOGGER = logging.getLogger(__name__) @@ -536,7 +535,7 @@ def sensor_dirty_left(self) -> timedelta: return timedelta(minutes=self.data["sensor_dirty_time_left_minutes"]) -class RoidmiVacuumMiot(VacuumMiotDevice): +class RoidmiVacuumMiot(MiotDevice, VacuumInterface): """Interface for Vacuum Eve Plus (roidmi.vacuum.v60)""" _mappings = _MAPPINGS diff --git a/miio/integrations/vacuum/vacuum_device.py b/miio/integrations/vacuum/vacuum_device.py deleted file mode 100644 index ee4b9dc06..000000000 --- a/miio/integrations/vacuum/vacuum_device.py +++ /dev/null @@ -1,30 +0,0 @@ -"""`VacuumDevice` is abstract class with shared API for all vacuum devices -`VacuumMiotDevice` is abstract class with shared API for all vacuum MIOT devices.""" -from abc import abstractmethod - -from miio.device import Device -from miio.miot_device import MiotDevice - - -class VacuumDevice(Device): - """Vacuum API interface.""" - - @abstractmethod - def home(self): - """Return to home.""" - - @abstractmethod - def start(self): - """Start cleaning.""" - - @abstractmethod - def stop(self): - """Validate that Stop cleaning.""" - - -class VacuumMiotDevice(VacuumDevice, MiotDevice): - """Vacuum API Interface for Miot devices.""" - - @abstractmethod - def start(self): - """Start cleaning.""" diff --git a/miio/integrations/vacuum/viomi/viomivacuum.py b/miio/integrations/vacuum/viomi/viomivacuum.py index e5137be6f..74bf1df27 100644 --- a/miio/integrations/vacuum/viomi/viomivacuum.py +++ b/miio/integrations/vacuum/viomi/viomivacuum.py @@ -58,10 +58,9 @@ ConsumableStatus, DNDStatus, ) +from miio.interfaces.vacuuminterface import VacuumInterface from miio.utils import pretty_seconds -from ..vacuum_device import VacuumDevice - _LOGGER = logging.getLogger(__name__) SUPPORTED_MODELS = [ @@ -484,7 +483,7 @@ def _get_rooms_from_schedules(schedules: List[str]) -> Tuple[bool, Dict]: return scheduled_found, rooms -class ViomiVacuum(VacuumDevice): +class ViomiVacuum(VacuumInterface): """Interface for Viomi vacuums (viomi.vacuum.v7).""" _supported_models = SUPPORTED_MODELS diff --git a/miio/interfaces/__init__.py b/miio/interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/miio/interfaces/vacuuminterface.py b/miio/interfaces/vacuuminterface.py new file mode 100644 index 000000000..0979c7eeb --- /dev/null +++ b/miio/interfaces/vacuuminterface.py @@ -0,0 +1,25 @@ +"""`VacuumInterface` is an interface (abstract class) with shared API for all vacuum +devices.""" +from abc import abstractmethod + +from miio import Device + + +class VacuumInterface(Device): + """Vacuum API interface.""" + + @abstractmethod + def home(self): + """Return to home.""" + + @abstractmethod + def start(self): + """Start cleaning.""" + + @abstractmethod + def stop(self): + """Validate that Stop cleaning.""" + + def pause(self): + """Pause cleaning.""" + raise RuntimeError("`pause` not supported") diff --git a/miio/tests/test_device.py b/miio/tests/test_device.py index 3c5d72fdf..701d3cb56 100644 --- a/miio/tests/test_device.py +++ b/miio/tests/test_device.py @@ -4,12 +4,11 @@ from miio import Device, MiotDevice, RoborockVacuum from miio.exceptions import DeviceInfoUnavailableException, PayloadDecodeException -from miio.integrations.vacuum import VacuumDevice, VacuumMiotDevice +from miio.interfaces.vacuuminterface import VacuumInterface DEVICE_CLASSES = Device.__subclasses__() + MiotDevice.__subclasses__() # type: ignore DEVICE_CLASSES.remove(MiotDevice) -DEVICE_CLASSES.remove(VacuumDevice) -DEVICE_CLASSES.remove(VacuumMiotDevice) +DEVICE_CLASSES.remove(VacuumInterface) @pytest.mark.parametrize("max_properties", [None, 1, 15]) From 4f7c5da70d37b7048a91be1dad44d2dfa68359c1 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Thu, 7 Apr 2022 18:28:45 +0200 Subject: [PATCH 13/15] Step2: VacuumInterface no moreinherits Device --- miio/integrations/vacuum/dreame/dreamevacuum_miot.py | 2 +- miio/integrations/vacuum/roborock/vacuum.py | 4 ++-- miio/integrations/vacuum/viomi/viomivacuum.py | 4 ++-- miio/interfaces/vacuuminterface.py | 4 +--- miio/tests/test_device.py | 2 -- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index 517053908..39f406f87 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -400,7 +400,7 @@ def is_water_box_carriage_attached(self) -> Optional[bool]: return None -class DreameVacuum(VacuumInterface, MiotDevice): +class DreameVacuum(MiotDevice, VacuumInterface): _mappings = MIOT_MAPPING @command( diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index d3fc93d16..9a0cbef64 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -20,7 +20,7 @@ LiteralParamType, command, ) -from miio.device import DeviceInfo +from miio.device import Device, DeviceInfo from miio.exceptions import DeviceException, DeviceInfoUnavailableException from miio.interfaces.vacuuminterface import VacuumInterface @@ -168,7 +168,7 @@ class CarpetCleaningMode(enum.Enum): ] -class RoborockVacuum(VacuumInterface): +class RoborockVacuum(Device, VacuumInterface): """Main class for roborock vacuums (roborock.vacuum.*).""" _supported_models = SUPPORTED_MODELS diff --git a/miio/integrations/vacuum/viomi/viomivacuum.py b/miio/integrations/vacuum/viomi/viomivacuum.py index 74bf1df27..d5f9ecc1f 100644 --- a/miio/integrations/vacuum/viomi/viomivacuum.py +++ b/miio/integrations/vacuum/viomi/viomivacuum.py @@ -52,7 +52,7 @@ import click from miio.click_common import EnumType, command, format_output -from miio.device import DeviceStatus +from miio.device import Device, DeviceStatus from miio.exceptions import DeviceException from miio.integrations.vacuum.roborock.vacuumcontainers import ( ConsumableStatus, @@ -483,7 +483,7 @@ def _get_rooms_from_schedules(schedules: List[str]) -> Tuple[bool, Dict]: return scheduled_found, rooms -class ViomiVacuum(VacuumInterface): +class ViomiVacuum(Device, VacuumInterface): """Interface for Viomi vacuums (viomi.vacuum.v7).""" _supported_models = SUPPORTED_MODELS diff --git a/miio/interfaces/vacuuminterface.py b/miio/interfaces/vacuuminterface.py index 0979c7eeb..842ed5775 100644 --- a/miio/interfaces/vacuuminterface.py +++ b/miio/interfaces/vacuuminterface.py @@ -2,10 +2,8 @@ devices.""" from abc import abstractmethod -from miio import Device - -class VacuumInterface(Device): +class VacuumInterface: """Vacuum API interface.""" @abstractmethod diff --git a/miio/tests/test_device.py b/miio/tests/test_device.py index 701d3cb56..18dc2e77d 100644 --- a/miio/tests/test_device.py +++ b/miio/tests/test_device.py @@ -4,11 +4,9 @@ from miio import Device, MiotDevice, RoborockVacuum from miio.exceptions import DeviceInfoUnavailableException, PayloadDecodeException -from miio.interfaces.vacuuminterface import VacuumInterface DEVICE_CLASSES = Device.__subclasses__() + MiotDevice.__subclasses__() # type: ignore DEVICE_CLASSES.remove(MiotDevice) -DEVICE_CLASSES.remove(VacuumInterface) @pytest.mark.parametrize("max_properties", [None, 1, 15]) From c25552ff30e4c4dea1b4f7d0bc65b3f4c81febdc Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Thu, 7 Apr 2022 18:31:08 +0200 Subject: [PATCH 14/15] Unit test fixed --- miio/tests/test_miotdevice.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/miio/tests/test_miotdevice.py b/miio/tests/test_miotdevice.py index 85c50b8e1..52787076a 100644 --- a/miio/tests/test_miotdevice.py +++ b/miio/tests/test_miotdevice.py @@ -1,14 +1,12 @@ import pytest from miio import Huizuo, MiotDevice -from miio.integrations.vacuum import VacuumMiotDevice from miio.miot_device import MiotValueType MIOT_DEVICES = MiotDevice.__subclasses__() # TODO: huizuo needs to be refactored to use _mappings, # until then, just disable the tests on it. MIOT_DEVICES.remove(Huizuo) # type: ignore -MIOT_DEVICES.remove(VacuumMiotDevice) # type: ignore @pytest.fixture(scope="module") From 1ac81384247eaf4f635c9b6b911d98394a976260 Mon Sep 17 00:00:00 2001 From: 2pirko <2pirko@gmail.com> Date: Mon, 25 Apr 2022 18:04:12 +0200 Subject: [PATCH 15/15] VaccumInterface published as symbol available for the "interface" package --- miio/integrations/vacuum/dreame/dreamevacuum_miot.py | 2 +- miio/integrations/vacuum/mijia/g1vacuum.py | 2 +- miio/integrations/vacuum/roborock/vacuum.py | 2 +- miio/integrations/vacuum/roidmi/roidmivacuum_miot.py | 2 +- miio/integrations/vacuum/viomi/viomivacuum.py | 2 +- miio/interfaces/__init__.py | 5 +++++ 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index 050b7b420..838ec24f8 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -8,7 +8,7 @@ from miio.click_common import command, format_output from miio.exceptions import DeviceException -from miio.interfaces.vacuuminterface import VacuumInterface +from miio.interfaces import VacuumInterface from miio.miot_device import DeviceStatus as DeviceStatusContainer from miio.miot_device import MiotDevice, MiotMapping diff --git a/miio/integrations/vacuum/mijia/g1vacuum.py b/miio/integrations/vacuum/mijia/g1vacuum.py index e0fd3c9cf..cc1c20016 100644 --- a/miio/integrations/vacuum/mijia/g1vacuum.py +++ b/miio/integrations/vacuum/mijia/g1vacuum.py @@ -5,7 +5,7 @@ import click from miio.click_common import EnumType, command, format_output -from miio.interfaces.vacuuminterface import VacuumInterface +from miio.interfaces import VacuumInterface from miio.miot_device import DeviceStatus, MiotDevice _LOGGER = logging.getLogger(__name__) diff --git a/miio/integrations/vacuum/roborock/vacuum.py b/miio/integrations/vacuum/roborock/vacuum.py index d32876a9f..7d48c4f30 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -22,7 +22,7 @@ ) from miio.device import Device, DeviceInfo from miio.exceptions import DeviceException, DeviceInfoUnavailableException -from miio.interfaces.vacuuminterface import VacuumInterface +from miio.interfaces import VacuumInterface from .vacuumcontainers import ( CarpetModeStatus, diff --git a/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py b/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py index eb8a7942f..39b540508 100644 --- a/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py +++ b/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py @@ -11,7 +11,7 @@ from miio.click_common import EnumType, command from miio.integrations.vacuum.roborock.vacuumcontainers import DNDStatus -from miio.interfaces.vacuuminterface import VacuumInterface +from miio.interfaces import VacuumInterface from miio.miot_device import DeviceStatus, MiotDevice, MiotMapping _LOGGER = logging.getLogger(__name__) diff --git a/miio/integrations/vacuum/viomi/viomivacuum.py b/miio/integrations/vacuum/viomi/viomivacuum.py index d5f9ecc1f..9de4617de 100644 --- a/miio/integrations/vacuum/viomi/viomivacuum.py +++ b/miio/integrations/vacuum/viomi/viomivacuum.py @@ -58,7 +58,7 @@ ConsumableStatus, DNDStatus, ) -from miio.interfaces.vacuuminterface import VacuumInterface +from miio.interfaces import VacuumInterface from miio.utils import pretty_seconds _LOGGER = logging.getLogger(__name__) diff --git a/miio/interfaces/__init__.py b/miio/interfaces/__init__.py index e69de29bb..156774fbf 100644 --- a/miio/interfaces/__init__.py +++ b/miio/interfaces/__init__.py @@ -0,0 +1,5 @@ +"""Interfaces API.""" + +from .vacuuminterface import VacuumInterface + +__all__ = ["VacuumInterface"]