opentrons 8.1.0__py2.py3-none-any.whl → 8.2.0__py2.py3-none-any.whl

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.
Files changed (238) hide show
  1. opentrons/cli/analyze.py +71 -7
  2. opentrons/config/__init__.py +9 -0
  3. opentrons/config/advanced_settings.py +22 -0
  4. opentrons/config/defaults_ot3.py +14 -36
  5. opentrons/config/feature_flags.py +4 -0
  6. opentrons/config/types.py +6 -17
  7. opentrons/drivers/absorbance_reader/abstract.py +27 -3
  8. opentrons/drivers/absorbance_reader/async_byonoy.py +208 -154
  9. opentrons/drivers/absorbance_reader/driver.py +24 -15
  10. opentrons/drivers/absorbance_reader/hid_protocol.py +79 -50
  11. opentrons/drivers/absorbance_reader/simulator.py +32 -6
  12. opentrons/drivers/types.py +23 -1
  13. opentrons/execute.py +2 -2
  14. opentrons/hardware_control/api.py +18 -10
  15. opentrons/hardware_control/backends/controller.py +3 -2
  16. opentrons/hardware_control/backends/flex_protocol.py +11 -5
  17. opentrons/hardware_control/backends/ot3controller.py +18 -50
  18. opentrons/hardware_control/backends/ot3simulator.py +7 -6
  19. opentrons/hardware_control/backends/ot3utils.py +1 -0
  20. opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
  21. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
  22. opentrons/hardware_control/module_control.py +43 -2
  23. opentrons/hardware_control/modules/__init__.py +7 -1
  24. opentrons/hardware_control/modules/absorbance_reader.py +232 -83
  25. opentrons/hardware_control/modules/errors.py +7 -0
  26. opentrons/hardware_control/modules/heater_shaker.py +8 -3
  27. opentrons/hardware_control/modules/magdeck.py +12 -3
  28. opentrons/hardware_control/modules/mod_abc.py +27 -2
  29. opentrons/hardware_control/modules/tempdeck.py +15 -7
  30. opentrons/hardware_control/modules/thermocycler.py +69 -3
  31. opentrons/hardware_control/modules/types.py +11 -5
  32. opentrons/hardware_control/modules/update.py +11 -5
  33. opentrons/hardware_control/modules/utils.py +3 -1
  34. opentrons/hardware_control/ot3_calibration.py +6 -6
  35. opentrons/hardware_control/ot3api.py +131 -94
  36. opentrons/hardware_control/poller.py +15 -11
  37. opentrons/hardware_control/protocols/__init__.py +1 -7
  38. opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
  39. opentrons/hardware_control/protocols/liquid_handler.py +5 -0
  40. opentrons/hardware_control/protocols/position_estimator.py +3 -1
  41. opentrons/hardware_control/types.py +2 -0
  42. opentrons/legacy_commands/helpers.py +8 -2
  43. opentrons/motion_planning/__init__.py +2 -0
  44. opentrons/motion_planning/waypoints.py +32 -0
  45. opentrons/protocol_api/__init__.py +2 -1
  46. opentrons/protocol_api/_liquid.py +87 -1
  47. opentrons/protocol_api/_parameter_context.py +10 -1
  48. opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
  49. opentrons/protocol_api/core/engine/instrument.py +29 -25
  50. opentrons/protocol_api/core/engine/labware.py +20 -4
  51. opentrons/protocol_api/core/engine/module_core.py +166 -17
  52. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +362 -0
  53. opentrons/protocol_api/core/engine/protocol.py +30 -2
  54. opentrons/protocol_api/core/instrument.py +2 -0
  55. opentrons/protocol_api/core/labware.py +4 -0
  56. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
  57. opentrons/protocol_api/core/legacy/legacy_labware_core.py +5 -0
  58. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +6 -2
  59. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
  60. opentrons/protocol_api/core/module.py +22 -4
  61. opentrons/protocol_api/core/protocol.py +6 -2
  62. opentrons/protocol_api/instrument_context.py +52 -20
  63. opentrons/protocol_api/labware.py +13 -1
  64. opentrons/protocol_api/module_contexts.py +115 -17
  65. opentrons/protocol_api/protocol_context.py +49 -5
  66. opentrons/protocol_api/validation.py +5 -3
  67. opentrons/protocol_engine/__init__.py +10 -9
  68. opentrons/protocol_engine/actions/__init__.py +3 -0
  69. opentrons/protocol_engine/actions/actions.py +30 -25
  70. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  71. opentrons/protocol_engine/clients/sync_client.py +1 -1
  72. opentrons/protocol_engine/clients/transports.py +1 -1
  73. opentrons/protocol_engine/commands/__init__.py +0 -4
  74. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
  75. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +148 -0
  76. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +65 -9
  77. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +148 -0
  78. opentrons/protocol_engine/commands/absorbance_reader/read.py +200 -0
  79. opentrons/protocol_engine/commands/aspirate.py +29 -16
  80. opentrons/protocol_engine/commands/aspirate_in_place.py +33 -16
  81. opentrons/protocol_engine/commands/blow_out.py +63 -14
  82. opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
  83. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
  84. opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
  85. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
  86. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
  87. opentrons/protocol_engine/commands/command.py +31 -18
  88. opentrons/protocol_engine/commands/command_unions.py +37 -24
  89. opentrons/protocol_engine/commands/comment.py +5 -3
  90. opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
  91. opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
  92. opentrons/protocol_engine/commands/custom.py +5 -3
  93. opentrons/protocol_engine/commands/dispense.py +42 -20
  94. opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
  95. opentrons/protocol_engine/commands/drop_tip.py +70 -16
  96. opentrons/protocol_engine/commands/drop_tip_in_place.py +59 -13
  97. opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
  98. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
  99. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
  100. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
  101. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
  102. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
  103. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
  104. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
  105. opentrons/protocol_engine/commands/home.py +11 -5
  106. opentrons/protocol_engine/commands/liquid_probe.py +146 -88
  107. opentrons/protocol_engine/commands/load_labware.py +28 -5
  108. opentrons/protocol_engine/commands/load_liquid.py +18 -7
  109. opentrons/protocol_engine/commands/load_module.py +4 -6
  110. opentrons/protocol_engine/commands/load_pipette.py +18 -17
  111. opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
  112. opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
  113. opentrons/protocol_engine/commands/move_labware.py +155 -23
  114. opentrons/protocol_engine/commands/move_relative.py +15 -3
  115. opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
  116. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
  117. opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
  118. opentrons/protocol_engine/commands/move_to_well.py +37 -10
  119. opentrons/protocol_engine/commands/pick_up_tip.py +51 -30
  120. opentrons/protocol_engine/commands/pipetting_common.py +47 -16
  121. opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
  122. opentrons/protocol_engine/commands/reload_labware.py +13 -4
  123. opentrons/protocol_engine/commands/retract_axis.py +6 -3
  124. opentrons/protocol_engine/commands/save_position.py +2 -3
  125. opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
  126. opentrons/protocol_engine/commands/set_status_bar.py +5 -3
  127. opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
  128. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
  129. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
  130. opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
  131. opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
  132. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
  133. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
  134. opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
  135. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
  136. opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
  137. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
  138. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
  139. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
  140. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
  141. opentrons/protocol_engine/commands/touch_tip.py +19 -7
  142. opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
  143. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
  144. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
  145. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
  146. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  147. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  148. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +10 -4
  149. opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
  150. opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
  151. opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
  152. opentrons/protocol_engine/create_protocol_engine.py +60 -10
  153. opentrons/protocol_engine/engine_support.py +2 -1
  154. opentrons/protocol_engine/error_recovery_policy.py +14 -3
  155. opentrons/protocol_engine/errors/__init__.py +20 -0
  156. opentrons/protocol_engine/errors/error_occurrence.py +8 -3
  157. opentrons/protocol_engine/errors/exceptions.py +127 -2
  158. opentrons/protocol_engine/execution/__init__.py +2 -0
  159. opentrons/protocol_engine/execution/command_executor.py +22 -13
  160. opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
  161. opentrons/protocol_engine/execution/door_watcher.py +1 -1
  162. opentrons/protocol_engine/execution/equipment.py +2 -1
  163. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  164. opentrons/protocol_engine/execution/gantry_mover.py +4 -2
  165. opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
  166. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
  167. opentrons/protocol_engine/execution/labware_movement.py +73 -22
  168. opentrons/protocol_engine/execution/movement.py +17 -7
  169. opentrons/protocol_engine/execution/pipetting.py +7 -4
  170. opentrons/protocol_engine/execution/queue_worker.py +6 -2
  171. opentrons/protocol_engine/execution/run_control.py +1 -1
  172. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
  173. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +77 -43
  175. opentrons/protocol_engine/notes/__init__.py +14 -2
  176. opentrons/protocol_engine/notes/notes.py +18 -1
  177. opentrons/protocol_engine/plugins.py +1 -1
  178. opentrons/protocol_engine/protocol_engine.py +47 -31
  179. opentrons/protocol_engine/resources/__init__.py +2 -0
  180. opentrons/protocol_engine/resources/deck_data_provider.py +19 -5
  181. opentrons/protocol_engine/resources/file_provider.py +161 -0
  182. opentrons/protocol_engine/resources/fixture_validation.py +11 -1
  183. opentrons/protocol_engine/resources/labware_validation.py +10 -0
  184. opentrons/protocol_engine/state/__init__.py +0 -70
  185. opentrons/protocol_engine/state/addressable_areas.py +1 -1
  186. opentrons/protocol_engine/state/command_history.py +21 -2
  187. opentrons/protocol_engine/state/commands.py +110 -31
  188. opentrons/protocol_engine/state/files.py +59 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +440 -0
  190. opentrons/protocol_engine/state/geometry.py +445 -59
  191. opentrons/protocol_engine/state/labware.py +264 -84
  192. opentrons/protocol_engine/state/liquids.py +1 -1
  193. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +21 -3
  194. opentrons/protocol_engine/state/modules.py +145 -90
  195. opentrons/protocol_engine/state/motion.py +33 -14
  196. opentrons/protocol_engine/state/pipettes.py +157 -317
  197. opentrons/protocol_engine/state/state.py +30 -1
  198. opentrons/protocol_engine/state/state_summary.py +3 -0
  199. opentrons/protocol_engine/state/tips.py +69 -114
  200. opentrons/protocol_engine/state/update_types.py +424 -0
  201. opentrons/protocol_engine/state/wells.py +236 -0
  202. opentrons/protocol_engine/types.py +90 -0
  203. opentrons/protocol_reader/file_format_validator.py +83 -15
  204. opentrons/protocol_runner/json_translator.py +21 -5
  205. opentrons/protocol_runner/legacy_command_mapper.py +27 -6
  206. opentrons/protocol_runner/legacy_context_plugin.py +27 -71
  207. opentrons/protocol_runner/protocol_runner.py +6 -3
  208. opentrons/protocol_runner/run_orchestrator.py +41 -6
  209. opentrons/protocols/advanced_control/mix.py +3 -5
  210. opentrons/protocols/advanced_control/transfers.py +125 -56
  211. opentrons/protocols/api_support/constants.py +1 -1
  212. opentrons/protocols/api_support/definitions.py +1 -1
  213. opentrons/protocols/api_support/labware_like.py +4 -4
  214. opentrons/protocols/api_support/tip_tracker.py +2 -2
  215. opentrons/protocols/api_support/types.py +15 -2
  216. opentrons/protocols/api_support/util.py +30 -42
  217. opentrons/protocols/duration/errors.py +1 -1
  218. opentrons/protocols/duration/estimator.py +50 -29
  219. opentrons/protocols/execution/dev_types.py +2 -2
  220. opentrons/protocols/execution/execute_json_v4.py +15 -10
  221. opentrons/protocols/execution/execute_python.py +8 -3
  222. opentrons/protocols/geometry/planning.py +12 -12
  223. opentrons/protocols/labware.py +17 -33
  224. opentrons/protocols/parameters/csv_parameter_interface.py +3 -1
  225. opentrons/simulate.py +3 -3
  226. opentrons/types.py +30 -3
  227. opentrons/util/logging_config.py +34 -0
  228. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/METADATA +5 -4
  229. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/RECORD +235 -223
  230. opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
  231. opentrons/protocol_engine/commands/configuring_common.py +0 -26
  232. opentrons/protocol_runner/thread_async_queue.py +0 -174
  233. /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
  234. /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
  235. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/LICENSE +0 -0
  236. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/WHEEL +0 -0
  237. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/top_level.txt +0 -0
@@ -7,10 +7,11 @@ from pydantic import BaseModel, Field
7
7
 
8
8
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
+ from ...state import update_types
10
11
  from opentrons.protocol_engine.types import MotorAxis
11
12
 
12
13
  if TYPE_CHECKING:
13
- from opentrons.protocol_engine.state import StateView
14
+ from opentrons.protocol_engine.state.state import StateView
14
15
  from opentrons.protocol_engine.execution import EquipmentHandler, MovementHandler
15
16
 
16
17
 
@@ -27,9 +28,7 @@ class CloseLidResult(BaseModel):
27
28
  """Result data from closing a Thermocycler's lid."""
28
29
 
29
30
 
30
- class CloseLidImpl(
31
- AbstractCommandImpl[CloseLidParams, SuccessData[CloseLidResult, None]]
32
- ):
31
+ class CloseLidImpl(AbstractCommandImpl[CloseLidParams, SuccessData[CloseLidResult]]):
33
32
  """Execution implementation of a Thermocycler's close lid command."""
34
33
 
35
34
  def __init__(
@@ -43,10 +42,10 @@ class CloseLidImpl(
43
42
  self._equipment = equipment
44
43
  self._movement = movement
45
44
 
46
- async def execute(
47
- self, params: CloseLidParams
48
- ) -> SuccessData[CloseLidResult, None]:
45
+ async def execute(self, params: CloseLidParams) -> SuccessData[CloseLidResult]:
49
46
  """Close a Thermocycler's lid."""
47
+ state_update = update_types.StateUpdate()
48
+
50
49
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
51
50
  params.moduleId
52
51
  )
@@ -61,11 +60,12 @@ class CloseLidImpl(
61
60
  MotorAxis.Y,
62
61
  ] + self._state_view.motion.get_robot_mount_axes()
63
62
  await self._movement.home(axes=axes_to_home)
63
+ state_update.clear_all_pipette_locations()
64
64
 
65
65
  if thermocycler_hardware is not None:
66
66
  await thermocycler_hardware.close()
67
67
 
68
- return SuccessData(public=CloseLidResult(), private=None)
68
+ return SuccessData(public=CloseLidResult(), state_update=state_update)
69
69
 
70
70
 
71
71
  class CloseLid(BaseCommand[CloseLidParams, CloseLidResult, ErrorOccurrence]):
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -27,7 +27,7 @@ class DeactivateBlockResult(BaseModel):
27
27
 
28
28
 
29
29
  class DeactivateBlockImpl(
30
- AbstractCommandImpl[DeactivateBlockParams, SuccessData[DeactivateBlockResult, None]]
30
+ AbstractCommandImpl[DeactivateBlockParams, SuccessData[DeactivateBlockResult]]
31
31
  ):
32
32
  """Execution implementation of a Thermocycler's deactivate block command."""
33
33
 
@@ -42,7 +42,7 @@ class DeactivateBlockImpl(
42
42
 
43
43
  async def execute(
44
44
  self, params: DeactivateBlockParams
45
- ) -> SuccessData[DeactivateBlockResult, None]:
45
+ ) -> SuccessData[DeactivateBlockResult]:
46
46
  """Unset a Thermocycler's target block temperature."""
47
47
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
48
48
  params.moduleId
@@ -54,7 +54,9 @@ class DeactivateBlockImpl(
54
54
  if thermocycler_hardware is not None:
55
55
  await thermocycler_hardware.deactivate_block()
56
56
 
57
- return SuccessData(public=DeactivateBlockResult(), private=None)
57
+ return SuccessData(
58
+ public=DeactivateBlockResult(),
59
+ )
58
60
 
59
61
 
60
62
  class DeactivateBlock(
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -27,7 +27,7 @@ class DeactivateLidResult(BaseModel):
27
27
 
28
28
 
29
29
  class DeactivateLidImpl(
30
- AbstractCommandImpl[DeactivateLidParams, SuccessData[DeactivateLidResult, None]]
30
+ AbstractCommandImpl[DeactivateLidParams, SuccessData[DeactivateLidResult]]
31
31
  ):
32
32
  """Execution implementation of a Thermocycler's deactivate lid command."""
33
33
 
@@ -42,7 +42,7 @@ class DeactivateLidImpl(
42
42
 
43
43
  async def execute(
44
44
  self, params: DeactivateLidParams
45
- ) -> SuccessData[DeactivateLidResult, None]:
45
+ ) -> SuccessData[DeactivateLidResult]:
46
46
  """Unset a Thermocycler's target lid temperature."""
47
47
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
48
48
  params.moduleId
@@ -54,7 +54,9 @@ class DeactivateLidImpl(
54
54
  if thermocycler_hardware is not None:
55
55
  await thermocycler_hardware.deactivate_lid()
56
56
 
57
- return SuccessData(public=DeactivateLidResult(), private=None)
57
+ return SuccessData(
58
+ public=DeactivateLidResult(),
59
+ )
58
60
 
59
61
 
60
62
  class DeactivateLid(
@@ -6,11 +6,12 @@ from typing_extensions import Literal, Type
6
6
  from pydantic import BaseModel, Field
7
7
 
8
8
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
+ from ...state import update_types
9
10
  from ...errors.error_occurrence import ErrorOccurrence
10
11
  from opentrons.protocol_engine.types import MotorAxis
11
12
 
12
13
  if TYPE_CHECKING:
13
- from opentrons.protocol_engine.state import StateView
14
+ from opentrons.protocol_engine.state.state import StateView
14
15
  from opentrons.protocol_engine.execution import EquipmentHandler, MovementHandler
15
16
 
16
17
 
@@ -27,7 +28,7 @@ class OpenLidResult(BaseModel):
27
28
  """Result data from opening a Thermocycler's lid."""
28
29
 
29
30
 
30
- class OpenLidImpl(AbstractCommandImpl[OpenLidParams, SuccessData[OpenLidResult, None]]):
31
+ class OpenLidImpl(AbstractCommandImpl[OpenLidParams, SuccessData[OpenLidResult]]):
31
32
  """Execution implementation of a Thermocycler's open lid command."""
32
33
 
33
34
  def __init__(
@@ -41,8 +42,10 @@ class OpenLidImpl(AbstractCommandImpl[OpenLidParams, SuccessData[OpenLidResult,
41
42
  self._equipment = equipment
42
43
  self._movement = movement
43
44
 
44
- async def execute(self, params: OpenLidParams) -> SuccessData[OpenLidResult, None]:
45
+ async def execute(self, params: OpenLidParams) -> SuccessData[OpenLidResult]:
45
46
  """Open a Thermocycler's lid."""
47
+ state_update = update_types.StateUpdate()
48
+
46
49
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
47
50
  params.moduleId
48
51
  )
@@ -57,11 +60,12 @@ class OpenLidImpl(AbstractCommandImpl[OpenLidParams, SuccessData[OpenLidResult,
57
60
  MotorAxis.Y,
58
61
  ] + self._state_view.motion.get_robot_mount_axes()
59
62
  await self._movement.home(axes=axes_to_home)
63
+ state_update.clear_all_pipette_locations()
60
64
 
61
65
  if thermocycler_hardware is not None:
62
66
  await thermocycler_hardware.open()
63
67
 
64
- return SuccessData(public=OpenLidResult(), private=None)
68
+ return SuccessData(public=OpenLidResult(), state_update=state_update)
65
69
 
66
70
 
67
71
  class OpenLid(BaseCommand[OpenLidParams, OpenLidResult, ErrorOccurrence]):
@@ -0,0 +1,165 @@
1
+ """Command models to execute a Thermocycler profile."""
2
+ from __future__ import annotations
3
+ from typing import List, Optional, TYPE_CHECKING, overload, Union
4
+ from typing_extensions import Literal, Type
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+ from opentrons.hardware_control.modules.types import ThermocyclerStep, ThermocyclerCycle
9
+
10
+ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
11
+ from ...errors.error_occurrence import ErrorOccurrence
12
+
13
+ if TYPE_CHECKING:
14
+ from opentrons.protocol_engine.state.state import StateView
15
+ from opentrons.protocol_engine.execution import EquipmentHandler
16
+ from opentrons.protocol_engine.state.module_substates.thermocycler_module_substate import (
17
+ ThermocyclerModuleSubState,
18
+ )
19
+
20
+
21
+ RunExtendedProfileCommandType = Literal["thermocycler/runExtendedProfile"]
22
+
23
+
24
+ class ProfileStep(BaseModel):
25
+ """An individual step in a Thermocycler extended profile."""
26
+
27
+ celsius: float = Field(..., description="Target temperature in °C.")
28
+ holdSeconds: float = Field(
29
+ ..., description="Time to hold target temperature in seconds."
30
+ )
31
+
32
+
33
+ class ProfileCycle(BaseModel):
34
+ """An individual cycle in a Thermocycler extended profile."""
35
+
36
+ steps: List[ProfileStep] = Field(..., description="Steps to repeat.")
37
+ repetitions: int = Field(..., description="Number of times to repeat the steps.")
38
+
39
+
40
+ class RunExtendedProfileParams(BaseModel):
41
+ """Input parameters for an individual Thermocycler profile step."""
42
+
43
+ moduleId: str = Field(..., description="Unique ID of the Thermocycler.")
44
+ profileElements: List[Union[ProfileStep, ProfileCycle]] = Field(
45
+ ...,
46
+ description="Elements of the profile. Each can be either a step or a cycle.",
47
+ )
48
+ blockMaxVolumeUl: Optional[float] = Field(
49
+ None,
50
+ description="Amount of liquid in uL of the most-full well"
51
+ " in labware loaded onto the thermocycler.",
52
+ )
53
+
54
+
55
+ class RunExtendedProfileResult(BaseModel):
56
+ """Result data from running a Thermocycler profile."""
57
+
58
+
59
+ def _transform_profile_step(
60
+ step: ProfileStep, thermocycler_state: ThermocyclerModuleSubState
61
+ ) -> ThermocyclerStep:
62
+ return ThermocyclerStep(
63
+ temperature=thermocycler_state.validate_target_block_temperature(step.celsius),
64
+ hold_time_seconds=step.holdSeconds,
65
+ )
66
+
67
+
68
+ @overload
69
+ def _transform_profile_element(
70
+ element: ProfileStep, thermocycler_state: ThermocyclerModuleSubState
71
+ ) -> ThermocyclerStep:
72
+ ...
73
+
74
+
75
+ @overload
76
+ def _transform_profile_element(
77
+ element: ProfileCycle, thermocycler_state: ThermocyclerModuleSubState
78
+ ) -> ThermocyclerCycle:
79
+ ...
80
+
81
+
82
+ def _transform_profile_element(
83
+ element: Union[ProfileStep, ProfileCycle],
84
+ thermocycler_state: ThermocyclerModuleSubState,
85
+ ) -> Union[ThermocyclerStep, ThermocyclerCycle]:
86
+ if isinstance(element, ProfileStep):
87
+ return _transform_profile_step(element, thermocycler_state)
88
+ else:
89
+ return ThermocyclerCycle(
90
+ steps=[
91
+ _transform_profile_step(step, thermocycler_state)
92
+ for step in element.steps
93
+ ],
94
+ repetitions=element.repetitions,
95
+ )
96
+
97
+
98
+ class RunExtendedProfileImpl(
99
+ AbstractCommandImpl[RunExtendedProfileParams, SuccessData[RunExtendedProfileResult]]
100
+ ):
101
+ """Execution implementation of a Thermocycler's run profile command."""
102
+
103
+ def __init__(
104
+ self,
105
+ state_view: StateView,
106
+ equipment: EquipmentHandler,
107
+ **unused_dependencies: object,
108
+ ) -> None:
109
+ self._state_view = state_view
110
+ self._equipment = equipment
111
+
112
+ async def execute(
113
+ self, params: RunExtendedProfileParams
114
+ ) -> SuccessData[RunExtendedProfileResult]:
115
+ """Run a Thermocycler profile."""
116
+ thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
117
+ params.moduleId
118
+ )
119
+ thermocycler_hardware = self._equipment.get_module_hardware_api(
120
+ thermocycler_state.module_id
121
+ )
122
+
123
+ profile = [
124
+ _transform_profile_element(element, thermocycler_state)
125
+ for element in params.profileElements
126
+ ]
127
+ target_volume: Optional[float]
128
+ if params.blockMaxVolumeUl is not None:
129
+ target_volume = thermocycler_state.validate_max_block_volume(
130
+ params.blockMaxVolumeUl
131
+ )
132
+ else:
133
+ target_volume = None
134
+
135
+ if thermocycler_hardware is not None:
136
+ # TODO(jbl 2022-06-27) hardcoded constant 1 for `repetitions` should be
137
+ # moved from HardwareControlAPI to the Python ProtocolContext
138
+ await thermocycler_hardware.execute_profile(
139
+ profile=profile, volume=target_volume
140
+ )
141
+
142
+ return SuccessData(
143
+ public=RunExtendedProfileResult(),
144
+ )
145
+
146
+
147
+ class RunExtendedProfile(
148
+ BaseCommand[RunExtendedProfileParams, RunExtendedProfileResult, ErrorOccurrence]
149
+ ):
150
+ """A command to execute a Thermocycler profile run."""
151
+
152
+ commandType: RunExtendedProfileCommandType = "thermocycler/runExtendedProfile"
153
+ params: RunExtendedProfileParams
154
+ result: Optional[RunExtendedProfileResult]
155
+
156
+ _ImplementationCls: Type[RunExtendedProfileImpl] = RunExtendedProfileImpl
157
+
158
+
159
+ class RunExtendedProfileCreate(BaseCommandCreate[RunExtendedProfileParams]):
160
+ """A request to execute a Thermocycler profile run."""
161
+
162
+ commandType: RunExtendedProfileCommandType = "thermocycler/runExtendedProfile"
163
+ params: RunExtendedProfileParams
164
+
165
+ _CommandCls: Type[RunExtendedProfile] = RunExtendedProfile
@@ -11,7 +11,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
11
11
  from ...errors.error_occurrence import ErrorOccurrence
12
12
 
13
13
  if TYPE_CHECKING:
14
- from opentrons.protocol_engine.state import StateView
14
+ from opentrons.protocol_engine.state.state import StateView
15
15
  from opentrons.protocol_engine.execution import EquipmentHandler
16
16
 
17
17
 
@@ -47,7 +47,7 @@ class RunProfileResult(BaseModel):
47
47
 
48
48
 
49
49
  class RunProfileImpl(
50
- AbstractCommandImpl[RunProfileParams, SuccessData[RunProfileResult, None]]
50
+ AbstractCommandImpl[RunProfileParams, SuccessData[RunProfileResult]]
51
51
  ):
52
52
  """Execution implementation of a Thermocycler's run profile command."""
53
53
 
@@ -60,9 +60,7 @@ class RunProfileImpl(
60
60
  self._state_view = state_view
61
61
  self._equipment = equipment
62
62
 
63
- async def execute(
64
- self, params: RunProfileParams
65
- ) -> SuccessData[RunProfileResult, None]:
63
+ async def execute(self, params: RunProfileParams) -> SuccessData[RunProfileResult]:
66
64
  """Run a Thermocycler profile."""
67
65
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
68
66
  params.moduleId
@@ -96,7 +94,9 @@ class RunProfileImpl(
96
94
  steps=steps, repetitions=1, volume=target_volume
97
95
  )
98
96
 
99
- return SuccessData(public=RunProfileResult(), private=None)
97
+ return SuccessData(
98
+ public=RunProfileResult(),
99
+ )
100
100
 
101
101
 
102
102
  class RunProfile(BaseCommand[RunProfileParams, RunProfileResult, ErrorOccurrence]):
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -46,7 +46,7 @@ class SetTargetBlockTemperatureResult(BaseModel):
46
46
  class SetTargetBlockTemperatureImpl(
47
47
  AbstractCommandImpl[
48
48
  SetTargetBlockTemperatureParams,
49
- SuccessData[SetTargetBlockTemperatureResult, None],
49
+ SuccessData[SetTargetBlockTemperatureResult],
50
50
  ]
51
51
  ):
52
52
  """Execution implementation of a Thermocycler's set block temperature command."""
@@ -63,7 +63,7 @@ class SetTargetBlockTemperatureImpl(
63
63
  async def execute(
64
64
  self,
65
65
  params: SetTargetBlockTemperatureParams,
66
- ) -> SuccessData[SetTargetBlockTemperatureResult, None]:
66
+ ) -> SuccessData[SetTargetBlockTemperatureResult]:
67
67
  """Set a Thermocycler's target block temperature."""
68
68
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
69
69
  params.moduleId
@@ -97,7 +97,6 @@ class SetTargetBlockTemperatureImpl(
97
97
  public=SetTargetBlockTemperatureResult(
98
98
  targetBlockTemperature=target_temperature
99
99
  ),
100
- private=None,
101
100
  )
102
101
 
103
102
 
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -34,7 +34,7 @@ class SetTargetLidTemperatureResult(BaseModel):
34
34
 
35
35
  class SetTargetLidTemperatureImpl(
36
36
  AbstractCommandImpl[
37
- SetTargetLidTemperatureParams, SuccessData[SetTargetLidTemperatureResult, None]
37
+ SetTargetLidTemperatureParams, SuccessData[SetTargetLidTemperatureResult]
38
38
  ]
39
39
  ):
40
40
  """Execution implementation of a Thermocycler's set lid temperature command."""
@@ -51,7 +51,7 @@ class SetTargetLidTemperatureImpl(
51
51
  async def execute(
52
52
  self,
53
53
  params: SetTargetLidTemperatureParams,
54
- ) -> SuccessData[SetTargetLidTemperatureResult, None]:
54
+ ) -> SuccessData[SetTargetLidTemperatureResult]:
55
55
  """Set a Thermocycler's target lid temperature."""
56
56
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
57
57
  params.moduleId
@@ -70,7 +70,6 @@ class SetTargetLidTemperatureImpl(
70
70
  public=SetTargetLidTemperatureResult(
71
71
  targetLidTemperature=target_temperature
72
72
  ),
73
- private=None,
74
73
  )
75
74
 
76
75
 
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -28,7 +28,7 @@ class WaitForBlockTemperatureResult(BaseModel):
28
28
 
29
29
  class WaitForBlockTemperatureImpl(
30
30
  AbstractCommandImpl[
31
- WaitForBlockTemperatureParams, SuccessData[WaitForBlockTemperatureResult, None]
31
+ WaitForBlockTemperatureParams, SuccessData[WaitForBlockTemperatureResult]
32
32
  ]
33
33
  ):
34
34
  """Execution implementation of Thermocycler's wait for block temperature command."""
@@ -45,7 +45,7 @@ class WaitForBlockTemperatureImpl(
45
45
  async def execute(
46
46
  self,
47
47
  params: WaitForBlockTemperatureParams,
48
- ) -> SuccessData[WaitForBlockTemperatureResult, None]:
48
+ ) -> SuccessData[WaitForBlockTemperatureResult]:
49
49
  """Wait for a Thermocycler's target block temperature."""
50
50
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
51
51
  params.moduleId
@@ -61,7 +61,9 @@ class WaitForBlockTemperatureImpl(
61
61
  if thermocycler_hardware is not None:
62
62
  await thermocycler_hardware.wait_for_block_target()
63
63
 
64
- return SuccessData(public=WaitForBlockTemperatureResult(), private=None)
64
+ return SuccessData(
65
+ public=WaitForBlockTemperatureResult(),
66
+ )
65
67
 
66
68
 
67
69
  class WaitForBlockTemperature(
@@ -9,7 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
 
11
11
  if TYPE_CHECKING:
12
- from opentrons.protocol_engine.state import StateView
12
+ from opentrons.protocol_engine.state.state import StateView
13
13
  from opentrons.protocol_engine.execution import EquipmentHandler
14
14
 
15
15
 
@@ -28,7 +28,7 @@ class WaitForLidTemperatureResult(BaseModel):
28
28
 
29
29
  class WaitForLidTemperatureImpl(
30
30
  AbstractCommandImpl[
31
- WaitForLidTemperatureParams, SuccessData[WaitForLidTemperatureResult, None]
31
+ WaitForLidTemperatureParams, SuccessData[WaitForLidTemperatureResult]
32
32
  ]
33
33
  ):
34
34
  """Execution implementation of Thermocycler's wait for lid temperature command."""
@@ -45,7 +45,7 @@ class WaitForLidTemperatureImpl(
45
45
  async def execute(
46
46
  self,
47
47
  params: WaitForLidTemperatureParams,
48
- ) -> SuccessData[WaitForLidTemperatureResult, None]:
48
+ ) -> SuccessData[WaitForLidTemperatureResult]:
49
49
  """Wait for a Thermocycler's lid temperature."""
50
50
  thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
51
51
  params.moduleId
@@ -61,7 +61,9 @@ class WaitForLidTemperatureImpl(
61
61
  if thermocycler_hardware is not None:
62
62
  await thermocycler_hardware.wait_for_lid_target()
63
63
 
64
- return SuccessData(public=WaitForLidTemperatureResult(), private=None)
64
+ return SuccessData(
65
+ public=WaitForLidTemperatureResult(),
66
+ )
65
67
 
66
68
 
67
69
  class WaitForLidTemperature(
@@ -4,6 +4,8 @@ from pydantic import Field
4
4
  from typing import TYPE_CHECKING, Optional, Type
5
5
  from typing_extensions import Literal
6
6
 
7
+ from opentrons.protocol_engine.state import update_types
8
+
7
9
  from ..errors import TouchTipDisabledError, LabwareIsTipRackError
8
10
  from ..types import DeckPoint
9
11
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
@@ -16,7 +18,7 @@ from .pipetting_common import (
16
18
 
17
19
  if TYPE_CHECKING:
18
20
  from ..execution import MovementHandler, GantryMover
19
- from ..state import StateView
21
+ from ..state.state import StateView
20
22
 
21
23
 
22
24
  TouchTipCommandType = Literal["touchTip"]
@@ -48,7 +50,7 @@ class TouchTipResult(DestinationPositionResult):
48
50
 
49
51
 
50
52
  class TouchTipImplementation(
51
- AbstractCommandImpl[TouchTipParams, SuccessData[TouchTipResult, None]]
53
+ AbstractCommandImpl[TouchTipParams, SuccessData[TouchTipResult]]
52
54
  ):
53
55
  """Touch tip command implementation."""
54
56
 
@@ -63,14 +65,14 @@ class TouchTipImplementation(
63
65
  self._movement = movement
64
66
  self._gantry_mover = gantry_mover
65
67
 
66
- async def execute(
67
- self, params: TouchTipParams
68
- ) -> SuccessData[TouchTipResult, None]:
68
+ async def execute(self, params: TouchTipParams) -> SuccessData[TouchTipResult]:
69
69
  """Touch tip to sides of a well using the requested pipette."""
70
70
  pipette_id = params.pipetteId
71
71
  labware_id = params.labwareId
72
72
  well_name = params.wellName
73
73
 
74
+ state_update = update_types.StateUpdate()
75
+
74
76
  if self._state_view.labware.get_has_quirk(labware_id, "touchTipDisabled"):
75
77
  raise TouchTipDisabledError(
76
78
  f"Touch tip not allowed on labware {labware_id}"
@@ -98,14 +100,24 @@ class TouchTipImplementation(
98
100
  center_point=center_point,
99
101
  )
100
102
 
101
- x, y, z = await self._gantry_mover.move_to(
103
+ final_point = await self._gantry_mover.move_to(
102
104
  pipette_id=pipette_id,
103
105
  waypoints=touch_waypoints,
104
106
  speed=touch_speed,
105
107
  )
108
+ final_deck_point = DeckPoint.construct(
109
+ x=final_point.x, y=final_point.y, z=final_point.z
110
+ )
111
+ state_update.set_pipette_location(
112
+ pipette_id=pipette_id,
113
+ new_labware_id=labware_id,
114
+ new_well_name=well_name,
115
+ new_deck_point=final_deck_point,
116
+ )
106
117
 
107
118
  return SuccessData(
108
- public=TouchTipResult(position=DeckPoint(x=x, y=y, z=z)), private=None
119
+ public=TouchTipResult(position=final_deck_point),
120
+ state_update=state_update,
109
121
  )
110
122
 
111
123
 
@@ -31,6 +31,24 @@ from .unsafe_engage_axes import (
31
31
  UnsafeEngageAxesCreate,
32
32
  )
33
33
 
34
+ from .unsafe_ungrip_labware import (
35
+ UnsafeUngripLabwareCommandType,
36
+ UnsafeUngripLabwareParams,
37
+ UnsafeUngripLabwareResult,
38
+ UnsafeUngripLabware,
39
+ UnsafeUngripLabwareCreate,
40
+ )
41
+
42
+
43
+ from .unsafe_place_labware import (
44
+ UnsafePlaceLabwareCommandType,
45
+ UnsafePlaceLabwareParams,
46
+ UnsafePlaceLabwareResult,
47
+ UnsafePlaceLabware,
48
+ UnsafePlaceLabwareCreate,
49
+ )
50
+
51
+
34
52
  __all__ = [
35
53
  # Unsafe blow-out-in-place command models
36
54
  "UnsafeBlowOutInPlaceCommandType",
@@ -56,4 +74,16 @@ __all__ = [
56
74
  "UnsafeEngageAxesResult",
57
75
  "UnsafeEngageAxes",
58
76
  "UnsafeEngageAxesCreate",
77
+ # Unsafe ungrip labware
78
+ "UnsafeUngripLabwareCommandType",
79
+ "UnsafeUngripLabwareParams",
80
+ "UnsafeUngripLabwareResult",
81
+ "UnsafeUngripLabware",
82
+ "UnsafeUngripLabwareCreate",
83
+ # Unsafe place labware
84
+ "UnsafePlaceLabwareCommandType",
85
+ "UnsafePlaceLabwareParams",
86
+ "UnsafePlaceLabwareResult",
87
+ "UnsafePlaceLabware",
88
+ "UnsafePlaceLabwareCreate",
59
89
  ]
@@ -16,7 +16,7 @@ from opentrons.hardware_control.types import Axis
16
16
 
17
17
  if TYPE_CHECKING:
18
18
  from ...execution import PipettingHandler
19
- from ...state import StateView
19
+ from ...state.state import StateView
20
20
 
21
21
 
22
22
  UnsafeBlowOutInPlaceCommandType = Literal["unsafe/blowOutInPlace"]
@@ -36,7 +36,7 @@ class UnsafeBlowOutInPlaceResult(BaseModel):
36
36
 
37
37
  class UnsafeBlowOutInPlaceImplementation(
38
38
  AbstractCommandImpl[
39
- UnsafeBlowOutInPlaceParams, SuccessData[UnsafeBlowOutInPlaceResult, None]
39
+ UnsafeBlowOutInPlaceParams, SuccessData[UnsafeBlowOutInPlaceResult]
40
40
  ]
41
41
  ):
42
42
  """UnsafeBlowOutInPlace command implementation."""
@@ -54,7 +54,7 @@ class UnsafeBlowOutInPlaceImplementation(
54
54
 
55
55
  async def execute(
56
56
  self, params: UnsafeBlowOutInPlaceParams
57
- ) -> SuccessData[UnsafeBlowOutInPlaceResult, None]:
57
+ ) -> SuccessData[UnsafeBlowOutInPlaceResult]:
58
58
  """Blow-out without moving the pipette even when position is unknown."""
59
59
  ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
60
60
  pipette_location = self._state_view.motion.get_pipette_location(
@@ -67,7 +67,9 @@ class UnsafeBlowOutInPlaceImplementation(
67
67
  pipette_id=params.pipetteId, flow_rate=params.flowRate
68
68
  )
69
69
 
70
- return SuccessData(public=UnsafeBlowOutInPlaceResult(), private=None)
70
+ return SuccessData(
71
+ public=UnsafeBlowOutInPlaceResult(),
72
+ )
71
73
 
72
74
 
73
75
  class UnsafeBlowOutInPlace(