opentrons 8.7.0a9__py3-none-any.whl → 8.8.0a7__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.
- opentrons/_version.py +2 -2
- opentrons/cli/analyze.py +4 -1
- opentrons/config/__init__.py +7 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +126 -49
- opentrons/drivers/heater_shaker/abstract.py +5 -0
- opentrons/drivers/heater_shaker/driver.py +10 -0
- opentrons/drivers/heater_shaker/simulator.py +4 -0
- opentrons/drivers/thermocycler/abstract.py +6 -0
- opentrons/drivers/thermocycler/driver.py +61 -10
- opentrons/drivers/thermocycler/simulator.py +6 -0
- opentrons/drivers/vacuum_module/__init__.py +5 -0
- opentrons/drivers/vacuum_module/abstract.py +93 -0
- opentrons/drivers/vacuum_module/driver.py +208 -0
- opentrons/drivers/vacuum_module/errors.py +39 -0
- opentrons/drivers/vacuum_module/simulator.py +85 -0
- opentrons/drivers/vacuum_module/types.py +79 -0
- opentrons/execute.py +3 -0
- opentrons/hardware_control/api.py +24 -5
- opentrons/hardware_control/backends/controller.py +8 -2
- opentrons/hardware_control/backends/flex_protocol.py +1 -0
- opentrons/hardware_control/backends/ot3controller.py +35 -2
- opentrons/hardware_control/backends/ot3simulator.py +3 -1
- opentrons/hardware_control/backends/ot3utils.py +37 -0
- opentrons/hardware_control/backends/simulator.py +2 -1
- opentrons/hardware_control/backends/subsystem_manager.py +5 -2
- opentrons/hardware_control/emulation/abstract_emulator.py +6 -4
- opentrons/hardware_control/emulation/connection_handler.py +8 -5
- opentrons/hardware_control/emulation/heater_shaker.py +12 -3
- opentrons/hardware_control/emulation/settings.py +1 -1
- opentrons/hardware_control/emulation/thermocycler.py +67 -15
- opentrons/hardware_control/module_control.py +105 -10
- opentrons/hardware_control/modules/__init__.py +3 -0
- opentrons/hardware_control/modules/absorbance_reader.py +11 -4
- opentrons/hardware_control/modules/flex_stacker.py +38 -9
- opentrons/hardware_control/modules/heater_shaker.py +42 -5
- opentrons/hardware_control/modules/magdeck.py +8 -4
- opentrons/hardware_control/modules/mod_abc.py +14 -6
- opentrons/hardware_control/modules/tempdeck.py +25 -5
- opentrons/hardware_control/modules/thermocycler.py +68 -11
- opentrons/hardware_control/modules/types.py +20 -1
- opentrons/hardware_control/modules/utils.py +11 -4
- opentrons/hardware_control/motion_utilities.py +6 -6
- opentrons/hardware_control/nozzle_manager.py +3 -0
- opentrons/hardware_control/ot3api.py +85 -17
- opentrons/hardware_control/poller.py +22 -8
- opentrons/hardware_control/protocols/liquid_handler.py +6 -2
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +43 -2
- opentrons/legacy_commands/commands.py +58 -5
- opentrons/legacy_commands/module_commands.py +52 -0
- opentrons/legacy_commands/protocol_commands.py +53 -1
- opentrons/legacy_commands/types.py +155 -1
- opentrons/motion_planning/deck_conflict.py +17 -12
- opentrons/motion_planning/waypoints.py +15 -29
- opentrons/protocol_api/__init__.py +5 -1
- opentrons/protocol_api/_transfer_liquid_validation.py +17 -2
- opentrons/protocol_api/_types.py +8 -1
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/_default_labware_versions.py +33 -11
- opentrons/protocol_api/core/engine/deck_conflict.py +3 -1
- opentrons/protocol_api/core/engine/instrument.py +109 -26
- opentrons/protocol_api/core/engine/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +95 -4
- opentrons/protocol_api/core/engine/protocol.py +51 -2
- opentrons/protocol_api/core/engine/stringify.py +2 -0
- opentrons/protocol_api/core/engine/tasks.py +48 -0
- opentrons/protocol_api/core/engine/well.py +8 -0
- opentrons/protocol_api/core/instrument.py +19 -2
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +19 -2
- opentrons/protocol_api/core/legacy/legacy_module_core.py +33 -2
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +23 -1
- opentrons/protocol_api/core/legacy/legacy_well_core.py +4 -0
- opentrons/protocol_api/core/legacy/tasks.py +19 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +19 -2
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +14 -2
- opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
- opentrons/protocol_api/core/module.py +58 -2
- opentrons/protocol_api/core/protocol.py +23 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/core/well.py +4 -0
- opentrons/protocol_api/instrument_context.py +388 -2
- opentrons/protocol_api/labware.py +10 -2
- opentrons/protocol_api/module_contexts.py +170 -6
- opentrons/protocol_api/protocol_context.py +87 -21
- opentrons/protocol_api/robot_context.py +41 -25
- opentrons/protocol_api/tasks.py +48 -0
- opentrons/protocol_api/validation.py +49 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/__init__.py +6 -2
- opentrons/protocol_engine/actions/actions.py +31 -9
- opentrons/protocol_engine/clients/sync_client.py +42 -7
- opentrons/protocol_engine/commands/__init__.py +56 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +2 -15
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +2 -15
- opentrons/protocol_engine/commands/absorbance_reader/read.py +22 -23
- opentrons/protocol_engine/commands/aspirate.py +1 -0
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +52 -19
- opentrons/protocol_engine/commands/capture_image.py +302 -0
- opentrons/protocol_engine/commands/command.py +2 -0
- opentrons/protocol_engine/commands/command_unions.py +62 -0
- opentrons/protocol_engine/commands/create_timer.py +83 -0
- opentrons/protocol_engine/commands/dispense.py +1 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +56 -19
- opentrons/protocol_engine/commands/drop_tip.py +32 -8
- opentrons/protocol_engine/commands/flex_stacker/common.py +35 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +7 -0
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +14 -0
- opentrons/protocol_engine/commands/heater_shaker/common.py +20 -0
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +5 -4
- opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +136 -0
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +31 -5
- opentrons/protocol_engine/commands/move_labware.py +3 -4
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +1 -1
- opentrons/protocol_engine/commands/movement_common.py +31 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
- opentrons/protocol_engine/commands/pipetting_common.py +48 -3
- opentrons/protocol_engine/commands/set_tip_state.py +97 -0
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +38 -7
- opentrons/protocol_engine/commands/thermocycler/__init__.py +16 -0
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +6 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +8 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +44 -7
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +43 -14
- opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +191 -0
- opentrons/protocol_engine/commands/touch_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +6 -22
- opentrons/protocol_engine/commands/wait_for_tasks.py +98 -0
- opentrons/protocol_engine/create_protocol_engine.py +12 -0
- opentrons/protocol_engine/engine_support.py +3 -0
- opentrons/protocol_engine/errors/__init__.py +12 -0
- opentrons/protocol_engine/errors/exceptions.py +119 -0
- opentrons/protocol_engine/execution/__init__.py +4 -0
- opentrons/protocol_engine/execution/command_executor.py +62 -1
- opentrons/protocol_engine/execution/create_queue_worker.py +9 -2
- opentrons/protocol_engine/execution/labware_movement.py +13 -15
- opentrons/protocol_engine/execution/movement.py +2 -0
- opentrons/protocol_engine/execution/pipetting.py +19 -25
- opentrons/protocol_engine/execution/queue_worker.py +4 -0
- opentrons/protocol_engine/execution/run_control.py +8 -0
- opentrons/protocol_engine/execution/task_handler.py +157 -0
- opentrons/protocol_engine/protocol_engine.py +137 -36
- opentrons/protocol_engine/resources/__init__.py +4 -0
- opentrons/protocol_engine/resources/camera_provider.py +110 -0
- opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
- opentrons/protocol_engine/resources/file_provider.py +133 -58
- opentrons/protocol_engine/resources/labware_validation.py +10 -6
- opentrons/protocol_engine/slot_standardization.py +2 -0
- opentrons/protocol_engine/state/_well_math.py +60 -18
- opentrons/protocol_engine/state/addressable_areas.py +2 -0
- opentrons/protocol_engine/state/camera.py +54 -0
- opentrons/protocol_engine/state/commands.py +37 -14
- opentrons/protocol_engine/state/geometry.py +276 -379
- opentrons/protocol_engine/state/labware.py +62 -108
- opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1336 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
- opentrons/protocol_engine/state/modules.py +30 -8
- opentrons/protocol_engine/state/motion.py +44 -0
- opentrons/protocol_engine/state/preconditions.py +59 -0
- opentrons/protocol_engine/state/state.py +44 -0
- opentrons/protocol_engine/state/state_summary.py +4 -0
- opentrons/protocol_engine/state/tasks.py +139 -0
- opentrons/protocol_engine/state/tips.py +177 -258
- opentrons/protocol_engine/state/update_types.py +26 -9
- opentrons/protocol_engine/types/__init__.py +23 -4
- opentrons/protocol_engine/types/command_preconditions.py +18 -0
- opentrons/protocol_engine/types/deck_configuration.py +5 -1
- opentrons/protocol_engine/types/instrument.py +8 -1
- opentrons/protocol_engine/types/labware.py +1 -13
- opentrons/protocol_engine/types/location.py +26 -2
- opentrons/protocol_engine/types/module.py +11 -1
- opentrons/protocol_engine/types/tasks.py +38 -0
- opentrons/protocol_engine/types/tip.py +9 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +29 -2
- opentrons/protocol_runner/protocol_runner.py +14 -1
- opentrons/protocol_runner/run_orchestrator.py +49 -2
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +2 -2
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +2 -1
- opentrons/simulate.py +51 -15
- opentrons/system/camera.py +334 -4
- opentrons/system/ffmpeg.py +110 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a7.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a7.dist-info}/RECORD +188 -160
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a7.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a7.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a7.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""CreateTimer command request, result, and implementation models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from typing import Optional, Type, TYPE_CHECKING
|
|
6
|
+
from typing_extensions import Literal
|
|
7
|
+
|
|
8
|
+
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
9
|
+
from ..errors.error_occurrence import ErrorOccurrence
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from ..execution import TaskHandler, RunControlHandler
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
CreateTimerCommandType = Literal["createTimer"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CreateTimerParams(BaseModel):
|
|
19
|
+
"""Payload required to annotate execution with a CreateTimer."""
|
|
20
|
+
|
|
21
|
+
time: float = Field(
|
|
22
|
+
...,
|
|
23
|
+
description="The time before the timer should elapse in seconds. This is the minimum time before the timer elapses; it may in practice take longer than this.",
|
|
24
|
+
)
|
|
25
|
+
task_id: str | None = Field(
|
|
26
|
+
None,
|
|
27
|
+
description="The id of the timer task",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CreateTimerResult(BaseModel):
|
|
32
|
+
"""Result data from the execution of a CreateTimer command."""
|
|
33
|
+
|
|
34
|
+
task_id: str = Field(..., description="The id of the timer task")
|
|
35
|
+
time: float = Field(..., description="The same time as the parameter.")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CreateTimerImplementation(
|
|
39
|
+
AbstractCommandImpl[CreateTimerParams, SuccessData[CreateTimerResult]]
|
|
40
|
+
):
|
|
41
|
+
"""CreateTimer command implementation."""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
task_handler: TaskHandler,
|
|
46
|
+
run_control: RunControlHandler,
|
|
47
|
+
**kwargs: object,
|
|
48
|
+
) -> None:
|
|
49
|
+
self._task_handler = task_handler
|
|
50
|
+
self._run_control = run_control
|
|
51
|
+
|
|
52
|
+
async def execute(
|
|
53
|
+
self, params: CreateTimerParams
|
|
54
|
+
) -> SuccessData[CreateTimerResult]:
|
|
55
|
+
"""No operation taken other than capturing message in command."""
|
|
56
|
+
|
|
57
|
+
async def timer(task_handler: TaskHandler) -> None:
|
|
58
|
+
async with task_handler.synchronize_concurrent("createTimer"):
|
|
59
|
+
await self._run_control.wait_for_duration(params.time)
|
|
60
|
+
|
|
61
|
+
task = await self._task_handler.create_task(timer, params.task_id)
|
|
62
|
+
return SuccessData(
|
|
63
|
+
public=CreateTimerResult(task_id=task.id, time=params.time),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class CreateTimer(BaseCommand[CreateTimerParams, CreateTimerResult, ErrorOccurrence]):
|
|
68
|
+
"""CreateTimer command model."""
|
|
69
|
+
|
|
70
|
+
commandType: CreateTimerCommandType = "createTimer"
|
|
71
|
+
params: CreateTimerParams
|
|
72
|
+
result: Optional[CreateTimerResult] = None
|
|
73
|
+
|
|
74
|
+
_ImplementationCls: Type[CreateTimerImplementation] = CreateTimerImplementation
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class CreateTimerCreate(BaseCommandCreate[CreateTimerParams]):
|
|
78
|
+
"""CreateTimer command request model."""
|
|
79
|
+
|
|
80
|
+
commandType: CreateTimerCommandType = "createTimer"
|
|
81
|
+
params: CreateTimerParams
|
|
82
|
+
|
|
83
|
+
_CommandCls: Type[CreateTimer] = CreateTimer
|
|
@@ -101,6 +101,7 @@ class DispenseImplementation(AbstractCommandImpl[DispenseParams, _ExecuteReturn]
|
|
|
101
101
|
well_name=well_name,
|
|
102
102
|
well_location=well_location,
|
|
103
103
|
operation_volume=volume,
|
|
104
|
+
offset_pipette_for_reservoir_subwells=False,
|
|
104
105
|
)
|
|
105
106
|
if isinstance(move_result, DefinedErrorData):
|
|
106
107
|
return move_result
|
|
@@ -19,7 +19,7 @@ from .pipetting_common import (
|
|
|
19
19
|
dispense_while_tracking,
|
|
20
20
|
)
|
|
21
21
|
from .movement_common import (
|
|
22
|
-
|
|
22
|
+
DynamicLiquidHandlingWellLocationMixin,
|
|
23
23
|
DestinationPositionResult,
|
|
24
24
|
StallOrCollisionError,
|
|
25
25
|
move_to_well,
|
|
@@ -49,7 +49,7 @@ class DispenseWhileTrackingParams(
|
|
|
49
49
|
PipetteIdMixin,
|
|
50
50
|
DispenseVolumeMixin,
|
|
51
51
|
FlowRateMixin,
|
|
52
|
-
|
|
52
|
+
DynamicLiquidHandlingWellLocationMixin,
|
|
53
53
|
):
|
|
54
54
|
"""Payload required to dispense to a specific well."""
|
|
55
55
|
|
|
@@ -100,14 +100,24 @@ class DispenseWhileTrackingImplementation(
|
|
|
100
100
|
# TODO(pbm, 10-15-24): call self._state_view.geometry.validate_dispense_volume_into_well()
|
|
101
101
|
|
|
102
102
|
state_update = StateUpdate()
|
|
103
|
+
|
|
104
|
+
end_point = self._state_view.geometry.get_well_position(
|
|
105
|
+
labware_id=params.labwareId,
|
|
106
|
+
well_name=params.wellName,
|
|
107
|
+
well_location=params.trackToLocation,
|
|
108
|
+
operation_volume=params.volume,
|
|
109
|
+
pipette_id=params.pipetteId,
|
|
110
|
+
)
|
|
111
|
+
|
|
103
112
|
move_result = await move_to_well(
|
|
104
113
|
movement=self._movement,
|
|
105
114
|
model_utils=self._model_utils,
|
|
106
115
|
pipette_id=params.pipetteId,
|
|
107
116
|
labware_id=params.labwareId,
|
|
108
117
|
well_name=params.wellName,
|
|
109
|
-
well_location=params.
|
|
118
|
+
well_location=params.trackFromLocation,
|
|
110
119
|
)
|
|
120
|
+
|
|
111
121
|
state_update.append(move_result.state_update)
|
|
112
122
|
if isinstance(move_result, DefinedErrorData):
|
|
113
123
|
return DefinedErrorData(
|
|
@@ -120,6 +130,7 @@ class DispenseWhileTrackingImplementation(
|
|
|
120
130
|
well_name=well_name,
|
|
121
131
|
volume=params.volume,
|
|
122
132
|
flow_rate=params.flowRate,
|
|
133
|
+
end_point=end_point,
|
|
123
134
|
push_out=params.pushOut,
|
|
124
135
|
location_if_error={
|
|
125
136
|
"retryLocation": (
|
|
@@ -130,7 +141,49 @@ class DispenseWhileTrackingImplementation(
|
|
|
130
141
|
},
|
|
131
142
|
pipetting=self._pipetting,
|
|
132
143
|
model_utils=self._model_utils,
|
|
144
|
+
movement_delay=params.movement_delay,
|
|
133
145
|
)
|
|
146
|
+
|
|
147
|
+
if isinstance(dispense_result, DefinedErrorData):
|
|
148
|
+
if isinstance(dispense_result.public, OverpressureError):
|
|
149
|
+
return DefinedErrorData(
|
|
150
|
+
public=OverpressureError(
|
|
151
|
+
id=dispense_result.public.id,
|
|
152
|
+
createdAt=dispense_result.public.createdAt,
|
|
153
|
+
wrappedErrors=dispense_result.public.wrappedErrors,
|
|
154
|
+
errorInfo=dispense_result.public.errorInfo,
|
|
155
|
+
),
|
|
156
|
+
state_update=dispense_result.state_update.set_liquid_operated(
|
|
157
|
+
labware_id=params.labwareId,
|
|
158
|
+
well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
|
|
159
|
+
params.labwareId,
|
|
160
|
+
params.wellName,
|
|
161
|
+
params.pipetteId,
|
|
162
|
+
),
|
|
163
|
+
volume_added=CLEAR,
|
|
164
|
+
),
|
|
165
|
+
state_update_if_false_positive=dispense_result.state_update_if_false_positive,
|
|
166
|
+
)
|
|
167
|
+
elif isinstance(dispense_result.public, StallOrCollisionError):
|
|
168
|
+
return DefinedErrorData(
|
|
169
|
+
public=StallOrCollisionError(
|
|
170
|
+
id=dispense_result.public.id,
|
|
171
|
+
createdAt=dispense_result.public.createdAt,
|
|
172
|
+
wrappedErrors=dispense_result.public.wrappedErrors,
|
|
173
|
+
errorInfo=dispense_result.public.errorInfo,
|
|
174
|
+
),
|
|
175
|
+
state_update=dispense_result.state_update.set_liquid_operated(
|
|
176
|
+
labware_id=params.labwareId,
|
|
177
|
+
well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
|
|
178
|
+
params.labwareId,
|
|
179
|
+
params.wellName,
|
|
180
|
+
params.pipetteId,
|
|
181
|
+
),
|
|
182
|
+
volume_added=CLEAR,
|
|
183
|
+
),
|
|
184
|
+
state_update_if_false_positive=dispense_result.state_update_if_false_positive,
|
|
185
|
+
)
|
|
186
|
+
|
|
134
187
|
position_after_dispense = await self._gantry_mover.get_position(
|
|
135
188
|
params.pipetteId
|
|
136
189
|
)
|
|
@@ -139,22 +192,6 @@ class DispenseWhileTrackingImplementation(
|
|
|
139
192
|
y=position_after_dispense.y,
|
|
140
193
|
z=position_after_dispense.z,
|
|
141
194
|
)
|
|
142
|
-
|
|
143
|
-
if isinstance(dispense_result, DefinedErrorData):
|
|
144
|
-
return DefinedErrorData(
|
|
145
|
-
public=dispense_result.public,
|
|
146
|
-
state_update=dispense_result.state_update.set_liquid_operated(
|
|
147
|
-
labware_id=params.labwareId,
|
|
148
|
-
well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
|
|
149
|
-
params.labwareId,
|
|
150
|
-
params.wellName,
|
|
151
|
-
params.pipetteId,
|
|
152
|
-
),
|
|
153
|
-
volume_added=CLEAR,
|
|
154
|
-
),
|
|
155
|
-
state_update_if_false_positive=dispense_result.state_update_if_false_positive,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
195
|
return SuccessData(
|
|
159
196
|
public=DispenseWhileTrackingResult(
|
|
160
197
|
volume=dispense_result.public.volume,
|
|
@@ -12,7 +12,7 @@ from opentrons.protocol_engine.errors.exceptions import TipAttachedError
|
|
|
12
12
|
from opentrons.protocol_engine.resources.model_utils import ModelUtils
|
|
13
13
|
|
|
14
14
|
from ..state.update_types import StateUpdate
|
|
15
|
-
from ..types import DropTipWellLocation
|
|
15
|
+
from ..types import DropTipWellLocation, TipRackWellState
|
|
16
16
|
from .pipetting_common import (
|
|
17
17
|
PipetteIdMixin,
|
|
18
18
|
TipPhysicallyAttachedError,
|
|
@@ -140,6 +140,25 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
|
|
|
140
140
|
partially_configured=is_partially_configured,
|
|
141
141
|
)
|
|
142
142
|
|
|
143
|
+
is_tip_rack = self._state_view.labware.get_definition(
|
|
144
|
+
labware_id
|
|
145
|
+
).parameters.isTiprack
|
|
146
|
+
|
|
147
|
+
# It's possible that we are dropping tips into a labware trash for pre API v2.14 OT-2 protocols
|
|
148
|
+
# (or something else unexpected), so if it is not a tip rack mark no wells as used
|
|
149
|
+
if is_tip_rack:
|
|
150
|
+
tips_to_mark_as_used = (
|
|
151
|
+
self._state_view.tips.compute_tips_to_mark_as_used_or_empty(
|
|
152
|
+
labware_id=labware_id,
|
|
153
|
+
well_name=well_name,
|
|
154
|
+
nozzle_map=self._state_view.pipettes.get_nozzle_configuration(
|
|
155
|
+
pipette_id
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
tips_to_mark_as_used = []
|
|
161
|
+
|
|
143
162
|
move_result = await move_to_well(
|
|
144
163
|
movement=self._movement_handler,
|
|
145
164
|
model_utils=self._model_utils,
|
|
@@ -152,12 +171,7 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
|
|
|
152
171
|
return move_result
|
|
153
172
|
|
|
154
173
|
scrape_type = TipScrapeType.NONE
|
|
155
|
-
if
|
|
156
|
-
params.scrape_tips
|
|
157
|
-
and self._state_view.geometry._labware.get_definition(
|
|
158
|
-
labware_id
|
|
159
|
-
).parameters.isTiprack
|
|
160
|
-
):
|
|
174
|
+
if params.scrape_tips and is_tip_rack:
|
|
161
175
|
if int("".join(filter(str.isdigit, well_name))) <= 6:
|
|
162
176
|
scrape_type = TipScrapeType.RIGHT_ONE_COL
|
|
163
177
|
else:
|
|
@@ -194,6 +208,10 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
|
|
|
194
208
|
pipette_id=params.pipetteId,
|
|
195
209
|
tip_geometry=None,
|
|
196
210
|
tip_source=None,
|
|
211
|
+
).update_tip_rack_well_state(
|
|
212
|
+
tip_state=TipRackWellState.USED,
|
|
213
|
+
labware_id=labware_id,
|
|
214
|
+
well_names=tips_to_mark_as_used,
|
|
197
215
|
),
|
|
198
216
|
)
|
|
199
217
|
else:
|
|
@@ -201,10 +219,16 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
|
|
|
201
219
|
public=DropTipResult(position=move_result.public.position),
|
|
202
220
|
state_update=move_result.state_update.set_fluid_unknown(
|
|
203
221
|
pipette_id=pipette_id
|
|
204
|
-
)
|
|
222
|
+
)
|
|
223
|
+
.update_pipette_tip_state(
|
|
205
224
|
pipette_id=params.pipetteId,
|
|
206
225
|
tip_geometry=None,
|
|
207
226
|
tip_source=None,
|
|
227
|
+
)
|
|
228
|
+
.update_tip_rack_well_state(
|
|
229
|
+
tip_state=TipRackWellState.USED,
|
|
230
|
+
labware_id=labware_id,
|
|
231
|
+
well_names=tips_to_mark_as_used,
|
|
208
232
|
),
|
|
209
233
|
)
|
|
210
234
|
|
|
@@ -10,6 +10,10 @@ from opentrons_shared_data.errors import ErrorCodes
|
|
|
10
10
|
from opentrons_shared_data.errors.exceptions import CommandPreconditionViolated
|
|
11
11
|
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
|
|
12
12
|
|
|
13
|
+
from opentrons.protocol_engine.errors.exceptions import (
|
|
14
|
+
LabwarePoolNotCompatibleWithModuleError,
|
|
15
|
+
)
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
from ...errors import ErrorOccurrence
|
|
15
19
|
from ...types import (
|
|
@@ -35,6 +39,15 @@ if TYPE_CHECKING:
|
|
|
35
39
|
from opentrons.protocol_engine.execution.equipment import LoadedLabwarePoolData
|
|
36
40
|
from opentrons.protocol_engine.state.module_substates import FlexStackerSubState
|
|
37
41
|
|
|
42
|
+
|
|
43
|
+
# The stacker cannot dispense labware where there is no gap between the top surface
|
|
44
|
+
# of the bottom labware being dispensed, and bottom surface of the top labware.
|
|
45
|
+
# This is because the stacker latch, which holds the labware stack, needs enough
|
|
46
|
+
# empty space to free the bottom labware, but still hold the top labware once it
|
|
47
|
+
# closes.
|
|
48
|
+
STACKER_INCOMPATIBLE_LABWARE = set(["opentrons_tough_universal_lid"])
|
|
49
|
+
|
|
50
|
+
|
|
38
51
|
INITIAL_COUNT_DESCRIPTION = dedent(
|
|
39
52
|
"""\
|
|
40
53
|
The number of labware that should be initially stored in the stacker. This number will be silently clamped to
|
|
@@ -911,3 +924,25 @@ def build_retrieve_labware_move_updates(
|
|
|
911
924
|
lid_offset_location,
|
|
912
925
|
)
|
|
913
926
|
return locations_for_ids, offset_ids_by_id
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def validate_labware_pool_compatible_with_stacker(
|
|
930
|
+
pool_primary_definition: LabwareDefinition,
|
|
931
|
+
pool_adapter_definition: LabwareDefinition | None,
|
|
932
|
+
pool_lid_definition: LabwareDefinition | None,
|
|
933
|
+
) -> None:
|
|
934
|
+
"""Verifies that the given labware pool is compatible with the stacker."""
|
|
935
|
+
labware_pool = set(
|
|
936
|
+
lw.parameters.loadName
|
|
937
|
+
for lw in [
|
|
938
|
+
pool_primary_definition,
|
|
939
|
+
pool_adapter_definition,
|
|
940
|
+
pool_lid_definition,
|
|
941
|
+
]
|
|
942
|
+
if lw is not None
|
|
943
|
+
)
|
|
944
|
+
incompatible_labware = list(labware_pool & STACKER_INCOMPATIBLE_LABWARE)
|
|
945
|
+
if incompatible_labware:
|
|
946
|
+
raise LabwarePoolNotCompatibleWithModuleError(
|
|
947
|
+
f"The stacker cannot store {incompatible_labware}"
|
|
948
|
+
)
|
|
@@ -31,6 +31,7 @@ from .common import (
|
|
|
31
31
|
primary_location_sequences,
|
|
32
32
|
adapter_location_sequences_with_default,
|
|
33
33
|
lid_location_sequences_with_default,
|
|
34
|
+
validate_labware_pool_compatible_with_stacker,
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
if TYPE_CHECKING:
|
|
@@ -210,6 +211,12 @@ class SetStoredLabwareImpl(
|
|
|
210
211
|
)
|
|
211
212
|
)
|
|
212
213
|
|
|
214
|
+
validate_labware_pool_compatible_with_stacker(
|
|
215
|
+
pool_primary_definition=labware_def,
|
|
216
|
+
pool_adapter_definition=adapter_def,
|
|
217
|
+
pool_lid_definition=lid_def,
|
|
218
|
+
)
|
|
219
|
+
|
|
213
220
|
pool_height = self._state_view.geometry.get_height_of_labware_stack(
|
|
214
221
|
pool_definitions
|
|
215
222
|
)
|
|
@@ -32,6 +32,14 @@ from .set_and_wait_for_shake_speed import (
|
|
|
32
32
|
SetAndWaitForShakeSpeedCommandType,
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
+
from .set_shake_speed import (
|
|
36
|
+
SetShakeSpeed,
|
|
37
|
+
SetShakeSpeedCreate,
|
|
38
|
+
SetShakeSpeedParams,
|
|
39
|
+
SetShakeSpeedResult,
|
|
40
|
+
SetShakeSpeedCommandType,
|
|
41
|
+
)
|
|
42
|
+
|
|
35
43
|
from .deactivate_shaker import (
|
|
36
44
|
DeactivateShaker,
|
|
37
45
|
DeactivateShakerCreate,
|
|
@@ -81,6 +89,12 @@ __all__ = [
|
|
|
81
89
|
"SetAndWaitForShakeSpeedParams",
|
|
82
90
|
"SetAndWaitForShakeSpeedResult",
|
|
83
91
|
"SetAndWaitForShakeSpeedCommandType",
|
|
92
|
+
# heaterShaker/setShakeSpeed
|
|
93
|
+
"SetShakeSpeed",
|
|
94
|
+
"SetShakeSpeedCreate",
|
|
95
|
+
"SetShakeSpeedParams",
|
|
96
|
+
"SetShakeSpeedResult",
|
|
97
|
+
"SetShakeSpeedCommandType",
|
|
84
98
|
# heaterShaker/deactivateShaker
|
|
85
99
|
"DeactivateShaker",
|
|
86
100
|
"DeactivateShakerCreate",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Common heatershaker base models."""
|
|
2
|
+
from opentrons.protocol_engine.state.module_substates.heater_shaker_module_substate import (
|
|
3
|
+
HeaterShakerModuleSubState,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def get_heatershaker_ready_to_shake(
|
|
8
|
+
hs_module_substate: HeaterShakerModuleSubState,
|
|
9
|
+
rpm: float,
|
|
10
|
+
) -> int:
|
|
11
|
+
"""Check heatershaker state to confirm if it is ready to shake.
|
|
12
|
+
|
|
13
|
+
This includes
|
|
14
|
+
- Checking latch closure
|
|
15
|
+
- Validating target speed
|
|
16
|
+
"""
|
|
17
|
+
hs_module_substate.raise_if_labware_latch_not_closed()
|
|
18
|
+
# Verify speed from hs module view
|
|
19
|
+
validated_speed = hs_module_substate.validate_target_speed(rpm)
|
|
20
|
+
return validated_speed
|
|
@@ -8,6 +8,8 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
|
|
|
8
8
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
9
9
|
from ...state import update_types
|
|
10
10
|
|
|
11
|
+
from .common import get_heatershaker_ready_to_shake
|
|
12
|
+
|
|
11
13
|
if TYPE_CHECKING:
|
|
12
14
|
from opentrons.protocol_engine.state.state import StateView
|
|
13
15
|
from opentrons.protocol_engine.execution import EquipmentHandler, MovementHandler
|
|
@@ -64,10 +66,9 @@ class SetAndWaitForShakeSpeedImpl(
|
|
|
64
66
|
module_id=params.moduleId
|
|
65
67
|
)
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
validated_speed = hs_module_substate.validate_target_speed(params.rpm)
|
|
69
|
+
validated_speed = await get_heatershaker_ready_to_shake(
|
|
70
|
+
hs_module_substate, params.rpm
|
|
71
|
+
)
|
|
71
72
|
|
|
72
73
|
pipette_should_retract = (
|
|
73
74
|
self._state_view.motion.check_pipette_blocking_hs_shaker(
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Command models to set a shake speed for a Heater-Shaker Module."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Optional, TYPE_CHECKING
|
|
4
|
+
from typing_extensions import Literal, Type
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
8
|
+
from ...errors.error_occurrence import ErrorOccurrence
|
|
9
|
+
from ...state import update_types
|
|
10
|
+
from .common import get_heatershaker_ready_to_shake
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from opentrons.protocol_engine.state.state import StateView
|
|
14
|
+
from opentrons.protocol_engine.execution import (
|
|
15
|
+
EquipmentHandler,
|
|
16
|
+
MovementHandler,
|
|
17
|
+
TaskHandler,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
SetShakeSpeedCommandType = Literal["heaterShaker/setShakeSpeed"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SetShakeSpeedParams(BaseModel):
|
|
24
|
+
"""Input parameters to set a shake speed for a Heater-Shaker Module."""
|
|
25
|
+
|
|
26
|
+
moduleId: str = Field(..., description="Unique ID of the Heater-Shaker Module.")
|
|
27
|
+
rpm: float = Field(..., description="Target speed in rotations per minute.")
|
|
28
|
+
taskId: str | None = Field(
|
|
29
|
+
None,
|
|
30
|
+
description="Id for the background task that manages the temperature",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SetShakeSpeedResult(BaseModel):
|
|
35
|
+
"""Result data from setting and waiting for a Heater-Shaker's shake speed."""
|
|
36
|
+
|
|
37
|
+
pipetteRetracted: bool = Field(
|
|
38
|
+
...,
|
|
39
|
+
description=(
|
|
40
|
+
"Whether this command automatically retracted the pipettes"
|
|
41
|
+
" before starting the shake, to avoid a potential collision."
|
|
42
|
+
),
|
|
43
|
+
)
|
|
44
|
+
taskId: str = Field(
|
|
45
|
+
description="The task id for the setTargetTemperature task",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SetShakeSpeedImpl(
|
|
50
|
+
AbstractCommandImpl[SetShakeSpeedParams, SuccessData[SetShakeSpeedResult]]
|
|
51
|
+
):
|
|
52
|
+
"""Execution implementation of Heater-Shaker's set shake speed command."""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
state_view: StateView,
|
|
57
|
+
equipment: EquipmentHandler,
|
|
58
|
+
movement: MovementHandler,
|
|
59
|
+
task_handler: TaskHandler,
|
|
60
|
+
**unused_dependencies: object,
|
|
61
|
+
) -> None:
|
|
62
|
+
self._state_view = state_view
|
|
63
|
+
self._equipment = equipment
|
|
64
|
+
self._movement = movement
|
|
65
|
+
self._task_handler = task_handler
|
|
66
|
+
|
|
67
|
+
async def execute(
|
|
68
|
+
self,
|
|
69
|
+
params: SetShakeSpeedParams,
|
|
70
|
+
) -> SuccessData[SetShakeSpeedResult]:
|
|
71
|
+
"""Set a Heater-Shaker's target shake speed."""
|
|
72
|
+
state_update = update_types.StateUpdate()
|
|
73
|
+
|
|
74
|
+
# Allow propagation of ModuleNotLoadedError and WrongModuleTypeError.
|
|
75
|
+
hs_module_substate = self._state_view.modules.get_heater_shaker_module_substate(
|
|
76
|
+
module_id=params.moduleId
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
validated_speed = await get_heatershaker_ready_to_shake(
|
|
80
|
+
hs_module_substate, params.rpm
|
|
81
|
+
)
|
|
82
|
+
pipette_should_retract = (
|
|
83
|
+
self._state_view.motion.check_pipette_blocking_hs_shaker(
|
|
84
|
+
hs_module_substate.module_id
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
if pipette_should_retract:
|
|
88
|
+
# Move pipette away if it is close to the heater-shaker
|
|
89
|
+
# TODO(jbl 2022-07-28) replace home movement with a retract movement
|
|
90
|
+
await self._movement.home(
|
|
91
|
+
axes=self._state_view.motion.get_robot_mount_axes()
|
|
92
|
+
)
|
|
93
|
+
state_update.clear_all_pipette_locations()
|
|
94
|
+
|
|
95
|
+
# Allow propagation of ModuleNotAttachedError.
|
|
96
|
+
hs_hardware_module = self._equipment.get_module_hardware_api(
|
|
97
|
+
hs_module_substate.module_id
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def start_shake(task_handler: TaskHandler) -> None:
|
|
101
|
+
if hs_hardware_module is not None:
|
|
102
|
+
async with task_handler.synchronize_cancel_previous(
|
|
103
|
+
hs_module_substate.module_id + "-shake"
|
|
104
|
+
):
|
|
105
|
+
await hs_hardware_module.set_speed(rpm=validated_speed)
|
|
106
|
+
|
|
107
|
+
task = await self._task_handler.create_task(
|
|
108
|
+
task_function=start_shake, id=params.taskId
|
|
109
|
+
)
|
|
110
|
+
return SuccessData(
|
|
111
|
+
public=SetShakeSpeedResult(
|
|
112
|
+
pipetteRetracted=pipette_should_retract, taskId=task.id
|
|
113
|
+
),
|
|
114
|
+
state_update=state_update,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class SetShakeSpeed(
|
|
119
|
+
BaseCommand[SetShakeSpeedParams, SetShakeSpeedResult, ErrorOccurrence]
|
|
120
|
+
):
|
|
121
|
+
"""A command to set a Heater-Shaker's shake speed."""
|
|
122
|
+
|
|
123
|
+
commandType: SetShakeSpeedCommandType = "heaterShaker/setShakeSpeed"
|
|
124
|
+
params: SetShakeSpeedParams
|
|
125
|
+
result: Optional[SetShakeSpeedResult] = None
|
|
126
|
+
|
|
127
|
+
_ImplementationCls: Type[SetShakeSpeedImpl] = SetShakeSpeedImpl
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class SetShakeSpeedCreate(BaseCommandCreate[SetShakeSpeedParams]):
|
|
131
|
+
"""A request to create a Heater-Shaker's set shake speed command."""
|
|
132
|
+
|
|
133
|
+
commandType: SetShakeSpeedCommandType = "heaterShaker/setShakeSpeed"
|
|
134
|
+
params: SetShakeSpeedParams
|
|
135
|
+
|
|
136
|
+
_CommandCls: Type[SetShakeSpeed] = SetShakeSpeed
|
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
"""Command models to start heating a Heater-Shaker Module."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
-
from typing import Optional, TYPE_CHECKING
|
|
3
|
+
from typing import Optional, TYPE_CHECKING, Any
|
|
4
4
|
from typing_extensions import Literal, Type
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
7
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
7
8
|
|
|
8
9
|
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
9
10
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from opentrons.protocol_engine.state.state import StateView
|
|
13
|
-
from opentrons.protocol_engine.execution import EquipmentHandler
|
|
14
|
+
from opentrons.protocol_engine.execution import EquipmentHandler, TaskHandler
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
SetTargetTemperatureCommandType = Literal["heaterShaker/setTargetTemperature"]
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
21
|
+
s.pop("default", None)
|
|
22
|
+
|
|
23
|
+
|
|
19
24
|
class SetTargetTemperatureParams(BaseModel):
|
|
20
25
|
"""Input parameters to set a Heater-Shaker's target temperature."""
|
|
21
26
|
|
|
22
27
|
moduleId: str = Field(..., description="Unique ID of the Heater-Shaker Module.")
|
|
23
28
|
celsius: float = Field(..., description="Target temperature in °C.")
|
|
29
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
30
|
+
None,
|
|
31
|
+
description="Id for the background task that manages the temperature",
|
|
32
|
+
json_schema_extra=_remove_default,
|
|
33
|
+
)
|
|
24
34
|
|
|
25
35
|
|
|
26
36
|
class SetTargetTemperatureResult(BaseModel):
|
|
27
37
|
"""Result data from setting a Heater-Shaker's target temperature."""
|
|
28
38
|
|
|
39
|
+
taskId: str | SkipJsonSchema[None] = Field(
|
|
40
|
+
None,
|
|
41
|
+
description="The task id for the setTargetTemperature task",
|
|
42
|
+
json_schema_extra=_remove_default,
|
|
43
|
+
)
|
|
44
|
+
|
|
29
45
|
|
|
30
46
|
class SetTargetTemperatureImpl(
|
|
31
47
|
AbstractCommandImpl[
|
|
@@ -38,10 +54,12 @@ class SetTargetTemperatureImpl(
|
|
|
38
54
|
self,
|
|
39
55
|
state_view: StateView,
|
|
40
56
|
equipment: EquipmentHandler,
|
|
57
|
+
task_handler: TaskHandler,
|
|
41
58
|
**unused_dependencies: object,
|
|
42
59
|
) -> None:
|
|
43
60
|
self._state_view = state_view
|
|
44
61
|
self._equipment = equipment
|
|
62
|
+
self._task_handler = task_handler
|
|
45
63
|
|
|
46
64
|
async def execute(
|
|
47
65
|
self,
|
|
@@ -61,11 +79,19 @@ class SetTargetTemperatureImpl(
|
|
|
61
79
|
hs_module_substate.module_id
|
|
62
80
|
)
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
async def start_set_temperature(task_handler: TaskHandler) -> None:
|
|
83
|
+
if hs_hardware_module is not None:
|
|
84
|
+
async with task_handler.synchronize_cancel_previous(
|
|
85
|
+
hs_module_substate.module_id + "-temp"
|
|
86
|
+
):
|
|
87
|
+
await hs_hardware_module.start_set_temperature(validated_temp)
|
|
88
|
+
await hs_hardware_module.await_temperature(validated_temp)
|
|
66
89
|
|
|
90
|
+
task = await self._task_handler.create_task(
|
|
91
|
+
task_function=start_set_temperature, id=params.taskId
|
|
92
|
+
)
|
|
67
93
|
return SuccessData(
|
|
68
|
-
public=SetTargetTemperatureResult(),
|
|
94
|
+
public=SetTargetTemperatureResult(taskId=task.id),
|
|
69
95
|
)
|
|
70
96
|
|
|
71
97
|
|