diff --git a/doc/news/DM-47363.feature.rst b/doc/news/DM-47363.feature.rst
new file mode 100644
index 000000000..d6669a1ac
--- /dev/null
+++ b/doc/news/DM-47363.feature.rst
@@ -0,0 +1 @@
+Add new `set_dof.py`` to set absolute DOF position
\ No newline at end of file
diff --git a/python/lsst/ts/standardscripts/data/scripts/maintel/set_dof.py b/python/lsst/ts/standardscripts/data/scripts/maintel/set_dof.py
new file mode 100755
index 000000000..f3c48b07d
--- /dev/null
+++ b/python/lsst/ts/standardscripts/data/scripts/maintel/set_dof.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# This file is part of ts_standardscripts
+#
+# Developed for the LSST Telescope and Site Systems.
+# This product includes software developed by the LSST Project
+# (https://www.lsst.org).
+# See the COPYRIGHT file at the top-level directory of this distribution
+# for details of code ownership.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import asyncio
+
+from lsst.ts.standardscripts.maintel import SetDOF
+
+asyncio.run(SetDOF.amain())
diff --git a/python/lsst/ts/standardscripts/maintel/__init__.py b/python/lsst/ts/standardscripts/maintel/__init__.py
index 9017f90d7..27c01a834 100644
--- a/python/lsst/ts/standardscripts/maintel/__init__.py
+++ b/python/lsst/ts/standardscripts/maintel/__init__.py
@@ -39,6 +39,7 @@
from .offset_mtcs import *
from .open_mirror_covers import *
from .point_azel import *
+from .set_dof import *
from .setup_mtcs import *
from .standby_comcam import *
from .standby_mtcs import *
diff --git a/python/lsst/ts/standardscripts/maintel/set_dof.py b/python/lsst/ts/standardscripts/maintel/set_dof.py
new file mode 100644
index 000000000..2024bde82
--- /dev/null
+++ b/python/lsst/ts/standardscripts/maintel/set_dof.py
@@ -0,0 +1,57 @@
+# This file is part of ts_standardscripts
+#
+# Developed for the LSST Telescope and Site Systems.
+# This product includes software developed by the LSST Project
+# (https://www.lsst.org).
+# See the COPYRIGHT file at the top-level directory of this distribution
+# for details of code ownership.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+__all__ = ["SetDOF"]
+
+from .apply_dof import ApplyDOF
+
+STD_TIMEOUT = 30
+
+
+class SetDOF(ApplyDOF):
+ """Set absolute positions DOF to the main telescope, either bending
+ mode or hexapod position.
+
+ Parameters
+ ----------
+ index : `int`
+ Index of Script SAL component.
+
+ Notes
+ -----
+ **Checkpoints**
+ "Setting DOF..." - The DOF absolute position is being applied.
+
+ """
+
+ async def run(self) -> None:
+ """Run script."""
+ # Assert feasibility
+ await self.assert_feasibility()
+
+ await self.checkpoint("Setting DOF...")
+ current_dof = await self.mtcs.rem.mtaos.evt_degreeOfFreedom.aget(
+ timeout=STD_TIMEOUT
+ )
+ dof_data = self.mtcs.rem.mtaos.cmd_offsetDOF.DataType()
+ for i, dof_absolute in enumerate(self.dofs):
+ dof_data.value[i] = dof_absolute - current_dof.aggregatedDoF[i]
+ await self.mtcs.rem.mtaos.cmd_offsetDOF.start(data=dof_data)
diff --git a/tests/test_maintel_set_dof.py b/tests/test_maintel_set_dof.py
new file mode 100644
index 000000000..19c1246e0
--- /dev/null
+++ b/tests/test_maintel_set_dof.py
@@ -0,0 +1,87 @@
+# This file is part of ts_standardscripts
+#
+# Developed for the LSST Telescope and Site Systems.
+# This product includes software developed by the LSST Project
+# (https://www.lsst.org).
+# See the COPYRIGHT file at the top-level directory of this distribution
+# for details of code ownership.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import asyncio
+import random
+import types
+import unittest
+
+import numpy as np
+from lsst.ts import standardscripts
+from lsst.ts.observatory.control.maintel.mtcs import MTCS, MTCSUsages
+from lsst.ts.observatory.control.utils.enums import DOFName
+from lsst.ts.standardscripts.maintel import SetDOF
+
+random.seed(47) # for set_random_lsst_dds_partition_prefix
+
+
+class TestSetDOF(standardscripts.BaseScriptTestCase, unittest.IsolatedAsyncioTestCase):
+ async def basic_make_script(self, index):
+ self.script = SetDOF(index=index)
+
+ # Mock the MTCS
+ self.script.mtcs = MTCS(
+ domain=self.script.domain,
+ intended_usage=MTCSUsages.DryTest,
+ log=self.script.log,
+ )
+ self.script.mtcs.rem.mtaos = unittest.mock.AsyncMock()
+ self.script.mtcs.rem.mtaos.cmd_offsetDOF.attach_mock(
+ unittest.mock.Mock(
+ return_value=types.SimpleNamespace(value=np.zeros(len(DOFName)))
+ ),
+ "DataType",
+ )
+ self.script.mtcs.rem.mtaos.configure_mock(
+ **{
+ "evt_degreeOfFreedom.aget": self.get_current_dof,
+ }
+ )
+
+ self.script.mtcs.assert_all_enabled = unittest.mock.AsyncMock()
+
+ return (self.script,)
+
+ async def get_current_dof(self, timeout):
+ await asyncio.sleep(0.5)
+ return np.zeros(len(DOFName))
+
+ async def test_run(self) -> None:
+ # Start the test itself
+ async with self.make_script():
+ config_dofs = {"M2_dz": 0.2, "Cam_dy": 0.3, "M1M3_B1": 0.5, "M2_B14": 0.7}
+
+ await self.configure_script(**config_dofs)
+
+ # Run the script
+ await self.run_script()
+
+ self.script.mtcs.rem.mtaos.cmd_offsetDOF.DataType.assert_called()
+ self.script.mtcs.rem.mtaos.cmd_offsetDOF.start.assert_awaited_once()
+
+ async def test_executable(self) -> None:
+ scripts_dir = standardscripts.get_scripts_dir()
+ script_path = scripts_dir / "maintel" / "set_dof.py"
+ await self.check_executable(script_path)
+
+
+if __name__ == "__main__":
+ unittest.main()