Просмотр исходного кода

add vacuum test and unit tests

zerzhang 2 месяцев назад
Родитель
Сommit
3cfbbd0076
5 измененных файлов с 615 добавлено и 11 удалено
  1. 2 2
      switchbot/adv_parser.py
  2. 1 1
      switchbot/const/__init__.py
  3. 9 8
      switchbot/devices/vacuum.py
  4. 460 0
      tests/test_adv_parser.py
  5. 143 0
      tests/test_vacuum.py

+ 2 - 2
switchbot/adv_parser.py

@@ -252,13 +252,13 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
     },
     },
     "3": {
     "3": {
         "modelName": SwitchbotModel.K10_PRO_COMBO_VACUUM,
         "modelName": SwitchbotModel.K10_PRO_COMBO_VACUUM,
-        "modelFriendlyName": "K10+ Pro Combo",
+        "modelFriendlyName": "K10+ Pro Combo Vacuum",
         "func": process_vacuum,
         "func": process_vacuum,
         "manufacturer_id": 2409,
         "manufacturer_id": 2409,
     },
     },
     "}": {
     "}": {
         "modelName": SwitchbotModel.K10_VACUUM,
         "modelName": SwitchbotModel.K10_VACUUM,
-        "modelFriendlyName": "K10 Vacuum",
+        "modelFriendlyName": "K10+ Vacuum",
         "func": process_vacuum_k,
         "func": process_vacuum_k,
         "manufacturer_id": 2409,
         "manufacturer_id": 2409,
     },
     },

+ 1 - 1
switchbot/const/__init__.py

@@ -69,6 +69,6 @@ class SwitchbotModel(StrEnum):
     CIRCULATOR_FAN = "Circulator Fan"
     CIRCULATOR_FAN = "Circulator Fan"
     K20_VACUUM = "K20 Vacuum"
     K20_VACUUM = "K20 Vacuum"
     S10_VACUUM = "S10 Vacuum"
     S10_VACUUM = "S10 Vacuum"
-    K10_VACUUM = "K10 Vacuum"
+    K10_VACUUM = "K10+ Vacuum"
     K10_PRO_VACUUM = "K10+ Pro Vacuum"
     K10_PRO_VACUUM = "K10+ Pro Vacuum"
     K10_PRO_COMBO_VACUUM = "K10+ Pro Combo Vacuum"
     K10_PRO_COMBO_VACUUM = "K10+ Pro Combo Vacuum"

+ 9 - 8
switchbot/devices/vacuum.py

@@ -1,12 +1,9 @@
 """Library to handle connection with Switchbot."""
 """Library to handle connection with Switchbot."""
 
 
 from __future__ import annotations
 from __future__ import annotations
-
+from typing import Any
 from .device import SwitchbotSequenceDevice, update_after_operation
 from .device import SwitchbotSequenceDevice, update_after_operation
 
 
-COMMMAND_HEAD = "5A40010101"
-COMMAND_RETURN_DOCK = F"{COMMMAND_HEAD}0225"
-
 COMMAND_CLEAN_UP = {
 COMMAND_CLEAN_UP = {
     1: "570F5A00FFFF7001",
     1: "570F5A00FFFF7001",
     2: "5A400101010126",
     2: "5A400101010126",
@@ -31,10 +28,14 @@ class SwitchbotVacuum(SwitchbotSequenceDevice):
     async def return_to_dock(self, protocol_version: int) -> bool:
     async def return_to_dock(self, protocol_version: int) -> bool:
         """Send command to return the dock."""
         """Send command to return the dock."""
         return await self._send_command(COMMAND_RETURN_DOCK[protocol_version])
         return await self._send_command(COMMAND_RETURN_DOCK[protocol_version])
-
-    def get_ble_version(self) -> int:
-        """Return device ble version."""
-        return self._get_adv_value("firmware")
+    
+    async def get_basic_info(self) -> dict[str, Any] | None:
+        """Only support get the ble version through the command."""
+        if not (_data := await self._get_basic_info()):
+            return None
+        return {
+            "firmware": _data[2],
+        }
     
     
     def get_soc_version(self) -> str:
     def get_soc_version(self) -> str:
         """Return device soc version."""
         """Return device soc version."""

+ 460 - 0
tests/test_adv_parser.py

@@ -2197,3 +2197,463 @@ def test_circulator_fan_with_empty_data() -> None:
         rssi=-97,
         rssi=-97,
         active=True,
         active=True,
     )
     )
+
+
+def test_k20_active() -> None:
+    """Test parsing k20 with active data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xb0\xe9\xfe\x01\xf3\x8f'\x01\x11S\x00\x10d\x0f"},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b".\x00d"},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K20_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b".\x00d",
+            "data": {
+                "sequence_number": 39,
+                "soc_version": "1.1.083",
+                "step": 0,
+                "mqtt_connected": True,
+                "battery": 100,
+                "work_status": 15,
+            },
+            "isEncrypted": False,
+            "model": ".",
+            "modelFriendlyName": "K20 Vacuum",
+            "modelName": SwitchbotModel.K20_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_k20_passive() -> None:
+    """Test parsing k20 with passive data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xb0\xe9\xfe\x01\xf3\x8f'\x01\x11S\x00\x10d\x0f"},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K20_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": None,
+            "data": {
+                "sequence_number": 39,
+                "soc_version": "1.1.083",
+                "step": 0,
+                "mqtt_connected": True,
+                "battery": 100,
+                "work_status": 15,
+            },
+            "isEncrypted": False,
+            "model": ".",
+            "modelFriendlyName": "K20 Vacuum",
+            "modelName": SwitchbotModel.K20_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=False,
+    )
+
+
+def test_k20_with_empty_data() -> None:
+    """Test parsing k20 with empty data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: None},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b".\x00d"},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K20_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b".\x00d",
+            "data": {},
+            "isEncrypted": False,
+            "model": ".",
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+    
+
+def test_k10_pro_active() -> None:
+    """Test parsing k10 pro with active data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xb0\xe9\xfeP\x8d\x8d\x02 d'},
+        service_data={'0000fd3d-0000-1000-8000-00805f9b34fb': b'(\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_PRO_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'(\x00',
+            "data": {
+                "sequence_number": 2,
+                'dusbin_connected': False,
+                'dustbin_bound': False,
+                'network_conncted': True,
+                "battery": 100,
+                "work_status": 0,
+            },
+            "isEncrypted": False,
+            "model": "(",
+            "modelFriendlyName": "K10+ Pro Vacuum",
+            "modelName": SwitchbotModel.K10_PRO_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_k10_pro_passive() -> None:
+    """Test parsing k10 pro with passive data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xb0\xe9\xfeP\x8d\x8d\x02 d'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_PRO_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": None,
+            "data": {
+                "sequence_number": 2,
+                'dusbin_connected': False,
+                'dustbin_bound': False,
+                'network_conncted': True,
+                "battery": 100,
+                "work_status": 0,
+            },
+            "isEncrypted": False,
+            "model": "(",
+            "modelFriendlyName": "K10+ Pro Vacuum",
+            "modelName": SwitchbotModel.K10_PRO_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=False,
+    )
+
+
+def test_k10_pro_with_empty_data() -> None:
+    """Test parsing k10 pro with empty data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: None},
+        service_data={'0000fd3d-0000-1000-8000-00805f9b34fb': b'(\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_PRO_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'(\x00',
+            "data": {},
+            "isEncrypted": False,
+            "model": "(",
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_k10_active() -> None:
+    """Test parsing k10+ with active data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xca8\x06\xa9_\xf1\x02 d'},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b'}\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'}\x00',
+            "data": {
+                "sequence_number": 2,
+                'dusbin_connected': False,
+                'dustbin_bound': False,
+                'network_conncted': True,
+                "battery": 100,
+                "work_status": 0,
+            },
+            "isEncrypted": False,
+            "model": "}",
+            "modelFriendlyName": "K10+ Vacuum",
+            "modelName": SwitchbotModel.K10_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_k10_passive() -> None:
+    """Test parsing k10+ with passive data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xca8\x06\xa9_\xf1\x02 d'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": None,
+            "data": {
+                "sequence_number": 2,
+                'dusbin_connected': False,
+                'dustbin_bound': False,
+                'network_conncted': True,
+                "battery": 100,
+                "work_status": 0,
+            },
+            "isEncrypted": False,
+            "model": "}",
+            "modelFriendlyName": "K10+ Vacuum",
+            "modelName": SwitchbotModel.K10_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=False,
+    )
+
+
+def test_k10_with_empty_data() -> None:
+    """Test parsing k10+ with empty data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: None},
+        service_data={'0000fd3d-0000-1000-8000-00805f9b34fb': b'}\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'}\x00',
+            "data": {},
+            "isEncrypted": False,
+            "model": "}",
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_k10_pro_combo_active() -> None:
+    """Test parsing k10+ pro combo with active data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xb0\xe9\xfe\x01\xf4\x1d\x0b\x01\x01\xb1\x03\x118\x01'},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b'3\x00\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_PRO_COMBO_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'3\x00\x00',
+            "data": {
+                "sequence_number": 11,
+                "soc_version": "1.0.945",
+                "step": 1,
+                "mqtt_connected": True,
+                "battery": 56,
+                "work_status": 1,
+            },
+            "isEncrypted": False,
+            "model": "3",
+            "modelFriendlyName": "K10+ Pro Combo Vacuum",
+            "modelName": SwitchbotModel.K10_PRO_COMBO_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_k10_pro_combo_passive() -> None:
+    """Test parsing k10+ pro combo with passive data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xb0\xe9\xfe\x01\xf4\x1d\x0b\x01\x01\xb1\x03\x118\x01'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_PRO_COMBO_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": None,
+            "data": {
+                "sequence_number": 11,
+                "soc_version": "1.0.945",
+                "step": 1,
+                "mqtt_connected": True,
+                "battery": 56,
+                "work_status": 1,
+            },
+            "isEncrypted": False,
+            "model": "3",
+            "modelFriendlyName": "K10+ Pro Combo Vacuum",
+            "modelName": SwitchbotModel.K10_PRO_COMBO_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=False,
+    )
+
+
+def test_k10_pro_combo_with_empty_data() -> None:
+    """Test parsing k10+ pro combo with empty data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: None},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b'3\x00\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.K10_PRO_COMBO_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'3\x00\x00',
+            "data": {},
+            "isEncrypted": False,
+            "model": "3",
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_s10_active() -> None:
+    """Test parsing s10 with active data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xb0\xe9\xfe\x00\x08|\n\x01\x11\x05\x00\x10M\x02'},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b'z\x00\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.S10_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'z\x00\x00',
+            "data": {
+                "sequence_number": 10,
+                "soc_version": "1.1.005",
+                "step": 0,
+                "mqtt_connected": True,
+                "battery": 77,
+                "work_status": 2,
+            },
+            "isEncrypted": False,
+            "model": "z",
+            "modelFriendlyName": "S10 Vacuum",
+            "modelName": SwitchbotModel.S10_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )
+
+
+def test_s10_passive() -> None:
+    """Test parsing s10 with passive data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b'\xb0\xe9\xfe\x00\x08|\n\x01\x11\x05\x00\x10M\x02'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.S10_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": None,
+            "data": {
+                "sequence_number": 10,
+                "soc_version": "1.1.005",
+                "step": 0,
+                "mqtt_connected": True,
+                "battery": 77,
+                "work_status": 2,
+            },
+            "isEncrypted": False,
+            "model": "z",
+            "modelFriendlyName": "S10 Vacuum",
+            "modelName": SwitchbotModel.S10_VACUUM,
+        },
+        device=ble_device,
+        rssi=-97,
+        active=False,
+    )
+
+
+def test_s10_with_empty_data() -> None:
+    """Test parsing s10 with empty data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: None},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b'z\x00\x00'},
+        rssi=-97,
+    )
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.S10_VACUUM
+    )
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "rawAdvData": b'z\x00\x00',
+            "data": {},
+            "isEncrypted": False,
+            "model": "z",
+        },
+        device=ble_device,
+        rssi=-97,
+        active=True,
+    )

+ 143 - 0
tests/test_vacuum.py

@@ -0,0 +1,143 @@
+from unittest.mock import AsyncMock
+
+import pytest
+from bleak.backends.device import BLEDevice
+
+from switchbot import SwitchBotAdvertisement
+from switchbot.devices import vacuum
+from switchbot.adv_parser import SUPPORTED_TYPES
+
+from .test_adv_parser import generate_ble_device
+
+common_params = [
+    (b".\x00d", ".", 2), 
+    (b'z\x00\x00', ".", 2), 
+    (b'3\x00\x00', ".", 2),
+    (b'(\x00', "(", 1),
+    (b'}\x00', "(", 1),
+]
+
+
+
+def create_device_for_command_testing(protocol_version: int, rawAdvData: bytes, model: str):
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    device = vacuum.SwitchbotVacuum(ble_device)
+    device.update_from_advertisement(make_advertisement_data(ble_device, protocol_version, rawAdvData, model))
+    device._send_command = AsyncMock()
+    device.update = AsyncMock()
+    return device
+
+
+def make_advertisement_data(ble_device: BLEDevice, protocol_version: int, rawAdvData: bytes, model: str):
+    """Set advertisement data with defaults."""
+    
+    if protocol_version == 1:
+        return SwitchBotAdvertisement(
+            address="aa:bb:cc:dd:ee:ff",
+            data={
+                "rawAdvData": rawAdvData,
+                "data": {
+                    "sequence_number": 2,
+                    'dusbin_connected': False,
+                    'dustbin_bound': False,
+                    'network_conncted': True,
+                    "battery": 100,
+                    "work_status": 0,
+                },
+                "isEncrypted": False,
+                "model": model,
+                "modelFriendlyName": SUPPORTED_TYPES[model]["modelFriendlyName"],
+                "modelName": SUPPORTED_TYPES[model]["modelName"],
+            },
+            device=ble_device,
+            rssi=-97,
+            active=True,
+    )
+    else:
+        return SwitchBotAdvertisement(
+            address="aa:bb:cc:dd:ee:ff",
+            data={
+                "rawAdvData": rawAdvData,
+                "data": {
+                    "soc_version": "1.1.083",
+                    "step": 0,
+                    "mqtt_connected": True,
+                    "battery": 100,
+                    "work_status": 15,
+                },
+                "isEncrypted": False,
+                "model": model,
+                "modelFriendlyName": SUPPORTED_TYPES[model]["modelFriendlyName"],
+                "modelName": SUPPORTED_TYPES[model]["modelName"],
+            },
+            device=ble_device,
+            rssi=-97,
+            active=True,
+    )
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "rawAdvData,model", 
+    [
+        (b".\x00d", "."),
+        (b'z\x00\x00', 'z'),
+        (b'3\x00\x00', "3") 
+    ]
+)
+async def test_status_from_proceess_adv(rawAdvData, model, protocol_version=2):
+    device = create_device_for_command_testing(protocol_version, rawAdvData, model)
+    assert device.get_soc_version() == "1.1.083"
+    assert device.get_last_step() == 0
+    assert device.get_mqtt_connnect_status() is True
+    assert device.get_battery() == 100
+    assert device.get_work_status() == 15
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "rawAdvData,model", 
+    [
+        (b'(\x00', "("), 
+        (b'}\x00', "}")
+    ]
+)
+async def test_status_from_proceess_adv_k(rawAdvData, model, protocol_version=1):
+    device = create_device_for_command_testing(protocol_version, rawAdvData, model)
+    assert device.get_dustbin_bound_status() is False
+    assert device.get_dustbin_connnected_status() is False
+    assert device.get_network_connected_status() is True
+    assert device.get_battery() == 100
+    assert device.get_work_status() == 0
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "rawAdvData,model,protocol_version", 
+    common_params
+)
+async def test_clean_up(rawAdvData, model, protocol_version):
+    device = create_device_for_command_testing(protocol_version, rawAdvData, model)
+    await device.clean_up(protocol_version)
+    device._send_command.assert_awaited_once_with(vacuum.COMMAND_CLEAN_UP[protocol_version])
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "rawAdvData,model,protocol_version", 
+    common_params
+)
+async def test_return_to_dock(rawAdvData, model, protocol_version):
+    device = create_device_for_command_testing(protocol_version, rawAdvData, model)
+    await device.return_to_dock(protocol_version)
+    device._send_command.assert_awaited_once_with(vacuum.COMMAND_RETURN_DOCK[protocol_version])
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "rawAdvData,model,protocol_version", 
+    common_params
+)
+async def test_get_basic_info_returns_none_when_no_data(rawAdvData, model, protocol_version):
+    device = create_device_for_command_testing(protocol_version, rawAdvData, model)
+    device._get_basic_info = AsyncMock(return_value=None)
+
+    assert await device.get_basic_info() is None