diff --git a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py index 0c702fba7..838ec24f8 100644 --- a/miio/integrations/vacuum/dreame/dreamevacuum_miot.py +++ b/miio/integrations/vacuum/dreame/dreamevacuum_miot.py @@ -8,6 +8,7 @@ from miio.click_common import command, format_output from miio.exceptions import DeviceException +from miio.interfaces import VacuumInterface from miio.miot_device import DeviceStatus as DeviceStatusContainer from miio.miot_device import MiotDevice, MiotMapping @@ -402,7 +403,7 @@ def is_water_box_carriage_attached(self) -> Optional[bool]: return None -class DreameVacuum(MiotDevice): +class DreameVacuum(MiotDevice, VacuumInterface): _mappings = MIOT_MAPPING @command( diff --git a/miio/integrations/vacuum/mijia/g1vacuum.py b/miio/integrations/vacuum/mijia/g1vacuum.py index 344be83f6..cc1c20016 100644 --- a/miio/integrations/vacuum/mijia/g1vacuum.py +++ b/miio/integrations/vacuum/mijia/g1vacuum.py @@ -5,6 +5,7 @@ import click from miio.click_common import EnumType, command, format_output +from miio.interfaces import VacuumInterface from miio.miot_device import DeviceStatus, MiotDevice _LOGGER = logging.getLogger(__name__) @@ -274,7 +275,7 @@ def total_clean_time(self) -> timedelta: return timedelta(hours=self.data["total_clean_area"]) -class G1Vacuum(MiotDevice): +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 aebe63ac0..7d48c4f30 100644 --- a/miio/integrations/vacuum/roborock/vacuum.py +++ b/miio/integrations/vacuum/roborock/vacuum.py @@ -22,6 +22,7 @@ ) from miio.device import Device, DeviceInfo from miio.exceptions import DeviceException, DeviceInfoUnavailableException +from miio.interfaces import VacuumInterface from .vacuumcontainers import ( CarpetModeStatus, @@ -169,7 +170,7 @@ class CarpetCleaningMode(enum.Enum): ] -class RoborockVacuum(Device): +class RoborockVacuum(Device, 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 d96275879..39b540508 100644 --- a/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py +++ b/miio/integrations/vacuum/roidmi/roidmivacuum_miot.py @@ -11,6 +11,7 @@ from miio.click_common import EnumType, command from miio.integrations.vacuum.roborock.vacuumcontainers import DNDStatus +from miio.interfaces import VacuumInterface from miio.miot_device import DeviceStatus, MiotDevice, MiotMapping _LOGGER = logging.getLogger(__name__) @@ -534,7 +535,7 @@ def sensor_dirty_left(self) -> timedelta: return timedelta(minutes=self.data["sensor_dirty_time_left_minutes"]) -class RoidmiVacuumMiot(MiotDevice): +class RoidmiVacuumMiot(MiotDevice, VacuumInterface): """Interface for Vacuum Eve Plus (roidmi.vacuum.v60)""" _mappings = _MAPPINGS diff --git a/miio/integrations/vacuum/viomi/viomivacuum.py b/miio/integrations/vacuum/viomi/viomivacuum.py index 28d622eb5..9de4617de 100644 --- a/miio/integrations/vacuum/viomi/viomivacuum.py +++ b/miio/integrations/vacuum/viomi/viomivacuum.py @@ -58,6 +58,7 @@ ConsumableStatus, DNDStatus, ) +from miio.interfaces import VacuumInterface from miio.utils import pretty_seconds _LOGGER = logging.getLogger(__name__) @@ -482,7 +483,7 @@ def _get_rooms_from_schedules(schedules: List[str]) -> Tuple[bool, Dict]: return scheduled_found, rooms -class ViomiVacuum(Device): +class ViomiVacuum(Device, 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..156774fbf --- /dev/null +++ b/miio/interfaces/__init__.py @@ -0,0 +1,5 @@ +"""Interfaces API.""" + +from .vacuuminterface import VacuumInterface + +__all__ = ["VacuumInterface"] diff --git a/miio/interfaces/vacuuminterface.py b/miio/interfaces/vacuuminterface.py new file mode 100644 index 000000000..842ed5775 --- /dev/null +++ b/miio/interfaces/vacuuminterface.py @@ -0,0 +1,23 @@ +"""`VacuumInterface` is an interface (abstract class) with shared API for all vacuum +devices.""" +from abc import abstractmethod + + +class VacuumInterface: + """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 4d97fc260..83d2b3d9c 100644 --- a/miio/tests/test_device.py +++ b/miio/tests/test_device.py @@ -6,6 +6,7 @@ from miio.exceptions import DeviceInfoUnavailableException, PayloadDecodeException DEVICE_CLASSES = Device.__subclasses__() + MiotDevice.__subclasses__() # type: ignore +DEVICE_CLASSES.remove(MiotDevice) @pytest.mark.parametrize("max_properties", [None, 1, 15]) @@ -144,7 +145,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