diff --git a/roborock/devices/device_manager.py b/roborock/devices/device_manager.py index 1717e94c..2ff01085 100644 --- a/roborock/devices/device_manager.py +++ b/roborock/devices/device_manager.py @@ -51,6 +51,10 @@ class DeviceVersion(enum.StrEnum): UNKNOWN = "unknown" +class UnsupportedDeviceError(RoborockException): + """Exception raised when a device is unsupported.""" + + class DeviceManager: """Central manager for Roborock device discovery and connections.""" @@ -95,11 +99,19 @@ async def discover_devices(self, prefer_cache: bool = True) -> list[RoborockDevi # These are connected serially to avoid overwhelming the MQTT broker new_devices = {} start_tasks = [] + supported_devices_counter = self._diagnostics.subkey("supported_devices") + unsupported_devices_counter = self._diagnostics.subkey("unsupported_devices") for duid, (device, product) in device_products.items(): _LOGGER.debug("[%s] Discovered device %s %s", duid, product.summary_info(), device.summary_info()) if duid in self._devices: continue - new_device = self._device_creator(home_data, device, product) + try: + new_device = self._device_creator(home_data, device, product) + except UnsupportedDeviceError: + _LOGGER.info("Skipping unsupported device %s %s", product.summary_info(), device.summary_info()) + unsupported_devices_counter.increment(device.pv or "unknown") + continue + supported_devices_counter.increment(device.pv or "unknown") start_tasks.append(new_device.start_connect()) new_devices[duid] = new_device @@ -228,16 +240,18 @@ def device_creator(home_data: HomeData, device: HomeDataDevice, product: HomeDat channel = create_mqtt_channel(user_data, mqtt_params, mqtt_session, device) model_part = product.model.split(".")[-1] if "ss" in model_part: - raise NotImplementedError( - f"Device {device.name} has unsupported version B01_{product.model.strip('.')[-1]}" + raise UnsupportedDeviceError( + f"Device {device.name} has unsupported version B01 product model {product.model}" ) elif "sc" in model_part: # Q7 devices start with 'sc' in their model naming. trait = b01.q7.create(channel) else: - raise NotImplementedError(f"Device {device.name} has unsupported B01 model: {product.model}") + raise UnsupportedDeviceError(f"Device {device.name} has unsupported B01 model: {product.model}") case _: - raise NotImplementedError(f"Device {device.name} has unsupported version {device.pv}") + raise UnsupportedDeviceError( + f"Device {device.name} has unsupported version {device.pv} {product.model}" + ) dev = RoborockDevice(device, product, channel, trait) if ready_callback: diff --git a/tests/devices/test_device_manager.py b/tests/devices/test_device_manager.py index f5ff75f7..a788dd0e 100644 --- a/tests/devices/test_device_manager.py +++ b/tests/devices/test_device_manager.py @@ -362,3 +362,55 @@ async def test_diagnostics_collection(home_data: HomeData) -> None: assert diagnostics.get("fetch_home_data") == 1 await device_manager.close() + + +async def test_unsupported_protocol_version() -> None: + """Test the DeviceManager with some supported and unsupported product IDs.""" + with patch("roborock.devices.device_manager.UserWebApiClient.get_home_data") as mock_home_data: + home_data = HomeData.from_dict( + { + "id": 1, + "name": "Test Home", + "devices": [ + { + "duid": "device-uid-1", + "name": "Device 1", + "pv": "1.0", + "productId": "product-id-1", + "localKey": mock_data.LOCAL_KEY, + }, + { + "duid": "device-uid-2", + "name": "Device 2", + "pv": "unknown-pv", # Fake new protocol version we've never seen + "productId": "product-id-2", + "localKey": mock_data.LOCAL_KEY, + }, + ], + "products": [ + { + "id": "product-id-1", + "name": "Roborock S7 MaxV", + "model": "roborock.vacuum.a27", + "category": "robot.vacuum.cleaner", + }, + { + "id": "product-id-2", + "name": "New Roborock Model", + "model": "roborock.vacuum.newmodel", + "category": "robot.vacuum.cleaner", + }, + ], + } + ) + mock_home_data.return_value = home_data + + device_manager = await create_device_manager(USER_PARAMS) + # Only the supported device should be created. The other device is ignored + devices = await device_manager.get_devices() + assert [device.duid for device in devices] == ["device-uid-1"] + + # Verify diagnostics + data = device_manager.diagnostic_data() + assert data.get("supported_devices") == {"1.0": 1} + assert data.get("unsupported_devices") == {"unknown-pv": 1}