diff --git a/.release b/.release
index 65b19ebeead68eafd0f1ef0a3d5443f62b70dba2..79932ec25b045f4fa1a26bb7d214c401ff78790e 100644
--- a/.release
+++ b/.release
@@ -1,2 +1,2 @@
-release=0.9.1
-tag=ska_tango_base-0.9.1
+release=0.10.0
+tag=ska_tango_base-0.10.0
diff --git a/README.md b/README.md
index 2c9aa5de87321f4cc5fdc3970b864ce4342faff0..cc34052ba2f246dc5ba2b47ac31a700ffd0237fb 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,15 @@ The ska-tango-base repository includes a set of eight classes as mentioned in SK
 
 ## Version History
 
+#### 0.10.0
+- Add `DebugDevice` command to `SKABaseDevice`.  This allows remote debugging to be
+  enabled on all devices.  It cannot be disabled without restarting the process.
+  If there are multiple devices in a device server, debugging is only enabled for
+  the requested device (i.e., methods patched for debugging cppTango threads).
+  However, all Python threads (not cppTango threads), will also be debuggable,
+  even if created by devices other than the one that was used to enable debugging.
+  There is only one debugger instance shared by the whole process.
+  
 #### 0.9.1
 - Changed dependency from `ska_logging` to `ska_ser_logging`.
 
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 7f7c596a2beed92397246b80072796cdf30c7851..361c6f7493eddae7dff276f55c9957e6636776f8 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -56,10 +56,11 @@ _MockObject.__call__ = call_mock
 # hack end
 
 autodoc_mock_imports = [
+    'debugpy',
+    'numpy',
+    'ska_ser_logging',
     'tango',
     'transitions',
-    'ska_ser_logging',
-    'numpy',
 ]
 
 autodoc_default_options = {
diff --git a/pogo/CspSubElementMaster.xmi b/pogo/CspSubElementMaster.xmi
index ce1f323152605e7a52c54aa96805d639b5479634..c6dfe00a341fa364b2ce81eee038bdba4d885539 100644
--- a/pogo/CspSubElementMaster.xmi
+++ b/pogo/CspSubElementMaster.xmi
@@ -93,6 +93,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <commands name="LoadFirmware" description="Deploy new versions of software and firmware and &#xA;trigger a restart so that a Component initializes using a &#xA;newly deployed version." execMethod="load_firmware" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
       <argin description="The file name or a pointer to the filename , &#xA;the list of components that use software or firmware package (file),&#xA;checksum or signing">
         <type xsi:type="pogoDsl:StringArrayType"/>
diff --git a/pogo/CspSubElementObsDevice.xmi b/pogo/CspSubElementObsDevice.xmi
index b13e945a1c9d132521f4d9396c2fb16ff5fe818c..217ebab8475db969d99302381d65493270ff8267 100644
--- a/pogo/CspSubElementObsDevice.xmi
+++ b/pogo/CspSubElementObsDevice.xmi
@@ -66,6 +66,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <commands name="ConfigureScan" description="Configure the observing device parameters for the current scan," execMethod="configure_scan" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
       <argin description="JSON formatted string with the scan configuration.">
         <type xsi:type="pogoDsl:StringType"/>
diff --git a/pogo/CspSubElementSubarray.xmi b/pogo/CspSubElementSubarray.xmi
index fae1774ea21a158f13819d526fe227c4a17000bc..6b6b9b8233f4c6936734dda50c207300ee2f978b 100644
--- a/pogo/CspSubElementSubarray.xmi
+++ b/pogo/CspSubElementSubarray.xmi
@@ -132,6 +132,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <commands name="ObsReset" description="Reset observation state machine to its default state" execMethod="obs_reset" displayLevel="OPERATOR" polledPeriod="0">
       <argin description="">
         <type xsi:type="pogoDsl:VoidType"/>
diff --git a/pogo/SKAAlarmHandler.xmi b/pogo/SKAAlarmHandler.xmi
index 5e46b69d59ecc4ee8f8af98968cf56194bd2a88d..d66d34d008ab6022e95a9da57cd966b5d531e002 100644
--- a/pogo/SKAAlarmHandler.xmi
+++ b/pogo/SKAAlarmHandler.xmi
@@ -104,6 +104,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true" concreteHere="false"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <commands name="GetVersionInfo" description="Array of version strings of all entities modelled by this device. &#xA;(One level down only)&#xA;Each string in the array lists the version info for one entity&#xA;managed by this device. &#xA;The first entry is version info for this TANGO Device itself.&#xA;The entities may be TANGO devices, or hardware LRUs or &#xA;anything else this devices manages/models.&#xA;The intention with this command is that it can provide more &#xA;detailed information than can be captured in the versionId &#xA;and buildState attributes, if necessary.&#xA;In the minimal case the GetVersionInfo will contain only the &#xA;versionId and buildState attributes of the next lower level&#xA;entities." execMethod="get_version_info" displayLevel="OPERATOR" polledPeriod="0">
       <argin description="">
         <type xsi:type="pogoDsl:VoidType"/>
diff --git a/pogo/SKABaseDevice.xmi b/pogo/SKABaseDevice.xmi
index 2678ee5918496c035f69c983bfa0fc2b2a884617..dec232f5082926c656a64ab642c186dfe07d91ff 100644
--- a/pogo/SKABaseDevice.xmi
+++ b/pogo/SKABaseDevice.xmi
@@ -59,6 +59,15 @@
       </argout>
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
+    </commands>
     <attributes name="buildState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false">
       <dataType xsi:type="pogoDsl:StringType"/>
       <dataReadyEvent fire="false" libCheckCriteria="true"/>
diff --git a/pogo/SKACapability.xmi b/pogo/SKACapability.xmi
index 2a12b45be7b1ccd32ed2b4e9fe1f3e94f26b2ad1..824d079a622c53f84d072c48a429ef86ee566eae 100644
--- a/pogo/SKACapability.xmi
+++ b/pogo/SKACapability.xmi
@@ -81,6 +81,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <attributes name="activationTime" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false">
       <dataType xsi:type="pogoDsl:DoubleType"/>
       <changeEvent fire="false" libCheckCriteria="false"/>
diff --git a/pogo/SKALogger.xmi b/pogo/SKALogger.xmi
index d61abf2f20dd94cfa3d5f369f326c8a73cc0830a..b1531989dabd7b95b1accbe272f2b10c9ff5b63a 100644
--- a/pogo/SKALogger.xmi
+++ b/pogo/SKALogger.xmi
@@ -69,6 +69,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <attributes name="buildState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true">
       <dataType xsi:type="pogoDsl:StringType"/>
       <status abstract="false" inherited="true" concrete="true"/>
diff --git a/pogo/SKAMaster.xmi b/pogo/SKAMaster.xmi
index 2862a87a3f36ce3ca1de6d0d818fa2e325f4431c..8463630a2e3a6ebea32856698e3296e23632f93c 100644
--- a/pogo/SKAMaster.xmi
+++ b/pogo/SKAMaster.xmi
@@ -82,6 +82,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <attributes name="elementLoggerAddress" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false">
       <dataType xsi:type="pogoDsl:StringType"/>
       <changeEvent fire="false" libCheckCriteria="false"/>
diff --git a/pogo/SKAObsDevice.xmi b/pogo/SKAObsDevice.xmi
index ca0feba3a37b6f97eec259d79e7ce8fc02dbec93..d10090156d372e8032ac19abaf881c70b8a0fb35 100644
--- a/pogo/SKAObsDevice.xmi
+++ b/pogo/SKAObsDevice.xmi
@@ -60,6 +60,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <attributes name="obsState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false">
       <dataType xsi:type="pogoDsl:EnumType"/>
       <changeEvent fire="true" libCheckCriteria="true"/>
diff --git a/pogo/SKASubarray.xmi b/pogo/SKASubarray.xmi
index e5a277d64ece861a7040200061bec9e723a9cbf6..b5e5e06f3f1daf79a8816028b09d8c4a0cb295b3 100644
--- a/pogo/SKASubarray.xmi
+++ b/pogo/SKASubarray.xmi
@@ -131,6 +131,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true" concreteHere="false"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <commands name="ObsReset" description="Reset observation state machine to its default state" execMethod="obsreset" displayLevel="OPERATOR" polledPeriod="0">
       <argin description="">
         <type xsi:type="pogoDsl:VoidType"/>
diff --git a/pogo/SKATelState.xmi b/pogo/SKATelState.xmi
index 39cbae8303c738ca75d78a6e9ac3fcca8cba30d6..4c3e3142d043862a8fa27b2ebb57433150eaa47d 100644
--- a/pogo/SKATelState.xmi
+++ b/pogo/SKATelState.xmi
@@ -64,6 +64,15 @@
       </argout>
       <status abstract="false" inherited="true" concrete="true"/>
     </commands>
+    <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false">
+      <argin description="">
+        <type xsi:type="pogoDsl:VoidType"/>
+      </argin>
+      <argout description="TCP port debugger is listening on">
+        <type xsi:type="pogoDsl:UShortType"/>
+      </argout>
+      <status abstract="false" inherited="true" concrete="true"/>
+    </commands>
     <attributes name="buildState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true">
       <dataType xsi:type="pogoDsl:StringType"/>
       <status abstract="false" inherited="true" concrete="true"/>
diff --git a/setup.py b/setup.py
index 8a1b17553ac2e5d54f97ab95a1a0587a7a79172c..a4fb47cb92b9249d832b3cb59c4b5ceb4d1365cc 100644
--- a/setup.py
+++ b/setup.py
@@ -35,7 +35,7 @@ setuptools.setup(
     ],
     platforms=["OS Independent"],
     setup_requires=[] + pytest_runner,
-    install_requires=["transitions", "ska_ser_logging"],
+    install_requires=["debugpy", "transitions", "ska_ser_logging"],
     tests_require=["pytest", "coverage", "pytest-json-report", "pytest-forked"],
     entry_points={
         "console_scripts": [
diff --git a/src/ska_tango_base/base_device.py b/src/ska_tango_base/base_device.py
index b78fac15264bcd25956f8e38a14109c8310e10ea..008e1f8bcde97ffc8d4246678e10bb915da41a74 100644
--- a/src/ska_tango_base/base_device.py
+++ b/src/ska_tango_base/base_device.py
@@ -13,13 +13,16 @@ device.
 # PROTECTED REGION ID(SKABaseDevice.additionnal_import) ENABLED START #
 # Standard imports
 import enum
+import inspect
 import logging
 import logging.handlers
 import socket
 import sys
 import threading
+import typing
 import warnings
-from transitions import MachineError
+
+from functools import partial
 from urllib.parse import urlparse
 from urllib.request import url2pathname
 
@@ -28,6 +31,7 @@ from tango import AttrWriteType, DebugIt, DevState
 from tango.server import run, Device, attribute, command, device_property
 
 # SKA specific imports
+import debugpy
 import ska_ser_logging
 from ska_tango_base import release
 from ska_tango_base.commands import (
@@ -44,6 +48,7 @@ from ska_tango_base.utils import get_groups_from_json, for_testing_only
 from ska_tango_base.faults import GroupDefinitionsError, LoggingTargetError, LoggingLevelError
 
 LOG_FILE_SIZE = 1024 * 1024  # Log file size 1MB.
+_DEBUGGER_PORT = 5678
 
 
 class _Log4TangoLoggingLevel(enum.IntEnum):
@@ -558,6 +563,8 @@ class SKABaseDevice(Device):
     A generic base device for SKA.
     """
 
+    _global_debugger_listening = False
+
     class InitCommand(ActionCommand):
         """
         A class for the SKABaseDevice's init_device() "command".
@@ -611,6 +618,7 @@ class SKABaseDevice(Device):
                                                       release.version,
                                                       release.description)
             device._version_id = release.version
+            device._methods_patched_for_debugger = False
 
             try:
                 # create TANGO Groups dict, according to property
@@ -1002,6 +1010,7 @@ class SKABaseDevice(Device):
         self.register_command_object(
             "GetVersionInfo", self.GetVersionInfoCommand(*device_args)
         )
+        self.register_command_object("DebugDevice", self.DebugDeviceCommand(*device_args))
 
     def always_executed_hook(self):
         # PROTECTED REGION ID(SKABaseDevice.always_executed_hook) ENABLED START #
@@ -1629,6 +1638,112 @@ class SKABaseDevice(Device):
         (return_code, message) = command()
         return [[return_code], [message]]
 
+    class DebugDeviceCommand(BaseCommand):
+        """
+        A class for the SKABaseDevice's DebugDevice() command.
+        """
+
+        def do(self):
+            """
+            Stateless hook for device DebugDevice() command.
+
+            Starts the ``debugpy`` debugger listening for remote connections
+            (via Debugger Adaptor Protocol), and patches all methods so that
+            they can be debugged.
+
+            If the debugger is already listening, additional execution of this
+            command will trigger a breakpoint.
+
+            :return: The TCP port the debugger is listening on.
+            :rtype: DevUShort
+            """
+            if not SKABaseDevice._global_debugger_listening:
+                self.start_debugger(_DEBUGGER_PORT)
+                SKABaseDevice._global_debugger_listening = True
+            device = self.target
+            if not device._methods_patched_for_debugger:
+                self.monkey_patch_all_methods_for_debugger()
+                device._methods_patched_for_debugger = True
+            else:
+                self.logger.warning("Triggering debugger breakpoint...")
+                debugpy.breakpoint()
+            return _DEBUGGER_PORT
+
+        def start_debugger(self, port):
+            self.logger.warning("Starting debugger...")
+            debugpy.listen(("0.0.0.0", port))
+            self.logger.warning(
+                f"Debugger listening on port {port}. Performance may be degraded."
+            )
+
+        def monkey_patch_all_methods_for_debugger(self):
+            all_methods = self.get_all_methods()
+            patched = []
+            for owner, name, method in all_methods:
+                if self.method_must_be_patched_for_debugger(owner, method):
+                    self.patch_method_for_debugger(owner, name, method)
+                    patched.append(
+                        f"{owner} {method.__func__.__qualname__} in {method.__func__.__module__}"
+                    )
+            self.logger.info("Patched %s of %s methods", len(patched), len(all_methods))
+            self.logger.debug("Patched methods: %s", sorted(patched))
+
+        def get_all_methods(self):
+            methods = []
+            device = self.target
+            for name, method in inspect.getmembers(device, inspect.ismethod):
+                methods.append((device, name, method))
+            for command_object in device._command_objects.values():
+                for name, method in inspect.getmembers(command_object, inspect.ismethod):
+                    methods.append((command_object, name, method))
+            return methods
+
+        @staticmethod
+        def method_must_be_patched_for_debugger(owner, method):
+            """Determine if methods are worth debugging.
+
+            The goal is to find all the user's Python methods, but not the
+            lower level PyTango device and Boost extension methods.  The
+            `typing.types.FunctionType` check excludes the Boost methods.
+            """
+            skip_module_names = ["tango.device_server", "tango.server", "logging"]
+            skip_owner_types = [SKABaseDevice.DebugDeviceCommand]
+            return (
+                isinstance(method.__func__, typing.types.FunctionType)
+                and method.__func__.__module__ not in skip_module_names
+                and type(owner) not in skip_owner_types
+            )
+
+        def patch_method_for_debugger(self, owner, name, method):
+            """Ensure method calls trigger the debugger.
+
+            Most methods in a device are executed by calls from threads spawned
+            by the cppTango layer.  These threads are not known to Python, so
+            we have to explicitly inform the debugger about them.
+            """
+
+            def debug_thread_wrapper(orig_method, *args, **kwargs):
+                debugpy.debug_this_thread()
+                return orig_method(*args, **kwargs)
+
+            patched_method = partial(debug_thread_wrapper, method)
+            setattr(owner, name, patched_method)
+
+    @command(
+        dtype_out="DevUShort",
+        doc_out="The TCP port the debugger is listening on."
+    )
+    @DebugIt()
+    def DebugDevice(self):
+        """
+        Enables remote debugging of this device.
+
+        To modify behaviour for this command, modify the do() method of
+        the command class: :class:`.DebugDeviceCommand`.
+        """
+        command = self.get_command_object("DebugDevice")
+        return command()
+
 
 # ----------
 # Run server
diff --git a/src/ska_tango_base/release.py b/src/ska_tango_base/release.py
index a3970d425784bca074cc7697719b5167fabff71f..6e60c36d97062f11c2303d32e4d1f2b65093a31e 100644
--- a/src/ska_tango_base/release.py
+++ b/src/ska_tango_base/release.py
@@ -7,7 +7,7 @@
 """Release information for ska_tango_base Python Package"""
 
 name = """ska_tango_base"""
-version = "0.9.1"
+version = "0.10.0"
 version_info = version.split(".")
 description = """A set of generic base devices for SKA Telescope."""
 author = "SKA India and SARAO and CSIRO and INAF"
diff --git a/tests/test_alarm_handler_device.py b/tests/test_alarm_handler_device.py
index 9675262b3f14c19b0475c93377a904c13dc62853..3757d8b512f632be1ab005176fb98eb73abbba34 100644
--- a/tests/test_alarm_handler_device.py
+++ b/tests/test_alarm_handler_device.py
@@ -90,7 +90,7 @@ class TestSKAAlarmHandler(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(SKAAlarmHandler.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'SKAAlarmHandler, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'SKAAlarmHandler, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -142,7 +142,7 @@ class TestSKAAlarmHandler(object):
         """Test for buildState"""
         # PROTECTED REGION ID(SKAAlarmHandler.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  SKAAlarmHandler.test_buildState
@@ -152,7 +152,7 @@ class TestSKAAlarmHandler(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKAAlarmHandler.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  SKAAlarmHandler.test_versionId
 
diff --git a/tests/test_base_device.py b/tests/test_base_device.py
index 896f6e12eb6b2c24ffe88843aa066d5f2717ecde..5e79d03e92440e88dd63e2d0de5505a44f17bd0b 100644
--- a/tests/test_base_device.py
+++ b/tests/test_base_device.py
@@ -25,6 +25,7 @@ from ska_tango_base.control_model import (
     AdminMode, ControlMode, HealthState, LoggingLevel, SimulationMode, TestMode
 )
 from ska_tango_base.base_device import (
+    _DEBUGGER_PORT,
     _Log4TangoLoggingLevel,
     _PYTHON_TO_TANGO_LOGGING_LEVEL,
     LoggingUtils,
@@ -425,7 +426,7 @@ class TestSKABaseDevice(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(SKABaseDevice.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'SKABaseDevice, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'SKABaseDevice, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -508,7 +509,7 @@ class TestSKABaseDevice(object):
         """Test for buildState"""
         # PROTECTED REGION ID(SKABaseDevice.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  SKABaseDevice.test_buildState
@@ -518,7 +519,7 @@ class TestSKABaseDevice(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKABaseDevice.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  SKABaseDevice.test_versionId
 
@@ -649,6 +650,31 @@ class TestSKABaseDevice(object):
         assert tango_context.device.testMode == TestMode.NONE
         # PROTECTED REGION END #    //  SKABaseDevice.test_testMode
 
+    def test_debugger_not_listening_by_default(self, tango_context):
+        assert not SKABaseDevice._global_debugger_listening
+        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+            with pytest.raises(ConnectionRefusedError):
+                s.connect(("localhost", _DEBUGGER_PORT))
+
+    def test_DebugDevice_starts_listening(self, tango_context):
+        port = tango_context.device.DebugDevice()
+        assert port == _DEBUGGER_PORT
+        assert SKABaseDevice._global_debugger_listening
+        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+            s.connect(("localhost", _DEBUGGER_PORT))
+        assert tango_context.device.state
+
+    def test_DebugDevice_twice_does_not_raise(self, tango_context):
+        tango_context.device.DebugDevice()
+        tango_context.device.DebugDevice()
+        assert SKABaseDevice._global_debugger_listening
+
+    def test_DebugDevice_does_not_break_a_command(self, tango_context):
+        tango_context.device.DebugDevice()
+        assert tango_context.device.State() == DevState.OFF
+        tango_context.device.On()
+        assert tango_context.device.State() == DevState.ON
+
 
 class TestSKABaseDevice_commands:
     """
diff --git a/tests/test_capability_device.py b/tests/test_capability_device.py
index 6c9820931227d6d9a58c94b2411de481054e94a9..fdc287abcf734f3446f9121815054a933c47cdee 100644
--- a/tests/test_capability_device.py
+++ b/tests/test_capability_device.py
@@ -88,7 +88,7 @@ class TestSKACapability(object):
         """Test for buildState"""
         # PROTECTED REGION ID(SKACapability.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  SKACapability.test_buildState
@@ -98,7 +98,7 @@ class TestSKACapability(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKACapability.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  SKACapability.test_versionId
 
diff --git a/tests/test_csp_subelement_master.py b/tests/test_csp_subelement_master.py
index 6ffaf7eaaa1fdfbcd84799063d9da58632bb128d..694fb89360629784d7a769e189c3cd9eec07281a 100644
--- a/tests/test_csp_subelement_master.py
+++ b/tests/test_csp_subelement_master.py
@@ -75,7 +75,7 @@ class TestCspSubElementMaster(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(CspSubelementMaster.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'CspSubElementMaster, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'CspSubElementMaster, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -86,7 +86,7 @@ class TestCspSubElementMaster(object):
         """Test for buildState"""
         # PROTECTED REGION ID(CspSubelementMaster.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  CspSubelementMaster.test_buildState
@@ -96,7 +96,7 @@ class TestCspSubElementMaster(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(CspSubelementMaster.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  CspSubelementMaster.test_versionId
 
diff --git a/tests/test_csp_subelement_obsdevice.py b/tests/test_csp_subelement_obsdevice.py
index 971a7f44cf54a467d331c8e5d0f21f6fbd8a35e6..963b889b9e271396ed7d8d483faf8121a6b61d45 100644
--- a/tests/test_csp_subelement_obsdevice.py
+++ b/tests/test_csp_subelement_obsdevice.py
@@ -102,7 +102,7 @@ class TestCspSubElementObsDevice(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(CspSubelementObsDevice.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'CspSubElementObsDevice, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'CspSubElementObsDevice, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -114,7 +114,7 @@ class TestCspSubElementObsDevice(object):
         """Test for buildState"""
         # PROTECTED REGION ID(CspSubelementObsDevice.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  CspSubelementObsDevice.test_buildState
@@ -124,7 +124,7 @@ class TestCspSubElementObsDevice(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(CspSubelementObsDevice.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  CspSubelementObsDevice.test_versionId
 
diff --git a/tests/test_csp_subelement_subarray.py b/tests/test_csp_subelement_subarray.py
index 1418a88854ef8b74d4d00e2e1cdcbee6408e29cb..fd5ca072adba85855fcdbdf993d99dc1e05801a1 100644
--- a/tests/test_csp_subelement_subarray.py
+++ b/tests/test_csp_subelement_subarray.py
@@ -78,7 +78,7 @@ class TestCspSubElementSubarray(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(CspSubelementSubarray.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'CspSubElementSubarray, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'CspSubElementSubarray, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -89,7 +89,7 @@ class TestCspSubElementSubarray(object):
         """Test for buildState"""
         # PROTECTED REGION ID(CspSubelementSubarray.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  CspSubelementSubarray.test_buildState
@@ -99,7 +99,7 @@ class TestCspSubElementSubarray(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(CspSubelementSubarray.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  CspSubelementSubarray.test_versionId
 
diff --git a/tests/test_logger_device.py b/tests/test_logger_device.py
index 25692affbe73ffa587d768d41c4d3abf6647dae4..f035c46833979642f7f920b4dfe3cde98f76319a 100644
--- a/tests/test_logger_device.py
+++ b/tests/test_logger_device.py
@@ -76,7 +76,7 @@ class TestSKALogger(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(SKALogger.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r"SKALogger, ska_tango_base, [0-9].[0-9].[0-9], "
+            r"SKALogger, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, "
             r"A set of generic base devices for SKA Telescope."
         )
         versionInfo = tango_context.device.GetVersionInfo()
@@ -89,7 +89,7 @@ class TestSKALogger(object):
         """Test for buildState"""
         # PROTECTED REGION ID(SKALogger.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r"ska_tango_base, [0-9].[0-9].[0-9], "
+            r"ska_tango_base, [0-9]+.[0-9]+.[0-9]+, "
             r"A set of generic base devices for SKA Telescope"
         )
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
@@ -100,7 +100,7 @@ class TestSKALogger(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKALogger.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r"[0-9].[0-9].[0-9]")
+        versionIdPattern = re.compile(r"[0-9]+.[0-9]+.[0-9]+")
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  SKALogger.test_versionId
 
diff --git a/tests/test_master_device.py b/tests/test_master_device.py
index 30e3b7cf6315f8e2912cb09123c0a2d10286763c..a9c9b45d23064fcdbd3361c09eed096171a01ef7 100644
--- a/tests/test_master_device.py
+++ b/tests/test_master_device.py
@@ -69,7 +69,7 @@ class TestSKAMaster(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(SKAMaster.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'SKAMaster, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'SKAMaster, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -129,7 +129,7 @@ class TestSKAMaster(object):
         """Test for buildState"""
         # PROTECTED REGION ID(SKAMaster.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (
             re.match(buildPattern, tango_context.device.buildState)
@@ -141,7 +141,7 @@ class TestSKAMaster(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKAMaster.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (
             re.match(versionIdPattern, tango_context.device.versionId)
         ) is not None
diff --git a/tests/test_obs_device.py b/tests/test_obs_device.py
index d6d8cbfa570f6d128bcb0cbb5aad78c461321c5e..7a6c230309142c3f35c013c72efd9af05c041064 100644
--- a/tests/test_obs_device.py
+++ b/tests/test_obs_device.py
@@ -72,7 +72,7 @@ class TestSKAObsDevice(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(SKAObsDevice.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'SKAObsDevice, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'SKAObsDevice, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -121,7 +121,7 @@ class TestSKAObsDevice(object):
         """Test for buildState"""
         # PROTECTED REGION ID(SKAObsDevice.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  SKAObsDevice.test_buildState
@@ -131,7 +131,7 @@ class TestSKAObsDevice(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKAObsDevice.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  SKAObsDevice.test_versionId
 
diff --git a/tests/test_subarray_device.py b/tests/test_subarray_device.py
index 9c817363495653ef73b9f6974ef9e6a4eab7bb56..8a1c49aaa5c0c5c1dc6652aa012a519fb43d2224 100644
--- a/tests/test_subarray_device.py
+++ b/tests/test_subarray_device.py
@@ -146,7 +146,7 @@ class TestSKASubarray:
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(SKASubarray.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'SKASubarray, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'SKASubarray, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -359,7 +359,7 @@ class TestSKASubarray:
         """Test for buildState"""
         # PROTECTED REGION ID(SKASubarray.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  SKASubarray.test_buildState
@@ -433,7 +433,7 @@ class TestSKASubarray:
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKASubarray.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  SKASubarray.test_versionId
 
diff --git a/tests/test_tel_state_device.py b/tests/test_tel_state_device.py
index 0e3b719c59a9987c2abcc4142ee7761682737a23..6f3eb7d8d559318cc6e7230d5ddc24424c1230e5 100644
--- a/tests/test_tel_state_device.py
+++ b/tests/test_tel_state_device.py
@@ -66,7 +66,7 @@ class TestSKATelState(object):
         """Test for GetVersionInfo"""
         # PROTECTED REGION ID(SKATelState.test_GetVersionInfo) ENABLED START #
         versionPattern = re.compile(
-            r'SKATelState, ska_tango_base, [0-9].[0-9].[0-9], '
+            r'SKATelState, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope.')
         versionInfo = tango_context.device.GetVersionInfo()
         assert (re.match(versionPattern, versionInfo[0])) is not None
@@ -78,7 +78,7 @@ class TestSKATelState(object):
         """Test for buildState"""
         # PROTECTED REGION ID(SKATelState.test_buildState) ENABLED START #
         buildPattern = re.compile(
-            r'ska_tango_base, [0-9].[0-9].[0-9], '
+            r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, '
             r'A set of generic base devices for SKA Telescope')
         assert (re.match(buildPattern, tango_context.device.buildState)) is not None
         # PROTECTED REGION END #    //  SKATelState.test_buildState
@@ -88,7 +88,7 @@ class TestSKATelState(object):
     def test_versionId(self, tango_context):
         """Test for versionId"""
         # PROTECTED REGION ID(SKATelState.test_versionId) ENABLED START #
-        versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]')
+        versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+')
         assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None
         # PROTECTED REGION END #    //  SKATelState.test_versionId