From aaac71957006806a67ef7801aa085fe9f5937c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Sun, 1 Feb 2026 08:20:49 +0100 Subject: [PATCH 1/3] feat(q10): Add VacuumTrait test scripts and documentation - Add test_q10_simple.py: Interactive test script with detailed debug info - Add test_q10_vacuum.py: Basic test script for Q10 vacuum commands - Add test_q10_advanced.py: Advanced test suite with complex features - Add confirmation prompt for map creation mode to prevent accidents - Update README.md with comprehensive usage guide and troubleshooting --- examples/Q10/README.md | 194 +++++++++++++++++++++ examples/Q10/test_q10_advanced.py | 278 ++++++++++++++++++++++++++++++ examples/Q10/test_q10_simple.py | 163 ++++++++++++++++++ examples/Q10/test_q10_vacuum.py | 135 +++++++++++++++ 4 files changed, 770 insertions(+) create mode 100644 examples/Q10/README.md create mode 100644 examples/Q10/test_q10_advanced.py create mode 100644 examples/Q10/test_q10_simple.py create mode 100755 examples/Q10/test_q10_vacuum.py diff --git a/examples/Q10/README.md b/examples/Q10/README.md new file mode 100644 index 00000000..806838cb --- /dev/null +++ b/examples/Q10/README.md @@ -0,0 +1,194 @@ +# Q10 VacuumTrait Test Scripts + +This directory contains test scripts for the Q10 VacuumTrait functionality added in [PR #754](https://github.com/Python-roborock/python-roborock/pull/754). + +## Scripts + +### test_q10_simple.py +Interactive test script with detailed debug information. This script: +- Shows comprehensive device information +- Lists all available property APIs +- Provides an interactive menu to test vacuum commands +- Includes error handling and detailed output + +**Use this script when:** +- You want to verify your device supports Q10 properties +- You need to debug connection or API issues +- You want detailed information about what's happening + +### test_q10_vacuum.py +Basic test script for Q10 vacuum commands. A simpler version focused on testing the vacuum trait. + +### test_q10_advanced.py (NEW!) +Advanced test suite with complex features and detailed diagnostics: +- Tests multiple cleaning modes (standard, area, fast map) +- Device status monitoring and diagnostics +- Structured test suite with multiple test categories +- Interactive menu for manual command testing +- Full test suite execution + +**Use this script when:** +- You want to test advanced cleaning modes +- You need comprehensive diagnostics +- You want to verify all VacuumTrait features +- You're developing or debugging Q10 device support + +## Usage + +### Quick Start + +1. Install the package in development mode: +```bash +python3 -m venv venv +source venv/bin/activate +pip install -e . +``` + +2. Run a test script: +```bash +# Simple script with debugging +python examples/Q10/test_q10_simple.py + +# Basic script (minimal output) +python examples/Q10/test_q10_vacuum.py + +# Advanced script with diagnostics +python examples/Q10/test_q10_advanced.py +``` + +3. On first run, you'll be prompted to log in: + - Enter your Roborock account email + - A code will be sent to your email + - Enter the code to complete authentication + - Credentials are cached for future runs in `~/.cache/roborock-user-params.pkl` + +### What Each Script Does + +#### test_q10_simple.py +```bash +$ python examples/Q10/test_q10_simple.py +๐Ÿ“ฑ Found 1 device(s) +Device 1: Roborock Q10 S5+ +โœ… Using device: Roborock Q10 S5+ +โœ… Device has Q10 properties! +โœ… Vacuum trait found: +``` +- Best for: Verifying device setup and API availability +- Features: Full device diagnostics and comprehensive menu + +#### test_q10_vacuum.py +```bash +$ python examples/Q10/test_q10_vacuum.py +๐Ÿ“ฑ Found 1 device(s) +1. Roborock Q10 S5+ (roborock.vacuum.ss07) +โœ… Using device: Roborock Q10 S5+ +``` +- Best for: Quick testing and command execution +- Features: Minimal output, clean interface + +#### test_q10_advanced.py +```bash +$ python examples/Q10/test_q10_advanced.py +[Main Menu] +1. Run basic commands test +2. Test advanced features +3. Check device status +4. Interactive menu +5. Run all tests +``` +- Best for: Comprehensive testing and development +- Features: Advanced modes, diagnostics, safety confirmations + +## Available Commands + +The VacuumTrait provides these commands: + +- **Start cleaning** - Initiates a full cleaning cycle +- **Pause cleaning** - Pauses the current cleaning operation +- **Resume cleaning** - Resumes a paused cleaning operation +- **Stop cleaning** - Stops the cleaning operation completely +- **Return to dock** - Sends the robot back to its charging dock + +### Advanced Cleaning Modes + +Additional cleaning modes can be tested with `test_q10_advanced.py` (option 2): + +- **Standard cleaning (cmd=1)** - Full cleaning cycle +- **Electoral/Area cleaning (cmd=2)** - Clean specific areas/zones +- **Fast map creation (cmd=4)** - Quickly generate room map + - โš ๏ธ **Warning**: This will start the robot moving! + - Requires explicit confirmation before execution + +### Device Status Information + +The `test_q10_advanced.py` script (option 3) provides: + +- **Connected**: Whether device is connected to Roborock services +- **Local connected**: Whether device is reachable on your local network + - `True` = Direct local connection (LAN/WiFi) + - `False` = Cloud connection via Roborock servers +- **Available APIs**: + - `Command API` = Low-level command interface + - `Vacuum Trait` = High-level vacuum control interface + +## Supported Devices + +These scripts are designed for Roborock Q10 devices that support the B01 Q10 protocol. The script will automatically detect if your device has the required `b01_q10_properties` API. + +## Troubleshooting + +### Device Issues + +**"This device doesn't have Q10 properties"** +- Your device may not be a Q10 model +- Check the device model shown in the output +- The device might use a different API (v1_properties, b01_q7_properties, etc.) +- Supported models: Roborock Q10 Series and compatible devices + +**Commands not working** +- Ensure device is powered on and connected +- Check that your account has proper permissions +- Try stopping any active cleaning cycle first +- Verify device is online in Roborock app + +### Authentication Issues + +**"Code login failed"** +- Ensure you entered the code correctly +- Codes expire after a few minutes - request a new one +- Check your email spam folder for the code + +**"No cached login data found"** +- Delete cached credentials: `rm ~/.cache/roborock-user-params.pkl` +- Try logging in again with fresh credentials +- Verify your email and password are correct + +### Connection Issues + +**Commands sent but no response** +- Ensure device is online and connected to the internet +- Check if your WiFi is stable +- Try the device in the official Roborock app first +- Verify the robot can reach Roborock servers + +**"Local connected: False"** +- This is normal - device is using cloud connection +- Commands may take a moment longer +- Both local and cloud connections work fine +- If you need local connection, configure device on same network + +## Related Documentation + +- [PR #754: Add VacuumTrait to q10 devices](https://github.com/Python-roborock/python-roborock/pull/754) +- [PR #758: Add Q10 VacuumTrait Test Scripts](https://github.com/Python-roborock/python-roborock/pull/758) +- [Main example script](../example.py) +- [Supported Features](../../SUPPORTED_FEATURES.md) +- [Device Manager Documentation](../../roborock/devices/README.md) + +## Testing Status + +โœ… **Successfully tested with:** +- Device: Roborock Q10 S5+ (`roborock.vacuum.ss07`) +- All basic commands working (start, pause, resume, stop, return to dock) +- Advanced modes available for testing +- Cloud and local connection modes supported diff --git a/examples/Q10/test_q10_advanced.py b/examples/Q10/test_q10_advanced.py new file mode 100644 index 00000000..1a952e9c --- /dev/null +++ b/examples/Q10/test_q10_advanced.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python3 +"""Advanced test script for Q10 VacuumTrait with complex features.""" + +import asyncio +import pathlib + +from roborock.devices.device_manager import UserParams, create_device_manager +from roborock.devices.file_cache import FileCache, load_value, store_value +from roborock.web_api import RoborockApiClient + +# Cache paths +USER_PARAMS_PATH = pathlib.Path.home() / ".cache" / "roborock-user-params.pkl" +CACHE_PATH = pathlib.Path.home() / ".cache" / "roborock-cache-data.pkl" + + +async def login_flow() -> UserParams: + """Perform the login flow to obtain UserData from the web API.""" + username = input("๐Ÿ“ง Email: ") + web_api = RoborockApiClient(username=username) + print("๐Ÿ“จ Requesting login code sent to email...") + await web_api.request_code() + code = input("๐Ÿ”‘ Code: ") + user_data = await web_api.code_login(code) + base_url = await web_api.base_url + return UserParams( + username=username, + user_data=user_data, + base_url=base_url, + ) + + +async def get_or_create_session() -> UserParams: + """Initialize the session by logging in if necessary.""" + user_params = await load_value(USER_PARAMS_PATH) + if user_params is None: + print("No cached login data found, please login.") + user_params = await login_flow() + print("โœ… Login successful, caching login data...") + await store_value(USER_PARAMS_PATH, user_params) + return user_params + + +async def test_basic_commands(vacuum): + """Test basic vacuum commands.""" + print("\n" + "=" * 50) + print("๐Ÿงช TESTING BASIC COMMANDS") + print("=" * 50) + + commands = [ + ("Start cleaning", vacuum.start_clean), + ("Pause cleaning", vacuum.pause_clean), + ("Resume cleaning", vacuum.resume_clean), + ("Stop cleaning", vacuum.stop_clean), + ("Return to dock", vacuum.return_to_dock), + ] + + for idx, (name, cmd) in enumerate(commands, 1): + try: + print(f"\n{idx}. Testing: {name}") + await cmd() + print(" โœ… Command sent successfully!") + except Exception as e: + print(f" โŒ Error: {e}") + import traceback + + traceback.print_exc() + + +async def test_advanced_features(vacuum): + """Test advanced cleaning features (based on code comments).""" + print("\n" + "=" * 50) + print("๐Ÿš€ TESTING ADVANCED FEATURES") + print("=" * 50) + + from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP + + # Test different cleaning modes + print("\n1. Testing different cleaning modes...") + + modes = [ + ("Standard cleaning (cmd=1)", 1), + ("Electoral/Area cleaning (cmd=2)", 2), + ("Fast map creation (cmd=4)", 4), + ] + + for mode_name, cmd_value in modes: + try: + # Ask for confirmation for map creation mode + if cmd_value == 4: + print(f"\n โš ๏ธ {mode_name}") + print(" โš ๏ธ WARNING: This will start the map creation process!") + print(" โš ๏ธ The robot will start moving to map your home.") + confirm = input(" Are you sure you want to proceed? (yes/no): ").strip().lower() + if confirm != "yes": + print(" โญ๏ธ Skipped!") + continue + + print(f"\n โ€ข {mode_name}") + await vacuum._command.send( + command=B01_Q10_DP.START_CLEAN, + params={"cmd": cmd_value}, + ) + print(" โœ… Sent!") + except Exception as e: + print(f" โŒ Error: {e}") + + +async def test_device_status(device): + """Test device status monitoring.""" + print("\n" + "=" * 50) + print("๐Ÿ“Š CHECKING DEVICE STATUS") + print("=" * 50) + + try: + # Check if device has properties for status + print(f"\nDevice: {device.name}") + print(f"Product: {device.product.name} ({device.product.model})") + print(f"Connected: {device.is_connected}") + print(f"Local connected: {device.is_local_connected}") + + # Try to get status if available + if device.v1_properties and device.v1_properties.status: + print("\n๐Ÿ” V1 Status available") + try: + await device.v1_properties.status.refresh() + status = device.v1_properties.status + print(f" Status: {status}") + except Exception as e: + print(f" Could not refresh status: {e}") + + # Check Q10 properties + if device.b01_q10_properties: + print("\nโœ… Q10 Properties available") + print(f" Command API: {device.b01_q10_properties.command}") + print(f" Vacuum Trait: {device.b01_q10_properties.vacuum}") + + except Exception as e: + print(f"โŒ Error checking device status: {e}") + import traceback + + traceback.print_exc() + + +async def interactive_menu(vacuum): + """Interactive menu for manual testing.""" + print("\n" + "=" * 50) + print("๐ŸŽฎ INTERACTIVE TEST MENU") + print("=" * 50) + print("\n1. Start cleaning") + print("2. Pause cleaning") + print("3. Resume cleaning") + print("4. Stop cleaning") + print("5. Return to dock") + print("6. Test all modes") + print("0. Exit") + print("=" * 50) + + while True: + try: + choice = input("\nEnter your choice (0-6): ").strip() + + if choice == "0": + print("๐Ÿ‘‹ Exiting...") + break + elif choice == "1": + print("โ–ถ๏ธ Starting cleaning...") + await vacuum.start_clean() + print("โœ… Command sent!") + elif choice == "2": + print("โธ๏ธ Pausing cleaning...") + await vacuum.pause_clean() + print("โœ… Command sent!") + elif choice == "3": + print("โ–ถ๏ธ Resuming cleaning...") + await vacuum.resume_clean() + print("โœ… Command sent!") + elif choice == "4": + print("โน๏ธ Stopping cleaning...") + await vacuum.stop_clean() + print("โœ… Command sent!") + elif choice == "5": + print("๐Ÿ  Returning to dock...") + await vacuum.return_to_dock() + print("โœ… Command sent!") + elif choice == "6": + await test_advanced_features(vacuum) + else: + print("โŒ Invalid choice") + except KeyboardInterrupt: + print("\n๐Ÿ‘‹ Exiting...") + break + except Exception as e: + print(f"โŒ Error: {e}") + + +async def main(): + """Main test function.""" + try: + user_params = await get_or_create_session() + cache = FileCache(CACHE_PATH) + + print("\n๐Ÿ”„ Connecting to devices...") + device_manager = await create_device_manager(user_params, cache=cache) + devices = await device_manager.get_devices() + + print(f"\n๐Ÿ“ฑ Found {len(devices)} device(s)") + for idx, device in enumerate(devices, 1): + print(f" {idx}. {device.name} ({device.product.model})") + + # Select device + if len(devices) == 1: + device = devices[0] + print(f"\nโœ… Using device: {device.name}") + else: + device_idx = int(input("\nSelect device number: ")) - 1 + device = devices[device_idx] + print(f"\nโœ… Selected device: {device.name}") + + # Check Q10 properties + if device.b01_q10_properties is None: + print("\nโŒ This device doesn't have Q10 properties") + await cache.flush() + return + + vacuum = device.b01_q10_properties.vacuum + + # Show main menu + print("\n" + "=" * 50) + print("Q10 ADVANCED TEST SUITE") + print("=" * 50) + print("\n1. Run basic commands test") + print("2. Test advanced features") + print("3. Check device status") + print("4. Interactive menu") + print("5. Run all tests") + print("0. Exit") + print("=" * 50) + + while True: + try: + choice = input("\nSelect test (0-5): ").strip() + + if choice == "0": + break + elif choice == "1": + await test_basic_commands(vacuum) + elif choice == "2": + await test_advanced_features(vacuum) + elif choice == "3": + await test_device_status(device) + elif choice == "4": + await interactive_menu(vacuum) + elif choice == "5": + await test_device_status(device) + await test_basic_commands(vacuum) + await test_advanced_features(vacuum) + else: + print("โŒ Invalid choice") + except KeyboardInterrupt: + print("\n๐Ÿ‘‹ Exiting...") + break + except Exception as e: + print(f"โŒ Error: {e}") + import traceback + + traceback.print_exc() + + await cache.flush() + + except Exception as e: + print(f"\nโŒ Fatal error: {e}") + import traceback + + traceback.print_exc() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/Q10/test_q10_simple.py b/examples/Q10/test_q10_simple.py new file mode 100644 index 00000000..e81d76b7 --- /dev/null +++ b/examples/Q10/test_q10_simple.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +"""Simple test script for Q10 VacuumTrait functionality.""" + +import asyncio +import pathlib + +from roborock.devices.device_manager import UserParams, create_device_manager +from roborock.devices.file_cache import FileCache, load_value, store_value +from roborock.web_api import RoborockApiClient + +# Cache paths +USER_PARAMS_PATH = pathlib.Path.home() / ".cache" / "roborock-user-params.pkl" +CACHE_PATH = pathlib.Path.home() / ".cache" / "roborock-cache-data.pkl" + + +async def login_flow() -> UserParams: + """Perform the login flow to obtain UserData from the web API.""" + username = input("๐Ÿ“ง Email: ") + web_api = RoborockApiClient(username=username) + print("๐Ÿ“จ Requesting login code sent to email...") + await web_api.request_code() + code = input("๐Ÿ”‘ Code: ") + user_data = await web_api.code_login(code) + base_url = await web_api.base_url + return UserParams( + username=username, + user_data=user_data, + base_url=base_url, + ) + + +async def get_or_create_session() -> UserParams: + """Initialize the session by logging in if necessary.""" + user_params = await load_value(USER_PARAMS_PATH) + if user_params is None: + print("No cached login data found, please login.") + user_params = await login_flow() + print("โœ… Login successful, caching login data...") + await store_value(USER_PARAMS_PATH, user_params) + return user_params + + +async def main(): + """Test Q10 vacuum commands.""" + print("๐Ÿ”„ Initializing...") + + try: + user_params = await get_or_create_session() + cache = FileCache(CACHE_PATH) + + print("๐Ÿ”„ Creating device manager...") + device_manager = await create_device_manager(user_params, cache=cache) + + print("๐Ÿ”„ Getting devices...") + devices = await device_manager.get_devices() + + print(f"\n๐Ÿ“ฑ Found {len(devices)} device(s)") + + # List all devices with their properties + for idx, device in enumerate(devices, 1): + print(f"\n Device {idx}: {device.name}") + print(f" Product: {device.product.name} ({device.product.model})") + print(f" Has v1_properties: {device.v1_properties is not None}") + print(f" Has b01_q10_properties: {device.b01_q10_properties is not None}") + + # Check what attributes the device has + attrs = [attr for attr in dir(device) if not attr.startswith("_") and "properties" in attr.lower()] + print(f" Available property APIs: {attrs}") + + # Select device + if len(devices) == 1: + device = devices[0] + print(f"\nโœ… Using device: {device.name}") + else: + device_idx = int(input("\nSelect device number: ")) - 1 + device = devices[device_idx] + print(f"\nโœ… Selected device: {device.name}") + + # Check if it's a Q10 device + if device.b01_q10_properties is None: + print("\nโŒ This device doesn't have Q10 properties") + print(f" Product: {device.product.name} ({device.product.model})") + print("\n๐Ÿ’ก Available properties:") + if device.v1_properties: + print(" - v1_properties (V1 API)") + if hasattr(device, "b01_q7_properties") and device.b01_q7_properties: + print(" - b01_q7_properties (Q7 API)") + await cache.flush() + return + + print("\nโœ… Device has Q10 properties!") + + # Check if vacuum trait exists + if not hasattr(device.b01_q10_properties, "vacuum"): + print("\nโŒ Q10 properties don't have 'vacuum' trait") + print( + f" Available traits: {[attr for attr in dir(device.b01_q10_properties) if not attr.startswith('_')]}" + ) + await cache.flush() + return + + vacuum = device.b01_q10_properties.vacuum + print(f"โœ… Vacuum trait found: {vacuum}") + + print("\n๐Ÿค– Q10 Vacuum Trait Test Menu") + print("=" * 50) + print("1. Start cleaning") + print("2. Pause cleaning") + print("3. Resume cleaning") + print("4. Stop cleaning") + print("5. Return to dock") + print("0. Exit") + print("=" * 50) + + while True: + try: + choice = input("\nEnter your choice (0-5): ").strip() + + if choice == "0": + print("๐Ÿ‘‹ Exiting...") + break + elif choice == "1": + print("โ–ถ๏ธ Starting cleaning...") + await vacuum.start_clean() + print("โœ… Start cleaning command sent!") + elif choice == "2": + print("โธ๏ธ Pausing cleaning...") + await vacuum.pause_clean() + print("โœ… Pause command sent!") + elif choice == "3": + print("โ–ถ๏ธ Resuming cleaning...") + await vacuum.resume_clean() + print("โœ… Resume command sent!") + elif choice == "4": + print("โน๏ธ Stopping cleaning...") + await vacuum.stop_clean() + print("โœ… Stop command sent!") + elif choice == "5": + print("๐Ÿ  Returning to dock...") + await vacuum.return_to_dock() + print("โœ… Return to dock command sent!") + else: + print("โŒ Invalid choice, please try again") + except KeyboardInterrupt: + print("\n๐Ÿ‘‹ Exiting...") + break + except Exception as e: + print(f"โŒ Error: {e}") + import traceback + + traceback.print_exc() + + await cache.flush() + + except Exception as e: + print(f"\nโŒ Fatal error: {e}") + import traceback + + traceback.print_exc() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/Q10/test_q10_vacuum.py b/examples/Q10/test_q10_vacuum.py new file mode 100755 index 00000000..443b62b4 --- /dev/null +++ b/examples/Q10/test_q10_vacuum.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +"""Basic test script for Q10 VacuumTrait functionality.""" + +import asyncio +import pathlib + +from roborock.devices.device_manager import UserParams, create_device_manager +from roborock.devices.file_cache import FileCache, load_value, store_value +from roborock.web_api import RoborockApiClient + +# Cache paths +USER_PARAMS_PATH = pathlib.Path.home() / ".cache" / "roborock-user-params.pkl" +CACHE_PATH = pathlib.Path.home() / ".cache" / "roborock-cache-data.pkl" + + +async def login_flow() -> UserParams: + """Perform the login flow to obtain UserData from the web API.""" + username = input("๐Ÿ“ง Email: ") + web_api = RoborockApiClient(username=username) + print("๐Ÿ“จ Requesting login code sent to email...") + await web_api.request_code() + code = input("๐Ÿ”‘ Code: ") + user_data = await web_api.code_login(code) + base_url = await web_api.base_url + return UserParams( + username=username, + user_data=user_data, + base_url=base_url, + ) + + +async def get_or_create_session() -> UserParams: + """Initialize the session by logging in if necessary.""" + user_params = await load_value(USER_PARAMS_PATH) + if user_params is None: + print("No cached login data found, please login.") + user_params = await login_flow() + print("โœ… Login successful, caching login data...") + await store_value(USER_PARAMS_PATH, user_params) + return user_params + + +async def main(): + """Test Q10 vacuum commands.""" + try: + user_params = await get_or_create_session() + cache = FileCache(CACHE_PATH) + + print("๐Ÿ”„ Connecting to devices...") + device_manager = await create_device_manager(user_params, cache=cache) + devices = await device_manager.get_devices() + + print(f"\n๐Ÿ“ฑ Found {len(devices)} device(s)") + + # List all devices + for idx, device in enumerate(devices, 1): + print(f" {idx}. {device.name} ({device.product.model})") + + # Select device + if len(devices) == 1: + device = devices[0] + print(f"\nโœ… Using device: {device.name}") + else: + device_idx = int(input("\nSelect device number: ")) - 1 + device = devices[device_idx] + print(f"\nโœ… Selected device: {device.name}") + + # Check if it's a Q10 device + if device.b01_q10_properties is None: + print("\nโŒ This device doesn't have Q10 properties") + print(f" Model: {device.product.model}") + await cache.flush() + return + + vacuum = device.b01_q10_properties.vacuum + + print("\n๐Ÿค– Q10 Vacuum Trait Test Menu") + print("=" * 50) + print("1. Start cleaning") + print("2. Pause cleaning") + print("3. Resume cleaning") + print("4. Stop cleaning") + print("5. Return to dock") + print("0. Exit") + print("=" * 50) + + while True: + try: + choice = input("\nEnter your choice (0-5): ").strip() + + if choice == "0": + print("๐Ÿ‘‹ Exiting...") + break + elif choice == "1": + print("โ–ถ๏ธ Starting cleaning...") + await vacuum.start_clean() + print("โœ… Start cleaning command sent!") + elif choice == "2": + print("โธ๏ธ Pausing cleaning...") + await vacuum.pause_clean() + print("โœ… Pause command sent!") + elif choice == "3": + print("โ–ถ๏ธ Resuming cleaning...") + await vacuum.resume_clean() + print("โœ… Resume command sent!") + elif choice == "4": + print("โน๏ธ Stopping cleaning...") + await vacuum.stop_clean() + print("โœ… Stop command sent!") + elif choice == "5": + print("๐Ÿ  Returning to dock...") + await vacuum.return_to_dock() + print("โœ… Return to dock command sent!") + else: + print("โŒ Invalid choice, please try again") + except KeyboardInterrupt: + print("\n๐Ÿ‘‹ Exiting...") + break + except Exception as e: + print(f"โŒ Error: {e}") + import traceback + + traceback.print_exc() + + await cache.flush() + + except Exception as e: + print(f"\nโŒ Fatal error: {e}") + import traceback + + traceback.print_exc() + + +if __name__ == "__main__": + asyncio.run(main()) From 48f30d3395e695ed97cd699fc21767c466f10154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Sun, 1 Feb 2026 08:22:44 +0100 Subject: [PATCH 2/3] fix: correct formatting in README for cleaning modes and available APIs --- examples/Q10/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Q10/README.md b/examples/Q10/README.md index 806838cb..6caf339b 100644 --- a/examples/Q10/README.md +++ b/examples/Q10/README.md @@ -114,7 +114,7 @@ The VacuumTrait provides these commands: Additional cleaning modes can be tested with `test_q10_advanced.py` (option 2): - **Standard cleaning (cmd=1)** - Full cleaning cycle -- **Electoral/Area cleaning (cmd=2)** - Clean specific areas/zones +- **Electoral/Area cleaning (cmd=2)** - Clean specific areas/zones - **Fast map creation (cmd=4)** - Quickly generate room map - โš ๏ธ **Warning**: This will start the robot moving! - Requires explicit confirmation before execution @@ -127,7 +127,7 @@ The `test_q10_advanced.py` script (option 3) provides: - **Local connected**: Whether device is reachable on your local network - `True` = Direct local connection (LAN/WiFi) - `False` = Cloud connection via Roborock servers -- **Available APIs**: +- **Available APIs**: - `Command API` = Low-level command interface - `Vacuum Trait` = High-level vacuum control interface From dffa387f85f0181b425e1fc11b35159b32d60323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Sun, 1 Feb 2026 08:25:01 +0100 Subject: [PATCH 3/3] fix(q10): Rename test scripts to avoid pytest discovery - Rename test_q10_*.py to run_q10_*.py to prevent pytest from treating them as unit tests - These are interactive scripts, not automated tests - Update README with new script names - Fixes pytest failures in CI --- examples/Q10/README.md | 18 +++++++++--------- ...est_q10_advanced.py => run_q10_advanced.py} | 0 .../{test_q10_simple.py => run_q10_simple.py} | 0 .../{test_q10_vacuum.py => run_q10_vacuum.py} | 0 4 files changed, 9 insertions(+), 9 deletions(-) rename examples/Q10/{test_q10_advanced.py => run_q10_advanced.py} (100%) rename examples/Q10/{test_q10_simple.py => run_q10_simple.py} (100%) rename examples/Q10/{test_q10_vacuum.py => run_q10_vacuum.py} (100%) diff --git a/examples/Q10/README.md b/examples/Q10/README.md index 6caf339b..39b787b8 100644 --- a/examples/Q10/README.md +++ b/examples/Q10/README.md @@ -4,7 +4,7 @@ This directory contains test scripts for the Q10 VacuumTrait functionality added ## Scripts -### test_q10_simple.py +### run_q10_simple.py Interactive test script with detailed debug information. This script: - Shows comprehensive device information - Lists all available property APIs @@ -16,10 +16,10 @@ Interactive test script with detailed debug information. This script: - You need to debug connection or API issues - You want detailed information about what's happening -### test_q10_vacuum.py +### run_q10_vacuum.py Basic test script for Q10 vacuum commands. A simpler version focused on testing the vacuum trait. -### test_q10_advanced.py (NEW!) +### run_q10_advanced.py (NEW!) Advanced test suite with complex features and detailed diagnostics: - Tests multiple cleaning modes (standard, area, fast map) - Device status monitoring and diagnostics @@ -47,13 +47,13 @@ pip install -e . 2. Run a test script: ```bash # Simple script with debugging -python examples/Q10/test_q10_simple.py +python examples/Q10/run_q10_simple.py # Basic script (minimal output) -python examples/Q10/test_q10_vacuum.py +python examples/Q10/run_q10_vacuum.py # Advanced script with diagnostics -python examples/Q10/test_q10_advanced.py +python examples/Q10/run_q10_advanced.py ``` 3. On first run, you'll be prompted to log in: @@ -66,7 +66,7 @@ python examples/Q10/test_q10_advanced.py #### test_q10_simple.py ```bash -$ python examples/Q10/test_q10_simple.py +$ python examples/Q10/run_q10_simple.py ๐Ÿ“ฑ Found 1 device(s) Device 1: Roborock Q10 S5+ โœ… Using device: Roborock Q10 S5+ @@ -78,7 +78,7 @@ Device 1: Roborock Q10 S5+ #### test_q10_vacuum.py ```bash -$ python examples/Q10/test_q10_vacuum.py +$ python examples/Q10/run_q10_vacuum.py ๐Ÿ“ฑ Found 1 device(s) 1. Roborock Q10 S5+ (roborock.vacuum.ss07) โœ… Using device: Roborock Q10 S5+ @@ -88,7 +88,7 @@ $ python examples/Q10/test_q10_vacuum.py #### test_q10_advanced.py ```bash -$ python examples/Q10/test_q10_advanced.py +$ python examples/Q10/run_q10_advanced.py [Main Menu] 1. Run basic commands test 2. Test advanced features diff --git a/examples/Q10/test_q10_advanced.py b/examples/Q10/run_q10_advanced.py similarity index 100% rename from examples/Q10/test_q10_advanced.py rename to examples/Q10/run_q10_advanced.py diff --git a/examples/Q10/test_q10_simple.py b/examples/Q10/run_q10_simple.py similarity index 100% rename from examples/Q10/test_q10_simple.py rename to examples/Q10/run_q10_simple.py diff --git a/examples/Q10/test_q10_vacuum.py b/examples/Q10/run_q10_vacuum.py similarity index 100% rename from examples/Q10/test_q10_vacuum.py rename to examples/Q10/run_q10_vacuum.py