benchlab-pytools 3.2.1__tar.gz → 3.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/CHANGELOG.md +39 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/PKG-INFO +36 -11
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/README.md +20 -1
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/__init__.py +1 -1
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/README.md +49 -6
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/config_client.py +238 -3
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/config_manager.py +76 -6
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/config_tool.py +34 -7
- benchlab_pytools-3.3.1/benchlab/config/http_dto.py +109 -0
- benchlab_pytools-3.3.1/benchlab/config/test_http_config_client.py +153 -0
- benchlab_pytools-3.3.1/benchlab/config/test_http_dto.py +115 -0
- benchlab_pytools-3.3.1/benchlab/flash/README.md +229 -0
- benchlab_pytools-3.3.1/benchlab/flash/__init__.py +6 -0
- benchlab_pytools-3.3.1/benchlab/flash/dfu.py +249 -0
- benchlab_pytools-3.3.1/benchlab/flash/flash_manager.py +509 -0
- benchlab_pytools-3.3.1/benchlab/flash/flash_tool.py +624 -0
- benchlab_pytools-3.3.1/benchlab/flash/image.py +178 -0
- benchlab_pytools-3.3.1/benchlab/flash/requirements.txt +11 -0
- benchlab_pytools-3.3.1/benchlab/flash/test_dfu.py +211 -0
- benchlab_pytools-3.3.1/benchlab/flash/test_flash_manager.py +560 -0
- benchlab_pytools-3.3.1/benchlab/flash/test_flash_tool.py +350 -0
- benchlab_pytools-3.3.1/benchlab/flash/test_image.py +158 -0
- benchlab_pytools-3.3.1/benchlab/link/requirements.txt +3 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/main.py +35 -7
- benchlab_pytools-3.3.1/benchlab/mqtt/requirements.txt +3 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tools.py +12 -1
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/pyproject.toml +27 -6
- benchlab_pytools-3.2.1/benchlab/mqtt/requirements.txt +0 -3
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/.flake8 +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/.gitignore +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/CONTRIBUTING.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/LICENSE +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/__main__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/bootstrap.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/diff.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/examples/complete_config.json +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/examples/rgb_lighting.json +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/examples/simple_fan.json +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/schema.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/test_config_client.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/test_config_manager.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/config/test_diff.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/config.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/datasource.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/datasource_manager.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/device_registry.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/discovery.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/infrastructure.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/process_manager.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/retry.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/shared_serial.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/core/statistics.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/csv_log/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/csv_log/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/csv_log/csv_logger_enhanced.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/csv_log/message_batcher.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/csv_log/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/csv_log/smart_retry.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/graph/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/graph/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/graph/app.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/graph/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/graph/runner.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/graph/test_graph.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/graph/ui.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/hwinfo/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/hwinfo/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/hwinfo/hwinfo_export.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/hwinfo/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/hwinfo/test_hwinfo_export.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/launcher.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/link/.env.EXAMPLE +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/link/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/link/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/link/link_main.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/link/test_link_main.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/menu.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/menu_classic.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/mqtt/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/mqtt/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/mqtt/mqtt.config_template +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/mqtt/mqtt_publisher.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/mqtt/test_mqtt_publisher.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/prefs.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/.env.example +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/favicon.ico +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/readme.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/telemetry_api.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/test_read_recovery.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/restapi/test_server.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/sources.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/config.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/test_tui_core.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/test_tui_main.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/tui_core.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/tui/tui_main.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/NOTICE.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/Running_from_source_on_linux.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/assets/vu1_hello_world.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/community_applications.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/config.yaml +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/database.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/dial_driver.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/dials/Comms_Hub_Gauge.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/dials/Comms_Hub_Server.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/dials/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/dials/base_logger.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/installer/inc/icon.ico +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/installer/install.nsi +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/make_version.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/pylintrc +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/serial_driver.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/server.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/server_config.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/server_dial_handler.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/vu_notifications.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/android-chrome-192x192.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/android-chrome-512x512.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/apple-touch-icon.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/demo.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/demo.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/demo.rtl.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/demo.rtl.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.rtl.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-flags.rtl.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.rtl.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-payments.rtl.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-social.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-social.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-social.rtl.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-social.rtl.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.rtl.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler-vendors.rtl.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler.rtl.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/tabler.rtl.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/css/theme.min.css +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/img/header.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/img/logo.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/dark-theme.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/dark-theme.min.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/demo.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/demo.min.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/jquery.min.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/tabler.esm.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/tabler.esm.min.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/tabler.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/tabler.min.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/vu1_api.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_api_key_settings.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_api_keys.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_dial.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_home.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/assets/js/vu1_gui_root.js +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/favicon-16x16.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/favicon-32x32.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/favicon.ico +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/favicon.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/index.html +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/views/api_keys.html +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/views/dial.html +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/views/footer.html +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/views/header.html +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/views/key_settings.html +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/VU-Server/www/views/start.html +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/assets/Barlow-Bold.ttf +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/assets/bl_dial_144x200.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/assets/bl_logo_144x200.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/devices.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/sensors.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/test_devices.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/test_vu_server_config.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/test_vu_server_manager.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/test_vu_tui.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/vu_dial.config_template +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/vu_logo_gen.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/vu_server.config_template +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/vu_server_config.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/vu_server_manager.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/vu_tui.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/vu/vu_updater.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/README.md +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/__init__.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/assets/Inter-Bold.ttf +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/assets/Inter.ttf +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/assets/benchlab.png +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/benchlab_fleet.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/benchlab_graph.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/benchlab_overview.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/benchlab_telemetry.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/benchlab_ui.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/benchlab_utils.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/requirements.txt +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/test_wigidash.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/wigidash_device.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/wigidash_manager.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/wigidash_session.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/wigidash_usb.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab/wigidash/wigidash_widget.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/benchlab.py +0 -0
- {benchlab_pytools-3.2.1 → benchlab_pytools-3.3.1}/pytest.ini +0 -0
|
@@ -4,6 +4,43 @@ All notable changes to BENCHLAB PyTools are documented here. Format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows
|
|
5
5
|
[SemVer](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [3.3.1] - 2026-10-17
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Packaging: `benchlab-pytools[flash]` was missing `pydantic` and
|
|
11
|
+
`requests` (`flash_manager` imports `config_manager` -> `config.schema`
|
|
12
|
+
-> `pydantic`; `--source service_http` needs `requests`), so a clean
|
|
13
|
+
`[flash]`-only install crashed with `ModuleNotFoundError` on first use.
|
|
14
|
+
The launcher also reported `ModuleNotFoundError` as "Flash tool module
|
|
15
|
+
not available in this build" regardless of cause; it now prints the
|
|
16
|
+
real missing module and the install command. (#90)
|
|
17
|
+
- `check_bootloader_supported()` only gated BENCHLAB1 firmware against
|
|
18
|
+
the CMD_BOOTLOADER version floor; a BENCHLAB2 unit on firmware below
|
|
19
|
+
`0x05` (which predates CMD_BOOTLOADER on BL2, per the firmware repo's
|
|
20
|
+
protocol contract freeze history) passed the check and then hit a 5s
|
|
21
|
+
timeout in `enter_dfu_mode()` instead of being told up front to use a
|
|
22
|
+
physical BOOT0/jumper bootloader entry. (#94)
|
|
23
|
+
|
|
24
|
+
## [3.3.0] - 2026-10-01
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Firmware flash tool (`-flash`, `benchlab.flash`): flashes new firmware
|
|
28
|
+
onto BENCHLAB1/2 devices (STM32F303RB) over USB DFU, commanding the
|
|
29
|
+
running firmware to jump into ST's factory ROM bootloader and talking to
|
|
30
|
+
it via a pure-Python `pyusb` client (no external flashing binary
|
|
31
|
+
required). Supports `.bin` (primary), `.hex`, and `.elf` images, and
|
|
32
|
+
`direct`/`named_pipe`/`service_http` sources for the initial CDC-side
|
|
33
|
+
bootloader jump. Falls back to a guided manual BOOT0 entry for firmware
|
|
34
|
+
too old to support the CDC bootloader command. (#84)
|
|
35
|
+
- `HttpConfigClient` (`service_http` source) now implements fan/RGB/
|
|
36
|
+
calibration read-write (`read_fan_config`/`write_fan_config`,
|
|
37
|
+
`read_rgb_config`/`write_rgb_config`, `read_calibration`/
|
|
38
|
+
`write_calibration`), via a new translation layer
|
|
39
|
+
(`benchlab.config.http_dto`) between the BL_Service HTTP API's DTO JSON
|
|
40
|
+
shapes and this repo's existing config-dict convention. Handles BL2's
|
|
41
|
+
wider calibration wire struct correctly. `config_tool.py` (`-config`)
|
|
42
|
+
gained `--source service_http` CLI support to match. (#86)
|
|
43
|
+
|
|
7
44
|
## [3.2.1] - 2026-10-01
|
|
8
45
|
|
|
9
46
|
### Fixed
|
|
@@ -173,6 +210,8 @@ export, MQTT publisher, VU dials, WigiDash, and config import/export tools,
|
|
|
173
210
|
sharing a common data-source layer (direct serial, FastAPI, MQTT, named
|
|
174
211
|
pipe, service HTTP, service WebSocket).
|
|
175
212
|
|
|
213
|
+
[3.3.1]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.3.0...v3.3.1
|
|
214
|
+
[3.3.0]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.2.1...v3.3.0
|
|
176
215
|
[3.2.1]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.2.0...v3.2.1
|
|
177
216
|
[3.2.0]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.1.0...v3.2.0
|
|
178
217
|
[3.1.0]: https://github.com/BenchLab-io/benchlab-pytools/compare/v3.0.4...v3.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: benchlab-pytools
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.1
|
|
4
4
|
Summary: Python control suite for BENCHLAB telemetry devices
|
|
5
5
|
Project-URL: Homepage, https://benchlab.io/
|
|
6
6
|
Project-URL: Repository, https://github.com/BenchLab-io/benchlab-pytools
|
|
@@ -24,16 +24,18 @@ Classifier: Topic :: System :: Monitoring
|
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Requires-Dist: benchlab-pycore>=0.8.0
|
|
26
26
|
Requires-Dist: packaging<26.0,>=23.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
27
28
|
Requires-Dist: python-dotenv<2.0,>=1.2.2
|
|
28
29
|
Requires-Dist: pywin32>=306; platform_system == 'Windows'
|
|
30
|
+
Requires-Dist: requests>=2.33.1
|
|
29
31
|
Requires-Dist: textual<9.0,>=0.60
|
|
30
32
|
Requires-Dist: windows-curses>=2.4.1; platform_system == 'Windows'
|
|
31
33
|
Provides-Extra: all
|
|
32
34
|
Requires-Dist: amqtt<0.13.0,>=0.12.0; extra == 'all'
|
|
33
|
-
Requires-Dist: benchlab-pycore>=0.8.0; extra == 'all'
|
|
34
35
|
Requires-Dist: blessed>=1.20.0; extra == 'all'
|
|
35
36
|
Requires-Dist: dearpygui>=2.1.1; extra == 'all'
|
|
36
37
|
Requires-Dist: fastapi==0.111.1; extra == 'all'
|
|
38
|
+
Requires-Dist: intelhex>=2.3.0; extra == 'all'
|
|
37
39
|
Requires-Dist: libusb-package>=1.0.26.1; (sys_platform == 'win32') and extra == 'all'
|
|
38
40
|
Requires-Dist: matplotlib>=3.10.7; extra == 'all'
|
|
39
41
|
Requires-Dist: numpy; extra == 'all'
|
|
@@ -41,49 +43,53 @@ Requires-Dist: numpy>=2.2.0; extra == 'all'
|
|
|
41
43
|
Requires-Dist: paho-mqtt<3.0,>=2.0; extra == 'all'
|
|
42
44
|
Requires-Dist: pillow>=10.0.0; extra == 'all'
|
|
43
45
|
Requires-Dist: pillow>=11.3.0; extra == 'all'
|
|
44
|
-
Requires-Dist:
|
|
46
|
+
Requires-Dist: pyelftools>=0.31; extra == 'all'
|
|
45
47
|
Requires-Dist: pyserial>=3.5; extra == 'all'
|
|
46
48
|
Requires-Dist: python-dotenv==1.2.2; extra == 'all'
|
|
47
49
|
Requires-Dist: pyusb>=1.3.1; extra == 'all'
|
|
48
50
|
Requires-Dist: pyyaml<7.0,>=6.0; extra == 'all'
|
|
49
51
|
Requires-Dist: pyyaml>=6.0; extra == 'all'
|
|
50
|
-
Requires-Dist: requests>=2.31.0; extra == 'all'
|
|
51
|
-
Requires-Dist: requests>=2.33.1; extra == 'all'
|
|
52
52
|
Requires-Dist: ruamel-yaml; extra == 'all'
|
|
53
53
|
Requires-Dist: tornado; extra == 'all'
|
|
54
|
+
Requires-Dist: tqdm>=4.66; extra == 'all'
|
|
54
55
|
Requires-Dist: uvicorn[standard]==0.24.0; extra == 'all'
|
|
55
56
|
Requires-Dist: websockets<16.0,>=13.0; extra == 'all'
|
|
56
57
|
Provides-Extra: csv-log
|
|
57
|
-
Requires-Dist: benchlab-pycore>=0.8.0; extra == 'csv-log'
|
|
58
58
|
Requires-Dist: pyserial>=3.5; extra == 'csv-log'
|
|
59
|
+
Provides-Extra: flash
|
|
60
|
+
Requires-Dist: intelhex>=2.3.0; extra == 'flash'
|
|
61
|
+
Requires-Dist: libusb-package>=1.0.26.1; (sys_platform == 'win32') and extra == 'flash'
|
|
62
|
+
Requires-Dist: pyelftools>=0.31; extra == 'flash'
|
|
63
|
+
Requires-Dist: pyserial>=3.5; extra == 'flash'
|
|
64
|
+
Requires-Dist: pyusb>=1.3.1; extra == 'flash'
|
|
65
|
+
Requires-Dist: tqdm>=4.66; extra == 'flash'
|
|
59
66
|
Provides-Extra: graph
|
|
60
67
|
Requires-Dist: dearpygui>=2.1.1; extra == 'graph'
|
|
61
68
|
Requires-Dist: pyserial>=3.5; extra == 'graph'
|
|
62
69
|
Provides-Extra: hwinfo
|
|
63
70
|
Requires-Dist: pyserial>=3.5; extra == 'hwinfo'
|
|
71
|
+
Provides-Extra: link
|
|
72
|
+
Requires-Dist: paho-mqtt<3.0,>=2.0; extra == 'link'
|
|
73
|
+
Requires-Dist: pyserial>=3.5; extra == 'link'
|
|
64
74
|
Provides-Extra: mqtt
|
|
65
75
|
Requires-Dist: amqtt<0.13.0,>=0.12.0; extra == 'mqtt'
|
|
66
76
|
Requires-Dist: paho-mqtt<3.0,>=2.0; extra == 'mqtt'
|
|
67
77
|
Requires-Dist: pyyaml<7.0,>=6.0; extra == 'mqtt'
|
|
68
78
|
Provides-Extra: restapi
|
|
69
79
|
Requires-Dist: fastapi==0.111.1; extra == 'restapi'
|
|
70
|
-
Requires-Dist: pydantic>=2.0.0; extra == 'restapi'
|
|
71
80
|
Requires-Dist: pyserial>=3.5; extra == 'restapi'
|
|
72
81
|
Requires-Dist: python-dotenv==1.2.2; extra == 'restapi'
|
|
73
82
|
Requires-Dist: uvicorn[standard]==0.24.0; extra == 'restapi'
|
|
74
83
|
Provides-Extra: service-ws
|
|
75
84
|
Requires-Dist: websockets<16.0,>=13.0; extra == 'service-ws'
|
|
76
85
|
Provides-Extra: tui
|
|
77
|
-
Requires-Dist: pydantic>=2.0.0; extra == 'tui'
|
|
78
86
|
Requires-Dist: pyserial>=3.5; extra == 'tui'
|
|
79
|
-
Requires-Dist: requests>=2.33.1; extra == 'tui'
|
|
80
87
|
Provides-Extra: vu
|
|
81
88
|
Requires-Dist: blessed>=1.20.0; extra == 'vu'
|
|
82
89
|
Requires-Dist: numpy; extra == 'vu'
|
|
83
90
|
Requires-Dist: pillow>=10.0.0; extra == 'vu'
|
|
84
91
|
Requires-Dist: pyserial>=3.5; extra == 'vu'
|
|
85
92
|
Requires-Dist: pyyaml>=6.0; extra == 'vu'
|
|
86
|
-
Requires-Dist: requests>=2.31.0; extra == 'vu'
|
|
87
93
|
Requires-Dist: ruamel-yaml; extra == 'vu'
|
|
88
94
|
Requires-Dist: tornado; extra == 'vu'
|
|
89
95
|
Provides-Extra: wigidash
|
|
@@ -109,6 +115,7 @@ BENCHLAB PyTools is the Python-based control suite for BENCHLAB telemetry device
|
|
|
109
115
|
- **VU Dials** — analog-style VU meter dial display and configuration
|
|
110
116
|
- **WigiDash** — telemetry/graph display on a G.SKILL WigiDash panel
|
|
111
117
|
- **Config Tool** — import/export device configuration (fan curves, RGB, etc.) via JSON
|
|
118
|
+
- **Flash Tool** — flash new firmware onto BENCHLAB1/2 devices over USB DFU
|
|
112
119
|
|
|
113
120
|
All tools share a common data-source layer, so the same telemetry can be read directly from a device, through a FastAPI server, over MQTT, or via the Windows BENCHLAB service — locally or remotely — without changing the consumer tool.
|
|
114
121
|
|
|
@@ -176,8 +183,25 @@ python -m benchlab -vu # VU analog dials
|
|
|
176
183
|
python -m benchlab -vuconfig # VU dial configuration UI
|
|
177
184
|
python -m benchlab -wigidash # WigiDash display
|
|
178
185
|
python -m benchlab -config ... # Device configuration import/export
|
|
186
|
+
python -m benchlab -flash ... # Firmware flash tool (STM32 ROM DFU)
|
|
179
187
|
```
|
|
180
188
|
|
|
189
|
+
The flash tool needs its extra: `pip install "benchlab-pytools[flash]"`. It takes a firmware
|
|
190
|
+
image with `--file` and does not download one; releases live in
|
|
191
|
+
[BENCHLAB-FW](https://github.com/BenchLab-io/BENCHLAB-FW/releases). Start with `--list` to see what
|
|
192
|
+
is connected, and `--verify-only` to check an image against a device without erasing or writing:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
python -m benchlab -flash --list
|
|
196
|
+
python -m benchlab -flash --file benchlab2-fw07-v0.7.2.bin --verify-only
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**On Windows, flashing needs a one-time driver step.** In DFU mode the board enumerates as
|
|
200
|
+
ST's ROM bootloader (VID `0483`, PID `DF11`), which does not bind to a driver libusb can use,
|
|
201
|
+
so the tool cannot see a device that is plugged in and sitting in its bootloader. Bind WinUSB
|
|
202
|
+
to it with [Zadig](https://zadig.akeo.ie/) first -- see
|
|
203
|
+
[Windows: USB driver setup](benchlab/flash/README.md#windows-usb-driver-setup-zadig).
|
|
204
|
+
|
|
181
205
|
Running with no flags is equivalent to launching the interactive menu.
|
|
182
206
|
|
|
183
207
|
### Data Sources
|
|
@@ -218,7 +242,7 @@ Common connection flags:
|
|
|
218
242
|
-i, --interval SECONDS Refresh interval (default: 1.0)
|
|
219
243
|
```
|
|
220
244
|
|
|
221
|
-
When a source needs a background service (`fastapi`, `mqtt`), the launcher starts and health-checks it automatically, and tears it down on exit. Not every tool supports every source — the config
|
|
245
|
+
When a source needs a background service (`fastapi`, `mqtt`), the launcher starts and health-checks it automatically, and tears it down on exit. Not every tool supports every source — the config and flash tools, for example, only support `direct`, `named_pipe`, and `service_http`. See each tool's README for specifics.
|
|
222
246
|
|
|
223
247
|
### Launch Profiles
|
|
224
248
|
|
|
@@ -259,6 +283,7 @@ Each consumer tool lives in its own subpackage under `benchlab/` with its own RE
|
|
|
259
283
|
- [Core](benchlab/core/README.md) — shared internals (data sources, process management, discovery)
|
|
260
284
|
- [CSV Logger](benchlab/csv_log/README.md)
|
|
261
285
|
- [FastAPI Server](benchlab/restapi/readme.md)
|
|
286
|
+
- [Flash Tool](benchlab/flash/README.md) — firmware flashing over USB DFU
|
|
262
287
|
- [Graph](benchlab/graph/README.md)
|
|
263
288
|
- [HWiNFO Export](benchlab/hwinfo/README.md)
|
|
264
289
|
- [Link](benchlab/link/README.md) — publish telemetry to BENCHLAB cloud
|
|
@@ -12,6 +12,7 @@ BENCHLAB PyTools is the Python-based control suite for BENCHLAB telemetry device
|
|
|
12
12
|
- **VU Dials** — analog-style VU meter dial display and configuration
|
|
13
13
|
- **WigiDash** — telemetry/graph display on a G.SKILL WigiDash panel
|
|
14
14
|
- **Config Tool** — import/export device configuration (fan curves, RGB, etc.) via JSON
|
|
15
|
+
- **Flash Tool** — flash new firmware onto BENCHLAB1/2 devices over USB DFU
|
|
15
16
|
|
|
16
17
|
All tools share a common data-source layer, so the same telemetry can be read directly from a device, through a FastAPI server, over MQTT, or via the Windows BENCHLAB service — locally or remotely — without changing the consumer tool.
|
|
17
18
|
|
|
@@ -79,8 +80,25 @@ python -m benchlab -vu # VU analog dials
|
|
|
79
80
|
python -m benchlab -vuconfig # VU dial configuration UI
|
|
80
81
|
python -m benchlab -wigidash # WigiDash display
|
|
81
82
|
python -m benchlab -config ... # Device configuration import/export
|
|
83
|
+
python -m benchlab -flash ... # Firmware flash tool (STM32 ROM DFU)
|
|
82
84
|
```
|
|
83
85
|
|
|
86
|
+
The flash tool needs its extra: `pip install "benchlab-pytools[flash]"`. It takes a firmware
|
|
87
|
+
image with `--file` and does not download one; releases live in
|
|
88
|
+
[BENCHLAB-FW](https://github.com/BenchLab-io/BENCHLAB-FW/releases). Start with `--list` to see what
|
|
89
|
+
is connected, and `--verify-only` to check an image against a device without erasing or writing:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python -m benchlab -flash --list
|
|
93
|
+
python -m benchlab -flash --file benchlab2-fw07-v0.7.2.bin --verify-only
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**On Windows, flashing needs a one-time driver step.** In DFU mode the board enumerates as
|
|
97
|
+
ST's ROM bootloader (VID `0483`, PID `DF11`), which does not bind to a driver libusb can use,
|
|
98
|
+
so the tool cannot see a device that is plugged in and sitting in its bootloader. Bind WinUSB
|
|
99
|
+
to it with [Zadig](https://zadig.akeo.ie/) first -- see
|
|
100
|
+
[Windows: USB driver setup](benchlab/flash/README.md#windows-usb-driver-setup-zadig).
|
|
101
|
+
|
|
84
102
|
Running with no flags is equivalent to launching the interactive menu.
|
|
85
103
|
|
|
86
104
|
### Data Sources
|
|
@@ -121,7 +139,7 @@ Common connection flags:
|
|
|
121
139
|
-i, --interval SECONDS Refresh interval (default: 1.0)
|
|
122
140
|
```
|
|
123
141
|
|
|
124
|
-
When a source needs a background service (`fastapi`, `mqtt`), the launcher starts and health-checks it automatically, and tears it down on exit. Not every tool supports every source — the config
|
|
142
|
+
When a source needs a background service (`fastapi`, `mqtt`), the launcher starts and health-checks it automatically, and tears it down on exit. Not every tool supports every source — the config and flash tools, for example, only support `direct`, `named_pipe`, and `service_http`. See each tool's README for specifics.
|
|
125
143
|
|
|
126
144
|
### Launch Profiles
|
|
127
145
|
|
|
@@ -162,6 +180,7 @@ Each consumer tool lives in its own subpackage under `benchlab/` with its own RE
|
|
|
162
180
|
- [Core](benchlab/core/README.md) — shared internals (data sources, process management, discovery)
|
|
163
181
|
- [CSV Logger](benchlab/csv_log/README.md)
|
|
164
182
|
- [FastAPI Server](benchlab/restapi/readme.md)
|
|
183
|
+
- [Flash Tool](benchlab/flash/README.md) — firmware flashing over USB DFU
|
|
165
184
|
- [Graph](benchlab/graph/README.md)
|
|
166
185
|
- [HWiNFO Export](benchlab/hwinfo/README.md)
|
|
167
186
|
- [Link](benchlab/link/README.md) — publish telemetry to BENCHLAB cloud
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# BENCHLAB Device Configuration Tool
|
|
2
2
|
|
|
3
|
-
A command-line tool for importing and exporting BENCHLAB device configuration via JSON files. Supports
|
|
3
|
+
A command-line tool for importing and exporting BENCHLAB device configuration via JSON files. Supports direct serial (pycore), Windows named pipe (BL_Service), and the BL_Service HTTP API as data sources.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Export Configuration**: Read current device settings and save to JSON
|
|
8
8
|
- **Import Configuration**: Apply JSON configuration to devices
|
|
9
|
-
- **Multi-Source Support**: Works with
|
|
9
|
+
- **Multi-Source Support**: Works with direct serial, named pipe, and HTTP connections
|
|
10
10
|
- **Interactive Mode**: Simple interface for loading JSON configs
|
|
11
11
|
- **Validation**: Schema validation with detailed error messages
|
|
12
12
|
- **Batch Operations**: Configure multiple devices from a single JSON file
|
|
@@ -29,6 +29,11 @@ For named pipe support (Windows only), you need:
|
|
|
29
29
|
pip install pywin32
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
For `service_http` support (talking to a running BL_Service instance over HTTP, any platform), you need:
|
|
33
|
+
```bash
|
|
34
|
+
pip install requests
|
|
35
|
+
```
|
|
36
|
+
|
|
32
37
|
## Usage
|
|
33
38
|
|
|
34
39
|
### Interactive Mode
|
|
@@ -55,6 +60,11 @@ python -m benchlab -config --list
|
|
|
55
60
|
|
|
56
61
|
# List devices via named pipe (Windows only)
|
|
57
62
|
python -m benchlab -config --list --source named_pipe
|
|
63
|
+
|
|
64
|
+
# List devices via the BL_Service HTTP API
|
|
65
|
+
python -m benchlab -config --list --source service_http
|
|
66
|
+
python -m benchlab -config --list --source service_http \
|
|
67
|
+
--service-url http://localhost:8585 --service-token secret
|
|
58
68
|
```
|
|
59
69
|
|
|
60
70
|
#### Export Configuration
|
|
@@ -68,6 +78,9 @@ python -m benchlab -config --export my_config.json --device COM4
|
|
|
68
78
|
|
|
69
79
|
# Export via named pipe
|
|
70
80
|
python -m benchlab -config --export my_config.json --source named_pipe
|
|
81
|
+
|
|
82
|
+
# Export via the BL_Service HTTP API
|
|
83
|
+
python -m benchlab -config --export my_config.json --source service_http
|
|
71
84
|
```
|
|
72
85
|
|
|
73
86
|
#### Import Configuration
|
|
@@ -88,6 +101,9 @@ python -m benchlab -config --import my_config.json --yes
|
|
|
88
101
|
|
|
89
102
|
# Import via named pipe
|
|
90
103
|
python -m benchlab -config --import my_config.json --source named_pipe
|
|
104
|
+
|
|
105
|
+
# Import via the BL_Service HTTP API
|
|
106
|
+
python -m benchlab -config --import my_config.json --source service_http
|
|
91
107
|
```
|
|
92
108
|
|
|
93
109
|
## JSON Configuration Format
|
|
@@ -120,6 +136,7 @@ python -m benchlab -config --import my_config.json --source named_pipe
|
|
|
120
136
|
- **`port`**: Match by serial port (e.g., "COM4" - not portable)
|
|
121
137
|
- **`productId`**: Match by product ID (0x10 or 0x11)
|
|
122
138
|
- **`pipeName`**: Match by named pipe name (named_pipe source only)
|
|
139
|
+
- **`guid`** is also required when using `service_http`, since the service addresses devices by their UID rather than a port or pipe name.
|
|
123
140
|
|
|
124
141
|
**Note:** When exporting, the tool automatically uses `guid` selector with the device UID for portability. This allows you to export configuration on one system and import it on another, and the tool will find the same device regardless of which COM port it's connected to.
|
|
125
142
|
|
|
@@ -235,6 +252,25 @@ Uses the Windows BL_Service named pipe interface for communication.
|
|
|
235
252
|
- Requires BL_Service to be running
|
|
236
253
|
- Requires pywin32 library
|
|
237
254
|
|
|
255
|
+
### Service HTTP (BL_Service HTTP API)
|
|
256
|
+
|
|
257
|
+
Talks to a running BL_Service instance over its HTTP API (`--service-url`,
|
|
258
|
+
default `http://localhost:8585`). Fan, RGB, and calibration read/write go
|
|
259
|
+
through a translation layer (`benchlab/config/http_dto.py`) between the
|
|
260
|
+
service's DTO JSON shapes and this tool's own config format — see that
|
|
261
|
+
module if you're integrating against the HTTP API directly rather than
|
|
262
|
+
through this tool.
|
|
263
|
+
|
|
264
|
+
**Advantages:**
|
|
265
|
+
- Cross-platform (the service itself may run anywhere reachable over HTTP)
|
|
266
|
+
- Multiple tools can access the device simultaneously
|
|
267
|
+
- No direct serial port management
|
|
268
|
+
|
|
269
|
+
**Limitations:**
|
|
270
|
+
- Requires BL_Service to be running and reachable
|
|
271
|
+
- Requires the `requests` library
|
|
272
|
+
- Device selector must use `guid` (the service addresses devices by UID, not port/pipe name)
|
|
273
|
+
|
|
238
274
|
## Configuration Profiles
|
|
239
275
|
|
|
240
276
|
### Profile IDs
|
|
@@ -276,6 +312,11 @@ Use `--dry-run` to preview exactly what would change on the device (connects and
|
|
|
276
312
|
- Check Windows service status
|
|
277
313
|
- Verify device is connected to service
|
|
278
314
|
|
|
315
|
+
**Service HTTP:**
|
|
316
|
+
- Ensure BL_Service is running and reachable at `--service-url`
|
|
317
|
+
- Check `--service-token` if the service has token auth enabled
|
|
318
|
+
- Verify the device UID is correct (use `--list` to see available UIDs)
|
|
319
|
+
|
|
279
320
|
### Import Fails
|
|
280
321
|
|
|
281
322
|
- Use `--dry-run` to validate JSON syntax
|
|
@@ -316,12 +357,14 @@ manager.import_config('input.json', dry_run=False)
|
|
|
316
357
|
```
|
|
317
358
|
|
|
318
359
|
For lower-level access (used internally by `ConfigManager`), `create_config_client()`
|
|
319
|
-
returns a `ConfigClient` with `
|
|
360
|
+
returns a `ConfigClient` with `read_fan_config()`/`write_fan_config()`,
|
|
361
|
+
`read_rgb_config()`/`write_rgb_config()`, `read_calibration()`/`write_calibration()`,
|
|
320
362
|
`save_config()`/`load_config()`/`reset_config()`, and (BL2 firmware 7+ only)
|
|
321
363
|
`factory_cal_unlock()`, which temporarily lifts write-protection on the factory
|
|
322
|
-
calibration slot.
|
|
323
|
-
|
|
324
|
-
|
|
364
|
+
calibration slot. All three sources (`direct`, `named_pipe`, `service_http`)
|
|
365
|
+
implement the full interface. There is no CLI/JSON-config surface for
|
|
366
|
+
`factory_cal_unlock()` — it's a niche factory-calibration tool, not a
|
|
367
|
+
user-facing config option, and a successful call resets the device immediately:
|
|
325
368
|
|
|
326
369
|
```python
|
|
327
370
|
from benchlab.config.config_client import create_config_client
|
|
@@ -10,6 +10,12 @@ import logging
|
|
|
10
10
|
from abc import ABC, abstractmethod
|
|
11
11
|
from typing import Optional, Dict, Any
|
|
12
12
|
|
|
13
|
+
from .http_dto import (
|
|
14
|
+
fan_dto_to_dict, fan_dict_to_dto,
|
|
15
|
+
rgb_dto_to_dict, rgb_dict_to_dto,
|
|
16
|
+
calibration_dto_to_dict, calibration_dict_to_dto,
|
|
17
|
+
)
|
|
18
|
+
|
|
13
19
|
logger = logging.getLogger("benchlab.config.client")
|
|
14
20
|
|
|
15
21
|
DISCOVERY_PIPE_NAME = "BenchlabDiscovery"
|
|
@@ -160,6 +166,18 @@ class ConfigClient(ABC):
|
|
|
160
166
|
"""
|
|
161
167
|
pass
|
|
162
168
|
|
|
169
|
+
@abstractmethod
|
|
170
|
+
def enter_bootloader(self) -> bool:
|
|
171
|
+
"""Jump the device into its USB DFU bootloader.
|
|
172
|
+
|
|
173
|
+
Disruptive: the device stops responding as a sensor/CDC device
|
|
174
|
+
immediately after this succeeds, and re-enumerates as USB DFU
|
|
175
|
+
(VID 0x0483 / PID 0xDF11). Callers must close this connection and
|
|
176
|
+
take over the device directly over USB from there -- see
|
|
177
|
+
benchlab.flash.flash_manager.
|
|
178
|
+
"""
|
|
179
|
+
pass
|
|
180
|
+
|
|
163
181
|
@abstractmethod
|
|
164
182
|
def close(self):
|
|
165
183
|
"""Close connection and cleanup resources."""
|
|
@@ -473,6 +491,16 @@ class DirectConfigClient(ConfigClient):
|
|
|
473
491
|
return False
|
|
474
492
|
return factory_cal_unlock(self.ser)
|
|
475
493
|
|
|
494
|
+
def enter_bootloader(self) -> bool:
|
|
495
|
+
"""Jump the device into its USB DFU bootloader.
|
|
496
|
+
|
|
497
|
+
Disruptive: the device stops responding over CDC immediately after
|
|
498
|
+
this succeeds. Mirrors enter_bootloader()'s own caveat in pycore --
|
|
499
|
+
the actual jump happens on the firmware's next 100ms task tick.
|
|
500
|
+
"""
|
|
501
|
+
from benchlab_pycore.core.config_io import enter_bootloader
|
|
502
|
+
return enter_bootloader(self.ser)
|
|
503
|
+
|
|
476
504
|
def close(self):
|
|
477
505
|
"""Close serial connection."""
|
|
478
506
|
if self.ser and self.ser.is_open:
|
|
@@ -657,18 +685,218 @@ class NamedPipeConfigClient(ConfigClient):
|
|
|
657
685
|
result = self._send_command("SendFactoryCalUnlock", payload="benchlab")
|
|
658
686
|
return result and result.get('success', False)
|
|
659
687
|
|
|
688
|
+
def enter_bootloader(self) -> bool:
|
|
689
|
+
"""Jump the device into its USB DFU bootloader.
|
|
690
|
+
|
|
691
|
+
Disruptive: the device stops responding over CDC immediately after
|
|
692
|
+
this succeeds, and the C# service marks it DISCONNECTED (it does
|
|
693
|
+
not treat this as a device error). Mirrors the service's own
|
|
694
|
+
SendBootloader pipe command and its HTTP equivalent
|
|
695
|
+
(POST /device/{uid}/bootloader).
|
|
696
|
+
"""
|
|
697
|
+
result = self._send_command("SendBootloader")
|
|
698
|
+
return result and result.get('success', False)
|
|
699
|
+
|
|
660
700
|
def close(self):
|
|
661
701
|
"""Close pipe connection."""
|
|
662
702
|
self._close_pipe()
|
|
663
703
|
logger.info(f"Closed named pipe connection to {self.pipe_name}")
|
|
664
704
|
|
|
665
705
|
|
|
666
|
-
|
|
706
|
+
class HttpConfigClient(ConfigClient):
|
|
707
|
+
"""Configuration client using the C# BenchLab service's HTTP API.
|
|
708
|
+
|
|
709
|
+
Implements device info, name, save/load config, fan/RGB/calibration
|
|
710
|
+
read-write, and the disruptive reset/bootloader/factory-cal-unlock
|
|
711
|
+
commands. Fan/RGB/calibration routes (GET/PUT /device/{uid}/fan,
|
|
712
|
+
/rgb/{profile}, /calibration) use DTOs with a different, more
|
|
713
|
+
structured shape than this repo's raw ctypes-struct-dict convention
|
|
714
|
+
(DirectConfigClient/NamedPipeConfigClient) -- see benchlab.config.
|
|
715
|
+
http_dto for the translation layer between the two. reset_config has
|
|
716
|
+
no HTTP equivalent at all (by design: the service only exposes
|
|
717
|
+
save/load actions, since no firmware contract defines a config-reset
|
|
718
|
+
opcode -- RESET power-cycles the whole device instead).
|
|
719
|
+
"""
|
|
720
|
+
|
|
721
|
+
DEFAULT_URL = "http://localhost:8585"
|
|
722
|
+
|
|
723
|
+
def __init__(self, uid: str, base_url: str = DEFAULT_URL,
|
|
724
|
+
token: Optional[str] = None, timeout: float = 5.0):
|
|
725
|
+
"""Initialize an HTTP client scoped to one device.
|
|
726
|
+
|
|
727
|
+
Args:
|
|
728
|
+
uid: Device UID (used in /device/{uid}/... routes).
|
|
729
|
+
base_url: Base URL of the C# BenchLab service.
|
|
730
|
+
token: Optional X-Benchlab-Token value (only needed when the
|
|
731
|
+
service has token auth configured -- required on every
|
|
732
|
+
request except GET /health).
|
|
733
|
+
timeout: HTTP request timeout in seconds.
|
|
734
|
+
"""
|
|
735
|
+
import requests
|
|
736
|
+
|
|
737
|
+
self.uid = uid
|
|
738
|
+
self.base_url = base_url.rstrip("/")
|
|
739
|
+
self.timeout = timeout
|
|
740
|
+
self._session = requests.Session()
|
|
741
|
+
if token:
|
|
742
|
+
self._session.headers["X-Benchlab-Token"] = token
|
|
743
|
+
|
|
744
|
+
def _get(self, path: str) -> Optional[Dict[str, Any]]:
|
|
745
|
+
try:
|
|
746
|
+
resp = self._session.get(
|
|
747
|
+
f"{self.base_url}{path}", timeout=self.timeout)
|
|
748
|
+
if resp.status_code == 200:
|
|
749
|
+
return resp.json()
|
|
750
|
+
logger.warning(
|
|
751
|
+
"GET %s -> %s: %s", path, resp.status_code, resp.text)
|
|
752
|
+
return None
|
|
753
|
+
except Exception as e:
|
|
754
|
+
logger.error(f"GET {path} failed: {e}")
|
|
755
|
+
return None
|
|
756
|
+
|
|
757
|
+
def _post(self, path: str, json_body=None) -> bool:
|
|
758
|
+
try:
|
|
759
|
+
resp = self._session.post(
|
|
760
|
+
f"{self.base_url}{path}", json=json_body,
|
|
761
|
+
timeout=self.timeout)
|
|
762
|
+
if resp.status_code in (200, 204):
|
|
763
|
+
return True
|
|
764
|
+
logger.warning(
|
|
765
|
+
"POST %s -> %s: %s", path, resp.status_code, resp.text)
|
|
766
|
+
return False
|
|
767
|
+
except Exception as e:
|
|
768
|
+
logger.error(f"POST {path} failed: {e}")
|
|
769
|
+
return False
|
|
770
|
+
|
|
771
|
+
def _put(self, path: str, json_body=None) -> bool:
|
|
772
|
+
try:
|
|
773
|
+
resp = self._session.put(
|
|
774
|
+
f"{self.base_url}{path}", json=json_body,
|
|
775
|
+
timeout=self.timeout)
|
|
776
|
+
if resp.status_code in (200, 204):
|
|
777
|
+
return True
|
|
778
|
+
logger.warning(
|
|
779
|
+
"PUT %s -> %s: %s", path, resp.status_code, resp.text)
|
|
780
|
+
return False
|
|
781
|
+
except Exception as e:
|
|
782
|
+
logger.error(f"PUT {path} failed: {e}")
|
|
783
|
+
return False
|
|
784
|
+
|
|
785
|
+
def get_device_info(self) -> Optional[Dict[str, Any]]:
|
|
786
|
+
"""Get device information from GET /device/{uid}/info."""
|
|
787
|
+
return self._get(f"/device/{self.uid}/info")
|
|
788
|
+
|
|
789
|
+
def read_device_name(self) -> Optional[str]:
|
|
790
|
+
info = self.get_device_info()
|
|
791
|
+
return info.get('deviceName') if info else None
|
|
792
|
+
|
|
793
|
+
def write_device_name(self, name: str) -> bool:
|
|
794
|
+
"""Write device friendly name via PUT /device/{uid}/name."""
|
|
795
|
+
return self._put(f"/device/{self.uid}/name", {"name": name})
|
|
796
|
+
|
|
797
|
+
def read_fan_config(self, profile_id: int,
|
|
798
|
+
fan_id: int) -> Optional[Dict[str, Any]]:
|
|
799
|
+
"""Read fan config via GET /device/{uid}/fan/{profile}/{fan}."""
|
|
800
|
+
dto = self._get(f"/device/{self.uid}/fan/{profile_id}/{fan_id}")
|
|
801
|
+
return fan_dto_to_dict(dto) if dto is not None else None
|
|
802
|
+
|
|
803
|
+
def write_fan_config(self, profile_id: int, fan_id: int,
|
|
804
|
+
config: Dict[str, Any]) -> bool:
|
|
805
|
+
"""Write fan config via PUT /device/{uid}/fan/{profile}/{fan}."""
|
|
806
|
+
return self._put(
|
|
807
|
+
f"/device/{self.uid}/fan/{profile_id}/{fan_id}",
|
|
808
|
+
fan_dict_to_dto(config))
|
|
809
|
+
|
|
810
|
+
def read_rgb_config(self, profile_id: int) -> Optional[Dict[str, Any]]:
|
|
811
|
+
"""Read RGB config via GET /device/{uid}/rgb/{profile}."""
|
|
812
|
+
dto = self._get(f"/device/{self.uid}/rgb/{profile_id}")
|
|
813
|
+
return rgb_dto_to_dict(dto) if dto is not None else None
|
|
814
|
+
|
|
815
|
+
def write_rgb_config(self, profile_id: int,
|
|
816
|
+
config: Dict[str, Any]) -> bool:
|
|
817
|
+
"""Write RGB config via PUT /device/{uid}/rgb/{profile}."""
|
|
818
|
+
return self._put(
|
|
819
|
+
f"/device/{self.uid}/rgb/{profile_id}", rgb_dict_to_dto(config))
|
|
820
|
+
|
|
821
|
+
def read_calibration(self) -> Optional[Dict[str, Any]]:
|
|
822
|
+
"""Read calibration via GET /device/{uid}/calibration.
|
|
823
|
+
|
|
824
|
+
Works for both BENCHLAB Original and BL2 -- the service resolves
|
|
825
|
+
the connected device's variant server-side and returns a
|
|
826
|
+
CalibrationDto with array lengths matching that variant.
|
|
827
|
+
"""
|
|
828
|
+
dto = self._get(f"/device/{self.uid}/calibration")
|
|
829
|
+
return calibration_dto_to_dict(dto) if dto is not None else None
|
|
830
|
+
|
|
831
|
+
def write_calibration(self, calibration: Dict[str, Any]) -> bool:
|
|
832
|
+
"""Write calibration via PUT /device/{uid}/calibration.
|
|
833
|
+
|
|
834
|
+
No read-before-write: the server validates the submitted array
|
|
835
|
+
lengths (Vin/Ts/TsB/power arrays) against the connected device's
|
|
836
|
+
own variant and rejects a mismatch with a 400 (surfaced here as a
|
|
837
|
+
logged warning and a False return). Callers are expected to pass
|
|
838
|
+
a dict shaped like read_calibration()'s own output. The submitted
|
|
839
|
+
Crc is ignored -- the server always recomputes it before writing.
|
|
840
|
+
"""
|
|
841
|
+
return self._put(
|
|
842
|
+
f"/device/{self.uid}/calibration",
|
|
843
|
+
calibration_dict_to_dto(calibration))
|
|
844
|
+
|
|
845
|
+
def save_config(self) -> bool:
|
|
846
|
+
"""Save configuration to device flash via
|
|
847
|
+
POST /device/{uid}/config/save."""
|
|
848
|
+
return self._post(f"/device/{self.uid}/config/save")
|
|
849
|
+
|
|
850
|
+
def load_config(self) -> bool:
|
|
851
|
+
"""Reload configuration from device flash via
|
|
852
|
+
POST /device/{uid}/config/load."""
|
|
853
|
+
return self._post(f"/device/{self.uid}/config/load")
|
|
854
|
+
|
|
855
|
+
def reset_config(self) -> bool:
|
|
856
|
+
raise NotImplementedError(
|
|
857
|
+
"HttpConfigClient does not support config reset -- the C# "
|
|
858
|
+
"service has no HTTP route for it, by design: no firmware "
|
|
859
|
+
"contract defines a config-reset opcode (use the device's "
|
|
860
|
+
"RESET command / POST /device/{uid}/reset to power-cycle "
|
|
861
|
+
"instead, which is not equivalent)")
|
|
862
|
+
|
|
863
|
+
def factory_cal_unlock(self) -> bool:
|
|
864
|
+
"""Temporarily lift write-protection on BL2's factory calibration
|
|
865
|
+
slot via POST /device/{uid}/factory-cal-unlock.
|
|
866
|
+
|
|
867
|
+
Disruptive: a successful call resets the device immediately -- the
|
|
868
|
+
service marks it DISCONNECTED until it re-enumerates.
|
|
869
|
+
"""
|
|
870
|
+
return self._post(
|
|
871
|
+
f"/device/{self.uid}/factory-cal-unlock",
|
|
872
|
+
json_body={"passphrase": "benchlab"})
|
|
873
|
+
|
|
874
|
+
def enter_bootloader(self) -> bool:
|
|
875
|
+
"""Jump the device into its USB DFU bootloader via
|
|
876
|
+
POST /device/{uid}/bootloader.
|
|
877
|
+
|
|
878
|
+
Disruptive: the device stops responding over CDC immediately after
|
|
879
|
+
this succeeds, and the C# service marks it DISCONNECTED (it does
|
|
880
|
+
not treat this as a device error).
|
|
881
|
+
"""
|
|
882
|
+
return self._post(f"/device/{self.uid}/bootloader")
|
|
883
|
+
|
|
884
|
+
def close(self):
|
|
885
|
+
"""Close the HTTP session."""
|
|
886
|
+
self._session.close()
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
def create_config_client(
|
|
890
|
+
source: str, identifier: str, base_url: Optional[str] = None,
|
|
891
|
+
token: Optional[str] = None) -> ConfigClient:
|
|
667
892
|
"""Factory function to create appropriate config client.
|
|
668
893
|
|
|
669
894
|
Args:
|
|
670
|
-
source: 'direct' or '
|
|
671
|
-
identifier: Port name for direct, pipe name for named_pipe
|
|
895
|
+
source: 'direct', 'named_pipe', or 'service_http'
|
|
896
|
+
identifier: Port name for direct, pipe name for named_pipe, device
|
|
897
|
+
UID for service_http
|
|
898
|
+
base_url: Service base URL (service_http only)
|
|
899
|
+
token: Optional X-Benchlab-Token (service_http only)
|
|
672
900
|
|
|
673
901
|
Returns:
|
|
674
902
|
ConfigClient instance
|
|
@@ -681,5 +909,12 @@ def create_config_client(source: str, identifier: str) -> ConfigClient:
|
|
|
681
909
|
return DirectConfigClient(identifier)
|
|
682
910
|
elif source == 'named_pipe':
|
|
683
911
|
return NamedPipeConfigClient(identifier)
|
|
912
|
+
elif source == 'service_http':
|
|
913
|
+
kwargs = {}
|
|
914
|
+
if base_url:
|
|
915
|
+
kwargs['base_url'] = base_url
|
|
916
|
+
if token:
|
|
917
|
+
kwargs['token'] = token
|
|
918
|
+
return HttpConfigClient(identifier, **kwargs)
|
|
684
919
|
else:
|
|
685
920
|
raise ValueError(f"Invalid source type: {source}")
|