diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json
index 4ce19112ec551a878b3e87fa1e7a4e3298769340..3b0456e8e74057faeaa57af08931640bf7c4e557 100644
--- a/CDB/LOFAR_ConfigDb.json
+++ b/CDB/LOFAR_ConfigDb.json
@@ -11,6 +11,9 @@
               "Station_Number": [
                 "999"
               ],
+              "Suppress_State_Transition_Failures": [
+                "True"
+              ],
               "Power_Children": [
                 "STAT/EC/1",
                 "STAT/PSOC/1",
@@ -19,7 +22,6 @@
                 "STAT/Configuration/1",
                 "STAT/ObservationControl/1",
                 "STAT/TemperatureManager/1",
-                "STAT/Docker/1",
                 "STAT/Calibration/1",
                 "STAT/Boot/1"
 
@@ -32,7 +34,6 @@
                 "STAT/PCON/1",
                 "STAT/ObservationControl/1",
                 "STAT/TemperatureManager/1",
-                "STAT/Docker/1",
                 "STAT/Calibration/1",
                 "STAT/Configuration/1",
                 "STAT/Boot/1",
@@ -55,14 +56,11 @@
       "STAT": {
         "CCD": {
           "STAT/CCD/1": {
-          }
-        }
-      }
-    },
-    "Docker": {
-      "STAT": {
-        "Docker": {
-          "STAT/Docker/1": {
+            "properties": {
+              "CCD_On_Off_timeout": [
+                "1"
+              ]
+            }
           }
         }
       }
@@ -71,6 +69,11 @@
       "STAT": {
         "Calibration": {
           "STAT/Calibration/1": {
+            "properties": {
+              "Station_Name": [
+                "DevStation"
+              ]
+            }
           }
         }
       }
@@ -206,8 +209,7 @@
                 "False"
               ],
               "Power_Children": [
-                "STAT/APS/H0",
-                "STAT/APSPU/1"
+                "STAT/APS/H0"
               ]
             }
           }
@@ -356,6 +358,7 @@
           "STAT/APS/H0": {
             "properties": {
               "Power_Children": [
+                "STAT/APSPU/1"
               ],
               "Control_Children": [
                 "STAT/APSCT/1",
@@ -372,6 +375,11 @@
       "STAT": {
         "APSCT": {
           "STAT/APSCT/1": {
+            "properties": {
+              "APSCT_On_Off_timeout": [
+                "1"
+              ]
+            }
           }
         }
       }
@@ -396,6 +404,14 @@
       "STAT": {
         "RECVH": {
           "STAT/RECVH/1": {
+            "properties": {
+              "RCU_On_Off_timeout": [
+                "1"
+              ],
+              "RCU_DTH_On_Off_timeout": [
+                "1"
+              ]
+            }
           }
         }
       }
@@ -404,6 +420,14 @@
       "STAT": {
         "RECVL": {
           "STAT/RECVL/1": {
+            "properties": {
+              "RCU_On_Off_timeout": [
+                "1"
+              ],
+              "RCU_DTH_On_Off_timeout": [
+                "1"
+              ]
+            }
           }
         }
       }
@@ -547,6 +571,11 @@
       "STAT": {
         "UNB2": {
           "STAT/UNB2/1": {
+            "properties": {
+              "UNB2_On_Off_timeout": [
+                "1"
+              ]
+            }
           }
         }
       }
diff --git a/CDB/stations/cs001.json b/CDB/stations/cs001.json
index 569bb889b82b49d09fc461ad8292722f05088864..af1c95884cd07f5d01c38018f3572dc0940ff9ba 100644
--- a/CDB/stations/cs001.json
+++ b/CDB/stations/cs001.json
@@ -46,6 +46,19 @@
         }
       }
     },
+    "Calibration": {
+      "STAT": {
+        "Calibration": {
+          "STAT/Calibration/1": {
+            "properties": {
+              "Station_Name": [
+                "CS001"
+              ]
+            }
+          }
+        }
+      }
+    },
     "PCON": {
       "STAT": {
         "PCON": {
@@ -114,19 +127,6 @@
         }
       }
     },
-    "CCD": {
-      "STAT": {
-        "CCD": {
-          "STAT/CCD/1": {
-            "properties": {
-              "OPC_Server_Name": [
-                  "ccd-sim"
-              ]
-            }
-          }
-        }
-      }
-    },
     "EC": {
       "STAT": {
         "EC": {
diff --git a/CDB/stations/dummy_positions_ConfigDb.json b/CDB/stations/dummy_positions_ConfigDb.json
index 020ebe51e6854b5150fd548afb4460d3784858ba..ee9fc852b9c772b65408bff8719e282cd663fd26 100644
--- a/CDB/stations/dummy_positions_ConfigDb.json
+++ b/CDB/stations/dummy_positions_ConfigDb.json
@@ -129,6 +129,16 @@
                                 "6", "0", "6", "1", "6", "2", "6", "3", "6", "4", "6", "5",
                                 "7", "0", "7", "1", "7", "2", "7", "3", "7", "4", "7", "5"
                             ],
+                            "Antenna_Names": [
+                                 "H0",  "H1",  "H2",  "H3",  "H4",  "H5",
+                                 "H6",  "H7",  "H8",  "H9", "H10", "H11",
+                                "H12", "H13", "H14", "H15", "H16", "H17",
+                                "H18", "H19", "H20", "H21", "H22", "H23",
+                                "H24", "H25", "H26", "H27", "H28", "H29",
+                                "H30", "H31", "H32", "H33", "H34", "H35",
+                                "H36", "H37", "H38", "H39", "H40", "H41",
+                                "H42", "H43", "H44", "H45", "H46", "H47"
+                            ],
                             "Antenna_Field_Reference_ETRS": [
                                 "3826896.631", "460979.131", "5064657.943"
                             ],
diff --git a/README.md b/README.md
index 9c5a78f4e06b29b666feba16371d8fb0f4f104a7..0423d581a9dfcf2d2418bf7cdf834a53bae4800a 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,10 @@ Next change the version in the following places:
 
 # Release Notes
 
+* 0.20.0 Complete implementation of station-state transitions in StationManager device.
+         Unified power management under power_hardware_on/off(), dropping prepare_hardware(),
+         disable_hardware().
+         Replaced device.warm_boot() by device.boot().
 * 0.19.0 Ensure requirements.txt are installed when using pip install
 * 0.18.3 Many configuration fixes in tango device configs, Fixed APS & EC device port mapping,
          fixed variable initialization in several devices, Fixed XST device going into
diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh
index ce817b134f80b71aab5f6e5722d6622a3990c287..5864730e09b8aa7626746867c8dfb0781759a804 100755
--- a/sbin/run_integration_test.sh
+++ b/sbin/run_integration_test.sh
@@ -89,7 +89,7 @@ echo '/usr/local/bin/wait-for-it.sh ${TANGO_HOST} --strict --timeout=300 -- true
 
 # Devices list is used to explitly word split when supplied to commands, must
 # disable shellcheck SC2086 for each case.
-DEVICES=(device-station-manager device-boot device-apsct device-ccd device-ec device-apspu device-sdpfirmware device-sdp device-recvh device-recvl device-bst device-sst device-unb2 device-xst device-beamlet device-digitalbeam device-tilebeam device-psoc device-pcon device-antennafield device-temperature-manager device-observation device-observation-control device-configuration device-calibration)
+DEVICES=(device-station-manager device-boot device-aps device-apsct device-ccd device-ec device-apspu device-sdpfirmware device-sdp device-recvh device-recvl device-bst device-sst device-unb2 device-xst device-beamlet device-digitalbeam device-tilebeam device-psoc device-pcon device-antennafield device-temperature-manager device-observation device-observation-control device-configuration device-calibration)
 
 SIMULATORS=(sdptr-sim recvh-sim recvl-sim unb2-sim apsct-sim apspu-sim ccd-sim ec-sim)
 
diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION
index 1cf0537c34385c81dd797cc51be2a589e17118e9..5a03fb737b381f0b07b830ba4bb6e87342a7a914 100644
--- a/tangostationcontrol/VERSION
+++ b/tangostationcontrol/VERSION
@@ -1 +1 @@
-0.19.0
+0.20.0
diff --git a/tangostationcontrol/integration_test/configuration/test_device_configuration.py b/tangostationcontrol/integration_test/configuration/test_device_configuration.py
index 37334af2441cac1c472cccba1600c6e3f7306dd4..fbc2d492919bd2164b92e5329018053b9ccf543c 100644
--- a/tangostationcontrol/integration_test/configuration/test_device_configuration.py
+++ b/tangostationcontrol/integration_test/configuration/test_device_configuration.py
@@ -55,7 +55,7 @@ class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
 
         if not INITIAL_CONFIGURATION:
             if self.proxy.state() == DevState.OFF:
-                self.proxy.warm_boot()
+                self.proxy.boot()
 
             INITIAL_CONFIGURATION = self.proxy.station_configuration_RW
 
@@ -73,7 +73,7 @@ class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
         global INITIAL_CONFIGURATION
 
         if self.proxy.state() == DevState.OFF:
-            self.proxy.warm_boot()
+            self.proxy.boot()
 
         self.proxy.station_configuration_RW = INITIAL_CONFIGURATION
 
@@ -83,7 +83,7 @@ class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
         """Upload station configuration with original default values"""
 
         if self.proxy.state() == DevState.OFF:
-            self.proxy.warm_boot()
+            self.proxy.boot()
 
         f = pkg_resources.resource_stream(
             __name__, f"configDB/{self.DB_DEFAULT_CONFIG_FILE}"
@@ -99,7 +99,7 @@ class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
         """Base method shared across read configuration tests"""
 
         self.assertEqual(DevState.OFF, self.proxy.state())
-        self.proxy.warm_boot()
+        self.proxy.boot()
         self.assertEqual(DevState.ON, self.proxy.state())
 
         station_configuration = self.proxy.station_configuration_RW
@@ -214,7 +214,7 @@ class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
         control_child_property = self.CONTROL_CHILD_PROPERTY.casefold()
 
         self.assertEqual(DevState.OFF, self.proxy.state())
-        self.proxy.warm_boot()
+        self.proxy.boot()
         self.assertEqual(DevState.ON, self.proxy.state())
 
         # So far configuration Database is set from files in the DB_FILE_LIST
@@ -262,7 +262,7 @@ class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
     def test_backup_station_configuration(self):
         """Test whether the station control configuration is correctly saved
         in the correspondent attribute as a backup"""
-        self.proxy.warm_boot()
+        self.proxy.boot()
         default_dbdata = json.loads(self.proxy.station_configuration_RW)
         # Load a full new configuration, and consequently erase the previous one
         self.proxy.station_configuration_RW = self.TEST_CONFIGURATION
@@ -275,7 +275,7 @@ class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
     def test_restore_station_configuration(self):
         """Test whether the backup station control configuration
         can be correctly restored after a loading/update operation"""
-        self.proxy.warm_boot()
+        self.proxy.boot()
         default_dbdata = json.loads(self.proxy.station_configuration_RW)
         backup_dbdata = json.loads(self.proxy.backup_station_configuration_R)
         # Load a full new configuration, and consequently erase the previous one
diff --git a/tangostationcontrol/integration_test/default/devices/base.py b/tangostationcontrol/integration_test/default/devices/base.py
index 0cbc5dbf6678e5c634301eb0615c757e528481fd..377b22df6d6210f030090362c26a3e8ff9d2911d 100644
--- a/tangostationcontrol/integration_test/default/devices/base.py
+++ b/tangostationcontrol/integration_test/default/devices/base.py
@@ -74,10 +74,10 @@ class AbstractTestBases:
 
             self.assertEqual(DevState.ON, self.proxy.state())
 
-        def test_device_warm_boot(self):
+        def test_device_boot(self):
             """Test if we can transition off -> on using a warm boot"""
 
-            self.proxy.warm_boot()
+            self.proxy.boot()
 
             self.assertEqual(DevState.ON, self.proxy.state())
 
diff --git a/tangostationcontrol/integration_test/default/devices/interfaces/test_power_hierarchy.py b/tangostationcontrol/integration_test/default/devices/interfaces/test_power_hierarchy.py
index 8e6cbd04dac53c8842f3ec2597cd89cbb87ed2fa..9b6433683c8199aed97d106bca086f035e0ddec8 100644
--- a/tangostationcontrol/integration_test/default/devices/interfaces/test_power_hierarchy.py
+++ b/tangostationcontrol/integration_test/default/devices/interfaces/test_power_hierarchy.py
@@ -4,13 +4,17 @@
 """
 Power Hierarchy module integration test
 """
-from tango import DevState, DeviceProxy, DevFailed
+from tango import DevState, DeviceProxy
 
 from tangostationcontrol.devices.interfaces.power_hierarchy import PowerHierarchy
 
 from integration_test import base
 from integration_test.device_proxy import TestDeviceProxy
 
+import logging
+
+logger = logging.getLogger()
+
 
 class TestPowerHierarchy(base.IntegrationTestCase):
     """Integration Test class for PowerHierarchy methods"""
@@ -35,9 +39,37 @@ class TestPowerHierarchy(base.IntegrationTestCase):
         super().setUp()
         self.setup_all_devices()
 
+    def _unacceptable_exceptions(self):
+        """Return the set of exceptions raised by the last state transition
+        in the StationManager, that we do not accept.
+
+        We must accept exceptions since we do not emulate interaction with
+        actual hardware. In this function, we make sure to just ignore those
+        which we know will be raised even in a sunny-day scenario."""
+
+        result = []
+
+        for ex_str in self.stationmanager_proxy.last_requested_transition_exceptions_R:
+            # Skip "vetted" exceptions that involve switching power
+            # on actual hardware, which obviously won't power on in
+            # our simulators.
+            if "Failed to execute command_inout on device" in ex_str:
+                if "command power_hardware_on" in ex_str:
+                    continue
+                if "command power_hardware_off" in ex_str:
+                    continue
+
+            # Anything left is not acceptable
+            result.append(ex_str)
+
+        return result
+
     def setup_stationmanager_proxy(self):
         """Initialise StationManager device"""
         stationmanager_proxy = TestDeviceProxy(self.stationmanager_name)
+        # extend timeout for running commands, as state transitions can take a long time
+        stationmanager_proxy.set_timeout_millis(60000)
+
         stationmanager_proxy.off()
         stationmanager_proxy.init()
         self.assertEqual(stationmanager_proxy.state(), DevState.ON)
@@ -104,16 +136,28 @@ class TestPowerHierarchy(base.IntegrationTestCase):
         self.assertEqual(self.ccd_proxy.state(), DevState.OFF)
         # Switch from OFF to HIBERNATE
         self.stationmanager_proxy.station_hibernate()
+        self.assertEqual(
+            self.stationmanager_proxy.last_requested_transition_R, "OFF -> HIBERNATE"
+        )
         self.assertEqual(self.psoc_proxy.state(), DevState.ON)
         self.assertEqual(self.pcon_proxy.state(), DevState.ON)
         self.assertEqual(self.ccd_proxy.state(), DevState.ON)
 
+        logger.info(
+            f"Exceptions suppressed in test_off_to_hibernate: {self.stationmanager_proxy.last_requested_transition_exceptions_R}"
+        )
+
+        self.assertListEqual([], self._unacceptable_exceptions())
+
     def test_hibernate_to_standby(self):
         """
         Test whether Tango devices are correctly triggered in the HIBERNATE to STANDBY transition
         """
         # Switch from OFF to HIBERNATE
         self.stationmanager_proxy.station_hibernate()
+        self.assertEqual(
+            self.stationmanager_proxy.last_requested_transition_R, "OFF -> HIBERNATE"
+        )
         self.assertEqual(self.apspu_proxy.state(), DevState.OFF)
         self.assertEqual(self.apsct_proxy.state(), DevState.OFF)
         self.assertEqual(self.unb2_proxy.state(), DevState.OFF)
@@ -122,6 +166,10 @@ class TestPowerHierarchy(base.IntegrationTestCase):
         self.assertEqual(self.sdpfirmware_proxy.state(), DevState.OFF)
         # Switch from HIBERNATE to STANDBY
         self.stationmanager_proxy.station_standby()
+        self.assertEqual(
+            self.stationmanager_proxy.last_requested_transition_R,
+            "HIBERNATE -> STANDBY",
+        )
         self.assertEqual(self.apspu_proxy.state(), DevState.ON)
         self.assertEqual(self.apsct_proxy.state(), DevState.ON)
         self.assertEqual(self.unb2_proxy.state(), DevState.ON)
@@ -135,6 +183,12 @@ class TestPowerHierarchy(base.IntegrationTestCase):
             firmware_images,
         )
 
+        logger.info(
+            f"Exceptions suppressed: {self.stationmanager_proxy.last_requested_transition_exceptions_R}"
+        )
+
+        self.assertListEqual([], self._unacceptable_exceptions())
+
     def test_standby_to_on(self):
         """
         Test whether Tango devices are correctly triggered in the STANDBY to ON transition
@@ -147,9 +201,18 @@ class TestPowerHierarchy(base.IntegrationTestCase):
         self.assertEqual(self.sdp_proxy.state(), DevState.OFF)
         self.assertEqual(self.antennafield_proxy.state(), DevState.OFF)
         self.stationmanager_proxy.station_on()
+        self.assertEqual(
+            self.stationmanager_proxy.last_requested_transition_R, "STANDBY -> ON"
+        )
         self.assertEqual(self.sdp_proxy.state(), DevState.ON)
         self.assertEqual(self.antennafield_proxy.state(), DevState.ON)
 
+        logger.info(
+            f"Exceptions suppressed: {self.stationmanager_proxy.last_requested_transition_exceptions_R}"
+        )
+
+        self.assertListEqual([], self._unacceptable_exceptions())
+
     def test_on_to_standby(self):
         """
         Test whether Tango devices are correctly triggered in the ON to STANDBY transition
@@ -162,9 +225,18 @@ class TestPowerHierarchy(base.IntegrationTestCase):
         self.stationmanager_proxy.station_on()
         # Reverse to STANDBY
         self.stationmanager_proxy.station_standby()
+        self.assertEqual(
+            self.stationmanager_proxy.last_requested_transition_R, "ON -> STANDBY"
+        )
         self.assertEqual(self.sdp_proxy.state(), DevState.OFF)
         self.assertEqual(self.antennafield_proxy.state(), DevState.OFF)
 
+        logger.info(
+            f"Exceptions suppressed: {self.stationmanager_proxy.last_requested_transition_exceptions_R}"
+        )
+
+        self.assertListEqual([], self._unacceptable_exceptions())
+
     def test_standby_to_hibernate(self):
         """
         Test whether Tango devices are correctly triggered in the STANDBY to HIBERNATE transition
@@ -175,6 +247,10 @@ class TestPowerHierarchy(base.IntegrationTestCase):
         self.stationmanager_proxy.station_standby()
         # Reverse to HIBERNATE
         self.stationmanager_proxy.station_hibernate()
+        self.assertEqual(
+            self.stationmanager_proxy.last_requested_transition_R,
+            "STANDBY -> HIBERNATE",
+        )
         self.assertEqual(self.apspu_proxy.state(), DevState.OFF)
         self.assertEqual(self.apsct_proxy.state(), DevState.OFF)
         self.assertEqual(self.unb2_proxy.state(), DevState.OFF)
@@ -182,21 +258,8 @@ class TestPowerHierarchy(base.IntegrationTestCase):
         self.assertEqual(self.recvl_proxy.state(), DevState.OFF)
         self.assertEqual(self.sdpfirmware_proxy.state(), DevState.OFF)
 
-    def test_ensure_power(self):
-        """
-        Test Tango devices ensure_power functionalities within state transitions
-        """
-        stationmanager_ph = PowerHierarchy()
-        stationmanager_ph.init(self.stationmanager_name)
-
-        # Boot CCD device
-        self.ccd_proxy.initialise()
-        self.ccd_proxy.set_translator_defaults()
-        self.ccd_proxy.set_defaults()
-        self.ccd_proxy.on()
-        self.assertEqual(self.ccd_proxy.state(), DevState.ON)
+        logger.info(
+            f"Exceptions suppressed: {self.stationmanager_proxy.last_requested_transition_exceptions_R}"
+        )
 
-        # Power monitoring attribute is False in development environment
-        self.assertEqual(self.ccd_proxy.CCD_PWR_on_R, False)
-        with self.assertRaises(DevFailed):
-            stationmanager_ph.ensure_power(self.ccd_proxy)
+        self.assertListEqual([], self._unacceptable_exceptions())
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py b/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py
index 85c81dc5d468f8643029b5070037bb828366edab..1dd6c9e23a1d1bf62f26443f516909d7932ba734 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py
@@ -68,7 +68,7 @@ class TestAntennaFieldDevice(AbstractTestBases.TestDeviceBase):
         # setup RECV
         recv_proxy = TestDeviceProxy("STAT/RECVH/1")
         recv_proxy.off()
-        recv_proxy.warm_boot()
+        recv_proxy.boot()
         recv_proxy.set_defaults()
         return recv_proxy
 
@@ -76,14 +76,14 @@ class TestAntennaFieldDevice(AbstractTestBases.TestDeviceBase):
         # setup SDPFirmware
         sdpfirmware_proxy = TestDeviceProxy("STAT/SDPFirmware/HBA")
         sdpfirmware_proxy.off()
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         return sdpfirmware_proxy
 
     def setup_sdp_proxy(self):
         # setup SDP
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         return sdp_proxy
 
     def setup_stationmanager_proxy(self):
@@ -120,7 +120,8 @@ class TestAntennaFieldDevice(AbstractTestBases.TestDeviceBase):
         antennafield_proxy.off()
         antennafield_proxy.put_property(antenna_properties)
         antennafield_proxy.put_property(mapping_properties)
-        antennafield_proxy.boot()  # initialises hardware values as well
+        antennafield_proxy.boot()
+        antennafield_proxy.power_hardware_on()
 
         # Verify all antennas are indicated to work
         numpy.testing.assert_equal(
@@ -171,14 +172,15 @@ class TestAntennaFieldDevice(AbstractTestBases.TestDeviceBase):
         antennafield_proxy.off()
         antennafield_proxy.put_property(antenna_properties)
         antennafield_proxy.put_property(mapping_properties)
-        antennafield_proxy.boot()  # initialises hardware values as well
+        antennafield_proxy.boot()
+        antennafield_proxy.power_hardware_on()
 
         # Antenna_Usage_Mask_R should be false except one
         numpy.testing.assert_equal(
             numpy.array([False] + [True] + [False] * (MAX_ANTENNA - 2)),
             antennafield_proxy.Antenna_Usage_Mask_R,
         )
-        # device.boot() writes Antenna_Usage_Mask_R to ANT_mask_RW
+        # device.power_hardware_on() writes Antenna_Usage_Mask_R to ANT_mask_RW
         numpy.testing.assert_equal(
             numpy.array([False] + [True] + [False] * (MAX_ANTENNA - 2)),
             antennafield_proxy.ANT_mask_RW,
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py b/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py
index 02814dcbe852546bb608db71b67fc32c0c6b4d4a..1eecddd87c21c609726862679ee8f7cc1593a73e 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py
@@ -34,7 +34,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase):
         # setup SDP, on which this device depends
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         sdp_proxy.set_defaults()
 
         # setup the frequencies as expected in the test
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_bst.py b/tangostationcontrol/integration_test/default/devices/test_device_bst.py
index c4a08d430c2ab0aca92ed55efc591a768e112135..e39bb9b2aee0409a566bc1f29b89be84e0bd3630 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_bst.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_bst.py
@@ -20,6 +20,6 @@ class TestDeviceBST(AbstractTestBases.TestDeviceBase):
         # setup SDP, on which this device depends
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         sdp_proxy.set_defaults()
         return sdp_proxy
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_calibration.py b/tangostationcontrol/integration_test/default/devices/test_device_calibration.py
index 725d50a60f5e8ddc7868e0b85c4ce38c0ca5b6fd..cdd9650060ae1015a1b503282f88f04e9988fcb8 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_calibration.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_calibration.py
@@ -121,7 +121,7 @@ class TestCalibrationDevice(AbstractTestBases.TestDeviceBase):
         # setup RECV
         recv_proxy = TestDeviceProxy("STAT/RECVH/1")
         recv_proxy.off()
-        recv_proxy.warm_boot()
+        recv_proxy.boot()
         recv_proxy.set_defaults()
         return recv_proxy
 
@@ -129,21 +129,21 @@ class TestCalibrationDevice(AbstractTestBases.TestDeviceBase):
         # setup SDP
         sdpfirmware_proxy = TestDeviceProxy("STAT/SDPFirmware/HBA")
         sdpfirmware_proxy.off()
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         return sdpfirmware_proxy
 
     def setup_sdp_proxy(self):
         # setup SDP
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         return sdp_proxy
 
     def setup_proxy(self, dev: str):
         # setup SDP
         proxy = TestDeviceProxy(dev)
         proxy.off()
-        proxy.warm_boot()
+        proxy.boot()
         self.addCleanup(self.shutdown(dev))
         return proxy
 
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py b/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py
index 76cfd79e1daa85569ae0c9abd522ab1f46dc80ca..42974a60e8198ec4bf558b8b497f753862532479 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py
@@ -56,7 +56,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase):
     def setup_recv_proxy(self):
         recv_proxy = TestDeviceProxy(self.recv_iden)
         recv_proxy.off()
-        recv_proxy.warm_boot()
+        recv_proxy.boot()
         recv_proxy.set_defaults()
         return recv_proxy
 
@@ -69,7 +69,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase):
     def setup_beamlet_proxy(self):
         beamlet_proxy = TestDeviceProxy(self.beamlet_iden)
         beamlet_proxy.off()
-        beamlet_proxy.warm_boot()
+        beamlet_proxy.boot()
         beamlet_proxy.set_defaults()
         return beamlet_proxy
 
@@ -77,7 +77,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase):
         # setup SDPFirmware
         sdpfirmware_proxy = TestDeviceProxy(self.sdpfirmware_iden)
         sdpfirmware_proxy.off()
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         sdpfirmware_proxy.set_defaults()
         return sdpfirmware_proxy
 
@@ -85,7 +85,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase):
         # setup SDP, on which this device depends
         sdp_proxy = TestDeviceProxy(self.sdp_iden)
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         sdp_proxy.set_defaults()
         return sdp_proxy
 
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_observation.py b/tangostationcontrol/integration_test/default/devices/test_device_observation.py
index bb26162533c2e86b48a9c58a1b10e6f3ac75ce14..dcd4b53662728021a11b2e55a07cdff794877554 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_observation.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_observation.py
@@ -139,7 +139,7 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase):
         # setup RECV
         recv_proxy = TestDeviceProxy("STAT/RECVH/1")
         recv_proxy.off()
-        recv_proxy.warm_boot()
+        recv_proxy.boot()
         recv_proxy.set_defaults()
         return recv_proxy
 
@@ -147,14 +147,14 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase):
         # setup SDPFirmware
         sdpfirmware_proxy = TestDeviceProxy("STAT/SDPFirmware/HBA")
         sdpfirmware_proxy.off()
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         return sdpfirmware_proxy
 
     def setup_sdp_proxy(self):
         # setup SDP
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         return sdp_proxy
 
     def setup_antennafield_proxy(self):
@@ -188,7 +188,7 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase):
         # setup Digitalbeam
         beamlet_proxy = TestDeviceProxy("STAT/Beamlet/HBA")
         beamlet_proxy.off()
-        beamlet_proxy.warm_boot()
+        beamlet_proxy.boot()
         beamlet_proxy.set_defaults()
         return beamlet_proxy
 
@@ -196,7 +196,7 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase):
         # setup Digitalbeam
         digitalbeam_proxy = TestDeviceProxy("STAT/DigitalBeam/HBA")
         digitalbeam_proxy.off()
-        digitalbeam_proxy.warm_boot()
+        digitalbeam_proxy.boot()
         digitalbeam_proxy.set_defaults()
         return digitalbeam_proxy
 
@@ -204,7 +204,7 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase):
         # Setup Tilebeam
         tilebeam_proxy = TestDeviceProxy("STAT/TileBeam/HBA")
         tilebeam_proxy.off()
-        tilebeam_proxy.warm_boot()
+        tilebeam_proxy.boot()
         tilebeam_proxy.set_defaults()
         return tilebeam_proxy
 
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_observation_control.py b/tangostationcontrol/integration_test/default/devices/test_device_observation_control.py
index 1f27234f55f3f9e6b7cb8ef95d9b1d9c9367f542..7457d826656e963c27d380adfcf41112d70237cd 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_observation_control.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_observation_control.py
@@ -130,7 +130,7 @@ class TestObservationControlDevice(AbstractTestBases.TestDeviceBase):
         # setup RECV
         recv_proxy = TestDeviceProxy("STAT/RECVH/1")
         recv_proxy.off()
-        recv_proxy.warm_boot()
+        recv_proxy.boot()
         recv_proxy.set_defaults()
         return recv_proxy
 
@@ -138,14 +138,14 @@ class TestObservationControlDevice(AbstractTestBases.TestDeviceBase):
         # setup SDPFirmware
         sdpfirmware_proxy = TestDeviceProxy("STAT/SDPFirmware/HBA")
         sdpfirmware_proxy.off()
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         return sdpfirmware_proxy
 
     def setup_sdp_proxy(self):
         # setup SDP
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         return sdp_proxy
 
     def setup_antennafield_proxy(self):
@@ -160,7 +160,7 @@ class TestObservationControlDevice(AbstractTestBases.TestDeviceBase):
             }
         )
         antennafield_proxy.off()
-        antennafield_proxy.warm_boot()
+        antennafield_proxy.boot()
         antennafield_proxy.set_defaults()
         return antennafield_proxy
 
@@ -168,7 +168,7 @@ class TestObservationControlDevice(AbstractTestBases.TestDeviceBase):
         # setup Digitalbeam
         beamlet_proxy = TestDeviceProxy("STAT/Beamlet/HBA")
         beamlet_proxy.off()
-        beamlet_proxy.warm_boot()
+        beamlet_proxy.boot()
         beamlet_proxy.set_defaults()
         return beamlet_proxy
 
@@ -176,7 +176,7 @@ class TestObservationControlDevice(AbstractTestBases.TestDeviceBase):
         # setup Digitalbeam
         digitalbeam_proxy = TestDeviceProxy("STAT/DigitalBeam/HBA")
         digitalbeam_proxy.off()
-        digitalbeam_proxy.warm_boot()
+        digitalbeam_proxy.boot()
         digitalbeam_proxy.set_defaults()
         return digitalbeam_proxy
 
@@ -184,7 +184,7 @@ class TestObservationControlDevice(AbstractTestBases.TestDeviceBase):
         # Setup Tilebeam
         tilebeam_proxy = TestDeviceProxy("STAT/TileBeam/HBA")
         tilebeam_proxy.off()
-        tilebeam_proxy.warm_boot()
+        tilebeam_proxy.boot()
         tilebeam_proxy.set_defaults()
         return tilebeam_proxy
 
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_sdpfirmware.py b/tangostationcontrol/integration_test/default/devices/test_device_sdpfirmware.py
index dfaf9030187c2afaf32bada38902a416c06cbad3..56487cdd10e2d70954eff0fec9205fe9c50ccaf7 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_sdpfirmware.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_sdpfirmware.py
@@ -14,7 +14,7 @@ class TestDeviceSDPFirmware(AbstractTestBases.TestDeviceBase):
     def test_device_sdpfirmware_read_attribute(self):
         """Test if we can read an attribute obtained over OPC-UA"""
 
-        self.proxy.warm_boot()
+        self.proxy.boot()
 
         self.assertListEqual(
             [True] * N_pn, list(self.proxy.TR_fpga_communication_error_R)
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_sst.py b/tangostationcontrol/integration_test/default/devices/test_device_sst.py
index 1f0781110daeed79efe82c09dace80825f9ef0a2..7ca8847ce505faa504672a16d2f258742e76b060 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_sst.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_sst.py
@@ -27,7 +27,7 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase):
         # setup SDP Firmware
         sdpfirmware_proxy = TestDeviceProxy("STAT/SDPFirmware/HBA")
         sdpfirmware_proxy.off()
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         sdpfirmware_proxy.set_defaults()
         return sdpfirmware_proxy
 
@@ -35,14 +35,14 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase):
         # setup SDP, on which this device depends
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         sdp_proxy.set_defaults()
         return sdp_proxy
 
     def test_device_sst_send_udp(self):
         port_property = {"Statistics_Client_TCP_Port": "4998"}
         self.proxy.put_property(port_property)
-        self.proxy.warm_boot()
+        self.proxy.boot()
 
         self.assertEqual(DevState.ON, self.proxy.state())
 
@@ -57,7 +57,7 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase):
     def test_device_sst_connect_tcp_receive(self):
         port_property = {"Statistics_Client_TCP_Port": "5101"}
         self.proxy.put_property(port_property)
-        self.proxy.warm_boot()
+        self.proxy.boot()
 
         time.sleep(2)
 
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_temperature_manager.py b/tangostationcontrol/integration_test/default/devices/test_device_temperature_manager.py
index 32bf3719c1e8f5964d177608a01dde8f18070dcd..4c6a33f2546ddc93fbc72b900c6d8f9ee20ee256 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_temperature_manager.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_temperature_manager.py
@@ -53,7 +53,7 @@ class TestDeviceTemperatureManager(AbstractTestBases.TestDeviceBase):
         # setup SDPFirmware
         sdpfirmware_proxy = TestDeviceProxy(self.sdp_firmware_name)
         sdpfirmware_proxy.off()
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         sdpfirmware_proxy.set_defaults()
         return sdpfirmware_proxy
 
@@ -61,7 +61,7 @@ class TestDeviceTemperatureManager(AbstractTestBases.TestDeviceBase):
         # setup SDP, on which this device depends
         sdp_proxy = TestDeviceProxy(self.sdp_name)
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         sdp_proxy.set_defaults()
         return sdp_proxy
 
@@ -86,10 +86,10 @@ class TestDeviceTemperatureManager(AbstractTestBases.TestDeviceBase):
         # is fine
         for dev in devices:
             if dev.state() == DevState.OFF:
-                dev.warm_boot()
+                dev.boot()
             elif dev.state() == DevState.FAULT:
                 dev.off()
-                dev.warm_boot()
+                dev.boot()
 
         self.assertEqual(
             self.proxy.get_property(self.shutdown_list_attribute)[
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_tilebeam.py b/tangostationcontrol/integration_test/default/devices/test_device_tilebeam.py
index 45a9f8e1ca11f5beb7c278a2d9b5fc281ebc07e5..528715dbda6544e364b3e0bb4933c6adb5d2f5b1 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_tilebeam.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_tilebeam.py
@@ -39,7 +39,7 @@ class TestDeviceTileBeam(AbstractTestBases.TestDeviceBase):
         """Setup RECV"""
         recv_proxy = TestDeviceProxy("STAT/RECVH/1")
         recv_proxy.off()
-        recv_proxy.warm_boot()
+        recv_proxy.boot()
         recv_proxy.set_defaults()
         return recv_proxy
 
@@ -78,7 +78,7 @@ class TestDeviceTileBeam(AbstractTestBases.TestDeviceBase):
         self.setup_antennafield_proxy()
 
         # setup BEAM
-        self.proxy.warm_boot()
+        self.proxy.boot()
 
         # verify delays method returns the correct dimensions
         delays = self.proxy.delays(self.POINTING_DIRECTION)
@@ -90,7 +90,7 @@ class TestDeviceTileBeam(AbstractTestBases.TestDeviceBase):
         antennafield_proxy = self.setup_antennafield_proxy()
 
         # setup BEAM
-        self.proxy.warm_boot()
+        self.proxy.boot()
         self.proxy.Tracking_enabled_RW = False
 
         # Verify attribute is present (all zeros if never used before)
@@ -119,7 +119,7 @@ class TestDeviceTileBeam(AbstractTestBases.TestDeviceBase):
         # setup AntennaField as well
         antennafield_proxy = self.setup_antennafield_proxy()
 
-        self.proxy.warm_boot()
+        self.proxy.boot()
         self.proxy.Tracking_enabled_RW = False
 
         # Point to Zenith
@@ -145,7 +145,7 @@ class TestDeviceTileBeam(AbstractTestBases.TestDeviceBase):
         # setup AntennaField as well
         antennafield_proxy = self.setup_antennafield_proxy()
 
-        self.proxy.warm_boot()
+        self.proxy.boot()
         self.proxy.Tracking_enabled_RW = False
 
         # point at north on the horizon
@@ -187,7 +187,7 @@ class TestDeviceTileBeam(AbstractTestBases.TestDeviceBase):
         # setup AntennaField as well
         antennafield_proxy = self.setup_antennafield_proxy()
 
-        self.proxy.warm_boot()
+        self.proxy.boot()
         self.proxy.Tracking_enabled_RW = False
 
         # Point to LOFAR 1 ref pointing (0.929342, 0.952579, J2000)
@@ -230,7 +230,7 @@ class TestDeviceTileBeam(AbstractTestBases.TestDeviceBase):
         self.setup_stationmanager_proxy()
         self.setup_recv_proxy()
         self.setup_antennafield_proxy()
-        self.proxy.warm_boot()
+        self.proxy.boot()
 
         # check if we're really tracking
         self.assertTrue(self.proxy.Tracking_enabled_R)
diff --git a/tangostationcontrol/integration_test/default/devices/test_device_xst.py b/tangostationcontrol/integration_test/default/devices/test_device_xst.py
index 806f3148d4f297d3ed35e142941d1fc9fcc5515f..9a8a0bb92b678dcf7016927c631835c988144964 100644
--- a/tangostationcontrol/integration_test/default/devices/test_device_xst.py
+++ b/tangostationcontrol/integration_test/default/devices/test_device_xst.py
@@ -20,6 +20,6 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase):
         # setup SDP, on which this device depends
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         sdp_proxy.set_defaults()
         return sdp_proxy
diff --git a/tangostationcontrol/integration_test/default/devices/test_observation.py b/tangostationcontrol/integration_test/default/devices/test_observation.py
index e262c512dc262ea7d22fc03c3df7b43d8df0579a..05f64a2a3cd9e6db1b081fb72a7e9fae3844d2c5 100644
--- a/tangostationcontrol/integration_test/default/devices/test_observation.py
+++ b/tangostationcontrol/integration_test/default/devices/test_observation.py
@@ -20,7 +20,7 @@ class TestObservation(base.IntegrationTestCase):
         self.setup_stationmanager_proxy()
         self.observation_control_proxy = TestDeviceProxy("STAT/ObservationControl/1")
         self.observation_control_proxy.off()
-        self.observation_control_proxy.warm_boot()
+        self.observation_control_proxy.boot()
 
         # make sure any devices we depend on are also started
         for device in [
@@ -33,7 +33,7 @@ class TestObservation(base.IntegrationTestCase):
         ]:
             proxy = TestDeviceProxy(device)
             proxy.off()
-            proxy.warm_boot()
+            proxy.boot()
 
     def setup_stationmanager_proxy(self):
         """Setup StationManager"""
diff --git a/tangostationcontrol/integration_test/default/prometheus/test_tango_prometheus_client.py b/tangostationcontrol/integration_test/default/prometheus/test_tango_prometheus_client.py
index 533366f5d7f68295973c4ba00138fd690de0ec54..3a8a042544a05ec63b86bab133ae8a8812a4aed3 100644
--- a/tangostationcontrol/integration_test/default/prometheus/test_tango_prometheus_client.py
+++ b/tangostationcontrol/integration_test/default/prometheus/test_tango_prometheus_client.py
@@ -52,7 +52,7 @@ class TestPrometheusClient(BaseIntegrationTestCase):
         # setup RECV
         recv_proxy = TestDeviceProxy(device_name)
         recv_proxy.off()
-        recv_proxy.warm_boot()
+        recv_proxy.boot()
         recv_proxy.set_defaults()
         return recv_proxy
 
diff --git a/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py b/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py
index 0b48e86d404d08c58777b004b2f83acadac0c408..f52746567f7f402fb5bc312d96fec6337b326c2f 100644
--- a/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py
+++ b/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py
@@ -49,7 +49,7 @@ class TestDigitalbeamPerformance(base.IntegrationTestCase):
         for proxy in sdpfirmware_proxies + sdp_proxies + recv_proxies + beamlet_proxies:
             proxy.off()
             self.assertTrue(proxy.state() is DevState.OFF)
-            proxy.warm_boot()
+            proxy.boot()
             proxy.set_defaults()
             self.assertTrue(proxy.state() is DevState.ON)
 
@@ -77,7 +77,7 @@ class TestDigitalbeamPerformance(base.IntegrationTestCase):
         for proxy in beam_proxies:
             proxy.off()
             self.assertTrue(proxy.state() is DevState.OFF)
-            proxy.warm_boot()
+            proxy.boot()
             proxy.set_defaults()
             proxy.Tracking_enabled_RW = tracking
             self.assertTrue(proxy.state() is DevState.ON)
diff --git a/tangostationcontrol/integration_test/tilebeam_performance/test_tilebeam_performance.py b/tangostationcontrol/integration_test/tilebeam_performance/test_tilebeam_performance.py
index 8eda5ab52609657f36a19bedc221f5927fa80eef..60a1984e1c4110cba1b0e65d6aa39eb85bb54619 100644
--- a/tangostationcontrol/integration_test/tilebeam_performance/test_tilebeam_performance.py
+++ b/tangostationcontrol/integration_test/tilebeam_performance/test_tilebeam_performance.py
@@ -43,14 +43,14 @@ class TestTilebeamPerformance(base.IntegrationTestCase):
         sdpfirmware_proxy = TestDeviceProxy("STAT/SDPFirmware/HBA")
         sdpfirmware_proxy.off()
         self.assertTrue(sdpfirmware_proxy.state() is DevState.OFF)
-        sdpfirmware_proxy.warm_boot()
+        sdpfirmware_proxy.boot()
         sdpfirmware_proxy.set_defaults()
         self.assertTrue(sdpfirmware_proxy.state() is DevState.ON)
 
         sdp_proxy = TestDeviceProxy("STAT/SDP/HBA")
         sdp_proxy.off()
         self.assertTrue(sdp_proxy.state() is DevState.OFF)
-        sdp_proxy.warm_boot()
+        sdp_proxy.boot()
         sdp_proxy.set_defaults()
         self.assertTrue(sdp_proxy.state() is DevState.ON)
 
@@ -64,7 +64,7 @@ class TestTilebeamPerformance(base.IntegrationTestCase):
         for proxy in recv_proxies:
             proxy.off()
             self.assertTrue(proxy.state() is DevState.OFF)
-            proxy.warm_boot()
+            proxy.boot()
             proxy.set_defaults()
             self.assertTrue(proxy.state() is DevState.ON)
 
@@ -114,7 +114,7 @@ class TestTilebeamPerformance(base.IntegrationTestCase):
         for proxy in beam_proxies:
             proxy.off()
             self.assertTrue(proxy.state() is DevState.OFF)
-            proxy.warm_boot()
+            proxy.boot()
             proxy.set_defaults()
             proxy.Tracking_enabled_RW = False
             self.assertTrue(proxy.state() is DevState.ON)
diff --git a/tangostationcontrol/tangostationcontrol/common/states.py b/tangostationcontrol/tangostationcontrol/common/states.py
index d8d4a9b4a1f40e932d76ecd7e097fd6a376c8bdb..6ab0c5a581978e00e1ae09bcb4c3d3d08c219879 100644
--- a/tangostationcontrol/tangostationcontrol/common/states.py
+++ b/tangostationcontrol/tangostationcontrol/common/states.py
@@ -1,7 +1,17 @@
 # Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
 
-from tango import DevState
+from enum import Enum
+from typing import Dict, Optional
+from functools import wraps
+
+from tango import DevState, DeviceProxy
+
+from .type_checking import device_class_matches
+
+# -----------------------
+# Device states
+# -----------------------
 
 # The Device states in which we consider our device operational,
 # and thus allow interaction.
@@ -19,3 +29,76 @@ POWER_ON_COMMAND_STATES = DEFAULT_COMMAND_STATES + [DevState.OFF, DevState.INIT]
 
 # States in which the hardware can be powered off
 POWER_OFF_COMMAND_STATES = OPERATIONAL_STATES + [DevState.STANDBY, DevState.DISABLE]
+
+
+# -----------------------
+# Station states
+# -----------------------
+
+
+class StationState(Enum):
+    """Station states enumeration"""
+
+    OFF = "OFF"
+    HIBERNATE = "HIBERNATE"
+    STANDBY = "STANDBY"
+    ON = "ON"
+
+
+# Contains which transitions are allowed for a given states
+ALLOWED_STATION_STATE_TRANSITIONS = {
+    StationState.OFF: [StationState.HIBERNATE],
+    StationState.HIBERNATE: [StationState.OFF, StationState.STANDBY],
+    StationState.STANDBY: [StationState.HIBERNATE, StationState.ON],
+    StationState.ON: [StationState.STANDBY],
+}
+
+
+DEVICES_ON_IN_STATION_STATE: Dict[str, Optional[StationState]] = {
+    """In which StationState each device class should be switched ON."""
+    "StationManager": StationState.HIBERNATE,
+    "CCD": StationState.HIBERNATE,
+    "PCON": StationState.HIBERNATE,
+    "PSOC": StationState.HIBERNATE,
+    "TemperatureManager": StationState.HIBERNATE,
+    "Docker": StationState.HIBERNATE,
+    "Configuration": StationState.HIBERNATE,
+    "SDPFirmware": StationState.STANDBY,
+    "APS": StationState.STANDBY,
+    "APSPU": StationState.STANDBY,
+    "APSCT": StationState.STANDBY,
+    "UNB2": StationState.STANDBY,
+    "RECVH": StationState.STANDBY,
+    "RECVL": StationState.STANDBY,
+    # TODO(JDM) Lingering observations (and debug observation 0) should not be booted as we do not persist their settings
+    "Observation": None,
+    # Unmentioned devices will go ON in this state
+    "_default": StationState.ON,
+}
+
+
+def run_if_device_on_in_station_state(
+    target_state: StationState,
+    state_table: Dict[str, StationState] = DEVICES_ON_IN_STATION_STATE,
+):
+    """Decorator for a function func(device). It only executes the decorated function if the device should be turned ON in the target_state. Returns None otherwise."""
+
+    def inner(func):
+        @wraps(func)
+        def wrapper(device: DeviceProxy, *args, **kwargs):
+            def should_execute():
+                for pattern, station_state in state_table.items():
+                    if not device_class_matches(device, pattern):
+                        continue
+
+                    return target_state == station_state
+
+                # no matches, use default (if any)
+                return target_state == state_table.get("_default")
+
+            # execute function if we should in the configured state
+            return func(device, *args, **kwargs) if should_execute() else None
+
+        return wrapper
+
+    return inner
diff --git a/tangostationcontrol/tangostationcontrol/common/type_checking.py b/tangostationcontrol/tangostationcontrol/common/type_checking.py
index d70ee6b6c750aa570b7b45d0eb2d56f59036e3eb..9d4496743412bce99b9554060c4859f1f8d11012 100644
--- a/tangostationcontrol/tangostationcontrol/common/type_checking.py
+++ b/tangostationcontrol/tangostationcontrol/common/type_checking.py
@@ -1,7 +1,10 @@
 # Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
 
+from typing import Union
+
 from tango.utils import is_seq
+from tango import DeviceProxy
 
 
 def sequence_not_str(obj):
@@ -12,3 +15,25 @@ def sequence_not_str(obj):
 def type_not_sequence(obj):
     """True for types that are not sequences"""
     return not is_seq(obj) and isinstance(obj, type)
+
+
+def device_class_matches(
+    device: DeviceProxy, class_name: Union[str, list[str]]
+) -> bool:
+    """Returns whether the clas of the device represented by `device`
+    matches `class_name`, which is either a string or a list of strings."""
+
+    # NB: We'd like to use device.info().dev_class but that requires
+    # a connection to the device instead of just to the database.
+    # If the pattern does not match, the device might not be needed,
+    # so we should not assume the device is actually up.
+    #
+    # In production, they always should be up, but it grealy helps
+    # development and debugging if we can ignore devices that are
+    # in the hierarchy but not needed for the requested state
+    # transition.
+    device_class_name = device.dev_name().split("/")[1].casefold()
+    if isinstance(class_name, str):
+        return device_class_name == class_name.casefold()
+    else:
+        return device_class_name in (name.casefold() for name in class_name)
diff --git a/tangostationcontrol/tangostationcontrol/devices/antennafield.py b/tangostationcontrol/tangostationcontrol/devices/antennafield.py
index 61329acaea2d85be8cdc153cc744e254e61c3f48..e408613cb53a39f6b1c6b62ba10e8bbca29b348c 100644
--- a/tangostationcontrol/tangostationcontrol/devices/antennafield.py
+++ b/tangostationcontrol/tangostationcontrol/devices/antennafield.py
@@ -999,15 +999,25 @@ class AntennaField(LOFARDevice):
         self.__setup_recv_mapper()
         self.__setup_sdp_mapper()
 
-    @log_exceptions()
-    def _prepare_hardware(self):
+    def _power_hardware_on(self):
         # Configure the devices that process our antennas
         self.configure_recv()
         self.configure_sdp()
 
+    def _power_hardware_off(self):
+        # Save actual mask values
+        ANT_mask_RW = self.read_attribute("ANT_mask_RW")
+
+        # Enable controlling all antennas
+        self.proxy.write_attribute("ANT_mask_RW", [True] * len(ANT_mask_RW))
+
+        # Turn off power to all antennas
+        self.proxy.write_attribute("RCU_PWR_ANT_on_RW", [True] * len(ANT_mask_RW))
+
     # --------
     # Commands
     # --------
+
     @command()
     @only_in_states(DEFAULT_COMMAND_STATES)
     @DebugIt()
@@ -1016,8 +1026,6 @@ class AntennaField(LOFARDevice):
         """Configure RECV to process our antennas."""
 
         # Disable controlling the tiles that fall outside the mask
-        # WARN: Needed in configure_for_initialise but Tango does not allow to write
-        # attributes in INIT state
         self.proxy.write_attribute(
             "ANT_mask_RW", self.read_attribute("Antenna_Usage_Mask_R")
         )
diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py
index 0c37b0f077661c99907c4d123862111cb8019590..a0274ea5bdcb52194c40b07608662b239d698ec9 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apsct.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py
@@ -191,8 +191,12 @@ class APSCT(OPCUADevice):
     # overloaded functions
     # --------
 
-    def _prepare_hardware(self):
-        """Initialise the APSCT hardware."""
+    def _read_hardware_powered_R(self):
+        """Read attribute which monitors the power"""
+        return self.read_attribute("APSCT_PWR_on_R")
+
+    def _power_hardware_on(self):
+        """Turns on the 200MHz clock."""
 
         # Cycle clock
         self.APSCT_off()
@@ -204,6 +208,8 @@ class APSCT(OPCUADevice):
             "APSCTTR_translator_busy_R", False, self.APSCT_On_Off_timeout
         )
 
+        self.wait_attribute("APSCT_PWR_on_R", True, self.APSCT_On_Off_timeout)
+
         if not self.read_attribute("APSCT_PLL_200MHz_locked_R"):
             if self.read_attribute("APSCTTR_I2C_error_R"):
                 raise Exception(
@@ -216,8 +222,8 @@ class APSCT(OPCUADevice):
                     + "The subrack probably do not receive clock input or the CLK PCB is broken?"
                 )
 
-    def _disable_hardware(self):
-        """Disable the APSCT hardware."""
+    def _power_hardware_off(self):
+        """Turns off the clock."""
 
         # Turn off the APSCT
         self.APSCT_off()
@@ -225,19 +231,7 @@ class APSCT(OPCUADevice):
             "APSCTTR_translator_busy_R", False, self.APSCT_On_Off_timeout
         )
 
-    def _read_hardware_powered_R(self):
-        """Read attribute which monitors the power"""
-        return self.read_attribute("APSCT_PWR_on_R")
-
-    def _power_hardware_on(self):
-        """Power hardware on and check the correctness of the operation"""
-        # TODO() power on operations
-        self.wait_attribute("APSCT_PWR_on_R", True, timeout=5)
-
-    def _power_hardware_off(self):
-        """Power hardware off and check the correctness of the operation"""
-        # TODO() power off operations
-        self.wait_attribute("APSCT_PWR_on_R", False, timeout=5)
+        self.wait_attribute("APSCT_PWR_on_R", False, self.APSCT_On_Off_timeout)
 
     # --------
     # Commands
diff --git a/tangostationcontrol/tangostationcontrol/devices/apspu.py b/tangostationcontrol/tangostationcontrol/devices/apspu.py
index 7691077de046530b7cd8bb1fea0b66c6217677e2..590fd63b88c9663c863b92644f8fe35e15a9d242 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apspu.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apspu.py
@@ -144,10 +144,6 @@ class APSPU(OPCUADevice):
     # overloaded functions
     # --------
 
-    def _disable_hardware(self):
-        """Disable the APSPU hardware."""
-        super()._disable_hardware()
-
     # --------
     # Commands
     # --------
diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py
index d942e27dabbce020533b50875c776128f6df6101..2f86b812b1b36b0acf3721c6a10ee7e7904b9158 100644
--- a/tangostationcontrol/tangostationcontrol/devices/boot.py
+++ b/tangostationcontrol/tangostationcontrol/devices/boot.py
@@ -207,10 +207,9 @@ class DevicesInitialiser(object):
         proxy = self.devices[device_name]
 
         self.set_status(f"[restarting {device_name}] Booting device.")
+        proxy.boot()
         if self.initialise_hardware:
-            proxy.boot()
-        else:
-            proxy.warm_boot()
+            proxy.power_hardware_on()
 
         if proxy.state() not in OPERATIONAL_STATES:
             raise InitialisationException(
@@ -247,7 +246,7 @@ class Boot(LOFARDevice):
         dtype="DevVarStringArray",
         mandatory=False,
         default_value=[
-            "STAT/Docker/1",
+            # "STAT/Docker/1", # TODO(JDM): disabled as it doesn't work in gitlab CI/CD dind.
             # Docker controls the device containers, so it goes before anything else
             "STAT/Configuration/1",
             # Configuration device loads and update station configuration
diff --git a/tangostationcontrol/tangostationcontrol/devices/calibration.py b/tangostationcontrol/tangostationcontrol/devices/calibration.py
index bbfac52d214ec052eb26277eb40fe1dfe234bd63..d4cfe7fcd7867bdddb7687fdce348808904e1df7 100644
--- a/tangostationcontrol/tangostationcontrol/devices/calibration.py
+++ b/tangostationcontrol/tangostationcontrol/devices/calibration.py
@@ -41,7 +41,6 @@ class Calibration(LOFARDevice):
         self.sdpfirmware_proxies: dict = {}
         self.sdp_proxies: dict = {}
         self.ant_proxies: dict = {}
-        self.station_name: str = None
 
         # Super must be called after variable assignment due to executing init_device!
         super().__init__(cl, name)
@@ -98,6 +97,17 @@ class Calibration(LOFARDevice):
                 self.calibrate_sdp(k)
                 self.calibrate_recv(k)
 
+    # TODO(JDM): While we could read this from our control parent (StationManager),
+    # doing so creates a deadlock when StationManager wants to initialise this
+    # device. At which point this device cannot reach StationManager to read the
+    # station name.
+    Station_Name = device_property(
+        doc="Name of the station, f.e. CS001",
+        dtype="DevString",
+        mandatory=False,
+        default_value="DevStation",
+    )
+
     Calibration_Table_Base_URL = device_property(
         doc="Base URL of the calibration tables",
         dtype="DevString",
@@ -190,11 +200,8 @@ class Calibration(LOFARDevice):
     def configure_for_initialise(self):
         super().configure_for_initialise()
 
-        self.station_name = self.control.read_parent_attribute(
-            self.control.parent(), "station_name_R"
-        )
         self._calibration_manager = CalibrationManager(
-            self.Calibration_Table_Base_URL, self.station_name
+            self.Calibration_Table_Base_URL, self.Station_Name
         )
 
         db = Database()
diff --git a/tangostationcontrol/tangostationcontrol/devices/ccd.py b/tangostationcontrol/tangostationcontrol/devices/ccd.py
index 4170f52a22b28efef0535d55d9dfafabf5dc0c7c..88c587451ea3e29eda5b66d1750500d20a839d05 100644
--- a/tangostationcontrol/tangostationcontrol/devices/ccd.py
+++ b/tangostationcontrol/tangostationcontrol/devices/ccd.py
@@ -201,14 +201,18 @@ class CCD(OPCUADevice):
         return self.read_attribute("CCD_PWR_on_R")
 
     def _power_hardware_on(self):
-        """Power hardware on and check the correctness of the operation"""
-        # TODO() power on operations
-        self.wait_attribute("CCD_PWR_on_R", True, timeout=5)
+        """Forward the clock signal."""
+
+        # CCD is powered on by default when it
+        # starts up.
+        pass
 
     def _power_hardware_off(self):
-        """Power hardware off and check the correctness of the operation"""
-        # TODO() power off operations
-        self.wait_attribute("CCD_PWR_on_R", False, timeout=5)
+        """Stop forwarding the clock signal."""
+
+        # CCD does not have to be powered off
+        # during shutdown sequence.
+        pass
 
     # --------
     # Commands
diff --git a/tangostationcontrol/tangostationcontrol/devices/device_decorators.py b/tangostationcontrol/tangostationcontrol/devices/device_decorators.py
index c70fe7cede03deafa5642cf6a24c4480f5a70116..406e4b3ddac5fb09cbc6912987df385e2a20fd17 100644
--- a/tangostationcontrol/tangostationcontrol/devices/device_decorators.py
+++ b/tangostationcontrol/tangostationcontrol/devices/device_decorators.py
@@ -8,12 +8,14 @@ import logging
 import time
 from functools import wraps
 
-from tango import DevState, DevFailed
+from tango import DevState, DevFailed, DeviceProxy
+
+from tangostationcontrol.common.lofar_logging import exception_to_str
 
 logger = logging.getLogger()
 
 __all__ = [
-    "hierarchy_device_connected",
+    "suppress_exceptions",
     "only_in_states",
     "only_when_on",
     "fault_on_error",
@@ -21,22 +23,40 @@ __all__ = [
 ]
 
 
-def hierarchy_device_connected():
-    """
-    Wrapper to handle a DevFailed Exception with a HierarchyDevice operation
-    """
+def suppress_exceptions(suppress: bool = True):
+    """Catch all Exceptions and collect them in a list `exceptions` attached
+    to the decorated function. If suppress = True, do not reraise them.
+
+    Each element in the `exceptions` list is a (device, exception) tuple,
+    where "device" is the parameter to func(device) on which func
+    threw the exception.
+
+    Never supresses programming/deployment errors (SyntaxError, etc)."""
 
     def inner(func):
         @wraps(func)
-        def error_wrapper(self, *args, **kwargs):
+        def wrapper(device: DeviceProxy):
             try:
-                return func(self, *args, **kwargs)
-            except DevFailed as _e:
-                reason = _e.args[-1].desc.replace("\n", " ")
-                logger.error("Error in state transition: %s", reason)
+                return func(device)
+            except (SyntaxError, NameError, ImportError) as e:
+                # These exceptions we never suppress
                 raise
+            except Exception as e:
+                logger.exception(
+                    f"Collected exception during execution of {func.__name__}({device}): {exception_to_str(e)}"
+                )
 
-        return error_wrapper
+                # collect it
+                wrapper.exceptions.append((device, e))
+
+                # reraise if requested
+                if not suppress:
+                    raise
+
+        # collection of exceptions raised by this function
+        wrapper.exceptions = []
+
+        return wrapper
 
     return inner
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/ec.py b/tangostationcontrol/tangostationcontrol/devices/ec.py
index 165eaf745c233416ff9f54d13aa6ce162ecfe388..ecf59482efea4626dbc1708ac4ebd65f8175b305 100644
--- a/tangostationcontrol/tangostationcontrol/devices/ec.py
+++ b/tangostationcontrol/tangostationcontrol/devices/ec.py
@@ -111,6 +111,7 @@ class EC(OPCUADevice):
     # --------
     # overloaded functions
     # --------
+
     def _read_hardware_powered_R(self):
         # the device and translator are one, so if
         # the translator is reachable, the hardware
diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy.py
index 01a343525fb5427dda178f47f5216cfd99d8ef3b..7601487e5477f7ece2aaeffe38cd4a56dfc0e4b6 100644
--- a/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy.py
+++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy.py
@@ -147,7 +147,7 @@ class AbstractHierarchy(ABC):
         device = device.casefold()
 
         if not self._proxies.get(device):
-            self._proxies[device] = create_device_proxy(device, 10000)
+            self._proxies[device] = create_device_proxy(device, 30000)
 
         return self._proxies[device]
 
@@ -430,3 +430,23 @@ class AbstractHierarchy(ABC):
             return None
 
         return _parent.state()
+
+    def walk_down(self, func, depth: int = -1):
+        """Execute the given function on every node in the tree downwards from the root, depth first."""
+
+        def _walk(children, func):
+            for child in children.values():
+                func(child["proxy"])
+                _walk(child["children"], func)
+
+        _walk(self.children(depth), func)
+
+    def walk_up(self, func, depth: int = -1):
+        """Execute the given function on every node in the tree upwards from the leaves, depth first."""
+
+        def _walk(children, func):
+            for child in reversed(children.values()):
+                _walk(child["children"], func)
+                func(child["proxy"])
+
+        _walk(self.children(depth), func)
diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py
index 36f2cad95907072ce003d2e38b902aebb24b3a63..a197c966dd2a8b0d8b8cd1d5a78c8be81629a34d 100644
--- a/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py
@@ -29,8 +29,6 @@ from tangostationcontrol.common.lofar_logging import log_exceptions
 from tangostationcontrol.common.states import (
     DEFAULT_COMMAND_STATES,
     INITIALISED_STATES,
-    POWER_ON_COMMAND_STATES,
-    POWER_OFF_COMMAND_STATES,
 )
 from tangostationcontrol.common.proxy import create_device_proxy
 from tangostationcontrol.common.type_checking import sequence_not_str
@@ -66,9 +64,9 @@ class LOFARDevice(Device):
         OFF  -> INIT:       Triggered by device. Device will initialise (connect to hardware, other devices),
         INIT -> STANDBY:    Triggered by device. Device is initialised, and is ready for additional configuration by the user,
         STANDBY -> ON:      Triggered by user.   Device reports to be functional,
-        STANDBY -> DISABLE: Triggered by user.   Device has shut down its hardware. Triggered by the disable_hardware() command,
-        ON -> DISABLE:      Triggered by user.   Device has shut down its hardware. Triggered by the disable_hardware() command,
-        ALARM -> DISABLE:   Triggered by user.   Device has shut down its hardware. Triggered by the disable_hardware() command,
+        STANDBY -> DISABLE: Triggered by user.   Device has shut down its hardware. Triggered by the power_hardware_off() command,
+        ON -> DISABLE:      Triggered by user.   Device has shut down its hardware. Triggered by the power_hardware_off() command,
+        ALARM -> DISABLE:   Triggered by user.   Device has shut down its hardware. Triggered by the power_hardware_off() command,
         ON -> ALARM:        Triggered by tango.  Device has attribute(s) with value(s) exceeding their alarm treshold,
         * -> FAULT:         Triggered by device. Device has degraded to malfunctional, for example because the connection to the hardware is lost,
         * -> FAULT:         Triggered by user.   Emulate a forced malfunction for integration testing purposes,
@@ -431,8 +429,9 @@ class LOFARDevice(Device):
 
     def _read_hardware_powered_R(self):
         """Overloadable function called in read attribute hardware_powered_R"""
-        logger.warning("Attribute hardware_powered_R not implemented on this device.")
-        return False
+
+        # If device backs no hardware, assume it's powered
+        return True
 
     def _set_defaults_for(self, attribute_names: list):
         """Set hardware points to their default value for attribute_names.
@@ -515,28 +514,24 @@ class LOFARDevice(Device):
         """
         self._set_defaults()
 
-    @only_in_states(DEFAULT_COMMAND_STATES)
-    @fault_on_error()
-    @command()
-    def set_translator_defaults(self):
-        """Initialise the translator translators to their configured settings."""
+    def _set_translator_defaults(self):
+        """Initialise any translators to their default settings."""
 
-        # This is just the command version of _set_translator_defaults().
-        self._set_translator_defaults()
+        self._set_defaults_for(self.TRANSLATOR_DEFAULT_SETTINGS)
 
     @command()
     @DebugIt()
-    def prepare_hardware(self):
-        """Load firmware required before configuring anything."""
+    def power_hardware_on(self):
+        """Power the hardware related to the device."""
 
         # This is just the command version of _prepare_hardware().
-        self._prepare_hardware()
+        self._power_hardware_on()
 
     @only_in_states(INITIALISED_STATES)
     @fault_on_error()
     @command()
     @DebugIt()
-    def disable_hardware(self):
+    def power_hardware_off(self):
         """Disable the hardware related to the device."""
 
         if self.get_state() == DevState.DISABLE:
@@ -546,58 +541,28 @@ class LOFARDevice(Device):
             )
             return
 
-        self._disable_hardware()
+        self._power_hardware_off()
 
         # Set state to DISABLE
         self.set_state(DevState.DISABLE)
         self.set_status("Device is in the DISABLE state.")
 
-    @only_in_states(POWER_ON_COMMAND_STATES)
-    @command()
-    @DebugIt()
-    def power_hardware_on(self):
-        """Power on the hardware related to the device."""
-
-        if self.read_attribute("hardware_powered_R"):
-            # Already powered on
-            logger.warning(
-                "Requested to power on the hardware, but have already powered it on."
-            )
-            return
-
-        self._power_hardware_on()
-
-        logger.info("Device hardware is now powered on.")
-
-    @only_in_states(POWER_OFF_COMMAND_STATES)
+    @only_in_states(DEFAULT_COMMAND_STATES)
+    @fault_on_error()
     @command()
-    @DebugIt()
-    def power_hardware_off(self):
-        """Power off the hardware related to the device."""
-
-        if not self.read_attribute("hardware_powered_R"):
-            # Already powered off
-            logger.warning(
-                "Requested to power off the hardware, but have already powered it off."
-            )
-            return
-
-        self._power_hardware_off()
+    def set_translator_defaults(self):
+        """Initialise the translator translators to their configured settings."""
 
-        logger.info("Device hardware is now powered off.")
+        # This is just the command version of _set_translator_defaults().
+        self._set_translator_defaults()
 
-    def _boot(self, initialise_hardware=True):
+    def _boot(self):
         # setup connections
         self._Initialise()
 
+        # initialise settings
         self.set_translator_defaults()
-
-        if initialise_hardware:
-            # prepare hardware to accept settings
-            self._prepare_hardware()
-
-            # initialise settings
-            self._set_defaults()
+        self.set_defaults()
 
         # make device available
         self._On()
@@ -605,35 +570,17 @@ class LOFARDevice(Device):
     @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 _set_translator_defaults(self):
-        """Initialise any translators to their default settings."""
-
-        self._set_defaults_for(self.TRANSLATOR_DEFAULT_SETTINGS)
+        self._boot()
 
     @only_in_states(DEFAULT_COMMAND_STATES)
     @fault_on_error()
-    def _prepare_hardware(self):
+    def _power_hardware_on(self):
         """Override this method to load any firmware or power cycle components
         before configuring the hardware."""
         pass
 
-    def _disable_hardware(self):
-        """Override this method to disable any hardware related to the device."""
-        pass
-
-    def _power_hardware_on(self):
-        """Override this method to power on the hardware related to the device"""
-        pass
-
     def _power_hardware_off(self):
-        """Override this method to power off the hardware related to the device"""
+        """Override this method to disable any hardware related to the device."""
         pass
 
     def _read_attribute(self, attr_name):
diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py
index b711c14c63a35eaca6979fc50470279a1b04cea3..ccf8892e730447c1d38dfa66bf39b7df89903c49 100644
--- a/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py
+++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py
@@ -3,18 +3,20 @@
 
 """Power Hierarchy for PyTango devices"""
 
-from typing import Dict
-from typing import Optional
+from typing import Dict, Optional
 import logging
 
-from tango import DeviceProxy, DevState, DevFailed
+from tango import DeviceProxy, DevState
 
-from tangostationcontrol.common.proxy import create_device_proxy
+from tangostationcontrol.common.states import (
+    StationState,
+    run_if_device_on_in_station_state,
+)
+from tangostationcontrol.common.type_checking import device_class_matches
 from tangostationcontrol.devices.interfaces.hierarchy_device import (
     AbstractHierarchyDevice,
-    HierarchyMatchingFilter,
 )
-from tangostationcontrol.devices.device_decorators import hierarchy_device_connected
+from tangostationcontrol.devices.device_decorators import suppress_exceptions
 
 logger = logging.getLogger()
 
@@ -29,243 +31,201 @@ class PowerHierarchy(AbstractHierarchyDevice):
     def init(
         self,
         device_name: str,
+        continue_on_failure: bool = False,
         proxies: Optional[Dict[str, DeviceProxy]] = None,
     ):
         super().init(device_name, self.POWER_CHILD_PROPERTY, proxies)
 
-    def ensure_power(
-        self,
-        device: DeviceProxy,
-        timeout_millis: int = 10000,
-    ):
-        """Check if a device is correctly power connected
-
-        :param device: DeviceProxy to be checked
-        :param timeout_millis: Tango device timeout to be increased from its default 3000 ms
-        """
-        attr_name = "hardware_powered_R"
-
-        # Try to power on again if attribute indicates power off
-        try:
-            pwr_attr = device.read_attribute(attr_name)
-            if pwr_attr.value is False:
-                logger.warning(
-                    "Device %s has not powered correctly. Attribute %s has value %s",
-                    device.name(),
-                    pwr_attr.name,
-                    pwr_attr.value,
-                )
-
-                # Wait for power
-                device.set_timeout_millis(timeout_millis)
-                device.power_hardware_on()
-        except DevFailed as exc:
-            error_string = (
-                f"Device power attribute {device.name()}/{pwr_attr.name} "
-                f"is not responding"
-            )
-            logger.exception(error_string)
-            raise DevFailed(error_string) from exc
-        except Exception as exc:
-            logger.exception(str(exc))
-            raise Exception from exc
-
-        # Final check
-        if pwr_attr.value is False:
-            error_string = (
-                f"Something went wrong. "
-                f"Device {device.name()} has not powered correctly."
-            )
-            logger.exception(error_string)
-            raise Exception(error_string)
+        self.continue_on_failure = continue_on_failure
 
     def _boot_device(self, device: DeviceProxy):
         """Default sequence of device booting operations"""
-        # Device must be in OFF state
-        if device.state() != DevState.OFF:
-            logger.warning(
-                "Device %s must be in OFF state. Current state is %s",
-                device.name(),
-                device.state(),
-            )
+        if device.state() == DevState.ON:
+            logger.info(f"Booting {device}: Succesful: It's already ON?")
+            return
+
+        logger.info(f"Booting {device}: off()")
         device.off()
+        logger.info(f"Booting {device}: initialise()")
         device.initialise()
+        logger.info(f"Booting {device}: set_translator_defaults()")
         device.set_translator_defaults()
+        logger.info(f"Booting {device}: set_defaults()")
         device.set_defaults()
+        logger.info(f"Booting {device}: on()")
         device.on()
-        logger.info(
-            "Device %s has been succesfully booted into %s state",
-            device.name(),
-            device.state(),
-        )
+        logger.info(f"Booting {device}: Succesful: state={device.state()}")
+
+    def _shutdown_device(self, device: DeviceProxy):
+        """Default sequence of dervice turning-off operations"""
+        if device.state() == DevState.OFF:
+            logger.info(f"Shutting down {device}: Succesful: It's already OFF?")
+            return
+
+        logger.info(f"Shutting down {device}: off()")
+        device.off()
+        logger.info(f"Shutting down {device}: Succesful: state={device.state()}")
 
-    @hierarchy_device_connected()
     def off_to_hibernate(self):
         """Manage the device operations involved in the OFF -> HIBERNATE state transition.
         Only minimal hardware is powered."""
-        # Boot (each) PSOC
-        for psoc_dev_name in self.children_names(
-            "*/psoc/*", HierarchyMatchingFilter.Regex
-        ):
-            psoc_proxy = self.child(psoc_dev_name)
-            self._boot_device(psoc_proxy)
-            logger.info("Device %s is in %s state", psoc_dev_name, psoc_proxy.state())
-            # Turn on power to CCD device which is child of PSOC
-            assert psoc_proxy.state() == DevState.ON
-            psoc_proxy.socket_on("socket_1")  # connected to CCD
-        # Boot the CCD (To be removed: must be booted from control sequence)
-        for ccd_dev_name in self.children_names(
-            "*/ccd/*", HierarchyMatchingFilter.Regex
-        ):
-            ccd_proxy = self.child(ccd_dev_name)
-            self._boot_device(ccd_proxy)
-            logger.info("Device %s is in %s state", ccd_dev_name, ccd_proxy.state())
-            # Check if CCD is correctly powered on
-            try:
-                self.ensure_power(self.child(ccd_dev_name))
-            except Exception:
-                # Do not raise on Development phase
-                pass
-        # Boot the PCON (To be removed: must be booted from control sequence)
-        for pcon_dev_name in self.children_names(
-            "*/pcon/*", HierarchyMatchingFilter.Regex
-        ):
-            pcon_proxy = self.child(pcon_dev_name)
-            self._boot_device(pcon_proxy)
-            logger.info("Device %s is in %s state", pcon_dev_name, pcon_proxy.state())
-
-    @hierarchy_device_connected()
+
+        @suppress_exceptions(self.continue_on_failure)
+        @run_if_device_on_in_station_state(StationState.HIBERNATE)
+        def boot_to_hibernate(device: DeviceProxy):
+            # TODO(JDM): wait for CCDTR to be powered on before booting it?
+            self._boot_device(device)
+
+            # PSOC: Power on CCD
+            if device_class_matches(device, "PSOC"):
+                logger.info(f"Powering on {device}: CCD")
+                device.power_hardware_on()
+                logger.info(f"Powering on {device}: Succesful: CCD")
+
+            # CCD: Power on clock
+            if device_class_matches(device, "CCD"):
+                logger.info(f"Powering on {device}: Clock")
+                device.power_hardware_on()
+                logger.info(f"Powering on {device}: Succesful: Clock")
+
+        self._hierarchy.walk_down(boot_to_hibernate, -1)
+
+        # Return the suppressed exceptions
+        return boot_to_hibernate.exceptions
+
     def hibernate_to_standby(self):
         """Manage the device operations involved in the HIBERNATE -> STANDBY state transition.
         Powers hardware except antennas and firmware.
         """
-        # Retrieve PCON Device(s)
-        for pcon_dev_name in self.children_names(
-            "*/pcon/*", HierarchyMatchingFilter.Regex
-        ):
-            pcon_proxy = self.child(pcon_dev_name)
-            # PCON must be in ON state since the previous transition (OFF -> HIBERNATE)
-            assert pcon_proxy.state() == DevState.ON
-            # Turn ON power (48V) for each APS
-            # Turn ON APSPU
-            for apspu_dev_name in self.children_names(
-                "*/apspu/*", HierarchyMatchingFilter.Regex
-            ):
-                apspu_proxy = self.child(apspu_dev_name)
-                self._boot_device(apspu_proxy)
-                logger.info(
-                    "Device %s is in %s state",
-                    apspu_dev_name,
-                    apspu_proxy.state(),
-                )
-                # Turn ON children of APSPU (APSCT, UNB2, RCUs)
-                apspu_children = self.children(-1)[pcon_dev_name]["children"][
-                    apspu_dev_name
-                ]["children"]
-                for name, device in apspu_children.items():
-                    # boot all children
-                    self._boot_device(device["proxy"])
-                    logger.info(
-                        "Device %s is in %s state",
-                        name,
-                        device["proxy"].state(),
-                    )
-                    # Check if APSCT is correctly powered on
-                    if "apsct" in name.casefold():
-                        try:
-                            self.ensure_power(device["proxy"])
-                        except Exception:
-                            # Do not raise on Development phase
-                            pass
-                    # Check if UNB2 is correctly powered on
-                    elif "unb2" in name.casefold():
-                        try:
-                            self.ensure_power(device["proxy"])
-                        except Exception:
-                            # Do not raise on Development phase
-                            pass
-        # Retrieve PSOC Device(s)
-        for psoc_dev_name in self.children_names(
-            "*/psoc/*", HierarchyMatchingFilter.Regex
-        ):
-            psoc_proxy = self.child(psoc_dev_name)
-            # Turn ON power to each SDP Firmware
-            assert psoc_proxy.state() == DevState.ON
-            psoc_proxy.socket_on("socket_2")  # connected to SDP
-            for firmware_dev_name in self.children_names(
-                "*/sdpfirmware/*", HierarchyMatchingFilter.Regex
-            ):
-                sdpfirmware_proxy = self.child(firmware_dev_name)
-                self._boot_device(sdpfirmware_proxy)
-                # Flash factory image
-                sdpfirmware_proxy.use_factory_image()
-
-    @hierarchy_device_connected()
+
+        @suppress_exceptions(self.continue_on_failure)
+        @run_if_device_on_in_station_state(StationState.STANDBY)
+        def boot_to_standby(device: DeviceProxy):
+            self._boot_device(device)
+
+            # UNB2: Power on the Uniboards
+            if device_class_matches(device, "UNB2"):
+                logger.info(f"Powering on {device}: Uniboards")
+                device.power_hardware_on()
+                logger.info(f"Powering on {device}: Succesful: Uniboards")
+
+        self._hierarchy.walk_down(boot_to_standby, -1)
+
+        # Return the suppressed exceptions
+        return boot_to_standby.exceptions
+
     def standby_to_hibernate(self):
         """Manage the device operations involved in the STANDBY -> HIBERNATE state transition."""
-        # PSOC: Turn OFF power to each SDP Firmware
-        for firmware_dev_name in self.children_names(
-            "*/sdpfirmware/*", HierarchyMatchingFilter.Regex
-        ):
-            # TODO(Stefano): implement a better parent search when L2SS-1151 will be completed
-            firmware_ph = PowerHierarchy()
-            firmware_ph.init(firmware_dev_name)
-            sdpfirmware_proxy = self.child(firmware_dev_name)
-            sdpfirmware_proxy.off()
-            logger.info(
-                "Device %s in %s state", firmware_dev_name, sdpfirmware_proxy.state()
-            )
-            if firmware_ph.parent_state(firmware_ph.parent()) == DevState.ON:
-                _psoc_proxy = create_device_proxy(firmware_ph.parent())
-                _psoc_proxy.socket_off("socket_2")  # connected to SDP Firmware
-        # PCON: Turn OFF power to each APS and RCUs
-        for apspu_dev_name in self.children_names(
-            "*/apspu/*", HierarchyMatchingFilter.Regex
-        ):
-            apspu_ph = PowerHierarchy()
-            apspu_ph.init(apspu_dev_name)
-            for name, device in apspu_ph.children().items():
-                device["proxy"].off()
-                logger.info("Device %s in %s state", name, device["proxy"].state())
-            self.child(apspu_dev_name).off()
-
-    @hierarchy_device_connected()
+
+        @suppress_exceptions(self.continue_on_failure)
+        @run_if_device_on_in_station_state(StationState.STANDBY)
+        def power_off_from_standby(device: DeviceProxy):
+            self._shutdown_device(device)
+
+            # UNB2: Power off the Uniboards
+            if device_class_matches(device, "UNB2"):
+                logger.info(f"Powering off {device}: Uniboards")
+                device.power_hardware_off()
+                logger.info(f"Powering off {device}: Succesful: Uniboards")
+
+        self._hierarchy.walk_up(power_off_from_standby, -1)
+
+        # Return the suppressed exceptions
+        return power_off_from_standby.exceptions
+
     def standby_to_on(self):
         """Manage the device operations involved in the STANDBY -> ON state transition.
         Powers power-hungry devices (SDP, antennas).
         """
-        # Initialises SDP and powers RCUs.
-        for sdp_dev_name in self.children_names(
-            "*/sdp/*", HierarchyMatchingFilter.Regex
-        ):
-            sdp_proxy = self.child(sdp_dev_name)
-            self._boot_device(sdp_proxy)
-            logger.info("Device %s is in %s state", sdp_dev_name, sdp_proxy.state())
-        # Power Antennas ON
-        for antennafield_dev_name in self.children_names(
-            "*/antennafield/*", HierarchyMatchingFilter.Regex
-        ):
-            antennafield_proxy = self.child(antennafield_dev_name)
-            antennafield_proxy.set_timeout_millis(10000)
-            self._boot_device(antennafield_proxy)
-            logger.info(
-                "Device %s is in %s state",
-                antennafield_dev_name,
-                antennafield_proxy.state(),
-            )
-
-    @hierarchy_device_connected()
+
+        # first, power on additional (power-hungry) hardware
+        @suppress_exceptions(self.continue_on_failure)
+        def power_on(device: DeviceProxy):
+            # APSCT: Select 200 MHz clock
+            if device_class_matches(device, "APSCT"):
+                logger.info(f"Powering on {device}: 200MHz clock")
+                device.power_hardware_on()
+                logger.info(f"Powering on {device}: Succesful: 200MHz clock")
+
+            # RECV: Power on RCUs
+            if device_class_matches(device, ["RECVH", "RECVL"]):
+                logger.info(f"Powering on {device}: RCUs")
+                device.power_hardware_on()
+                logger.info(f"Powering on {device}: Succesful: RCUs")
+
+            # SDPFirmware: Flash user image
+            if device_class_matches(device, "SDPFirmware"):
+                logger.info(f"Powering on {device}: User image")
+                device.power_hardware_on()
+                logger.info(f"Powering on {device}: Succesful: User image")
+
+        self._hierarchy.walk_down(power_on, -1)
+
+        # now transition to on
+        @suppress_exceptions(self.continue_on_failure)
+        @run_if_device_on_in_station_state(StationState.ON)
+        def boot_to_on(device: DeviceProxy):
+            self._boot_device(device)
+
+        self._hierarchy.walk_down(boot_to_on, -1)
+
+        # power on antennas (now that AntennaField is booted)
+        @suppress_exceptions(self.continue_on_failure)
+        def power_antennas_on(device: DeviceProxy):
+            # AntennaField: Power on used antennas
+            if device_class_matches(device, "AntennaField"):
+                logger.info(f"Powering on {device}: Antennas")
+                device.power_hardware_on()
+                # TODO(JDM): Report which antennas
+                logger.info(f"Powering on {device}: Succesful: Antennas")
+
+        self._hierarchy.walk_down(power_antennas_on, -1)
+
+        # Return the suppressed exceptions
+        return (
+            power_on.exceptions + boot_to_on.exceptions + power_antennas_on.exceptions
+        )
+
     def on_to_standby(self):
         """Manage the device operations involved in the ON -> STANDBY state transition."""
-        # Antennafield: Power OFF Antennas
-        for antennafield_dev_name in self.children_names(
-            "*/antennafield/*", HierarchyMatchingFilter.Regex
-        ):
-            antennafield_proxy = self.child(antennafield_dev_name)
-            antennafield_proxy.off()
-        # Power OFF SDP
-        for sdp_dev_name in self.children_names(
-            "*/sdp/*", HierarchyMatchingFilter.Regex
-        ):
-            sdp_proxy = self.child(sdp_dev_name)
-            sdp_proxy.off()
+
+        # turn off software devices
+        @suppress_exceptions(self.continue_on_failure)
+        @run_if_device_on_in_station_state(StationState.ON)
+        def power_off_from_on(device: DeviceProxy):
+            # AntennaField: Power off all antennas
+            if device_class_matches(device, "AntennaField"):
+                logger.info(f"Powering off {device}: Antennas")
+                device.power_hardware_off()
+                # TODO(JDM): Report which antennas
+                logger.info(f"Powering off {device}: Succesful: Antennas")
+
+            self._shutdown_device(device)
+
+        self._hierarchy.walk_up(power_off_from_on, -1)
+
+        # now turn off power to power-hungry hardware
+        @suppress_exceptions(self.continue_on_failure)
+        def power_off(device: DeviceProxy):
+            # APSCT: Turn off clock
+            if device_class_matches(device, "APSCT"):
+                logger.info(f"Powering off {device}: Clock")
+                device.power_hardware_off()
+                logger.info(f"Powering off {device}: Succesful: Clock")
+
+            # RECV: Power off RCUs
+            if device_class_matches(device, ["RECVH", "RECVL"]):
+                logger.info(f"Powering off {device}: RCUs")
+                device.power_hardware_off()
+                logger.info(f"Powering off {device}: Succesful: RCUs")
+
+            # SDPFirmware: Flash factory image
+            if device_class_matches(device, "SDPFirmware"):
+                logger.info(f"Powering off {device}: Factory image")
+                device.power_hardware_off()
+                logger.info(f"Powering off {device}: Succesful: Factory image")
+
+        self._hierarchy.walk_up(power_off, -1)
+
+        # Return the suppressed exceptions
+        return power_off_from_on.exceptions + power_off.exceptions
diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py
index c1a44436c043a00cbcd3ad6d5f679907a51b4945..d7d04f454209ea09e29c6823e99293c4ec2a3019 100644
--- a/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py
@@ -383,8 +383,8 @@ class RECVDevice(OPCUADevice):
     # overloaded functions
     # --------
 
-    def _prepare_hardware(self):
-        """Initialise the RCU hardware."""
+    def _power_hardware_on(self):
+        """Power the RCUs."""
 
         # Cycle RCUs
         self.RCU_off()
@@ -392,8 +392,8 @@ class RECVDevice(OPCUADevice):
         self.RCU_on()
         self.wait_attribute("RECVTR_translator_busy_R", False, self.RCU_On_Off_timeout)
 
-    def _disable_hardware(self):
-        """Disable the RECV hardware."""
+    def _power_hardware_off(self):
+        """Turns off the RCUs."""
 
         # Save actual mask values
         RCU_mask = self.proxy.RCU_mask_RW
diff --git a/tangostationcontrol/tangostationcontrol/devices/psoc.py b/tangostationcontrol/tangostationcontrol/devices/psoc.py
index 08eabc9817b35013817062731808e7a622ac4a22..1104c68406d7497177e69f4e4b44c97660aa7d38 100644
--- a/tangostationcontrol/tangostationcontrol/devices/psoc.py
+++ b/tangostationcontrol/tangostationcontrol/devices/psoc.py
@@ -19,6 +19,8 @@ from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
 )
+from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
+from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.interfaces.snmp_device import SNMPDevice
 from tangostationcontrol.clients.snmp.attribute_classes import PSOC_sim
 
@@ -128,6 +130,16 @@ class PSOC(SNMPDevice):
         # write the correct value
         attr.write_function([socket_set])
 
+    def _power_hardware_on(self):
+        self.power_ccd_on()
+
+    def _power_hardware_off(self):
+        self.power_ccd_off()
+
+    # --------
+    # Commands
+    # --------
+
     @command(dtype_in=str)
     def socket_on(self, socket_name):
         """Turn socket on"""
@@ -159,6 +171,18 @@ class PSOC(SNMPDevice):
         # for whatever reason, the uptime is given in hundredts of a second
         return str(timedelta(seconds=uptime_attr.read_function()))
 
+    @command()
+    @only_in_states(DEFAULT_COMMAND_STATES)
+    def power_ccd_on(self):
+        """Turn on 230V to the CCD"""
+        self.socket_on("socket_1")
+
+    @command()
+    @only_in_states(DEFAULT_COMMAND_STATES)
+    def power_ccd_off(self):
+        """Turn off 230V to the CCD"""
+        self.socket_off("socket_1")
+
 
 # ----------
 # Run server
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
index 9bd1aef198e8d765aae0b858df7a6a49342005c9..2d86dbc55c11c46fd7dd5da020cdc2303c52709a 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
@@ -8,19 +8,16 @@
 import logging
 import numpy
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
-from tango import AttrWriteType, DebugIt
+from tango import AttrWriteType
 
 # PyTango imports
-from tango.server import device_property, attribute, command
+from tango.server import device_property, attribute
 
 # Additional import
 from tangostationcontrol.common.constants import N_pn
 from tangostationcontrol.common.entrypoint import entry
-from tangostationcontrol.common.lofar_logging import log_exceptions
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.devices.interfaces.opcua_device import OPCUADevice
-from tangostationcontrol.devices.device_decorators import only_in_states
-from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
 from tangostationcontrol.devices.types import DeviceTypes
 
 logger = logging.getLogger()
@@ -100,7 +97,8 @@ class SDPFirmware(OPCUADevice):
     # --------
     # overloaded functions
     # --------
-    def _prepare_hardware(self):
+
+    def _power_hardware_on(self):
         """Boot the SDP Firmware user image"""
         # FPGAs that are actually reachable and we care about
         wait_for = ~(
@@ -115,7 +113,7 @@ class SDPFirmware(OPCUADevice):
             "FPGA_boot_image_R", lambda attr: ((attr == 1) | ~wait_for).all(), 60
         )
 
-    def _disable_hardware(self):
+    def _power_hardware_off(self):
         """Use the SDP Firmware factory image"""
         # Save actual mask values
         TR_fpga_mask = self.proxy.TR_fpga_mask_RW
@@ -129,21 +127,6 @@ class SDPFirmware(OPCUADevice):
     # --------
     # Commands
     # --------
-    @command()
-    @only_in_states(DEFAULT_COMMAND_STATES)
-    @DebugIt()
-    @log_exceptions()
-    def use_user_image(self):
-        """Boot the SDP Firmware user image"""
-        self._prepare_hardware()
-
-    @command()
-    @only_in_states(DEFAULT_COMMAND_STATES)
-    @DebugIt()
-    @log_exceptions()
-    def use_factory_image(self):
-        """Use the SDP Firmware factory image"""
-        self._disable_hardware()
 
 
 # ----------
diff --git a/tangostationcontrol/tangostationcontrol/devices/station_manager.py b/tangostationcontrol/tangostationcontrol/devices/station_manager.py
index 0d1c8dfb8f6fb93c69c736885fe9da1a8e80955c..0ebcc01c9bbf9f76cddf9a5b10e6bed4d559d6f3 100644
--- a/tangostationcontrol/tangostationcontrol/devices/station_manager.py
+++ b/tangostationcontrol/tangostationcontrol/devices/station_manager.py
@@ -5,7 +5,6 @@
 
 """
 
-from enum import Enum
 import logging
 
 # pytango imports
@@ -15,7 +14,12 @@ from tango import DebugIt, DevState, DevFailed, Except
 # Additional import
 from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
+from tangostationcontrol.common.lofar_logging import exception_to_str
 from tangostationcontrol.common.lofar_logging import log_exceptions
+from tangostationcontrol.common.states import (
+    StationState,
+    ALLOWED_STATION_STATE_TRANSITIONS,
+)
 from tangostationcontrol.devices.interfaces.lofar_device import LOFARDevice
 from tangostationcontrol.devices.interfaces.power_hierarchy import PowerHierarchy
 
@@ -25,50 +29,60 @@ logger = logging.getLogger()
 __all__ = ["StationManager", "main"]
 
 
-class StationState(Enum):
-    """Station states enumeration"""
-
-    OFF = "OFF"
-    HIBERNATE = "HIBERNATE"
-    STANDBY = "STANDBY"
-    ON = "ON"
-
-
 @device_logging_to_python()
 class StationManager(LOFARDevice):
     """StationManager Device Server for LOFAR2.0"""
 
-    # Contains which transitions are allowed for a given states
-    ALLOWED_TRANSITIONS = {
-        StationState.OFF: [StationState.HIBERNATE],
-        StationState.HIBERNATE: [StationState.OFF, StationState.STANDBY],
-        StationState.STANDBY: [StationState.HIBERNATE, StationState.ON],
-        StationState.ON: [StationState.STANDBY],
-    }
-
     # -----------------
     # Device Properties
     # -----------------
 
     Station_Name = device_property(
-        dtype="DevString", mandatory=False, default_value="DevStation"
+        doc="Name of the station, f.e. CS001",
+        dtype="DevString",
+        mandatory=False,
+        default_value="DevStation",
     )
     Station_Number = device_property(
-        dtype="DevLong64", mandatory=False, default_value=999
+        doc="Number of the station, f.e. CS001 has number 1",
+        dtype="DevLong64",
+        mandatory=False,
+        default_value=999,
+    )
+    Suppress_State_Transition_Failures = device_property(
+        doc="Allow state transitions to continue even if a device fails to initialise.",
+        dtype="DevBoolean",
+        mandatory=False,
+        default_value=False,
     )
+
     # ----------
     # Attributes
     # ----------
-    station_name_R = attribute(dtype=str, fisallowed="is_attribute_access_allowed")
 
-    def read_station_name_R(self):
+    @attribute(dtype=str, fisallowed="is_attribute_access_allowed")
+    def station_name_R(self):
         return self.Station_Name
 
-    station_state_R = attribute(dtype=str, fisallowed="is_attribute_access_allowed")
-
-    def read_station_state_R(self):
+    @attribute(dtype=str, fisallowed="is_attribute_access_allowed")
+    def station_state_R(self):
         return self.station_state.name
 
+    @attribute(dtype=str, fisallowed="is_attribute_access_allowed")
+    def last_requested_transition_R(self):
+        return self.last_requested_transition or ""
+
+    @attribute(dtype=(str,), max_dim_x=1024, fisallowed="is_attribute_access_allowed")
+    def last_requested_transition_exceptions_R(self):
+        return [
+            f"{device}: {exception_to_str(ex)}"
+            for (device, ex) in self.last_requested_transition_exceptions
+        ][:1024]
+
+    @attribute(dtype=bool, fisallowed="is_attribute_access_allowed")
+    def last_requested_transition_ok_R(self):
+        return self.last_requested_transition_exceptions == []
+
     # --------
     # overloaded functions
     # --------
@@ -76,6 +90,8 @@ class StationManager(LOFARDevice):
     def __init__(self, cl, name):
         self.station_state = StationState.OFF
         self.stationmanager_ph = None
+        self.last_requested_transition = None
+        self.last_requested_transition_exceptions = []
 
         # Super must be called after variable assignment due to executing init_device!
         super().__init__(cl, name)
@@ -101,11 +117,13 @@ class StationManager(LOFARDevice):
         """Create and initialise the PowerHierarchy to manage the power sequence"""
         # create a power hierarchy device instance
         self.stationmanager_ph = PowerHierarchy()
-        self.stationmanager_ph.init(self.get_name())
+        self.stationmanager_ph.init(
+            self.get_name(), continue_on_failure=self.Suppress_State_Transition_Failures
+        )
 
     def _is_transition_allowed(self, to_state) -> bool:
         # get allowed transitions for the current state
-        allowed_transitions = self.ALLOWED_TRANSITIONS[self.station_state]
+        allowed_transitions = ALLOWED_STATION_STATE_TRANSITIONS[self.station_state]
 
         # check if the station is already in state it wants to go to
         if to_state == self.station_state:
@@ -128,68 +146,27 @@ class StationManager(LOFARDevice):
             # the requested state transition is allowed
             return True
 
-    def _off_to_hibernate(self):
-        """Manage the device operations involved in the OFF -> HIBERNATE state transition.
-        Only minimal hardware is powered.
-        """
+    def _transition(self, transition_desc: str, transition_func):
+        """Transition to a station state using `transition_func`."""
+
         # StationManager device must be in ON state
         if self.proxy.state() != DevState.ON:
             raise Exception(
                 f"Station Manager must be in ON state. Current state is {self.proxy.state()}"
             )
         # Power Sequence OFF -> HIBERNATE
-        self.stationmanager_ph.off_to_hibernate()
-        logger.info(
-            "Station %s has correctly completed the OFF->HIBERNATE Power Sequence",
-            self.proxy.station_name_R,
-        )
-
-    def _hibernate_to_off(self):
-        """Manage the device operations involved in the HIBERNATE -> OFF state transition.
-        Transition is currently not allowed.
-        """
-        # Not allowed in PowerSequence but it may be allowed for the other sequences
-        # TODO: functionality
-        return
-
-    def _hibernate_to_standby(self):
-        """Manage the device operations involved in the HIBERNATE -> STANDBY state transition.
-        Called on demand. Powers hardware except antennas and firmware.
-        """
-        # Power Sequence HIBERNATE -> STANDBY
-        self.stationmanager_ph.hibernate_to_standby()
-        logger.info(
-            "Station %s has correctly completed the HIBERNATE->STANDBY Power Sequence",
-            self.proxy.station_name_R,
-        )
-
-    def _standby_to_hibernate(self):
-        """Manage the device operations involved in the STANDBY -> HIBERNATE state transition."""
-        # Power Sequence STANDBY -> HIBERNATE
-        self.stationmanager_ph.standby_to_hibernate()
-        logger.info(
-            "Station %s has correctly completed the STANDBY->HIBERNATE Power Sequence",
-            self.proxy.station_name_R,
-        )
-
-    def _standby_to_on(self):
-        """Manage the device operations involved in the STANDBY -> ON state transition.
-        Called on demand. Powers power-hungry devices (SDP firmware, antennas, RCUs).
-        """
-        # Power Sequence STANDBY -> ON
-        self.stationmanager_ph.standby_to_on()
-        logger.info(
-            "Station %s has correctly completed the STANDBY->ON Power Sequence",
-            self.proxy.station_name_R,
-        )
+        try:
+            self.last_requested_transition = transition_desc
+            self.last_requested_transition_exceptions = transition_func()
+        except Exception as ex:
+            # unsuppressed exception
+            self.last_requested_transition_exceptions = [(None, ex)]
+            raise
 
-    def _on_to_standby(self):
-        """Manage the device operations involved in the ON -> STANDBY state transition."""
-        # Power Sequence ON -> STANDBY
-        self.stationmanager_ph.on_to_standby()
         logger.info(
-            "Station %s has correctly completed the STANDBY->HIBERNATE Power Sequence",
+            "Station %s has correctly completed the %s Power Sequence",
             self.proxy.station_name_R,
+            transition_desc,
         )
 
     # --------
@@ -208,8 +185,9 @@ class StationManager(LOFARDevice):
             raise Exception(f"Station did not transition to {StationState.OFF.name}")
 
         # call the correct state transition function
-        else:
-            self._hibernate_to_off()
+
+        # not implemented
+        pass
 
         # update the station_state variable when successful
         self.station_state = StationState.OFF
@@ -230,9 +208,13 @@ class StationManager(LOFARDevice):
         # call the correct state transition function
         try:
             if self.station_state == StationState.OFF:
-                self._off_to_hibernate()
+                self._transition(
+                    "OFF -> HIBERNATE", self.stationmanager_ph.off_to_hibernate
+                )
             elif self.station_state == StationState.STANDBY:
-                self._standby_to_hibernate()
+                self._transition(
+                    "STANDBY -> HIBERNATE", self.stationmanager_ph.standby_to_hibernate
+                )
         except DevFailed as exc:
             error_string = f"Station {self.proxy.station_name_R} \
                 did not transition to {StationState.HIBERNATE.name} state. \
@@ -259,9 +241,11 @@ class StationManager(LOFARDevice):
         # call the correct state transition function
         try:
             if self.station_state == StationState.HIBERNATE:
-                self._hibernate_to_standby()
+                self._transition(
+                    "HIBERNATE -> STANDBY", self.stationmanager_ph.hibernate_to_standby
+                )
             elif self.station_state == StationState.ON:
-                self._on_to_standby()
+                self._transition("ON -> STANDBY", self.stationmanager_ph.on_to_standby)
         except DevFailed as exc:
             error_string = f"Station {self.proxy.station_name_R} \
                 did not transition to {StationState.STANDBY.name} state. \
@@ -285,7 +269,7 @@ class StationManager(LOFARDevice):
 
         # call the correct state transition function
         try:
-            self._standby_to_on()
+            self._transition("STANDBY -> ON", self.stationmanager_ph.standby_to_on)
         except DevFailed as exc:
             error_string = f"Station {self.proxy.station_name_R} \
                 did not transition to {StationState.ON.name} state. \
diff --git a/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py b/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py
index bd006b767c49f9327327194cc0f207a4f9d78a4e..62de714a909f6dad62ff9d08da951ded8d4814d9 100644
--- a/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py
+++ b/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py
@@ -195,7 +195,7 @@ class TemperatureManager(LOFARDevice):
         for dev_name in self.Shutdown_Device_List:
             try:
                 proxy = create_device_proxy(dev_name)
-                proxy.disable_hardware()
+                proxy.power_hardware_off()
             except Exception as e:
                 logger.warning(
                     f"Automatic hardware shutdown of device {dev_name} has failed: {e.args[0]}"
diff --git a/tangostationcontrol/tangostationcontrol/devices/unb2.py b/tangostationcontrol/tangostationcontrol/devices/unb2.py
index 7e225969b620afe92e118efbdbf809065ebf81aa..44ef128b278bf0336b7d82631e4f756250a49521 100644
--- a/tangostationcontrol/tangostationcontrol/devices/unb2.py
+++ b/tangostationcontrol/tangostationcontrol/devices/unb2.py
@@ -406,8 +406,13 @@ class UNB2(OPCUADevice):
     # overloaded functions
     # --------
 
-    def _prepare_hardware(self):
-        """Initialise the UNB2 hardware."""
+    def _read_hardware_powered_R(self):
+        """Read attribute which monitors the power"""
+        # Return True if all uniboards are powered
+        return all(self.read_attribute("UNB2_PWR_on_R"))
+
+    def _power_hardware_on(self):
+        """Power the Uniboards."""
 
         # Cycle UNB2s
         self.UNB2_off()
@@ -415,8 +420,10 @@ class UNB2(OPCUADevice):
         self.UNB2_on()
         self.wait_attribute("UNB2TR_translator_busy_R", False, self.UNB2_On_Off_timeout)
 
-    def _disable_hardware(self):
-        """Disable the UNB2 hardware."""
+        self.wait_attribute("hardware_powered_R", True, self.UNB2_On_Off_timeout)
+
+    def _power_hardware_off(self):
+        """Disable the Uniboards."""
 
         # Save actual mask values
         UNB2_mask = self.proxy.UNB2_mask_RW
@@ -428,22 +435,6 @@ class UNB2(OPCUADevice):
         # Restore the mask
         self.UNB2_mask_RW = UNB2_mask
 
-    def _read_hardware_powered_R(self):
-        """Read attribute which monitors the power"""
-        # Return True if all uniboards are powered
-        for pwr_val in self.read_attribute("UNB2_PWR_on_R"):
-            if not pwr_val:
-                return False
-        return True
-
-    def _power_hardware_on(self):
-        """Power hardware on and check the correctness of the operation"""
-        # TODO() power on operations
-
-    def _power_hardware_off(self):
-        """Power hardware off and check the correctness of the operation"""
-        # TODO() power off operations
-
     # --------
     # Commands
     # --------
diff --git a/tangostationcontrol/test/devices/interfaces/test_hierarchy.py b/tangostationcontrol/test/devices/interfaces/test_hierarchy.py
index 1564eb1f820ae01ee2569ac953e6a6963aa08d7e..68f3ee04070f54798bcc13a25ebf92abe50bdd1b 100644
--- a/tangostationcontrol/test/devices/interfaces/test_hierarchy.py
+++ b/tangostationcontrol/test/devices/interfaces/test_hierarchy.py
@@ -6,9 +6,9 @@ import logging
 from typing import Callable
 from typing import Dict
 from typing import List
-from unittest.mock import Mock
+from unittest.mock import Mock, patch
 
-from tango import DevState
+from tango import DevState, DeviceProxy
 
 from tangostationcontrol.devices.interfaces import hierarchy
 
@@ -424,3 +424,70 @@ class TestAbstractHierarchy(device_base.DeviceTestCase):
             self.device_proxy_mock[
                 "object"
             ].return_value.get_property.side_effect = self.TEST_GET_PROPERTY_CALLS
+
+    class FakeDeviceProxy:
+        """A stateful fake to return the right values for the right device regardless of calling order."""
+
+        def __init__(self, name, timeout):
+            self.name = name
+
+        def dev_name(self):
+            return self.name
+
+        def get_property(self, prop_name):
+            children = {
+                "1": ["1.1", "1.2"],
+                "2": ["2.1", "2.2"],
+                "2.1": ["2.1.1"],
+            }
+            return {
+                TestAbstractHierarchy.TEST_PROPERTY_NAME: children.get(self.name, [])
+            }
+
+    @patch.object(hierarchy, "create_device_proxy", wraps=FakeDeviceProxy)
+    def test_walk_down(self, m_create_device_proxy):
+        """Test whether walking down the hierarchy tree (root -> leaves) works."""
+
+        test_hierarchy = TestAbstractHierarchy.ConcreteHierarchy(
+            self.TEST_PROPERTY_NAME, ["1", "2", "3"], None
+        )
+
+        def walker(device: DeviceProxy):
+            walk_order.append(device.dev_name())
+
+        # walk one level
+        walk_order = []
+        test_hierarchy.walk_down(walker, depth=1)
+        self.assertListEqual(["1", "2", "3"], walk_order)
+
+        # walk whole tree
+        walk_order = []
+        test_hierarchy.walk_down(walker, depth=-1)
+        self.assertListEqual(
+            ["1", "1.1", "1.2", "2", "2.1", "2.1.1", "2.2", "3"],
+            walk_order,
+        )
+
+    @patch.object(hierarchy, "create_device_proxy", wraps=FakeDeviceProxy)
+    def test_walk_up(self, m_create_device_proxy):
+        """Test whether walking up the hierarchy (leaves -> root) tree works."""
+
+        test_hierarchy = TestAbstractHierarchy.ConcreteHierarchy(
+            self.TEST_PROPERTY_NAME, ["1", "2", "3"], None
+        )
+
+        def walker(device: DeviceProxy):
+            walk_order.append(device.dev_name())
+
+        # walk one level
+        walk_order = []
+        test_hierarchy.walk_up(walker, depth=1)
+        self.assertListEqual(["3", "2", "1"], walk_order)
+
+        # walk whole tree
+        walk_order = []
+        test_hierarchy.walk_up(walker, depth=-1)
+        self.assertListEqual(
+            ["3", "2.2", "2.1.1", "2.1", "2", "1.2", "1.1", "1"],
+            walk_order,
+        )
diff --git a/tangostationcontrol/test/devices/interfaces/test_lofar_device.py b/tangostationcontrol/test/devices/interfaces/test_lofar_device.py
index 9fdcc2fb6b2830cdb17161ab160e77bbf4fd282e..1c5b21d2d9eb43d1e68134619d86064467ea95db 100644
--- a/tangostationcontrol/test/devices/interfaces/test_lofar_device.py
+++ b/tangostationcontrol/test/devices/interfaces/test_lofar_device.py
@@ -55,18 +55,18 @@ class TestLofarDevice(device_base.DeviceTestCase):
             self.assertEqual(DevState.STANDBY, proxy.state())
             proxy.on()
             self.assertEqual(DevState.ON, proxy.state())
-            proxy.disable_hardware()
+            proxy.power_hardware_off()
             self.assertEqual(DevState.DISABLE, proxy.state())
 
     def test_disable_state_transitions(self):
         with DeviceTestContext(self.test_device, process=False, timeout=10) as proxy:
             proxy.off()
             with self.assertRaises(DevFailed):
-                proxy.disable_hardware()
-            proxy.warm_boot()
+                proxy.power_hardware_off()
+            proxy.boot()
             proxy.Fault()
             with self.assertRaises(DevFailed):
-                proxy.disable_hardware()
+                proxy.power_hardware_off()
 
     def test_atomic_read_modify_write(self):
         """Test atomic read modify write for attribute"""