diff --git a/CDB/stations/DTS_ConfigDb.json b/CDB/stations/DTS_ConfigDb.json
index c5bbf009334e47e2dd0eed89dbddea6889f83933..741d9dc910e6ff35d04e993dab21dbd3eb08cc01 100644
--- a/CDB/stations/DTS_ConfigDb.json
+++ b/CDB/stations/DTS_ConfigDb.json
@@ -1,5 +1,18 @@
 {
     "servers": {
+        "boot": {
+            "STAT": {
+                "Boot": {
+                    "STAT/Boot/1": {
+                        "properties": {
+                            "Initialise_Hardware": [
+                                "True"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "APSCT": {
             "STAT": {
                 "APSCT": {
diff --git a/CDB/stations/LTS_ConfigDb.json b/CDB/stations/LTS_ConfigDb.json
index 7c03ff1434f5e88860d6d174ad7ce952750606b6..faa42937967fe6e1514414d4606556033fcf1959 100644
--- a/CDB/stations/LTS_ConfigDb.json
+++ b/CDB/stations/LTS_ConfigDb.json
@@ -1,5 +1,18 @@
 {
     "servers": {
+        "boot": {
+            "STAT": {
+                "Boot": {
+                    "STAT/Boot/1": {
+                        "properties": {
+                            "Initialise_Hardware": [
+                                "True"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "RECV": {
             "STAT": {
                 "RECV": {
diff --git a/CDB/stations/simulators_ConfigDb.json b/CDB/stations/simulators_ConfigDb.json
index df2ffc1c1194282f7cc92cd0e7df1e5eb90b3a58..c9c1b8135f389e8df23b266fbda4246139590a22 100644
--- a/CDB/stations/simulators_ConfigDb.json
+++ b/CDB/stations/simulators_ConfigDb.json
@@ -1,5 +1,18 @@
 {
     "servers": {
+        "boot": {
+            "STAT": {
+                "Boot": {
+                    "STAT/Boot/1": {
+                        "properties": {
+                            "Initialise_Hardware": [
+                                "False"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "APSCT": {
             "STAT": {
                 "APSCT": {
diff --git a/docker-compose/grafana/dashboards/home.json b/docker-compose/grafana/dashboards/home.json
index 4ef59179fa14153bf814975ed74d55c2c92c2d10..98250c378ec60c9a79205cbb5afc3e125f75e31c 100644
--- a/docker-compose/grafana/dashboards/home.json
+++ b/docker-compose/grafana/dashboards/home.json
@@ -96,7 +96,7 @@
       "targets": [
         {
           "exemplar": true,
-          "expr": "device_attribute{device=\"stat/boot/1\",name=\"initialisation_progress_R\"}",
+          "expr": "device_attribute{device=\"stat/boot/1\",name=\"progress_R\"}",
           "interval": "",
           "legendFormat": "",
           "refId": "A"
@@ -425,7 +425,7 @@
       "targets": [
         {
           "exemplar": true,
-          "expr": "device_attribute{device=\"stat/boot/1\",name=\"initialisation_status_R\"}",
+          "expr": "device_attribute{device=\"stat/boot/1\",name=\"status_R\"}",
           "instant": true,
           "interval": "",
           "legendFormat": "",
@@ -449,7 +449,7 @@
               "Time": true,
               "Value": true,
               "device": true,
-              "device_attribute{device=\"stat/boot/1\", dim_x=\"1\", dim_y=\"0\", instance=\"tango-prometheus-exporter:8000\", job=\"tango\", label=\"initialisation_status_R\", name=\"initialisation_status_R\", str_value=\"Initialisation completed\", type=\"string\", x=\"0\", y=\"0\"}": true,
+              "device_attribute{device=\"stat/boot/1\", dim_x=\"1\", dim_y=\"0\", instance=\"tango-prometheus-exporter:8000\", job=\"tango\", label=\"status_R\", name=\"status_R\", str_value=\"Initialisation completed\", type=\"string\", x=\"0\", y=\"0\"}": true,
               "dim_x": true,
               "dim_y": true,
               "instance": true,
diff --git a/docs/source/devices/boot.rst b/docs/source/devices/boot.rst
index 45af638d95ac12ac7f3236a5f0b126a4777714ad..84f2f5a0ad4ed7f21148ed24555a27f8e8c15181 100644
--- a/docs/source/devices/boot.rst
+++ b/docs/source/devices/boot.rst
@@ -5,22 +5,34 @@ Boot
 
 The ``boot == DeviceProxy("STAT/Boot/1")`` device is responsible for (re)starting and initialising the other devices. Devices which are not reachable, for example because their docker container is explicitly stopped, are skipped during initialisation. This device provides the following commands:
 
-:initialise_station(): Stop and start the other devices in the correct order, set their default values, and command them to initialise their hardware. This procedure runs asynchronously, causing this command to return immediately. Initialisation is aborted if an error is encountered.
+:boot(): Stop and start the other devices in the correct order, set their default values, and command them to initialise their hardware. This procedure runs asynchronously, causing this command to return immediately. Initialisation is aborted if an error is encountered.
+wwww
+  :returns: ``None``
+
+:resume(): Resume an earlier boot attempt: start initialising devices from the first one that failed to initialise, instead of from scratch.
 
   :returns: ``None``
 
 The initialisation process can subsequently be followed through monitoring the following attributes:
 
-:initialising_R: Whether the initialisation procedure is still ongoing.
+:booting_R: Whether the initialisation procedure is still ongoing.
 
   :type: ``bool``
 
-:initialisation_progress_R: Percentage completeness of the initialisation procedure. Each succesfully configured device increments progress.
+:progress_R: Percentage completeness of the initialisation procedure. Each succesfully configured device increments progress.
 
   :type: ``int``
 
-:initialisation_status_R: A description of what the device is currently trying to do. If an error occurs, this will hint towards the cause.
+:status_R: A description of what the device is currently trying to do. If an error occurs, this will hint towards the cause.
 
   :type: ``str``
 
-A useful pattern is thus to call ``initialise_station()``, wait for ``initialising_R == False``, and then check whether the initalisation was succesful, if ``initialisation_progress_R == 100``. If a device fails to initialise, most likely the :doc:`../interfaces/logs` will need to be consulted.
+:initialised_devices_R: Which devices were initialised succesfully.
+
+  :type: ``str[]``
+
+:uninitialised_devices_R: Which devices have not yet been initialised, or failed to initialiase.
+
+  :type: ``str[]``
+
+A useful pattern is thus to call ``boot()``, wait for ``booting_R == False``, and then check whether the initalisation was succesful, if ``progress_R == 100``. If a device fails to initialise, most likely the :doc:`../interfaces/logs` will need to be consulted.
diff --git a/jupyter-notebooks/Home.ipynb b/jupyter-notebooks/Home.ipynb
index 1b6001f9e5a87a0d626e310cc8038ede2d5f589f..33c166c3505e48a28a27e1f83651eab85a5b72d1 100644
--- a/jupyter-notebooks/Home.ipynb
+++ b/jupyter-notebooks/Home.ipynb
@@ -58,7 +58,7 @@
     "# Request to reinitialise the station.\n",
     "#\n",
     "# WARNING: This will reset settings across the station!\n",
-    "boot.initialise_station()\n",
+    "boot.boot()\n",
     "assert boot.state() != DevState.FAULT"
    ]
   },
@@ -71,14 +71,16 @@
    "source": [
     "import time\n",
     "\n",
-    "while boot.initialising_station_R:\n",
-    "  print(f\"Still initialising station. {boot.initialisation_progress_R}% complete. State: {boot.initialisation_status_R}\")\n",
+    "while boot.booting_R:\n",
+    "  print(f\"Still initialising station. {boot.progress_R}% complete. State: {boot.status_R}\")\n",
     "  time.sleep(1)\n",
     "\n",
-    "if boot.initialisation_progress_R == 100:\n",
+    "if boot.progress_R == 100:\n",
     "    print(\"Done initialising station.\")\n",
     "else:\n",
-    "    print(f\"Failed to initialise station: {boot.initialisation_status_R}\")"
+    "    print(f\"Failed to initialise station: {boot.status_R}\")\n",
+    "print(f\"Initialised devices: {boot.initialised_devices_R}\")\n",
+    "print(f\"Uninitialised devices: {boot.uninitialised_devices_R}\")"
    ]
   },
   {
diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh
index 4e988f7fce03eaf4142193c8156ddbcf60ace0bf..c3c37983ae63688658211337ebc17e83fa7ff546 100755
--- a/sbin/run_integration_test.sh
+++ b/sbin/run_integration_test.sh
@@ -15,7 +15,7 @@ cd "$LOFAR20_DIR/docker-compose" || exit 1
 make build
 
 # Start and stop sequence
-make stop device-sdp device-recv device-sst device-unb2 device-xst sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim
+make stop device-boot device-docker device-apsct device-apspu device-sdp device-recv device-sst device-unb2 device-xst sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim
 make start databaseds dsconfig elk
 
 # Give dsconfig and databaseds time to start
@@ -32,7 +32,7 @@ make start sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim
 # Give the simulators time to start
 sleep 5
 
-make start device-sdp device-recv device-sst device-unb2 device-xst
+make start device-boot device-apsct device-apspu device-sdp device-recv device-sst device-unb2 device-xst
 
 # Give devices time to restart
 # TODO(Corne Lukken): Use a nicer more reliable mechanism
diff --git a/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
index 718ea431d6d24962ad7f437e3941e7609f89927f..4dd07132621f660b825b57c2ff0683fa3b6b85d4 100644
--- a/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
+++ b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
@@ -1,8 +1,8 @@
 from tango.server import attribute
-from tango import AttrWriteType
+from tango import AttrWriteType, DevState
 import numpy
 
-from tangostationcontrol.devices.device_decorators import only_when_on, fault_on_error
+from tangostationcontrol.devices.device_decorators import only_in_states, fault_on_error
 import logging
 
 logger = logging.getLogger()
@@ -72,7 +72,7 @@ class attribute_wrapper(attribute):
         if access == AttrWriteType.READ_WRITE:
             """ if the attribute is of READ_WRITE type, assign the RW and write function to it"""
 
-            @only_when_on()
+            @only_in_states([DevState.STANDBY, DevState.ON], log=False)
             @fault_on_error()
             def read_RW(device):
                 # print("read_RW {}, {}x{}, {}, {}".format(me.name, me.dim_x, me.dim_y, me.attr_type, me.value))
@@ -85,7 +85,7 @@ class attribute_wrapper(attribute):
                     raise Exception("Attribute read_RW function error, attempted to read value_dict with key: `%s`, are you sure this exists?",
                                     self) from e
 
-            @only_when_on()
+            @only_in_states([DevState.STANDBY, DevState.ON], log=False)
             @fault_on_error()
             def write_RW(device, value):
                 """
@@ -102,7 +102,7 @@ class attribute_wrapper(attribute):
         else:
             """ if the attribute is of READ type, assign the read function to it"""
 
-            @only_when_on()
+            @only_in_states([DevState.STANDBY, DevState.ON], log=False)
             @fault_on_error()
             def read_R(device):
                 """
diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py
index add4e146cdb5fb4282a49f9394c552465b088a26..476c8306ea7201fc7dfc0e75adf55bfaa5fdca51 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apsct.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py
@@ -73,19 +73,14 @@ class APSCT(opcua_device):
     def _initialise_hardware(self):
         """ Initialise the APSCT hardware. """
 
-        # method calls don't work yet, so don't use them to allow the boot
-        # device to initialise us without errors
-        logger.error("OPC-UA methods not supported yet, not initialising APSCT hardware!")
-        return
-
         # Cycle clock
-        self.CLK_off()
+        self.APSCT_off()
         self.wait_attribute("APSCTTR_translator_busy_R", False, 10)
-        self.CLK_on()
+        self.APSCT_200MHz_on()
         self.wait_attribute("APSCTTR_translator_busy_R", False, 10)
 
-        if not self.APSCT_PLL_200MHz_locked_R:
-            if self.APSCT_I2C_error_R:
+        if not self.proxy.APSCT_PLL_200MHz_locked_R:
+            if self.proxy.APSCTTR_I2C_error_R:
                 raise Exception("I2C is not working. Maybe power cycle subrack to restart CLK board and translator?")
             else:
                 raise Exception("200MHz signal is not locked. The subrack probably do not receive clock input or the CLK PCB is broken?")
@@ -96,7 +91,7 @@ class APSCT(opcua_device):
 
     @command()
     @DebugIt()
-    @only_when_on()
+    @only_in_states([DevState.STANDBY, DevState.ON])
     def APSCT_off(self):
         """
 
@@ -106,13 +101,23 @@ class APSCT(opcua_device):
 
     @command()
     @DebugIt()
-    @only_when_on()
-    def APSCT_on(self):
+    @only_in_states([DevState.STANDBY, DevState.ON])
+    def APSCT_200MHz_on(self):
+        """
+
+        :return:None
+        """
+        self.opcua_connection.call_method(["APSCT_200MHz_on"])
+
+    @command()
+    @DebugIt()
+    @only_in_states([DevState.STANDBY, DevState.ON])
+    def APSCT_160MHz_on(self):
         """
 
         :return:None
         """
-        self.opcua_connection.call_method(["APSCT_on"])
+        self.opcua_connection.call_method(["APSCT_160MHz_on"])
 
 
 # ----------
diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py
index 5b0acd2a587f6076e3a416151a30369a8a924478..c4df70bf5027a8068a1e58326c5ad442e63ad4fc 100644
--- a/tangostationcontrol/tangostationcontrol/devices/boot.py
+++ b/tangostationcontrol/tangostationcontrol/devices/boot.py
@@ -38,7 +38,7 @@ __all__ = ["Boot", "main"]
 class InitialisationException(Exception):
     pass
 
-class DevicesInitialiser(Thread):
+class DevicesInitialiser(object):
     """
         Initialise devices on this station.
 
@@ -49,37 +49,50 @@ class DevicesInitialiser(Thread):
         the start() method, and progress can be followed by inspecting the
         members progress (0-100), status (string), and is_running() (bool).
     """
-    def __init__(self, device_names, ignore_unavailable_devices=True, proxy_timeout=10.0):
+    def __init__(self, device_names, ignore_unavailable_devices=True, initialise_hardware=True, proxy_timeout=10.0):
         self.ignore_unavailable_devices = ignore_unavailable_devices
+        self.initialise_hardware = initialise_hardware
 
         self.device_names = device_names
         self.proxy_timeout = proxy_timeout
 
         # setup initial state
+        self.thread = None
         self.progress = 0
+        self.devices = []
+        self.device_initialised = {name: False for name in device_names}
         self.set_status("Initialisation not started yet")
 
-        super().__init__()
+    def _get_device_proxies(self):
+        """ Obtain the Device Proxies to all the devices we are to initialise. """
+
+        # Since Python3.7+, the insertion order equals the iteration order, which is what we depend on
+        # to process the devices in the same order as in device_names.
+        self.set_status("Obtaining DeviceProxies")
+        devices = {}
+        for name in self.device_names:
+            self.set_status(f"Obtaining a DeviceProxy to {name}")
+            devices[name] = DeviceProxy(name)
+
+        # set the timeout for all proxies
+        self.set_status("Configuring DeviceProxies")
+        for device in devices.values():
+            device.set_timeout_millis(int(self.proxy_timeout * 1000))
+
+        return devices
 
     def run(self):
         self.set_status("Starting initialisation")
 
         try:
-            # Since Python3.7+, the insertion order equals the iteration order, which is what we depend on
-            # to process the devices in the same order as in device_names.
-            self.devices = {}
-            for name in self.device_names:
-                self.set_status(f"Obtaining a DeviceProxy to {name}")
-                self.devices[name] = DeviceProxy(name)
-
-            # set the timeout for all proxies
-            self.set_status("Configuring DeviceProxies")
-            for device in self.devices.values():
-                device.set_timeout_millis(int(self.proxy_timeout * 1000))
+            # get the device proxies if we didn't already
+            self.devices = self.devices or self._get_device_proxies()
 
+            # initialise the devices
             self.set_status("Initialisation started")
             self.initialise_devices()
 
+            # if we get here without throwing an exception, we're done
             self.set_status("Initialisation completed")
         except Exception as e:
             logger.exception("Failed to initialise station")
@@ -90,15 +103,28 @@ class DevicesInitialiser(Thread):
             # we keep the status stuck at the last thing it tried
 
     def is_running(self):
-        return self.is_alive()
+        return self.thread and self.thread.is_alive()
+
+    def start(self):
+        if self.is_running():
+            # still busy, don't start
+            return
 
+        if self.thread:
+            # done, but thread still exist. reap it first
+            self.stop()
+
+        self.thread = Thread(target=self.run)
+        self.thread.start()
 
     def stop(self):
-        if not self.is_alive():
+        if not self.is_running():
             return
 
         # Just wait for the current initialisation to finish. It's a finite process.
-        self.join()
+        self.thread.join()
+
+        self.thread = None
 
     def set_status(self, status):
         self.status = status
@@ -109,6 +135,9 @@ class DevicesInitialiser(Thread):
         """
         Initialise or re-initialise all devices on the station.
 
+        If a device fails to initialise, the process is stopped. Calling
+        this function again will resume initialisation from the failed device.
+
         :return:None
         """
 
@@ -117,9 +146,16 @@ class DevicesInitialiser(Thread):
 
         # restart devices in order
         for num_restarted_devices, device in enumerate(self.devices.keys(), 1):
+            # allow resuming by skipping already initialised devices
+            if self.device_initialised[device]:
+                continue
+
             if self.is_available(device) or not self.ignore_unavailable_devices:
                 self.start_device(device)
 
+                # mark device as initialised
+                self.device_initialised[device] = True
+
             self.progress = 100.0 * num_restarted_devices / len(self.devices)
 
         # make sure we always finish at 100% in case of success
@@ -172,8 +208,9 @@ class DevicesInitialiser(Thread):
         self.set_status(f"[restarting {device_name}] Setting defaults.")
         proxy.set_defaults()
 
-        self.set_status(f"[restarting {device_name}] Initialising hardware.")
-        proxy.initialise_hardware()
+        if self.initialise_hardware:
+            self.set_status(f"[restarting {device_name}] Initialising hardware.")
+            proxy.initialise_hardware()
 
         # mark as ready for service
         self.set_status(f"[restarting {device_name}] Turning on device.")
@@ -195,6 +232,13 @@ class Boot(lofar_device):
         default_value=10.0,
     )
 
+    # Initialise the hardware when initialising a station. Can end badly when using simulators.
+    Initialise_Hardware = device_property(
+        dtype='DevBoolean',
+        mandatory=False,
+        default_value=True,
+    )
+
     # Which devices to initialise, and in which order
     Device_Names = device_property(
         dtype='DevVarStringArray',
@@ -223,9 +267,11 @@ class Boot(lofar_device):
     # ----------
     # Attributes
     # ----------
-    initialising_station_R = attribute(dtype=numpy.bool_, access=AttrWriteType.READ, fget=lambda self: self.initialiser.is_running())
-    initialisation_progress_R = attribute(dtype=numpy.int, access=AttrWriteType.READ, fget=lambda self: numpy.int(self.initialiser.progress))
-    initialisation_status_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: self.initialiser.status)
+    booting_R = attribute(dtype=numpy.bool_, access=AttrWriteType.READ, fget=lambda self: self.initialiser.is_running(), doc="Whether booting is in progress.")
+    progress_R = attribute(dtype=numpy.int, access=AttrWriteType.READ, fget=lambda self: numpy.int(self.initialiser.progress), doc="Percentage of devices that was initialised")
+    status_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: self.initialiser.status, doc="Description of current boot activity")
+    initialised_devices_R = attribute(dtype=(str,), max_dim_x=128, access=AttrWriteType.READ, fget=lambda self: [name for name,initialised in self.initialiser.device_initialised.items() if initialised], doc="Which devices were initialised succesfully")
+    uninitialised_devices_R = attribute(dtype=(str,), max_dim_x=128, access=AttrWriteType.READ, fget=lambda self: [name for name,initialised in self.initialiser.device_initialised.items() if not initialised], doc="Which devices have not been initialised or failed to initialise")
 
     @log_exceptions()
     def delete_device(self):
@@ -254,19 +300,22 @@ class Boot(lofar_device):
     @log_exceptions()
     def configure_for_initialise(self):
         # create an initialiser object so we can query it even before starting the (first) initialisation
-        self.initialiser = DevicesInitialiser(self.Device_Names, self.Ignore_Unavailable_Devices, self.DeviceProxy_Time_Out)
+        self.initialiser = DevicesInitialiser(self.Device_Names, self.Ignore_Unavailable_Devices, self.Initialise_Hardware, self.DeviceProxy_Time_Out)
 
     @command()
     @DebugIt()
     @only_in_states([DevState.ON])
     @fault_on_error()
     @log_exceptions()
-    def initialise_station(self):
+    def boot(self):
         """
         Initialise or re-initialise all devices on the station.
 
         This command will take a while to execute, so should be called asynchronously.
 
+        If resume == True, a previously started attempt is resumed from the device
+        that failed to initialise earlier.
+
         :return:None
         """
 
@@ -274,14 +323,37 @@ class Boot(lofar_device):
             # already initialising
             return
 
+
         # join any previous attempt, if any
         try:
-            self.initialiser.join()
+            self.initialiser.stop()
         except RuntimeError:
             pass
 
         # start new initialisation attempt
-        self.initialiser = DevicesInitialiser(self.Device_Names, self.Ignore_Unavailable_Devices, self.DeviceProxy_Time_Out)
+        self.initialiser = DevicesInitialiser(self.Device_Names, self.Ignore_Unavailable_Devices, self.Initialise_Hardware, self.DeviceProxy_Time_Out)
+        self.initialiser.start()
+
+    @command()
+    @DebugIt()
+    @only_in_states([DevState.ON])
+    @fault_on_error()
+    @log_exceptions()
+    def resume(self):
+        """
+        Resume booting. A previously started boot() attempt is resumed from
+        the first device that failed to initialise.
+
+        This command will take a while to execute, so should be called asynchronously.
+
+        :return:None
+        """
+
+        if self.initialiser.is_running():
+            # already initialising
+            return
+
+        # just start it again
         self.initialiser.start()
 
 # ----------
diff --git a/tangostationcontrol/tangostationcontrol/devices/device_decorators.py b/tangostationcontrol/tangostationcontrol/devices/device_decorators.py
index b3f203bfff1fec77efbc0b2d95d8c464a97dcb71..bb04cdbca5026c9485026856faf80b59fcae4c18 100644
--- a/tangostationcontrol/tangostationcontrol/devices/device_decorators.py
+++ b/tangostationcontrol/tangostationcontrol/devices/device_decorators.py
@@ -7,7 +7,7 @@ logger = logging.getLogger()
 
 __all__ = ["only_in_states", "only_when_on", "fault_on_error"]
 
-def only_in_states(allowed_states):
+def only_in_states(allowed_states, log=True):
     """
       Wrapper to call and return the wrapped function if the device is
       in one of the given states. Otherwise a PyTango exception is thrown.
@@ -18,7 +18,9 @@ def only_in_states(allowed_states):
             if self.get_state() in allowed_states:
                 return func(self, *args, **kwargs)
 
-            logger.warning("Illegal command: Function %s can only be called in states %s. Current state: %s" % (func.__name__, allowed_states, self.get_state()))
+            if log:
+                logger.warning("Illegal command: Function %s can only be called in states %s. Current state: %s" % (func.__name__, allowed_states, self.get_state()))
+
             Except.throw_exception("IllegalCommand", "Function can only be called in states %s. Current state: %s" % (allowed_states, self.get_state()), func.__name__)
 
         return state_check_wrapper
diff --git a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
index 8886abd8a40b38df1fa9ec181783d9f4a2ff08ae..39a1ca4e81bf3953bc975abb0fa5c024b822d87f 100644
--- a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
@@ -95,6 +95,15 @@ class lofar_device(Device, metaclass=AbstractDeviceMetas):
         # trigger a write_{name} call. See https://www.tango-controls.org/community/forum/c/development/c/accessing-own-deviceproxy-class/?page=1#post-2021
         self.proxy = DeviceProxy(self.get_name())
 
+        # register a proxy to ourselves, to interact with
+        # our attributes and commands as a client would.
+        #
+        # this is required to get/set attributes.
+        #
+        # we cannot write directly to our attribute, as that would not
+        # trigger a write_{name} call. See https://www.tango-controls.org/community/forum/c/development/c/accessing-own-deviceproxy-class/?page=1#post-2021
+        self.proxy = DeviceProxy(self.get_name())
+
     @log_exceptions()
     def delete_device(self):
         """Hook to delete resources allocated in init_device.
diff --git a/tangostationcontrol/tangostationcontrol/devices/opcua_device.py b/tangostationcontrol/tangostationcontrol/devices/opcua_device.py
index 9f846533533e5211cbb7a5aa5018b87364f08463..d3668cfae31a23758573db82d29b5cb9ed38d1ba 100644
--- a/tangostationcontrol/tangostationcontrol/devices/opcua_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/opcua_device.py
@@ -119,7 +119,7 @@ class opcua_device(lofar_device):
                 i.set_pass_func()
                 self.opcua_missing_attributes.append(",".join(self.opcua_connection.get_node_path(i.comms_annotation)))
 
-                logger.warning("error while setting the attribute {} read/write function. {}".format(i, e))
+                logger.warning(f"Error while setting the attribute {i.comms_annotation} read/write function.", exc_info=True)
 
     @log_exceptions()
     def configure_for_off(self):
diff --git a/tangostationcontrol/tangostationcontrol/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py
index 4ac93d85abcf66d4c8dc25ab60a1fae89fbef794..5070b8cef0f54e5d4165701ccaefe0a637faf2d7 100644
--- a/tangostationcontrol/tangostationcontrol/devices/recv.py
+++ b/tangostationcontrol/tangostationcontrol/devices/recv.py
@@ -110,7 +110,7 @@ class RECV(opcua_device):
     # --------
     @command()
     @DebugIt()
-    @only_when_on()
+    @only_in_states([DevState.STANDBY, DevState.ON])
     def RCU_off(self):
         """
 
@@ -120,7 +120,7 @@ class RECV(opcua_device):
 
     @command()
     @DebugIt()
-    @only_when_on()
+    @only_in_states([DevState.STANDBY, DevState.ON])
     def RCU_on(self):
         """
 
@@ -130,7 +130,7 @@ class RECV(opcua_device):
 
     @command()
     @DebugIt()
-    @only_when_on()
+    @only_in_states([DevState.STANDBY, DevState.ON])
     def RCU_DTH_off(self):
         """
 
@@ -140,7 +140,7 @@ class RECV(opcua_device):
 
     @command()
     @DebugIt()
-    @only_when_on()
+    @only_in_states([DevState.STANDBY, DevState.ON])
     def RCU_DTH_on(self):
         """
 
@@ -151,11 +151,6 @@ class RECV(opcua_device):
     def _initialise_hardware(self):
         """ Initialise the RCU hardware. """
 
-        # method calls don't work yet, so don't use them to allow the boot
-        # device to initialise us without errors
-        logger.error("OPC-UA methods not supported yet, not initialising RCU hardware!")
-        return
-
         # Cycle RCUs
         self.RCU_off()
         self.wait_attribute("RECVTR_translator_busy_R", False, 5)
diff --git a/tangostationcontrol/tangostationcontrol/devices/unb2.py b/tangostationcontrol/tangostationcontrol/devices/unb2.py
index c4f623c5df5846858dd8d0aa0ab5fd53dff56ac3..a937f8d42c1b06e5e2952a33dfd2d5575aaf8eed 100644
--- a/tangostationcontrol/tangostationcontrol/devices/unb2.py
+++ b/tangostationcontrol/tangostationcontrol/devices/unb2.py
@@ -21,7 +21,7 @@ from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
 from tangostationcontrol.devices.opcua_device import opcua_device
 from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
-from tangostationcontrol.devices.device_decorators import only_when_on
+from tangostationcontrol.devices.device_decorators import only_in_states
 
 import numpy
 
@@ -124,7 +124,7 @@ class UNB2(opcua_device):
 
     @command()
     @DebugIt()
-    @only_when_on()
+    @only_in_states([DevState.STANDBY, DevState.ON])
     def UNB2_off(self):
         """
 
@@ -134,7 +134,7 @@ class UNB2(opcua_device):
 
     @command()
     @DebugIt()
-    @only_when_on()
+    @only_in_states([DevState.STANDBY, DevState.ON])
     def UNB2_on(self):
         """
 
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/base.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..555f7256ea49d68465b1c45bf038d47b39beeb25
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/base.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+import time
+import unittest
+
+from tango._tango import DevState
+
+from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
+from tangostationcontrol.integration_test import base
+
+class AbstractTestBases:
+    """ Holder for abstract test base classes. If we define these at the top level,
+        the test runner will execute them. """
+
+    class TestDeviceBase(base.IntegrationTestCase):
+        __test__ = False
+
+        def setUp(self, name = ""):
+            #if name == "":
+            #    raise unittest.SkipTest("This is a base class for other tests")
+
+            """Intentionally recreate the device object in each test"""
+            # create a proxy
+            self.name = name
+            self.proxy = TestDeviceProxy(self.name)
+
+            # make sure the device starts in Off
+            self.proxy.Off()
+
+            super().setUp()
+
+        def tearDown(self):
+            """Turn device Off in teardown to prevent blocking tests"""
+            self.proxy.Off()
+
+        def test_device_initialize(self):
+            """Test if we can transition to standby"""
+
+            self.proxy.initialise()
+
+            self.assertEqual(DevState.STANDBY, self.proxy.state())
+
+        def test_device_on(self):
+            """Test if we can transition to on"""
+
+            self.proxy.initialise()
+            self.proxy.on()
+
+            self.assertEqual(DevState.ON, self.proxy.state())
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_apsct.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_apsct.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca73fc236a7486b858298e863663cf997c70ccc8
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_apsct.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+from .base import AbstractTestBases
+
+class TestDeviceAPSCT(AbstractTestBases.TestDeviceBase):
+
+    def setUp(self):
+        super().setUp("STAT/APSCT/1")
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_apspu.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_apspu.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9d2bc3d44acf0d4c2d3e1dd9b0adc095c86037b
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_apspu.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+from .base import AbstractTestBases
+
+class TestDeviceAPSPU(AbstractTestBases.TestDeviceBase):
+
+    def setUp(self):
+        super().setUp("STAT/APSPU/1")
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_boot.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_boot.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc39c45d0a35da050aa041f0e5f2063df6312169
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_boot.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+import time
+
+from .base import AbstractTestBases
+
+class TestDeviceBoot(AbstractTestBases.TestDeviceBase):
+
+    def setUp(self):
+        super().setUp("STAT/Boot/1")
+
+    def test_device_boot_initialise_station(self):
+        """Test if we can initialise the station"""
+
+        self.proxy.initialise()
+        self.proxy.on()
+
+        self.proxy.boot()
+
+        # wait for a few seconds for the station to initialise
+        timeout = 10
+        while self.proxy.booting_R and timeout:
+            time.sleep(1)
+
+        # check whether initialisation succeeded
+        self.assertEqual(100, self.proxy.progress_R, msg=f"Initialisation of station failed. Status: {self.proxy.status_R}")
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py
index 6d9353936537a116442409b9ce7c343fa4d803b1..26e02ef312214df4a9304eafd050ea1438002da2 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py
@@ -7,50 +7,9 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-from tango._tango import DevState
+from .base import AbstractTestBases
 
-from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
-from tangostationcontrol.integration_test import base
-
-
-class TestDeviceRECV(base.IntegrationTestCase):
+class TestDeviceRECV(AbstractTestBases.TestDeviceBase):
 
     def setUp(self):
-        super(TestDeviceRECV, self).setUp()
-
-    def tearDown(self):
-        """Turn device Off in teardown to prevent blocking tests"""
-        d = TestDeviceProxy("STAT/RECV/1")
-
-        try:
-            d.Off()
-        except Exception as e:
-            """Failing to turn Off devices should not raise errors here"""
-            print(f"Failed to turn device off in teardown {e}")
-
-    def test_device_proxy_recv(self):
-        """Test if we can successfully create a DeviceProxy and fetch state"""
-
-        d = TestDeviceProxy("STAT/RECV/1")
-
-        self.assertEqual(DevState.OFF, d.state())
-
-    def test_device_recv_initialize(self):
-        """Test if we can transition to standby"""
-
-        d = TestDeviceProxy("STAT/RECV/1")
-
-        d.Initialise()
-
-        self.assertEqual(DevState.STANDBY, d.state())
-
-    def test_device_recv_on(self):
-        """Test if we can transition to on"""
-
-        d = TestDeviceProxy("STAT/RECV/1")
-
-        d.Initialise()
-
-        d.on()
-
-        self.assertEqual(DevState.ON, d.state())
+        super().setUp("STAT/RECV/1")
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py
index f12fc0ae4f9094c6c82a2dd9076f49cea7fd7ef1..2df399ed4607ea802abcf647c02078193f1b7f03 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py
@@ -7,70 +7,17 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-from tango import DeviceProxy
-from tango._tango import DevState
+from .base import AbstractTestBases
 
-from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
-from tangostationcontrol.integration_test import base
-
-
-class TestDeviceSDP(base.IntegrationTestCase):
+class TestDeviceSDP(AbstractTestBases.TestDeviceBase):
 
     def setUp(self):
-        """Intentionally recreate the device object in each test"""
-        super(TestDeviceSDP, self).setUp()
-
-    def tearDown(self):
-        """Turn device Off in teardown to prevent blocking tests"""
-        d = TestDeviceProxy("STAT/SDP/1")
-
-        try:
-            d.Off()
-        except Exception as e:
-            """Failing to turn Off devices should not raise errors here"""
-            print(f"Failed to turn device off in teardown {e}")
-
-    def test_device_proxy_sdp(self):
-        """Test if we can successfully create a DeviceProxy and fetch state"""
-
-        d = TestDeviceProxy("STAT/SDP/1")
-
-        self.assertEqual(DevState.OFF, d.state())
-
-    def test_device_sdp_ping(self):
-        """Test if we can successfully ping the device server"""
-
-        d = TestDeviceProxy("STAT/SDP/1")
-
-        self.assertGreater(d.ping(), 0)
-
-    def test_device_sdp_initialize(self):
-        """Test if we can transition to standby"""
-
-        d = TestDeviceProxy("STAT/SDP/1")
-
-        d.Initialise()
-
-        self.assertEqual(DevState.STANDBY, d.state())
-
-    def test_device_sdp_on(self):
-        """Test if we can transition to on"""
-
-        d = TestDeviceProxy("STAT/SDP/1")
-
-        d.Initialise()
-
-        d.on()
-
-        self.assertEqual(DevState.ON, d.state())
+        super().setUp("STAT/SDP/1")
 
     def test_device_sdp_read_attribute(self):
         """Test if we can read an attribute obtained over OPC-UA"""
 
-        d = TestDeviceProxy("STAT/SDP/1")
-
-        d.initialise()
-
-        d.on()
+        self.proxy.initialise()
+        self.proxy.on()
 
-        self.assertListEqual([True]*16, list(d.TR_fpga_communication_error_R))
+        self.assertListEqual([True]*16, list(self.proxy.TR_fpga_communication_error_R))
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py
index 210dc93defc98c51f9fb9a2b0497a52e5547e5a7..38f3528f531660704baa00f70a7073974256a19f 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py
@@ -1,3 +1,4 @@
+
 # -*- coding: utf-8 -*-
 #
 # This file is part of the LOFAR 2.0 Station Software
@@ -12,81 +13,36 @@ import time
 
 from tango._tango import DevState
 
-from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
-from tangostationcontrol.integration_test import base
-
+from .base import AbstractTestBases
 
-class TestDeviceSST(base.IntegrationTestCase):
+class TestDeviceSST(AbstractTestBases.TestDeviceBase):
 
     def setUp(self):
-        """Intentionally recreate the device object in each test"""
-        super(TestDeviceSST, self).setUp()
-
-    def tearDown(self):
-        """Turn device Off in teardown to prevent blocking tests"""
-        d = TestDeviceProxy("STAT/SST/1")
-
-        try:
-            d.Off()
-        except Exception as e:
-            """Failing to turn Off devices should not raise errors here"""
-            print(f"Failed to turn device off in teardown {e}")
-
-    def test_device_proxy_sst(self):
-        """Test if we can successfully create a DeviceProxy and fetch state"""
-
-        d = TestDeviceProxy("STAT/SST/1")
-
-        self.assertEqual(DevState.OFF, d.state())
+        super().setUp("STAT/SST/1")
 
-    def test_device_sst_initialize(self):
-        """Test if we can transition to standby"""
-
-        d = TestDeviceProxy("STAT/SST/1")
-
-        d.initialise()
-
-        self.assertEqual(DevState.STANDBY, d.state())
-
-    def test_device_sst_on(self):
+    def test_device_on(self):
         """Test if we can transition to on"""
 
         port_property = {"Statistics_Client_TCP_Port": "4999"}
+        self.proxy.put_property(port_property)
+        self.proxy.initialise()
 
-        d = TestDeviceProxy("STAT/SST/1")
-
-        self.assertEqual(DevState.OFF, d.state(),
-                         "Prerequisite could not be met "
-                         "this test can not continue")
-
-        d.put_property(port_property)
+        self.assertEqual(DevState.STANDBY, self.proxy.state())
 
-        d.initialise()
+        self.proxy.on()
 
-        self.assertEqual(DevState.STANDBY, d.state())
-
-        d.on()
-
-        self.assertEqual(DevState.ON, d.state())
+        self.assertEqual(DevState.ON, self.proxy.state())
 
     def test_device_sst_send_udp(self):
         port_property = {"Statistics_Client_TCP_Port": "4998"}
+        self.proxy.put_property(port_property)
+        self.proxy.initialise()
 
-        d = TestDeviceProxy("STAT/SST/1")
-
-        self.assertEqual(DevState.OFF, d.state(),
-                         "Prerequisite could not be met "
-                         "this test can not continue")
-
-        d.put_property(port_property)
+        self.assertEqual(DevState.STANDBY, self.proxy.state())
 
-        d.initialise()
+        self.proxy.on()
 
-        self.assertEqual(DevState.STANDBY, d.state())
-
-        d.on()
-
-        self.assertEqual(DevState.ON, d.state())
+        self.assertEqual(DevState.ON, self.proxy.state())
 
         s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         s1.connect(("device-sst", 5001))
@@ -98,24 +54,14 @@ class TestDeviceSST(base.IntegrationTestCase):
 
     def test_device_sst_connect_tcp_receive(self):
         port_property = {"Statistics_Client_TCP_Port": "5101"}
+        self.proxy.put_property(port_property)
+        self.proxy.initialise()
 
-        m_data = "Hello World!".encode("UTF-8")
+        self.assertEqual(DevState.STANDBY, self.proxy.state())
 
-        d = TestDeviceProxy("STAT/SST/1")
+        self.proxy.on()
 
-        self.assertEqual(DevState.OFF, d.state(),
-                         "Prerequisite could not be met "
-                         "this test can not continue")
-
-        d.put_property(port_property)
-
-        d.initialise()
-
-        self.assertEqual(DevState.STANDBY, d.state())
-
-        d.on()
-
-        self.assertEqual(DevState.ON, d.state())
+        self.assertEqual(DevState.ON, self.proxy.state())
 
         time.sleep(2)
 
@@ -128,6 +74,7 @@ class TestDeviceSST(base.IntegrationTestCase):
         time.sleep(2)
 
         # TODO(Corne): Change me into an actual SST packet
+        m_data = "Hello World!".encode("UTF-8")
         s1.send(m_data)
 
         time.sleep(2)
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py
index 4a0382f2aea7c370562ff4d23e37c85c7963a436..a35f70adde7af58408882e3b5ee3256a4838db84 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py
@@ -7,51 +7,9 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-from tango._tango import DevState
+from .base import AbstractTestBases
 
-from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
-from tangostationcontrol.integration_test import base
-
-
-class TestDeviceUNB2(base.IntegrationTestCase):
+class TestDeviceUNB2(AbstractTestBases.TestDeviceBase):
 
     def setUp(self):
-        """Intentionally recreate the device object in each test"""
-        super(TestDeviceUNB2, self).setUp()
-
-    def tearDown(self):
-        """Turn device Off in teardown to prevent blocking tests"""
-        d = TestDeviceProxy("STAT/UNB2/1")
-
-        try:
-            d.Off()
-        except Exception as e:
-            """Failing to turn Off devices should not raise errors here"""
-            print(f"Failed to turn device off in teardown {e}")
-
-    def test_device_proxy_unb2(self):
-        """Test if we can successfully create a DeviceProxy and fetch state"""
-
-        d = TestDeviceProxy("STAT/UNB2/1")
-
-        self.assertEqual(DevState.OFF, d.state())
-
-    def test_device_unb2_initialize(self):
-        """Test if we can transition to standby"""
-
-        d = TestDeviceProxy("STAT/UNB2/1")
-
-        d.initialise()
-
-        self.assertEqual(DevState.STANDBY, d.state())
-
-    def test_device_unb2_on(self):
-        """Test if we can transition to on"""
-
-        d = TestDeviceProxy("STAT/UNB2/1")
-
-        d.initialise()
-
-        d.on()
-
-        self.assertEqual(DevState.ON, d.state())
+        super().setUp("STAT/UNB2/1")