text: from typing import List
  
  from opentrons.drivers import utils
  from opentrons.hardware_control.modules import ThermocyclerStep
  
  from . import types as command_types
  
  
  def magdeck_engage() -> command_types.MagdeckEngageCommand:
      text = "Engaging Magnetic Module"
      return {"name": command_types.MAGDECK_ENGAGE, "payload": {"text": text}}
  
  
  def magdeck_disengage() -> command_types.MagdeckDisengageCommand:
      text = "Disengaging Magnetic Module"
      return {"name": command_types.MAGDECK_DISENGAGE, "payload": {"text": text}}
  
  
  def magdeck_calibrate() -> command_types.MagdeckCalibrateCommand:
      text = "Calibrating Magnetic Module"
      return {"name": command_types.MAGDECK_CALIBRATE, "payload": {"text": text}}
  
  
  def tempdeck_set_temp(celsius: float) -> command_types.TempdeckSetTempCommand:
      temp = round(float(celsius), utils.TEMPDECK_GCODE_ROUNDING_PRECISION)
      text = (
          f"Setting Temperature Module temperature "
          f"to {temp} °C (rounded off to nearest integer)"
      )
      return {
          "name": command_types.TEMPDECK_SET_TEMP,
          "payload": {"celsius": celsius, "text": text},
      }
  
  
  def tempdeck_await_temp(celsius: float) -> command_types.TempdeckAwaitTempCommand:
      text = (
          "Waiting for Temperature Module to reach temperature "
          "{temp} °C (rounded off to nearest integer)".format(
              temp=round(float(celsius), utils.TEMPDECK_GCODE_ROUNDING_PRECISION)
          )
      )
      return {
          "name": command_types.TEMPDECK_AWAIT_TEMP,
          "payload": {"celsius": celsius, "text": text},
      }
  
  
  def tempdeck_deactivate() -> command_types.TempdeckDeactivateCommand:
      text = "Deactivating Temperature Module"
      return {"name": command_types.TEMPDECK_DEACTIVATE, "payload": {"text": text}}
  
  
  def thermocycler_open() -> command_types.ThermocyclerOpenCommand:
      text = "Opening Thermocycler lid"
      return {"name": command_types.THERMOCYCLER_OPEN, "payload": {"text": text}}
  
  
  def thermocycler_set_block_temp(
      temperature: float, hold_time_seconds: float, hold_time_minutes: float
  ) -> command_types.ThermocyclerSetBlockTempCommand:
      temp = round(float(temperature), utils.TC_GCODE_ROUNDING_PRECISION)
      text = f"Setting Thermocycler well block temperature to {temp} °C"
      total_seconds = None
      # TODO: BC 2019-09-05 this time resolving logic is partially duplicated
      # in the thermocycler api class definition, with this command logger
      # implementation, there isn't a great way to avoid this, but it should
      # be consolidated as soon as an alternative to the publisher is settled on.
      if hold_time_seconds or hold_time_minutes:
          given_seconds = hold_time_seconds or 0
          given_minutes = hold_time_minutes or 0
          total_seconds = given_seconds + (given_minutes * 60)
  
          clean_seconds = total_seconds % 60
          clean_minutes = (total_seconds - clean_seconds) / 60
          text += " with a hold time of "
          if clean_minutes > 0:
              text += f"{clean_minutes} minutes and "
          text += f"{clean_seconds} seconds"
      return {
          "name": command_types.THERMOCYCLER_SET_BLOCK_TEMP,
          "payload": {
              "temperature": temperature,
              "hold_time": total_seconds,
              "text": text,
          },
      }
  
  
  def thermocycler_execute_profile(
      steps: List[ThermocyclerStep], repetitions: int
  ) -> command_types.ThermocyclerExecuteProfileCommand:
      text = (
          f"Thermocycler starting {repetitions} repetitions"
          f" of cycle composed of the following steps: {steps}"
      )
      return {
          "name": command_types.THERMOCYCLER_EXECUTE_PROFILE,
          "payload": {"text": text, "steps": steps},
      }
  
  
  def thermocycler_wait_for_hold() -> command_types.ThermocyclerWaitForHoldCommand:
      text = "Waiting for hold time duration"
      return {"name": command_types.THERMOCYCLER_WAIT_FOR_HOLD, "payload": {"text": text}}
  
  
  def thermocycler_wait_for_temp() -> command_types.ThermocyclerWaitForTempCommand:
      text = "Waiting for Thermocycler to reach target"
      return {"name": command_types.THERMOCYCLER_WAIT_FOR_TEMP, "payload": {"text": text}}
  
  
  def thermocycler_set_lid_temperature(
      temperature: float,
  ) -> command_types.ThermocyclerSetLidTempCommand:
      temp = round(float(temperature), utils.TC_GCODE_ROUNDING_PRECISION)
      text = f"Setting Thermocycler lid temperature to {temp} °C"
      return {"name": command_types.THERMOCYCLER_SET_LID_TEMP, "payload": {"text": text}}
  
  
  def thermocycler_deactivate_lid() -> command_types.ThermocyclerDeactivateLidCommand:
      text = "Deactivating Thermocycler lid heating"
      return {
          "name": command_types.THERMOCYCLER_DEACTIVATE_LID,
          "payload": {"text": text},
      }
  
  
  def thermocycler_deactivate_block() -> command_types.ThermocyclerDeactivateBlockCommand:
      text = "Deactivating Thermocycler well block heating"
      return {
          "name": command_types.THERMOCYCLER_DEACTIVATE_BLOCK,
          "payload": {"text": text},
      }
  
  
  def thermocycler_deactivate() -> command_types.ThermocyclerDeactivateCommand:
      text = "Deactivating Thermocycler"
      return {"name": command_types.THERMOCYCLER_DEACTIVATE, "payload": {"text": text}}
  
  
  def thermocycler_wait_for_lid_temp() -> command_types.ThermocyclerWaitForLidTempCommand:
      text = "Waiting for Thermocycler lid to reach target temperature"
      return {
          "name": command_types.THERMOCYCLER_WAIT_FOR_LID_TEMP,
          "payload": {"text": text},
      }
  
  
  def thermocycler_close() -> command_types.ThermocyclerCloseCommand:
      text = "Closing Thermocycler lid"
      return {"name": command_types.THERMOCYCLER_CLOSE, "payload": {"text": text}}
  