diff --git a/api/src/opentrons/protocol_api/core/engine/instrument.py b/api/src/opentrons/protocol_api/core/engine/instrument.py index 0813e702f9b..aeda023dee7 100644 --- a/api/src/opentrons/protocol_api/core/engine/instrument.py +++ b/api/src/opentrons/protocol_api/core/engine/instrument.py @@ -84,6 +84,9 @@ def __init__( self._flow_rates = FlowRates(self) self.set_default_speed(speed=default_movement_speed) + self._liquid_presence_detection = bool( + self._engine_client.state.pipettes.get_liquid_presence_detection(pipette_id) + ) @property def pipette_id(self) -> str: @@ -747,6 +750,9 @@ def get_nozzle_configuration(self) -> NozzleConfigurationType: self._pipette_id ) + def get_liquid_presence_detection(self) -> bool: + return self._liquid_presence_detection + def is_tip_tracking_available(self) -> bool: primary_nozzle = self._engine_client.state.pipettes.get_primary_nozzle( self._pipette_id @@ -780,6 +786,9 @@ def set_flow_rate( assert blow_out > 0 self._blow_out_flow_rate = blow_out + def set_liquid_presence_detection(self, enable: bool) -> None: + self._liquid_presence_detection = enable + def configure_for_volume(self, volume: float) -> None: self._engine_client.execute_command( cmd.ConfigureForVolumeParams( diff --git a/api/src/opentrons/protocol_api/core/instrument.py b/api/src/opentrons/protocol_api/core/instrument.py index fec252a009e..aaa11867891 100644 --- a/api/src/opentrons/protocol_api/core/instrument.py +++ b/api/src/opentrons/protocol_api/core/instrument.py @@ -247,6 +247,14 @@ def get_dispense_flow_rate(self, rate: float = 1.0) -> float: def get_blow_out_flow_rate(self, rate: float = 1.0) -> float: ... + @abstractmethod + def get_liquid_presence_detection(self) -> bool: + ... + + @abstractmethod + def set_liquid_presence_detection(self, enable: bool) -> None: + ... + @abstractmethod def set_flow_rate( self, diff --git a/api/src/opentrons/protocol_api/core/legacy/legacy_instrument_core.py b/api/src/opentrons/protocol_api/core/legacy/legacy_instrument_core.py index 3755b093e78..dac5933d355 100644 --- a/api/src/opentrons/protocol_api/core/legacy/legacy_instrument_core.py +++ b/api/src/opentrons/protocol_api/core/legacy/legacy_instrument_core.py @@ -61,6 +61,7 @@ def __init__( blow_out_defaults=pipette_state["default_blow_out_flow_rates"], api_level=self._api_version, ) + self._liquid_presence_detection = False def get_default_speed(self) -> float: """Gets the speed at which the robot's gantry moves.""" @@ -471,6 +472,9 @@ def get_return_height(self) -> float: def get_flow_rate(self) -> FlowRates: return self._flow_rates + def get_liquid_presence_detection(self) -> bool: + return self._liquid_presence_detection + def get_aspirate_flow_rate(self, rate: float = 1.0) -> float: return self.get_hardware_state()["aspirate_flow_rate"] * rate @@ -497,6 +501,9 @@ def set_flow_rate( blow_out=blow_out, ) + def set_liquid_presence_detection(self, enable: bool) -> None: + self._protocol_interface.get_hardware().set_liquid_presence_detection(enable) + def set_pipette_speed( self, aspirate: Optional[float] = None, diff --git a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py index 9e8912c5629..be35d699748 100644 --- a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py +++ b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py @@ -88,6 +88,7 @@ def __init__( self._module_cores: List[legacy_module_core.LegacyModuleCore] = [] self._labware_cores: List[LegacyLabwareCore] = [self.fixed_trash] self._disposal_locations: List[Union[Labware, TrashBin, WasteChute]] = [] + self._liquid_presence_detection = False @property def api_version(self) -> APIVersion: diff --git a/api/src/opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py b/api/src/opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py index ffcdda5019c..e9cd80c8c2c 100644 --- a/api/src/opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +++ b/api/src/opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py @@ -74,6 +74,7 @@ def __init__( self._instrument_max_height = ( protocol_interface.get_hardware().get_instrument_max_height(self._mount) ) + self._liquid_presence_detection = False def get_default_speed(self) -> float: return self._default_speed @@ -363,6 +364,12 @@ def get_return_height(self) -> float: def get_speed(self) -> PlungerSpeeds: return self._plunger_speeds + def get_liquid_presence_detection(self) -> bool: + return self._liquid_presence_detection + + def set_liquid_presence_detection(self, enable: bool) -> None: + self._liquid_presence_detection = enable + def get_flow_rate(self) -> FlowRates: return self._flow_rate diff --git a/api/src/opentrons/protocol_api/instrument_context.py b/api/src/opentrons/protocol_api/instrument_context.py index 9dd0c482ded..c761cd94a82 100644 --- a/api/src/opentrons/protocol_api/instrument_context.py +++ b/api/src/opentrons/protocol_api/instrument_context.py @@ -1670,6 +1670,24 @@ def tip_racks(self) -> List[labware.Labware]: def tip_racks(self, racks: List[labware.Labware]) -> None: self._tip_racks = racks + @property + @requires_version(2, 20) + def liquid_detection(self) -> bool: + """ + Gets the global setting for liquid level detection. + + When True, `liquid_probe` will be called before + aspirates and dispenses to bring the tip to the liquid level. + + The default value is False. + """ + return self._core.get_liquid_presence_detection() + + @liquid_detection.setter + @requires_version(2, 20) + def liquid_detection(self, enable: bool) -> None: + self._core.set_liquid_presence_detection(enable) + @property @requires_version(2, 0) def trash_container(self) -> Union[labware.Labware, TrashBin, WasteChute]: diff --git a/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py b/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py index 1cbf8b143fd..457e81ffe20 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py @@ -1149,6 +1149,20 @@ def test_has_tip( assert subject.has_tip() is True +def test_liquid_presence_detection( + decoy: Decoy, + subject: InstrumentCore, + mock_engine_client: EngineClient, +) -> None: + """It should have a default liquid presence detection boolean set to False.""" + decoy.when( + mock_engine_client.state.pipettes.get_liquid_presence_detection( + subject.pipette_id + ) + ).then_return(False) + assert subject.get_liquid_presence_detection() is False + + @pytest.mark.parametrize( argnames=["style", "primary_nozzle", "front_right_nozzle", "expected_model"], argvalues=[ diff --git a/api/tests/opentrons/protocol_api/test_instrument_context.py b/api/tests/opentrons/protocol_api/test_instrument_context.py index 43fbd62e09d..21b6f6ca6ae 100644 --- a/api/tests/opentrons/protocol_api/test_instrument_context.py +++ b/api/tests/opentrons/protocol_api/test_instrument_context.py @@ -1059,6 +1059,16 @@ def test_flow_rate( assert result == flow_rates +def test_liquid_presence_detection( + decoy: Decoy, mock_instrument_core: InstrumentCore, subject: InstrumentContext +) -> None: + """It should have a default liquid presence detection boolean set to False.""" + decoy.when(mock_instrument_core.get_liquid_presence_detection()).then_return(False) + assert subject.liquid_detection is False + subject.liquid_detection = True + decoy.verify(mock_instrument_core.set_liquid_presence_detection(True), times=1) + + @pytest.mark.parametrize("api_version", [APIVersion(2, 13)]) @pytest.mark.parametrize( "mock_instrument_core",