diff --git a/tangostationcontrol/docs/source/devices/boot.rst b/tangostationcontrol/docs/source/devices/boot.rst
index cbb76e4877e767f87893956d99a782dc99122633..a9d788d4246bfc87f7a7e71f6b8a0d1468451f56 100644
--- a/tangostationcontrol/docs/source/devices/boot.rst
+++ b/tangostationcontrol/docs/source/devices/boot.rst
@@ -3,13 +3,19 @@
 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:
+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. It is the only device that starts in the ``ON`` state, so it can be used immediately to initialise other devices.
 
-: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.
+This device provides the following commands:
+
+:boot(): Start the other devices in the correct order, if they are not ON. 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.
+
+  :returns: ``None``
+
+:warm_boot(): Like ``boot()``, but do not initialise the hardware. Just start our software devices.
 
   :returns: ``None``
 
-:resume(): Resume an earlier boot attempt: start initialising devices from the first one that failed to initialise, instead of from scratch.
+:reboot(): Like ``boot()``, but turn off all other devices first.
 
   :returns: ``None``
 
diff --git a/tangostationcontrol/docs/source/devices/using.rst b/tangostationcontrol/docs/source/devices/using.rst
index d72fee2af770a47a88c8937181115dcc93f189f0..c04302062d09147c4fd0567dd1331544b4dd7abc 100644
--- a/tangostationcontrol/docs/source/devices/using.rst
+++ b/tangostationcontrol/docs/source/devices/using.rst
@@ -20,17 +20,18 @@ The state of a device is then queried with ``device.state()``. Each device can b
 - ``DevState.OFF``: The device is not operating,
 - ``DevState.INIT``: The device is being initialised,
 - ``DevState.STANDBY``: The device is initialised and ready to be configured further,
-- ``DevState.ON``: The device is operational.
+- ``DevState.ON``: The device is operational,
+- ``DevState.ALARM``: The device is operational, but one or more attributes are in alarm,
 - ``DevState.FAULT``: The device is malfunctioning. Functionality cannot be counted on.
 - The ``device.state()`` function can throw an error, if the device cannot be reached at all. For example, because it's docker container is not running. See the :ref:`docker` device on how to start it.
 
 Each device provides the following commands to change the state:
 
-:off(): Turn the device ``OFF`` from any state.
+:boot(): Turn on the device, and initialise the hardware. Moves from ``OFF`` to ``ON``.
 
-:initialise(): Initialise the device from the ``OFF`` state, to bring it to the ``STANDBY`` state.
+:warm_boot(): Turn on the device, but do not change the hardware. Moves from ``OFF`` to ``ON``.
 
-:on(): Mark the device as operational, from the ``STANDBY`` state, bringing it to ``ON``.
+:off(): Turn the device ``OFF`` from any state.
 
 The following procedure is a good way to bring a device to ``ON`` from any state::
 
@@ -38,9 +39,7 @@ The following procedure is a good way to bring a device to ``ON`` from any state
       if device.state() == DevState.FAULT:
           device.off()
       if device.state() == DevState.OFF:
-          device.initialise()
-      if device.state() == DevState.STANDBY:
-          device.on()
+          device.boot()
 
       return device.state()
 
@@ -77,7 +76,13 @@ Most devices provide the following commands, in order to configure the hardware
 
 :initialise_hardware(): For devices that control hardware, this command runs the hardware initialisation procedure.
 
-Typically, ``set_defaults()`` and ``initialise_hardware()`` are called in that order in the ``STANDBY`` state. See also :ref:`boot`, which provides functionality to initialise all the devices.
+Typically, ``set_defaults()`` and ``initialise_hardware()`` are called in that order in the ``STANDBY`` state, during ``boot()``. To manually go through the boot sequence, use the following commands:
+
+:initialise(): Initialise the device (connect to the hardware). Moves from ``OFF`` to ``STANDBY``.
+
+:on(): Mark the device as operational. Moves from ``STANDBY`` to ``ON``.
+
+See also :ref:`boot`, which provides functionality to initialise all the devices.
 
 .. _attributes:
 
diff --git a/tangostationcontrol/docs/source/installation.rst b/tangostationcontrol/docs/source/installation.rst
index 58ccf2cd360261256774826a7904973ac6c44b70..763b3e1e1453f0dcb44c72d1e0f1ff9716800206 100644
--- a/tangostationcontrol/docs/source/installation.rst
+++ b/tangostationcontrol/docs/source/installation.rst
@@ -44,29 +44,25 @@ After bootstrapping, and after a reboot, the software and hardware of the statio
 
 The following commands start all the software devices to control the station hardware, and initialise the hardware with the configured default settings. Go to http://localhost:8888, start a new *Station Control* notebook, and initiate the software boot sequence::
 
-  # reset our boot device
-  boot.off()
-  assert boot.state() == DevState.OFF
-  boot.initialise()
-  assert boot.state() == DevState.STANDBY
-  boot.on()
-  assert boot.state() == DevState.ON
-
   # start and initialise the other devices
-  boot.initialise_station()
+  boot.boot()
 
   # wait for the devices to be initialised
   import time
 
-  while boot.initialising_station_R:
-    print(f"Still initialising station. {boot.initialisation_progress_R}% complete. State: {boot.initialisation_status_R}")
+  while boot.booting_R:
+    print(f"Still initialising station. {boot.progress_R}% complete. State: {boot.status_R}")
     time.sleep(1)
 
   # print conclusion
-  if boot.initialisation_progress_R == 100:
+  if boot.progress_R == 100:
     print("Done initialising station.")
   else:
-    print(f"Failed to initialise station: {boot.initialisation_status_R}")
+    print(f"Failed to initialise station: {boot.status_R}")
+
+  # print what did and did not get initialised
+  print(f"Initialised devices: {boot.initialised_devices_R}")
+  print(f"Uninitialised devices: {boot.uninitialised_devices_R}")
 
 See :ref:`boot` for more information on the ``boot`` device.
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py
index 1a6fa655c86414d86219ac0cdd43b985bba2c305..f75707676a19bbea21eef4352b3e53852cdc3968 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apsct.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py
@@ -13,7 +13,7 @@
 
 # PyTango imports
 from tango import DebugIt
-from tango.server import command
+from tango.server import command, attribute, device_property
 from tango import AttrWriteType, DevState
 import numpy
 # Additional import
@@ -36,6 +36,12 @@ class APSCT(opcua_device):
     # Device Properties
     # -----------------
 
+    APSCTTR_monitor_rate_RW_default = device_property(
+        dtype='DevLong64',
+        mandatory=False,
+        default_value=1
+    )
+
     # ----------
     # Attributes
     # ----------
@@ -66,6 +72,15 @@ class APSCT(opcua_device):
     APSCT_PWR_PPSDIST_3V3_R      = attribute_wrapper(comms_annotation=["APSCT_PWR_PPSDIST_3V3_R"   ],datatype=numpy.float64)
     APSCT_TEMP_R                 = attribute_wrapper(comms_annotation=["APSCT_TEMP_R"              ],datatype=numpy.float64)
 
+    # ----------
+    # Summarising Attributes
+    # ----------
+    APSCT_error_R                 = attribute(dtype=bool)
+
+    def read_APSCT_error_R(self):
+        return ((self.proxy.APSCTTR_I2C_error_R > 0)
+               | self.alarm_val("APSCT_PCB_ID_R"))
+
     # --------
     # overloaded functions
     # --------
diff --git a/tangostationcontrol/tangostationcontrol/devices/apspu.py b/tangostationcontrol/tangostationcontrol/devices/apspu.py
index 3a106ba1f7291b538eadb5fa67839652fde3722b..44b58c25ed3aca314fb93ae2f26f8cea90b663ee 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apspu.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apspu.py
@@ -13,6 +13,7 @@
 
 # PyTango imports
 from tango import AttrWriteType
+from tango.server import attribute, device_property
 import numpy
 # Additional import
 
@@ -30,6 +31,12 @@ class APSPU(opcua_device):
     # Device Properties
     # -----------------
 
+    APSPUTR_monitor_rate_RW_default = device_property(
+        dtype='DevLong64',
+        mandatory=False,
+        default_value=1
+    )
+
     # ----------
     # Attributes
     # ----------
@@ -53,6 +60,18 @@ class APSPU(opcua_device):
     APSPU_RCU2D_TEMP_R           = attribute_wrapper(comms_annotation=["APSPU_RCU2D_TEMP_R"        ],datatype=numpy.float64)
     APSPU_RCU2D_VOUT_R           = attribute_wrapper(comms_annotation=["APSPU_RCU2D_VOUT_R"        ],datatype=numpy.float64)
 
+    # ----------
+    # Summarising Attributes
+    # ----------
+    APSPU_error_R                 = attribute(dtype=bool)
+
+    def read_APSPU_error_R(self):
+        return ((self.proxy.APSPUTR_I2C_error_R > 0)
+               | self.alarm_val("APSPU_PCB_ID_R")
+               | self.alarm_val("APSPU_FAN1_RPM_R")
+               | self.alarm_val("APSPU_FAN2_RPM_R") 
+               | self.alarm_val("APSPU_FAN3_RPM_R"))
+
     # --------
     # overloaded functions
     # --------
diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py
index 2d5054f78789258046296409ece063dadb23103f..7dc95e7480231237a6c45ba349797a01e2c10056 100644
--- a/tangostationcontrol/tangostationcontrol/devices/boot.py
+++ b/tangostationcontrol/tangostationcontrol/devices/boot.py
@@ -17,7 +17,7 @@ Boots the rest of the station software.
 from tango import DebugIt
 from tango.server import command
 from tango.server import device_property, attribute
-from tango import AttrWriteType, DeviceProxy, DevState
+from tango import AttrWriteType, DeviceProxy, DevState, DevSource
 # Additional import
 import numpy
 
@@ -40,17 +40,18 @@ class InitialisationException(Exception):
 
 class DevicesInitialiser(object):
     """
-        Initialise devices on this station.
+        Initialise devices on this station which are not already on (reboot=False),
+        or all of them (reboot=True).
 
         Devices which are unreachable are assumed to be brought down explicitly,
-        and are ignored (unless ignore_unavailable_devices == False).
+        and are ignored.
 
         Initialisation happens in a separate thread. It is started by calling
         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, initialise_hardware=True, proxy_timeout=10.0):
-        self.ignore_unavailable_devices = ignore_unavailable_devices
+    def __init__(self, device_names, reboot=False, initialise_hardware=True, proxy_timeout=10.0):
+        self.reboot = reboot
         self.initialise_hardware = initialise_hardware
 
         self.device_names = device_names
@@ -74,10 +75,11 @@ class DevicesInitialiser(object):
             self.set_status(f"Obtaining a DeviceProxy to {name}")
             devices[name] = DeviceProxy(name)
 
-        # set the timeout for all proxies
+        # set the timeout for all proxies, and never read state from a cache
         self.set_status("Configuring DeviceProxies")
         for device in devices.values():
             device.set_timeout_millis(int(self.proxy_timeout * 1000))
+            device.set_source(DevSource.DEV)
 
         return devices
 
@@ -150,8 +152,10 @@ class DevicesInitialiser(object):
             if self.device_initialised[device]:
                 continue
 
-            if self.is_available(device) or not self.ignore_unavailable_devices:
-                self.start_device(device)
+            if self.is_available(device):
+                if self.reboot or self.devices[device].state() not in [DevState.ON, DevState.ALARM]:
+                    self.stop_device(device)
+                    self.boot_device(device)
 
                 # mark device as initialised
                 self.device_initialised[device] = True
@@ -185,40 +189,25 @@ class DevicesInitialiser(object):
 
         proxy.Off()
         if proxy.state() != DevState.OFF:
-            raise InitialisationException(f"Could not turn off device {device_name}. Please look at its logs.")
+            raise InitialisationException(f"Could not turn off device {device_name}. It reports status: {proxy.status()}")
 
         self.set_status(f"[stopping {device_name}] Stopped device.")
 
-    def start_device(self, device_name: str):
+    def boot_device(self, device_name: str):
         """ Run the startup sequence for device 'device_name'. """
 
         proxy = self.devices[device_name]
 
-        # go to a well-defined state, which may be needed if the user calls
-        # this function explicitly.
-        self.stop_device(device_name)
-
-        # setup connections to hardware
-        self.set_status(f"[restarting {device_name}] Initialising device.")
-        proxy.Initialise()
-        if proxy.state() != DevState.STANDBY:
-            raise InitialisationException(f"Could not initialise device {device_name}. Please look at its logs.")
-
-        # configure the device
-        self.set_status(f"[restarting {device_name}] Setting defaults.")
-        proxy.set_defaults()
-
+        self.set_status(f"[restarting {device_name}] Booting device.")
         if self.initialise_hardware:
-            self.set_status(f"[restarting {device_name}] Initialising hardware.")
-            proxy.initialise_hardware()
+            proxy.boot()
+        else:
+            proxy.warm_boot()
 
-        # mark as ready for service
-        self.set_status(f"[restarting {device_name}] Turning on device.")
-        proxy.On()
-        if proxy.state() != DevState.ON:
-            raise InitialisationException(f"Could not turn on device {device_name}. Please look at its logs.")
+        if proxy.state() not in [DevState.ON, DevState.ALARM]: # ALARM still means booting was succesful
+            raise InitialisationException(f"Could not boot device {device_name}. It reports status: {proxy.status()}")
 
-        self.set_status(f"[restarting {device_name}] Succesfully started.")
+        self.set_status(f"[restarting {device_name}] Succesfully booted.")
 
 @device_logging_to_python()
 class Boot(lofar_device):
@@ -255,16 +244,6 @@ class Boot(lofar_device):
                       ],
     )
 
-    # By default, we assume any device is not available
-    # because its docker container was not started, which
-    # is an explicit and thus intentional action.
-    # We ignore such devices when initialising the station.
-    Ignore_Unavailable_Devices = device_property(
-        dtype='DevBoolean',
-        mandatory=False,
-        default_value=True,
-    )
-
     # ----------
     # Attributes
     # ----------
@@ -274,22 +253,17 @@ class Boot(lofar_device):
     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):
-        """Hook to delete resources allocated in init_device.
-
-        This method allows for any memory or other resources allocated in the
-        init_device method to be released.  This method is called by the device
-        destructor and by the device Init command (a Tango built-in).
-        """
-        logger.debug("Shutting down...")
-
-        self.Off()
-        logger.debug("Shut down.  Good bye.")
-
     # --------
     # overloaded functions
     # --------
+    def init_device(self):
+        super().init_device()
+
+        # always turn on automatically, so the user doesn't have to boot the boot device
+        # note: we overloaded our own boot() to boot the station, so explicitly call our own initialisation
+        self.Initialise()
+        self.On()
+
     @log_exceptions()
     def configure_for_off(self):
         """ user code here. is called when the state is set to OFF """
@@ -301,21 +275,16 @@ 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.Initialise_Hardware, self.DeviceProxy_Time_Out)
+        self.initialiser = DevicesInitialiser(self.Device_Names, False, self.Initialise_Hardware, self.DeviceProxy_Time_Out)
 
-    @command()
-    @DebugIt()
-    @only_in_states([DevState.ON])
-    @fault_on_error()
-    @log_exceptions()
-    def boot(self):
+    def _boot(self, reboot=False, initialise_hardware=True):
         """
-        Initialise or re-initialise all devices on the station.
+        Initialise all devices on the station that are not yet on (reboot=False), or shut them down first (reboot=True).
 
-        This command will take a while to execute, so should be called asynchronously.
+        If initialise_hardware is set, the hardware behind the devices is also explicitly reinitialised. Turn this
+        off to perform a warm boot.
 
-        If resume == True, a previously started attempt is resumed from the device
-        that failed to initialise earlier.
+        This command will take a while to execute, so should be called asynchronously.
 
         :return:None
         """
@@ -324,7 +293,6 @@ class Boot(lofar_device):
             # already initialising
             return
 
-
         # join any previous attempt, if any
         try:
             self.initialiser.stop()
@@ -332,30 +300,29 @@ class Boot(lofar_device):
             pass
 
         # start new initialisation attempt
-        self.initialiser = DevicesInitialiser(self.Device_Names, self.Ignore_Unavailable_Devices, self.Initialise_Hardware, self.DeviceProxy_Time_Out)
+        self.initialiser = DevicesInitialiser(self.Device_Names, reboot, 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
-        """
+    def boot(self):
+        self._boot(reboot=False, initialise_hardware=self.Initialise_Hardware)
 
-        if self.initialiser.is_running():
-            # already initialising
-            return
+    @command()
+    @DebugIt()
+    @only_in_states([DevState.ON])
+    @log_exceptions()
+    def reboot(self):
+        self._boot(reboot=True, initialise_hardware=self.Initialise_Hardware)
 
-        # just start it again
-        self.initialiser.start()
+    @command()
+    @DebugIt()
+    @only_in_states([DevState.ON])
+    @log_exceptions()
+    def warm_boot(self):
+        self._boot(reboot=False, initialise_hardware=False)
 
 # ----------
 # Run server
diff --git a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
index 5ca671d4c73b37c1035c69ed75ce82f08def2095..9d68a5decefe79e03211d6b587a3b299d88e954b 100644
--- a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
@@ -13,7 +13,7 @@
 
 # PyTango imports
 from tango.server import attribute, command, Device, DeviceMeta
-from tango import AttrWriteType, DevState, DebugIt, Attribute, DeviceProxy
+from tango import AttrWriteType, DevState, DebugIt, Attribute, DeviceProxy, AttrDataFormat
 import time
 import math
 
@@ -266,10 +266,36 @@ class lofar_device(Device, metaclass=DeviceMeta):
         # This is just the command version of _initialise_hardware().
         self._initialise_hardware()
 
+    def _boot_device(self, initialise_hardware=True):
+        # setup connections
+        self.Initialise()
+
+        if initialise_hardware:
+            # initialise settings
+            self.set_defaults()
+
+            # powercycle backing hardware
+            self.initialise_hardware()
+
+        # make device available
+        self.On()
+
+
+    @only_in_states([DevState.OFF])
+    @command()
+    def boot(self):
+        self._boot(initialise_hardware=True)
+
+    @only_in_states([DevState.OFF])
+    @command()
+    def warm_boot(self):
+        self._boot(initialise_hardware=False)
+
     def _initialise_hardware(self):
         """ Override this method to initialise any hardware after configuring it. """
         pass
 
+
     def wait_attribute(self, attr_name, value, timeout=10, pollperiod=0.2):
         """ Wait until the given attribute obtains the given value.
 
@@ -287,3 +313,34 @@ class lofar_device(Device, metaclass=DeviceMeta):
             time.sleep(pollperiod)
 
         raise Exception(f"{attr_name} != {value} after {timeout} seconds still.")
+
+    def alarm_val(self, attr_name):
+        """ Returns whether min_alarm < attr_value < max_alarm for the given attribute,
+            if these values are set. For arrays, an array of booleans of the same shape
+            is returned. """
+
+        # fetch attribute configuration
+        attr_config = self.proxy.get_attribute_config(attr_name)
+        alarms = attr_config.alarms
+        is_scalar = attr_config.data_format == AttrDataFormat.SCALAR
+
+        # fetch attribute value as an array
+        value = self.proxy.read_attribute(attr_name).value
+        if is_scalar:
+            value = numpy.array(value)
+
+        # construct alarm state, in the same shape as the attribute
+        alarm_state = numpy.zeros(value.shape, dtype=bool)
+
+        if alarms.max_alarm != 'Not specified':
+            alarm_state |= value >= value.dtype.type(alarms.max_alarm)
+
+        if alarms.min_alarm != 'Not specified':
+            alarm_state |= value <= value.dtype.type(alarms.min_alarm)
+
+        # return alarm state, as the same type as the attribute
+        if is_scalar:
+            return alarm_state[0]
+        else:
+            return alarm_state
+
diff --git a/tangostationcontrol/tangostationcontrol/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py
index 22f2f84831eb218c67733026d0271d086e004f33..965f276482bb50b370cd8be33ca0039de779f09e 100644
--- a/tangostationcontrol/tangostationcontrol/devices/recv.py
+++ b/tangostationcontrol/tangostationcontrol/devices/recv.py
@@ -49,6 +49,18 @@ class RECV(opcua_device):
         default_value=[True] * 32
     )
 
+    RCU_mask_lock = device_property(
+        dtype='DevVarBooleanArray',
+        mandatory=False,
+        default_value=[False] * 32
+    )
+
+    RECVTR_monitor_rate_RW_default = device_property(
+        dtype='DevLong64',
+        mandatory=False,
+        default_value=1
+    )
+
     HBAT_bf_delay_step_delays = device_property(
         dtype="DevVarFloatArray",
         mandatory=False,
@@ -100,9 +112,6 @@ class RECV(opcua_device):
     # ----------
     # Attributes
     # ----------
-    ANT_status_R = attribute(dtype=(str,), max_dim_x=3, max_dim_y=32)
-    RCU_LED_colour_R = attribute(dtype=(numpy.uint32,), max_dim_x=32, fget=lambda self: (2 * self.proxy.RCU_LED_green_on_R + 4 * self.proxy.RCU_LED_red_on_R).astype(numpy.uint32))
-
     ANT_mask_RW                  = attribute_wrapper(comms_annotation=["ANT_mask_RW"               ],datatype=numpy.bool_  , dims=(3,32), access=AttrWriteType.READ_WRITE)
     
     # The HBAT beamformer delays represent 32 delays for each of the 96 inputs.
@@ -155,6 +164,28 @@ class RECV(opcua_device):
         dtype=((numpy.float,),), max_dim_x=3, max_dim_y=96,
         fget=lambda self: numpy.array(self.HBAT_reference_itrf).reshape(96,3))
 
+    # ----------
+    # Summarising Attributes
+    # ----------
+    RCU_LED_colour_R = attribute(dtype=(numpy.uint32,), max_dim_x=32)
+
+    def read_RCU_LED_colour_R(self):
+        return (2 * self.proxy.RCU_LED_green_on_R + 4 * self.proxy.RCU_LED_red_on_R).astype(numpy.uint32)
+
+    RCU_error_R                  = attribute(dtype=(bool,), max_dim_x=32)
+    ANT_error_R                  = attribute(dtype=((bool,),), max_dim_x=3, max_dim_y=32)
+
+    def read_RCU_error_R(self):
+        return self.proxy.RCU_mask_RW & (
+                 (self.proxy.RECVTR_I2C_error_R > 0)
+               | self.alarm_val("RCU_PCB_ID_R")
+               )
+
+    def read_ANT_error_R(self):
+        return self.proxy.ANT_mask_RW & (
+                 ~self.proxy.RCU_ADC_locked_R
+               )
+
     # --------
     # overloaded functions
     # --------
@@ -248,68 +279,9 @@ class RECV(opcua_device):
         self.RCU_on()
         self.wait_attribute("RECVTR_translator_busy_R", False, 5)
 
-    def read_RCU_status_R(self):
-        """ Returns a set of strings denoting the status of each RCU.
-
-            An empty string means no problems were detected. A non-empty
-            string means the RCU seems unusable.
-
-            This function can be used as input to modify the RCU_mask_RW. """
-
-        rcu_mask = self.proxy.RCU_mask_RW
-        i2c_errors = self.proxy.RCU_I2C_STATUS_R
-
-        nr_rcus = len(rcu_mask)
-        rcu_status = [""] * nr_rcus
-
-        # construct status of each RCU
-        for rcu in range(nr_rcus):
-            status = []
-
-            if not i2c_status[rcu]:
-                status.append("[I2C error]")
-
-            rcu_status[rcu] = " ".join(status)
-
-        return rcu_status
-
-    def read_ANT_status_R(self):
-        """ Returns a set of strings denoting the status of each antenna.
-
-            An empty string means no problems were detected. A non-empty
-            string means the antenna seems unusable.
-
-            This function can be used as input to modify the Ant_mask_RW. """
-
-        ant_mask = self.proxy.ANT_mask_RW
-        rcu_mask = self.proxy.RCU_mask_RW
-        adc_lock = self.proxy.RCU_ADC_locked_R
-        i2c_errors = self.proxy.RCU_I2C_STATUS_R
-
-        nr_rcus = len(ant_mask)
-        nr_ants_per_rcu = len(ant_mask[0])
-
-        # Collect status, join them into a single string per antenna later 
-        ant_status = [""] * nr_ants
-
-
-        for rcu in range(nr_rcus):
-            for ant in range(nr_ants_per_rcu):
-                status = []
-
-                if i2c_status[rcu] != 0:
-                    status.append("[I2C error]")
-
-                if not rcu_mask[rcu]:
-                    status.append("[RCU masked out]")
-
-                if not adc_lock[rcu][ant]:
-                    status.append("[ADC lock error]")
-
-                ant_status[rcu][ant] = " ".join(status)
-
-        return ant_status
- 
+        # Turn off DTH by default
+        recv.RCU_DTH_off()
+        self.wait_attribute("RECVTR_translator_busy_R", False, 5)
 
 # ----------
 # Run server
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
index f9e91e5e558e35351ebd56640a26e3ba30a6111f..275d8fe8a44ad44e947ae573563cfd8f9454e304 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
@@ -12,7 +12,7 @@
 """
 
 # PyTango imports
-from tango.server import device_property
+from tango.server import device_property, attribute
 from tango import AttrWriteType
 
 # Additional import
@@ -205,7 +205,22 @@ class SDP(opcua_device):
     FPGA_bf_weights_yy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yy_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn))
     FPGA_bf_weights_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yy_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE)
 
-
+    # ----------
+    # Summarising Attributes
+    # ----------
+    FPGA_error_R                 = attribute(dtype=(bool,), max_dim_x=16)
+    FPGA_processing_error_R      = attribute(dtype=(bool,), max_dim_x=16)
+
+    def read_FPGA_error_R(self):
+        return self.proxy.TR_fpga_mask_RW & (
+                 self.proxy.TR_fpga_communication_error_R
+               )
+
+    def read_FPGA_processing_error_R(self):
+        return self.proxy.TR_fpga_mask_RW & (
+                 ~self.proxy.FPGA_processing_enable_R
+               | ~self.proxy.FPGA_wg_enable_R.any(axis=1)
+               )
 
     # --------
     # overloaded functions
diff --git a/tangostationcontrol/tangostationcontrol/devices/unb2.py b/tangostationcontrol/tangostationcontrol/devices/unb2.py
index cbad7fad14cf7d66371f92767e2aede331bd1fa2..de7f736588b6c9cf303ff44b0a5fea9f8f9c9b30 100644
--- a/tangostationcontrol/tangostationcontrol/devices/unb2.py
+++ b/tangostationcontrol/tangostationcontrol/devices/unb2.py
@@ -12,8 +12,7 @@
 """
 
 # PyTango imports
-from tango.server import command
-from tango.server import device_property
+from tango.server import command, attribute, device_property
 from tango import AttrWriteType, DebugIt, DevState
 # Additional import
 
@@ -39,6 +38,12 @@ class UNB2(opcua_device):
         default_value=[True] * 2
     )
 
+    UNB2TR_monitor_rate_RW_default = device_property(
+        dtype='DevLong64',
+        mandatory=False,
+        default_value=1
+    )
+
     # ----------
     # Attributes
     # ----------
@@ -114,6 +119,30 @@ class UNB2(opcua_device):
     UNB2_POL_SWITCH_PHY_VOUT_R   = attribute_wrapper(comms_annotation=["UNB2_POL_SWITCH_PHY_VOUT_R"],datatype=numpy.float64, dims=(2,))
     UNB2_PWR_on_R                = attribute_wrapper(comms_annotation=["UNB2_PWR_on_R"             ],datatype=numpy.bool_  , dims=(2,))
 
+    # ----------
+    # Summarising Attributes
+    # ----------
+    UNB2_error_R                  = attribute(dtype=(bool,), max_dim_x=2)
+    UNB2_FPGA_error_R             = attribute(dtype=((bool,),), max_dim_x=4, max_dim_y=2)
+    UNB2_QSFP_error_R             = attribute(dtype=((bool,),), max_dim_x=24, max_dim_y=2)
+
+    def read_UNB2_error_R(self):
+        return self.proxy.UNB2_mask_RW & (
+                 (self.proxy.UNB2TR_I2C_bus_error_R > 0)
+               | self.alarm_val("UNB2_PCB_ID_R")
+               )
+
+    def read_UNB2_FPGA_error_R(self):
+        return self.proxy.UNB2_mask_RW & (
+                 (self.proxy.UNB2TR_I2C_bus_DDR4_error_R > 0)
+               | (self.proxy.UNB2TR_I2C_bus_FPGA_PS_error_R > 0)
+               )
+
+    def read_UNB2_QSFP_error_R(self):
+        return self.proxy.UNB2_mask_RW & (
+                 (self.proxy.UNB2TR_I2C_bus_QSFP_error_R > 0)
+               )
+
     # --------
     # overloaded functions
     # --------
@@ -142,6 +171,15 @@ class UNB2(opcua_device):
         """
         self.opcua_connection.call_method(["UNB2_on"])
 
+    def _initialise_hardware(self):
+        """ Initialise the UNB2 hardware. """
+
+        # Cycle UNB2s
+        self.UNB2_off()
+        self.wait_attribute("UNB2TR_translator_busy_R", False, 5)
+        self.UNB2_on()
+        self.wait_attribute("UNB2TR_translator_busy_R", False, 5)
+
 # ----------
 # Run server
 # ----------
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_boot.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_boot.py
index 6d93080cc59ca176aea226893927f3321f503d89..d515bb29e5216d77a85f693fdc76229542145511 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_boot.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_boot.py
@@ -9,21 +9,26 @@
 
 import time
 
-from .base import AbstractTestBases
+from tango import DevState
 
+from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
+from tangostationcontrol.integration_test import base
 
-class TestDeviceBoot(AbstractTestBases.TestDeviceBase):
+
+class TestDeviceBoot(base.IntegrationTestCase):
 
     def setUp(self):
-        super().setUp("STAT/Boot/1")
+        self.proxy = TestDeviceProxy("STAT/Boot/1")
+
+    def test_start_in_on(self):
+        """Test whether we start in the ON state"""
 
-    def test_device_boot_initialise_station(self):
-        """Test if we can initialise the station"""
+        self.assertEqual(DevState.ON, self.proxy.state())
 
-        self.proxy.initialise()
-        self.proxy.on()
+    def test_reboot(self):
+        """Test if we can reinitialise the station"""
 
-        self.proxy.boot()
+        self.proxy.reboot()
 
         # wait for a few seconds for the station to initialise
         timeout = 10