Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 19 additions & 5 deletions roborock/devices/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down
52 changes: 52 additions & 0 deletions tests/devices/test_device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}