diff --git a/docs/user_guide/00_liquid-handling/hamilton-star/_hamilton-star.rst b/docs/user_guide/00_liquid-handling/hamilton-star/_hamilton-star.rst index b0f65a7049a..1b62e1e8e3f 100644 --- a/docs/user_guide/00_liquid-handling/hamilton-star/_hamilton-star.rst +++ b/docs/user_guide/00_liquid-handling/hamilton-star/_hamilton-star.rst @@ -1,6 +1,15 @@ Hamilton STAR ============= +Installation +------------ + +.. code-block:: bash + + pip install pylabrobot[usb] + +See :ref:`using-the-usb-interface` for platform-specific driver setup (libusb on Mac, Zadig on Windows). + Tools for working with Hamilton-STAR specific functions. .. toctree:: diff --git a/docs/user_guide/00_liquid-handling/pumps/cole-parmer-masterflex.md b/docs/user_guide/00_liquid-handling/pumps/cole-parmer-masterflex.md index 57e8501d5cf..00f0f4c6621 100644 --- a/docs/user_guide/00_liquid-handling/pumps/cole-parmer-masterflex.md +++ b/docs/user_guide/00_liquid-handling/pumps/cole-parmer-masterflex.md @@ -1,5 +1,11 @@ # Cole Parmer Masterflex +## Installation + +```bash +pip install pylabrobot[serial] +``` + PyLabRobot supports the following pumps: - {ref}`Cole Parmer Masterflex ` diff --git a/docs/user_guide/01_material-handling/heating_shaking/heating_shaking.md b/docs/user_guide/01_material-handling/heating_shaking/heating_shaking.md index 53105fda48e..5f863f563ea 100644 --- a/docs/user_guide/01_material-handling/heating_shaking/heating_shaking.md +++ b/docs/user_guide/01_material-handling/heating_shaking/heating_shaking.md @@ -1,5 +1,15 @@ # Heater Shakers +## Installation + +Most heater-shakers use a serial connection: + +```bash +pip install pylabrobot[serial] +``` + +For Inheco devices in HID mode, install `pip install pylabrobot[hid]` instead. + Heater-shakers are a hybrid of {class}`~pylabrobot.temperature_controllers.temperature_controller.TemperatureController` and {class}`~pylabrobot.shakers.shaker.Shaker`. They are used to control the temperature of a sample while shaking it. PyLabRobot supports the following heater shakers: diff --git a/docs/user_guide/01_material-handling/sealers/sealers.md b/docs/user_guide/01_material-handling/sealers/sealers.md index a45c13ac0fc..3283f401992 100644 --- a/docs/user_guide/01_material-handling/sealers/sealers.md +++ b/docs/user_guide/01_material-handling/sealers/sealers.md @@ -1,5 +1,11 @@ # Sealers +## Installation + +```bash +pip install pylabrobot[serial] +``` + In automated wet lab workflows, **microplate sealers** are essential for preserving sample integrity. They prevent **evaporation**, **cross-contamination**, and **spillage**, especially during heating, shaking, storage, or robotic transport. diff --git a/docs/user_guide/01_material-handling/tilting.md b/docs/user_guide/01_material-handling/tilting.md index dbcf84850a4..9c9fb36bf8a 100644 --- a/docs/user_guide/01_material-handling/tilting.md +++ b/docs/user_guide/01_material-handling/tilting.md @@ -1,5 +1,11 @@ # Tilting +## Installation + +```bash +pip install pylabrobot[serial] +``` + Currently only the Hamilton tilt module is supported. ```python diff --git a/docs/user_guide/02_analytical/plate-reading/synergyh1.ipynb b/docs/user_guide/02_analytical/plate-reading/synergyh1.ipynb index 1680fdc79e7..8821545e00f 100644 --- a/docs/user_guide/02_analytical/plate-reading/synergyh1.ipynb +++ b/docs/user_guide/02_analytical/plate-reading/synergyh1.ipynb @@ -3,11 +3,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "# Synergy H1\n", - "\n", - "Synergy H1 is an Agilent BioTek microplate reader that can read absorbance, fluorescence, and luminescence. Please refer to the [user guide](https://cqls.oregonstate.edu/sites/cqls.oregonstate.edu/files/synergy_h1_user_manual_sd-xb000426.pdf) for installation instructions." - ] + "source": "# Synergy H1\n\n## Installation\n\n```bash\npip install pylabrobot[ftdi]\n```\n\nSynergy H1 is an Agilent BioTek microplate reader that can read absorbance, fluorescence, and luminescence. Please refer to the [user guide](https://cqls.oregonstate.edu/sites/cqls.oregonstate.edu/files/synergy_h1_user_manual_sd-xb000426.pdf) for installation instructions." }, { "cell_type": "code", @@ -286,4 +282,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/docs/user_guide/_getting-started/installation.md b/docs/user_guide/_getting-started/installation.md index 85b7609449f..7aaac737bd6 100644 --- a/docs/user_guide/_getting-started/installation.md +++ b/docs/user_guide/_getting-started/installation.md @@ -2,7 +2,7 @@ These instructions describe how to install PyLabRobot. -Note that there are additional installation steps for using the firmware (universal) interface to Hamiltons and Tecans, see {ref}`below `. +Note that there are additional installation steps for using the USB interface to Hamiltons and Tecans, see {ref}`below `. ## Installing PyLabRobot @@ -49,21 +49,23 @@ pip install "pylabrobot[testing]" There's a multitude of other optional dependencies that you can install. Replace `[docs]` with one of the following items to install the desired dependencies. -- `fw`: Needed for firmware control over Hamilton robots. -- `http`: Needed for the HTTP backend. -- `websockets`: Needed for the WebSocket backend. -- `simulation`: Needed for the simulation backend. -- `opentrons`: Needed for the Opentrons backend. -- `agrow`: Needed for the AgrowPumpArray backend. -- `plate_reading`: Needed to interact with the CLARIO Star plate reader. -- `inheco`: Needed for the Inheco backend. -- `dev`: Everything you need for development. -- `all`: Everything. May not be available on all platforms. +| Group | Packages | When you need it | +|-------|----------|-----------------| +| `serial` | pyserial | Serial devices: BioShake, Cytomat, Inheco (serial mode), Hamilton Tilt Module, Cole Parmer Masterflex, A4S Sealer, XPeel Peeler | +| `usb` | pyusb, libusb-package | USB devices: Hamilton STAR/STARlet, Tecan EVO (firmware) | +| `ftdi` | pylibftdi, pyusb | FTDI devices: BioTek Synergy H1 plate reader | +| `hid` | hid | HID devices: Inheco Incubator/Shaker (HID mode) | +| `modbus` | pymodbus | Modbus devices: Agrow Pump Array | +| `http` | requests | HTTP-based backends | +| `websockets` | websockets | WebSocket backend | +| `opentrons` | opentrons-http-api-client | Opentrons backend | +| `gui` | flask | PyLabRobot GUI | +| `dev` | All of the above + testing/linting tools | Development | To install multiple dependencies, separate them with a comma: ```bash -pip install 'pylabrobot[fw,http]' +pip install 'pylabrobot[serial,usb]' ``` Or install all dependencies at once: @@ -72,16 +74,16 @@ Or install all dependencies at once: pip install 'pylabrobot[all]' ``` -(using-the-firmware-interface)= +(using-the-usb-interface)= -## Using the firmware interface with Hamilton or Tecan robots +## Using the USB interface with Hamilton or Tecan robots If you want to use the firmware version of the Hamilton or Tecan interfaces, you need to install a backend for [PyUSB](https://github.com/pyusb/pyusb/). You can find the official installation instructions [here](https://github.com/pyusb/pyusb#requirements-and-platform-support). The following is a complete (and probably easier) guide for macOS, Linux and Windows. -Reminder: when you are using the firmware version, make sure to install the firmware dependencies as follows: +First, install the USB dependencies: ```bash -pip install pylabrobot[fw] +pip install pylabrobot[usb] ``` ### On Linux diff --git a/pylabrobot/io/ftdi.py b/pylabrobot/io/ftdi.py index 6758e905edd..4093e27f2e6 100644 --- a/pylabrobot/io/ftdi.py +++ b/pylabrobot/io/ftdi.py @@ -62,10 +62,16 @@ def __init__( ): if not HAS_PYLIBFTDI: global _FTDI_ERROR - raise RuntimeError(f"pylibftdi not installed. Import error: {_FTDI_ERROR}") + raise RuntimeError( + "pylibftdi is not installed. Install with: pip install pylabrobot[ftdi]. " + f"Import error: {_FTDI_ERROR}" + ) if not HAS_PYUSB: global _PYUSB_ERROR - raise RuntimeError(f"pyusb not installed. Import error: {_PYUSB_ERROR}") + raise RuntimeError( + "pyusb is not installed. Install with: pip install pylabrobot[ftdi]. " + f"Import error: {_PYUSB_ERROR}" + ) self._device_id = device_id self._vid = vid diff --git a/pylabrobot/io/hid.py b/pylabrobot/io/hid.py index f4b15434266..51630452b56 100644 --- a/pylabrobot/io/hid.py +++ b/pylabrobot/io/hid.py @@ -47,8 +47,8 @@ async def setup(self): """ if not USE_HID: raise RuntimeError( - "This backend requires the `hid` package to be installed." - f" Import error: {_HID_IMPORT_ERROR}" + "hid is not installed. Install with: pip install pylabrobot[hid]. " + f"Import error: {_HID_IMPORT_ERROR}" ) # --- 1. Enumerate all HID devices --- diff --git a/pylabrobot/io/serial.py b/pylabrobot/io/serial.py index 327d446a184..9572bee73a0 100644 --- a/pylabrobot/io/serial.py +++ b/pylabrobot/io/serial.py @@ -114,7 +114,10 @@ async def setup(self): """ if not HAS_SERIAL: - raise RuntimeError(f"pyserial not installed. Import error: {_SERIAL_IMPORT_ERROR}") + raise RuntimeError( + "pyserial is not installed. Install with: pip install pylabrobot[serial]. " + f"Import error: {_SERIAL_IMPORT_ERROR}" + ) loop = asyncio.get_running_loop() self._executor = ThreadPoolExecutor(max_workers=1) diff --git a/pylabrobot/io/usb.py b/pylabrobot/io/usb.py index f9d1c1afd7a..5d6ffb240e6 100644 --- a/pylabrobot/io/usb.py +++ b/pylabrobot/io/usb.py @@ -329,8 +329,8 @@ async def setup(self): if not USE_USB: raise RuntimeError( - f"USB dependencies could not be imported due to the following error: {_USB_IMPORT_ERROR}. " - "Please install pyusb and libusb. " + "pyusb/libusb is not installed. Install with: pip install pylabrobot[usb]. " + f"Import error: {_USB_IMPORT_ERROR}. " "https://docs.pylabrobot.org/installation.html" ) diff --git a/pylabrobot/pumps/agrowpumps/agrowdosepump_backend.py b/pylabrobot/pumps/agrowpumps/agrowdosepump_backend.py index 47d9e28f6f8..db2226e9278 100644 --- a/pylabrobot/pumps/agrowpumps/agrowdosepump_backend.py +++ b/pylabrobot/pumps/agrowpumps/agrowdosepump_backend.py @@ -121,7 +121,7 @@ async def setup(self): async def _setup_modbus(self): if AsyncModbusSerialClient is None: raise RuntimeError( - "pymodbus is not installed. Please install it with 'pip install pymodbus'." + "pymodbus is not installed. Install with: pip install pylabrobot[modbus]." f" Import error: {_MODBUS_IMPORT_ERROR}" ) self._modbus = AsyncModbusSerialClient( diff --git a/pyproject.toml b/pyproject.toml index 7b86bf83aee..7c7456bb67a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,17 +12,18 @@ dynamic = ["version"] dependencies = ["typing_extensions==4.15.0"] [project.optional-dependencies] -fw = ["pyserial==3.5", "pyusb==1.3.1", "libusb-package==1.0.26.1"] +serial = ["pyserial==3.5"] +usb = ["pyusb==1.3.1", "libusb-package==1.0.26.1"] +ftdi = ["pylibftdi==0.23.0", "pyusb==1.3.1"] +hid = ["hid==1.0.8"] +modbus = ["pymodbus==3.6.8"] http = ["requests==2.32.5", "types-requests==2.32.4.20250913"] -plate_reading = ["pylibftdi==0.23.0"] websockets = ["websockets==15.0.1"] visualizer = ["websockets==15.0.1"] opentrons = ["opentrons-http-api-client"] -inheco = ["hid==1.0.8"] -agrow = ["pymodbus==3.6.8"] gui = ["flask[async]==3.1.2"] dev = [ - "PyLabRobot[fw,http,plate_reading,websockets,visualizer,opentrons,inheco,agrow,gui]", + "PyLabRobot[serial,usb,ftdi,hid,modbus,http,websockets,visualizer,opentrons,gui]", "pytest==8.4.2", "pytest-timeout==2.4.0", "mypy==1.18.2", @@ -70,23 +71,23 @@ python = ">=3.9" [tool.pixi.pypi-dependencies] pylabrobot = { path = ".", editable = true } -[tool.pixi.feature.fw.dependencies] +[tool.pixi.feature.usb.dependencies] libusb = "*" -[tool.pixi.feature.fw.pypi-dependencies] -pylabrobot = { path = ".", editable = true, extras = ["fw"] } +[tool.pixi.feature.usb.pypi-dependencies] +pylabrobot = { path = ".", editable = true, extras = ["usb"] } -[tool.pixi.feature.plate-reading.dependencies] +[tool.pixi.feature.ftdi.dependencies] libftdi = "*" -[tool.pixi.feature.plate-reading.pypi-dependencies] -pylabrobot = { path = ".", editable = true, extras = ["plate_reading"] } +[tool.pixi.feature.ftdi.pypi-dependencies] +pylabrobot = { path = ".", editable = true, extras = ["ftdi"] } -[tool.pixi.feature.inheco.dependencies] +[tool.pixi.feature.hid.dependencies] libhidapi = "*" -[tool.pixi.feature.inheco.pypi-dependencies] -pylabrobot = { path = ".", editable = true, extras = ["inheco"] } +[tool.pixi.feature.hid.pypi-dependencies] +pylabrobot = { path = ".", editable = true, extras = ["hid"] } [tool.pixi.feature.dev.dependencies] libusb = "*" @@ -105,7 +106,7 @@ typecheck = "python -m mypy pylabrobot --check-untyped-defs" [tool.pixi.environments] default = { solve-group = "default" } -fw = { features = ["fw"], solve-group = "fw" } -plate-reading = { features = ["plate-reading"], solve-group = "plate-reading" } -inheco = { features = ["inheco"], solve-group = "inheco" } +usb = { features = ["usb"], solve-group = "usb" } +ftdi = { features = ["ftdi"], solve-group = "ftdi" } +hid = { features = ["hid"], solve-group = "hid" } dev = { features = ["dev"], solve-group = "dev" }