From 3a5b83d97fd40831e5845bdb36fc1809e500dc65 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Wed, 22 Apr 2026 10:15:21 -0700 Subject: [PATCH 1/2] Skip VW query on STAR pipetting channels with firmware <= 2015 The unconditional VW query from #991 fails on older STARlets (e.g. April 2015 firmware) with er30 "Unknown command", breaking lh.setup(). Probe channel-0 firmware year first: leave _pip_channel_information as None when unsupported, otherwise populate the full per-channel list. Fixes #1004. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../backends/hamilton/STAR_backend.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index fbecc6703a2..c409381b922 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -1366,7 +1366,7 @@ def __init__( self._unsafe = UnSafe(self) self._iswap_version: Optional[str] = None # loaded lazily - self._pip_channel_information: List[PipChannelInformation] = [] + self._pip_channel_information: Optional[List[PipChannelInformation]] = None self._default_1d_symbology: Barcode1DSymbology = "Code 128 (Subset B and C)" @@ -1720,10 +1720,15 @@ async def set_up_pip(): await self.initialize_pip() self._channels_minimum_y_spacing = await self.channels_request_y_minimum_spacing() - # Cache per-channel hardware configuration for version-specific behavior - self._pip_channel_information = [ - await self._pip_channel_request_configuration(ch) for ch in range(self.num_channels) - ] + # VW is not supported on firmware <= 2015 (see issue #1004). Skip the query there + # and leave the cache as None; otherwise populate it for every channel. + pip_fw = self._parse_firmware_version_datetime(await self.request_pip_channel_version(0)) + if pip_fw.year <= 2015: + self._pip_channel_information = None + else: + self._pip_channel_information = [ + await self._pip_channel_request_configuration(ch) for ch in range(self.num_channels) + ] async def set_up_autoload(): if self.machine_conf.auto_load_installed and not skip_autoload: From 4878b9cff26b756118e703a8ad8d06c39bf9289c Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Wed, 22 Apr 2026 12:42:13 -0700 Subject: [PATCH 2/2] Raise in _pip_channel_request_configuration on firmware <= 2016 Bumps the VW cutoff from 2015 to 2016 and fails fast inside the per-channel helper so the command is never sent on unsupported firmware. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../backends/hamilton/STAR_backend.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index c409381b922..1545b399cb2 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -1536,6 +1536,12 @@ async def _pip_channel_request_configuration(self, channel: int) -> PipChannelIn Args: channel: 0-indexed channel number. """ + pip_fw = self._parse_firmware_version_datetime(await self.request_pip_channel_version(channel)) + if pip_fw.year <= 2016: + raise RuntimeError( + f"VW (pip channel configuration) is not supported on firmware from 2016 or older " + f"(channel {channel} firmware date: {pip_fw.isoformat()})." + ) resp: str = await self.send_command(STARBackend.channel_id(channel), "VW") hw_tokens = resp.split("vw")[-1].strip().split() return PipChannelInformation( @@ -1720,10 +1726,10 @@ async def set_up_pip(): await self.initialize_pip() self._channels_minimum_y_spacing = await self.channels_request_y_minimum_spacing() - # VW is not supported on firmware <= 2015 (see issue #1004). Skip the query there - # and leave the cache as None; otherwise populate it for every channel. + # VW is not supported on firmware from 2016 or older (see issue #1004). Skip the + # query there and leave the cache as None; otherwise populate it for every channel. pip_fw = self._parse_firmware_version_datetime(await self.request_pip_channel_version(0)) - if pip_fw.year <= 2015: + if pip_fw.year <= 2016: self._pip_channel_information = None else: self._pip_channel_information = [