diff --git a/Modules/include/StatusMonitor.h b/Modules/include/StatusMonitor.h
index dde8511f4bc0b7c7054660d50a72f7ecc328093b..e9548fc80add29668c88facde9684139a1b4946f 100644
--- a/Modules/include/StatusMonitor.h
+++ b/Modules/include/StatusMonitor.h
@@ -13,7 +13,6 @@
  *  - MinMonitor to monitor a value depending upon two MIN thresholds for warning and fault.
  *  - RangeMonitor to monitor a value depending upon two ranges of thresholds for warning and fault.
  *  - ExactMonitor to monitor a value which should be exactly same as required value.
- *  - StateMonitor to monitor On/Off state.
  * Depending upon the value and condition on of the four states are reported.
  *  -  OFF, OK, WARNING, FAULT.
  *
@@ -34,144 +33,105 @@ For more info see \ref statusmonitordoc
 #include "HierarchyModifyingGroup.h"
 #include "ScalarAccessor.h"
 #include "StatusAccessor.h"
-#include "VariableGroup.h"
 
 namespace ChimeraTK {
 
-  /** Common base for StatusMonitors
-   *
-   *  This holds common process variables that are not dependant on the
-   *  type of the variable to be monitored. A non-template base class
-   *  facilitates checking for the type in the StatusAggregator, which
-   *  needs to identify any StatusMonitor.
-   *
-   *  FIXME This distinction between StatusMonitorImpl and StatusMonitor is no longer required. Merge the classes!
-   */
-  struct StatusMonitor : ApplicationModule {
-    StatusMonitor(EntityOwner* owner, const std::string& name, const std::string& description, const std::string& input,
-        const std::string& output, HierarchyModifier modifier, const std::unordered_set<std::string>& outputTags = {},
-        const std::unordered_set<std::string>& parameterTags = {}, const std::unordered_set<std::string>& tags = {})
-    : ApplicationModule(owner, name, description, modifier, tags), _parameterTags(parameterTags), _input(input),
-      status(this, output, "", outputTags) {}
+  /*******************************************************************************************************************/
+  /* Declaration of MonitorBase **************************************************************************************/
+  /*******************************************************************************************************************/
+  struct MonitorBase : ApplicationModule {
+    // make constructors protected not to allow of instantiancion of this object - this is just a base class for other monitors
+   protected:
+    MonitorBase(EntityOwner* owner, const std::string& description, const std::string& outputPath,
+        const std::string& disablePath, const std::unordered_set<std::string>& outputTags = {},
+        const std::unordered_set<std::string>& parameterTags = {});
+
+    MonitorBase() = default;
+
+   public:
+    /** Disable/enable the entire status monitor */
+    ModifyHierarchy<ScalarPushInput<ChimeraTK::Boolean>> disable;
+    /** Result of the monitor */
+    ModifyHierarchy<StatusOutput> status;
 
-    StatusMonitor(StatusMonitor&&) = default;
+   protected:
+    DataValidity lastStatusValidity = DataValidity::ok;
+    void setStatus(StatusOutput::Status newStatus);
+  };
 
-    ~StatusMonitor() override {}
-    void prepare() override {}
+  /*******************************************************************************************************************/
+  /* Declaration of MaxMonitor ***************************************************************************************/
+  /*******************************************************************************************************************/
 
-    /**Tags for parameters. This makes it easier to connect them to e.g, to control system*/
-    std::unordered_set<std::string> _parameterTags;
+  /** Module for status monitoring depending on a maximum threshold value*/
+  template<typename T>
+  struct MaxMonitor : MonitorBase {
+    MaxMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+        const std::string& parameterPath, const std::string& description,
+        const std::unordered_set<std::string>& outputTags = {},
+        const std::unordered_set<std::string>& parameterTags = {});
 
-    const std::string _input;
+    MaxMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+        const std::string& warningThresholdPath, const std::string& faultThresholdPath, const std::string& disablePath,
+        const std::string& description, const std::unordered_set<std::string>& outputTags = {},
+        const std::unordered_set<std::string>& parameterTags = {});
 
-    /** Status to be reported */
-    StatusOutput status;
+    MaxMonitor() = default;
+
+    /** Variable to monitor */
+    ModifyHierarchy<ScalarPushInput<T>> watch;
+
+    /** WARNING state to be reported if threshold is reached or exceeded*/
+    ModifyHierarchy<ScalarPushInput<T>> warningThreshold;
+
+    /** FAULT state to be reported if threshold is reached or exceeded*/
+    ModifyHierarchy<ScalarPushInput<T>> faultThreshold;
 
     /** Disable the monitor. The status will always be OFF. You don't have to connect this input.
      *  When there is no feeder, ApplicationCore will connect it to a constant feeder with value 0, hence the monitor is
      * always enabled.
      */
-    ScalarPushInput<int> disable{this, "disable", "", "Disable the status monitor"};
+
+    /** This is where state evaluation is done */
+    void mainLoop();
   };
 
-  /** Common template base class for StatusMonitors
-   *
-   *  This provides a ScalarPushInput for the variable to be monitored, which
-   *  can be specified by the input parameter of the constructor.
-   */
+  /*******************************************************************************************************************/
+  /* Declaration of MinMonitor ***************************************************************************************/
+  /*******************************************************************************************************************/
+
+  /** Module for status monitoring depending on a minimum threshold value*/
   template<typename T>
-  struct StatusMonitorImpl : public StatusMonitor {
-    /** Number of convience constructors for ease of use.
-     * The input and output variable names can be given by user which
-     * should be mapped with the variables of module to be watched.
-     */
-    StatusMonitorImpl(EntityOwner* owner, const std::string& name, const std::string& description,
-        const std::string& input, const std::string& output, HierarchyModifier modifier,
+  struct MinMonitor : MonitorBase {
+    MinMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+        const std::string& parameterPath, const std::string& description,
         const std::unordered_set<std::string>& outputTags = {},
-        const std::unordered_set<std::string>& parameterTags = {}, const std::unordered_set<std::string>& tags = {})
-    : StatusMonitor(owner, name, description, input, output, modifier, outputTags, parameterTags, tags),
-      oneUp(this, input) {}
-
-    StatusMonitorImpl() { throw logic_error("Default constructor unusable. Just exists to work around gcc bug."); }
-    StatusMonitorImpl(StatusMonitorImpl&&) = default;
-
-    ~StatusMonitorImpl() override {}
-    void prepare() override {}
-
-    /**Input value that should be monitored. It is moved one level up, so it's parallel to this monitor object.*/
-    struct OneUp : public VariableGroup {
-      OneUp(EntityOwner* owner, const std::string& watchName)
-      : VariableGroup(owner, "hidden", "", HierarchyModifier::oneUpAndHide), watch(this, watchName, "", "") {}
-      ScalarPushInput<T> watch;
-    } oneUp;
-  };
+        const std::unordered_set<std::string>& parameterTags = {});
+
+    MinMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+        const std::string& warningThresholdPath, const std::string& faultThresholdPath, const std::string& disablePath,
+        const std::string& description, const std::unordered_set<std::string>& outputTags = {},
+        const std::unordered_set<std::string>& parameterTags = {});
+
+    MinMonitor() = default;
+
+    /** Variable to monitor */
+    ModifyHierarchy<ScalarPushInput<T>> watch;
 
-  /** Module for status monitoring depending on a maximum threshold value*/
-  template<typename T>
-  struct MaxMonitor : public StatusMonitorImpl<T> {
-    using StatusMonitorImpl<T>::StatusMonitorImpl;
     /** WARNING state to be reported if threshold is reached or exceeded*/
-    ScalarPushInput<T> warning{this, "upperWarningThreshold", "", "", StatusMonitor::_parameterTags};
+    ModifyHierarchy<ScalarPushInput<T>> warningThreshold;
+
     /** FAULT state to be reported if threshold is reached or exceeded*/
-    ScalarPushInput<T> fault{this, "upperFaultThreshold", "", "", StatusMonitor::_parameterTags};
-
-    /**This is where state evaluation is done*/
-    void mainLoop() {
-      /** If there is a change either in value monitored or in thershold values, the status is re-evaluated*/
-      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, warning, fault};
-      while(true) {
-        // evaluate and publish first, then read and wait. This takes care of the publishing the initial variables
-        if(StatusMonitorImpl<T>::disable != 0) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::OFF;
-        }
-        else if(StatusMonitorImpl<T>::oneUp.watch >= fault) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::FAULT;
-        }
-        else if(StatusMonitorImpl<T>::oneUp.watch >= warning) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::WARNING;
-        }
-        else {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::OK;
-        }
-        StatusMonitorImpl<T>::status.write();
-        group.readAny();
-      }
-    }
-  };
+    ModifyHierarchy<ScalarPushInput<T>> faultThreshold;
 
-  /** Module for status monitoring depending on a minimum threshold value*/
-  template<typename T>
-  struct MinMonitor : public StatusMonitorImpl<T> {
-    using StatusMonitorImpl<T>::StatusMonitorImpl;
-
-    /** WARNING state to be reported if threshold is crossed*/
-    ScalarPushInput<T> warning{this, "lowerWarningThreshold", "", "", StatusMonitor::_parameterTags};
-    /** FAULT state to be reported if threshold is crossed*/
-    ScalarPushInput<T> fault{this, "lowerFaultThreshold", "", "", StatusMonitor::_parameterTags};
-
-    /**This is where state evaluation is done*/
-    void mainLoop() {
-      /** If there is a change either in value monitored or in thershold values, the status is re-evaluated*/
-      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, warning, fault};
-      while(true) {
-        if(StatusMonitorImpl<T>::disable != 0) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::OFF;
-        }
-        else if(StatusMonitorImpl<T>::oneUp.watch <= fault) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::FAULT;
-        }
-        else if(StatusMonitorImpl<T>::oneUp.watch <= warning) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::WARNING;
-        }
-        else {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::OK;
-        }
-        StatusMonitorImpl<T>::status.write();
-        group.readAny();
-      }
-    }
+    /** This is where state evaluation is done */
+    void mainLoop();
   };
 
+  /*******************************************************************************************************************/
+  /* Declaration of RangeMonitor *************************************************************************************/
+  /*******************************************************************************************************************/
+
   /** Module for status monitoring depending on range of threshold values.
    * As long as a monitored value is in the range defined by user it goes
    * to fault or warning state. If the monitored value exceeds the upper limmit
@@ -180,48 +140,44 @@ namespace ChimeraTK {
    * set the ranges correctly to issue warning or fault.
    */
   template<typename T>
-  struct RangeMonitor : public StatusMonitorImpl<T> {
-    using StatusMonitorImpl<T>::StatusMonitorImpl;
+  struct RangeMonitor : MonitorBase {
+    RangeMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+        const std::string& parameterPath, const std::string& description,
+        const std::unordered_set<std::string>& outputTags = {},
+        const std::unordered_set<std::string>& parameterTags = {});
+
+    RangeMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+        const std::string& warningLowerThresholdPath, const std::string& warningUpperThresholdPath,
+        const std::string& faultLowerThresholdPath, const std::string& faultUpperThresholdPath,
+        const std::string& disablePath, const std::string& description,
+        const std::unordered_set<std::string>& outputTags = {},
+        const std::unordered_set<std::string>& parameterTags = {});
+
+    RangeMonitor() = default;
+
+    /** Variable to monitor */
+    ModifyHierarchy<ScalarPushInput<T>> watch;
 
     /** WARNING state to be reported if value is in between the upper and
      * lower threshold including the start and end of thresholds.
      */
-    ScalarPushInput<T> warningUpperThreshold{this, "upperWarningThreshold", "", "", StatusMonitor::_parameterTags};
-    ScalarPushInput<T> warningLowerThreshold{this, "lowerWarningThreshold", "", "", StatusMonitor::_parameterTags};
+    ModifyHierarchy<ScalarPushInput<T>> warningLowerThreshold;
+    ModifyHierarchy<ScalarPushInput<T>> warningUpperThreshold;
+
     /** FAULT state to be reported if value is in between the upper and
      * lower threshold including the start and end of thresholds.
      */
-    ScalarPushInput<T> faultUpperThreshold{this, "upperFaultThreshold", "", "", StatusMonitor::_parameterTags};
-    ScalarPushInput<T> faultLowerThreshold{this, "lowerFaultThreshold", "", "", StatusMonitor::_parameterTags};
-
-    /**This is where state evaluation is done*/
-    void mainLoop() {
-      /** If there is a change either in value monitored or in thershold values, the status is re-evaluated*/
-      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, warningUpperThreshold,
-          warningLowerThreshold, faultUpperThreshold, faultLowerThreshold};
-      while(true) {
-        if(StatusMonitorImpl<T>::disable != 0) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::OFF;
-        }
-        // Check for fault limits first. Like this they supersede the warning,
-        // even if they are stricter then the warning limits (mis-configuration)
-        else if(StatusMonitorImpl<T>::oneUp.watch <= faultLowerThreshold ||
-            StatusMonitorImpl<T>::oneUp.watch >= faultUpperThreshold) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::FAULT;
-        }
-        else if(StatusMonitorImpl<T>::oneUp.watch <= warningLowerThreshold ||
-            StatusMonitorImpl<T>::oneUp.watch >= warningUpperThreshold) {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::WARNING;
-        }
-        else {
-          StatusMonitorImpl<T>::status = StatusOutput::Status::OK;
-        }
-        StatusMonitorImpl<T>::status.write();
-        group.readAny();
-      }
-    }
+    ModifyHierarchy<ScalarPushInput<T>> faultLowerThreshold;
+    ModifyHierarchy<ScalarPushInput<T>> faultUpperThreshold;
+
+    /** This is where state evaluation is done */
+    void mainLoop();
   };
 
+  /*******************************************************************************************************************/
+  /* Declaration of ExactMonitor *************************************************************************************/
+  /*******************************************************************************************************************/
+
   /**
    *  Module for status monitoring of an exact value.
    *
@@ -233,7 +189,7 @@ namespace ChimeraTK {
    *  data types should never be compared with exact equality.
    */
   template<typename T>
-  struct ExactMonitor : ApplicationModule {
+  struct ExactMonitor : MonitorBase {
     /**
      *  Constructor for exact monitoring module.
      *
@@ -247,9 +203,7 @@ namespace ChimeraTK {
     ExactMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
         const std::string& parameterPath, const std::string& description,
         const std::unordered_set<std::string>& outputTags = {},
-        const std::unordered_set<std::string>& parameterTags = {})
-    : ExactMonitor(owner, inputPath, outputPath, parameterPath + "/requiredValue", parameterPath + "/disable",
-          description, outputTags, parameterTags) {}
+        const std::unordered_set<std::string>& parameterTags = {});
 
     /**
      *  Constructor for exact monitoring module.
@@ -265,12 +219,7 @@ namespace ChimeraTK {
     ExactMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
         const std::string& requiredValuePath, const std::string& disablePath, const std::string& description,
         const std::unordered_set<std::string>& outputTags = {},
-        const std::unordered_set<std::string>& parameterTags = {})
-    : ApplicationModule(owner, "hidden", description, HierarchyModifier::hideThis),
-      watch(this, inputPath, "", "Value to monitor"),
-      requiredValue(this, requiredValuePath, "", "Value to compare with", parameterTags),
-      disable(this, disablePath, "", "Disable the status monitor", parameterTags),
-      status(this, outputPath, "Resulting status", outputTags) {}
+        const std::unordered_set<std::string>& parameterTags = {});
 
     ExactMonitor() = default;
 
@@ -280,41 +229,212 @@ namespace ChimeraTK {
     /** The required value to compare with */
     ModifyHierarchy<ScalarPushInput<T>> requiredValue;
 
-    /** Disable/enable the entire status monitor */
-    ModifyHierarchy<ScalarPushInput<int>> disable;
+    /** This is where state evaluation is done */
+    void mainLoop();
+  };
 
-    /** Result of the monitor */
-    ModifyHierarchy<StatusOutput> status;
+  /*******************************************************************************************************************/
+  /* Implementation starts here **************************************************************************************/
+  /*******************************************************************************************************************/
+
+  /*******************************************************************************************************************/
+  /* Implementation of MonitorBase ***********************************************************************************/
+  /*******************************************************************************************************************/
+  MonitorBase::MonitorBase(EntityOwner* owner, const std::string& description, const std::string& outputPath,
+      const std::string& disablePath, const std::unordered_set<std::string>& outputTags,
+      const std::unordered_set<std::string>& parameterTags)
+  : ApplicationModule(owner, "hidden", description, HierarchyModifier::hideThis),
+    disable(this, disablePath, "", "Disable the status monitor", parameterTags),
+    status(this, outputPath, "Resulting status", outputTags) {}
+
+  /*******************************************************************************************************************/
+  void MonitorBase::setStatus(StatusOutput::Status newStatus) {
+    // update only if status has changed, but always in case of initial value
+    if(status.value != newStatus || getDataValidity() != lastStatusValidity ||
+        status.value.getVersionNumber() == VersionNumber{nullptr}) {
+      status.value = newStatus;
+      status.value.write();
+      lastStatusValidity = getDataValidity();
+    }
+  }
 
-    /** This is where state evaluation is done */
-    void mainLoop() {
-      // If there is a change either in value monitored or in requiredValue, the status is re-evaluated
-      ReadAnyGroup group{watch.value, disable.value, requiredValue.value};
-
-      DataValidity lastStatusValidity = DataValidity::ok;
-
-      while(true) {
-        StatusOutput::Status newStatus;
-        if(disable.value != 0) {
-          newStatus = StatusOutput::Status::OFF;
-        }
-        else if(watch.value != requiredValue.value) {
-          newStatus = StatusOutput::Status::FAULT;
-        }
-        else {
-          newStatus = StatusOutput::Status::OK;
-        }
-
-        // update only if status has changed, but always in case of initial value
-        if(status.value != newStatus || getDataValidity() != lastStatusValidity ||
-            status.value.getVersionNumber() == VersionNumber{nullptr}) {
-          status.value = newStatus;
-          status.value.write();
-          lastStatusValidity = getDataValidity();
-        }
-        group.readAny();
+  /*******************************************************************************************************************/
+  /* Implementation of MaxMonitor ************************************************************************************/
+  /*******************************************************************************************************************/
+  template<typename T>
+  MaxMonitor<T>::MaxMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& parameterPath, const std::string& description,
+      const std::unordered_set<std::string>& outputTags, const std::unordered_set<std::string>& parameterTags)
+  : MaxMonitor(owner, inputPath, outputPath, parameterPath + "/upperWarningThreshold",
+        parameterPath + "/upperFaultThreshold", parameterPath + "/disable", description, outputTags, parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  MaxMonitor<T>::MaxMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& warningThresholdPath, const std::string& faultThresholdPath, const std::string& disablePath,
+      const std::string& description, const std::unordered_set<std::string>& outputTags,
+      const std::unordered_set<std::string>& parameterTags)
+  : MonitorBase(owner, description, outputPath, disablePath, outputTags, parameterTags),
+    watch(this, inputPath, "", "Value to monitor"),
+    warningThreshold(this, warningThresholdPath, "", "Warning threshold to compare with", parameterTags),
+    faultThreshold(this, faultThresholdPath, "", "Fault threshold to compare with", parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  void MaxMonitor<T>::mainLoop() {
+    // If there is a change either in value monitored or in requiredValue, the status is re-evaluated
+    ReadAnyGroup group{watch.value, disable.value, warningThreshold.value, faultThreshold.value};
+
+    while(true) {
+      if(disable.value) {
+        setStatus(StatusOutput::Status::OFF);
+      }
+      else if(watch.value >= faultThreshold.value) {
+        setStatus(StatusOutput::Status::FAULT);
+      }
+      else if(watch.value >= warningThreshold.value) {
+        setStatus(StatusOutput::Status::WARNING);
       }
+      else {
+        setStatus(StatusOutput::Status::OK);
+      }
+      group.readAny();
     }
-  };
+  }
+
+  /*******************************************************************************************************************/
+  /* Implementation of MinMonitor ************************************************************************************/
+  /*******************************************************************************************************************/
+  template<typename T>
+  MinMonitor<T>::MinMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& parameterPath, const std::string& description,
+      const std::unordered_set<std::string>& outputTags, const std::unordered_set<std::string>& parameterTags)
+  : MinMonitor(owner, inputPath, outputPath, parameterPath + "/lowerWarningThreshold",
+        parameterPath + "/lowerFaultThreshold", parameterPath + "/disable", description, outputTags, parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  MinMonitor<T>::MinMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& warningThresholdPath, const std::string& faultThresholdPath, const std::string& disablePath,
+      const std::string& description, const std::unordered_set<std::string>& outputTags,
+      const std::unordered_set<std::string>& parameterTags)
+  : MonitorBase(owner, description, outputPath, disablePath, outputTags, parameterTags),
+    watch(this, inputPath, "", "Value to monitor"),
+    warningThreshold(this, warningThresholdPath, "", "Warning threshold to compare with", parameterTags),
+    faultThreshold(this, faultThresholdPath, "", "Fault threshold to compare with", parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  void MinMonitor<T>::mainLoop() {
+    // If there is a change either in value monitored or in requiredValue, the status is re-evaluated
+    ReadAnyGroup group{watch.value, disable.value, warningThreshold.value, faultThreshold.value};
+
+    while(true) {
+      if(disable.value) {
+        setStatus(StatusOutput::Status::OFF);
+      }
+      else if(watch.value <= faultThreshold.value) {
+        setStatus(StatusOutput::Status::FAULT);
+      }
+      else if(watch.value <= warningThreshold.value) {
+        setStatus(StatusOutput::Status::WARNING);
+      }
+      else {
+        setStatus(StatusOutput::Status::OK);
+      }
+      group.readAny();
+    }
+  }
+
+  /*******************************************************************************************************************/
+  /* Implementation of RangeMonitor **********************************************************************************/
+  /*******************************************************************************************************************/
+  template<typename T>
+  RangeMonitor<T>::RangeMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& parameterPath, const std::string& description,
+      const std::unordered_set<std::string>& outputTags, const std::unordered_set<std::string>& parameterTags)
+  : RangeMonitor(owner, inputPath, outputPath, parameterPath + "/lowerWarningThreshold",
+        parameterPath + "/upperWarningThreshold", parameterPath + "/lowerFaultThreshold",
+        parameterPath + "/upperFaultThreshold", parameterPath + "/disable", description, outputTags, parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  RangeMonitor<T>::RangeMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& warningLowerThresholdPath, const std::string& warningUpperThresholdPath,
+      const std::string& faultLowerThresholdPath, const std::string& faultUpperThresholdPath,
+      const std::string& disablePath, const std::string& description, const std::unordered_set<std::string>& outputTags,
+      const std::unordered_set<std::string>& parameterTags)
+  : MonitorBase(owner, description, outputPath, disablePath, outputTags, parameterTags),
+    watch(this, inputPath, "", "Value to monitor"), warningLowerThreshold(this, warningLowerThresholdPath, "",
+                                                        "Lower warning threshold to compare with", parameterTags),
+    warningUpperThreshold(
+        this, warningUpperThresholdPath, "", "Upper warning threshold to compare with", parameterTags),
+    faultLowerThreshold(this, faultLowerThresholdPath, "", "Lower fault threshold to compare with", parameterTags),
+    faultUpperThreshold(this, faultUpperThresholdPath, "", "Upper fault threshold to compare with", parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  void RangeMonitor<T>::mainLoop() {
+    // If there is a change either in value monitored or in requiredValue, the status is re-evaluated
+    ReadAnyGroup group{watch.value, disable.value, warningLowerThreshold.value, warningUpperThreshold.value,
+        faultLowerThreshold.value, faultUpperThreshold.value};
+
+    while(true) {
+      if(disable.value) {
+        setStatus(StatusOutput::Status::OFF);
+      }
+      // Check for fault limits first. Like this they supersede the warning,
+      // even if they are stricter then the warning limits (mis-configuration)
+      else if(watch.value <= faultLowerThreshold.value || watch.value >= faultUpperThreshold.value) {
+        setStatus(StatusOutput::Status::FAULT);
+      }
+      else if(watch.value <= warningLowerThreshold.value || watch.value >= warningUpperThreshold.value) {
+        setStatus(StatusOutput::Status::WARNING);
+      }
+      else {
+        setStatus(StatusOutput::Status::OK);
+      }
+      group.readAny();
+    }
+  }
+
+  /*******************************************************************************************************************/
+  /* Implementation of ExactMonitor **********************************************************************************/
+  /*******************************************************************************************************************/
+  template<typename T>
+  ExactMonitor<T>::ExactMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& parameterPath, const std::string& description,
+      const std::unordered_set<std::string>& outputTags, const std::unordered_set<std::string>& parameterTags)
+  : ExactMonitor(owner, inputPath, outputPath, parameterPath + "/requiredValue", parameterPath + "/disable",
+        description, outputTags, parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  ExactMonitor<T>::ExactMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
+      const std::string& requiredValuePath, const std::string& disablePath, const std::string& description,
+      const std::unordered_set<std::string>& outputTags, const std::unordered_set<std::string>& parameterTags)
+  : MonitorBase(owner, description, outputPath, disablePath, outputTags, parameterTags),
+    watch(this, inputPath, "", "Value to monitor"),
+    requiredValue(this, requiredValuePath, "", "Value to compare with", parameterTags) {}
+
+  /*******************************************************************************************************************/
+  template<typename T>
+  void ExactMonitor<T>::mainLoop() {
+    // If there is a change either in value monitored or in requiredValue, the status is re-evaluated
+    ReadAnyGroup group{watch.value, disable.value, requiredValue.value};
+
+    while(true) {
+      if(disable.value) {
+        setStatus(StatusOutput::Status::OFF);
+      }
+      else if(watch.value != requiredValue.value) {
+        setStatus(StatusOutput::Status::FAULT);
+      }
+      else {
+        setStatus(StatusOutput::Status::OK);
+      }
+      group.readAny();
+    }
+  }
 
 } // namespace ChimeraTK
diff --git a/example_status_monitor/demoStatusMonitor.cc b/example_status_monitor/demoStatusMonitor.cc
index 5473a7f4e0a74326b410bec9257bbd3e9de1051e..ab86a7f70d3b9d0c2146970541e5dbb80ddd0286 100644
--- a/example_status_monitor/demoStatusMonitor.cc
+++ b/example_status_monitor/demoStatusMonitor.cc
@@ -63,8 +63,13 @@ struct ExampleApp : public ctk::Application {
     // the name of the output variable. The monitor automatically connects to the input variable that is in the same
     // hierarchy level. We add output and parameter tags (STATUS and CONFIG, respectively) for easier connetion of the
     // variables.
-    ctk::RangeMonitor<double> temperatureMonitor{this, "TemperatureMonitor", "monitor for the simulated temperature",
-        "temperature", "temperatureStatus", ctk::HierarchyModifier::none, {"STATUS"}, {"CONFIG"}, {}};
+    // ctk::RangeMonitor<double> temperatureMonitor{this, "TemperatureMonitor", "monitor for the simulated temperature",
+    //    "temperature", "temperatureStatus", ctk::HierarchyModifier::none, {"STATUS"}, {"CONFIG"}, {}};
+
+    ctk::RangeMonitor<double> temperatureMonitor{this, "/TemperatureMonitor/temperature",
+        "/TemperatureMonitor/temperatureStatus", "/TemperatureMonitor", "monitor for the simulated temperature",
+        ctk::TAGS{"MON_OUTPUT"}, ctk::TAGS{"MON_PARAMS"}};
+
   } simulationGroup{this, "Simulation", ""};
 
   ctk::ConfigReader config{this, "Config", "demoStatusMonitor_config.xml"};
diff --git a/include/HierarchyModifyingGroup.h b/include/HierarchyModifyingGroup.h
index 16e6a1936061be58dd4c8f0f06efbcb2f6e8e7a0..1c8f70e3aa249166d0d49dabfb94bf4ade2aa3da 100644
--- a/include/HierarchyModifyingGroup.h
+++ b/include/HierarchyModifyingGroup.h
@@ -68,6 +68,7 @@ namespace ChimeraTK {
     : HierarchyModifyingGroup(owner, HierarchyModifyingGroup::getPathName(qualifiedName), ""),
       value(this, HierarchyModifyingGroup::getUnqualifiedName(qualifiedName), args...) {}
 
+    ModifyHierarchy() = default;
     ACCESSOR value;
   };
 
diff --git a/tests/executables_src/testPropagateDataFaultFlag.cc b/tests/executables_src/testPropagateDataFaultFlag.cc
index f202b930477f4169c5ddcd0be882b40c2e035ca8..5dcda699e3b0d6dc48eecd849d5829a17977432a 100644
--- a/tests/executables_src/testPropagateDataFaultFlag.cc
+++ b/tests/executables_src/testPropagateDataFaultFlag.cc
@@ -9,7 +9,6 @@
 #include "ArrayAccessor.h"
 #include "check_timeout.h"
 #include "ControlSystemModule.h"
-#include "ModuleGroup.h"
 #include "ScalarAccessor.h"
 #include "TestFacility.h"
 
diff --git a/tests/executables_src/testStatusAggregator.cc b/tests/executables_src/testStatusAggregator.cc
index 9ca62120b9388569216d2cd26ca89a1bd5c66e58..bf5370d818b98a99347fec164ed93feebe6637e7 100644
--- a/tests/executables_src/testStatusAggregator.cc
+++ b/tests/executables_src/testStatusAggregator.cc
@@ -4,7 +4,6 @@
 #include "Application.h"
 #include "ModuleGroup.h"
 #include "StatusAggregator.h"
-#include "StatusMonitor.h"
 #include "TestFacility.h"
 
 #define BOOST_NO_EXCEPTIONS
diff --git a/tests/executables_src/testStatusModule.cc b/tests/executables_src/testStatusModule.cc
deleted file mode 100644
index a38ddd507001f54e8bcf099e8f238497cf7aa543..0000000000000000000000000000000000000000
--- a/tests/executables_src/testStatusModule.cc
+++ /dev/null
@@ -1,669 +0,0 @@
-// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
-// SPDX-License-Identifier: LGPL-3.0-or-later
-#include <future>
-
-#define BOOST_TEST_MODULE testExceptionHandling
-
-#include "Application.h"
-#include "ApplicationModule.h"
-#include "ControlSystemModule.h"
-#include "ScalarAccessor.h"
-#include "StatusMonitor.h"
-#include "TestFacility.h"
-
-#include <boost/mpl/list.hpp>
-#include <boost/test/included/unit_test.hpp>
-
-using namespace boost::unit_test_framework;
-namespace ctk = ChimeraTK;
-
-/* dummy application */
-template<typename T>
-struct TestApplication : public ctk::Application {
-  TestApplication() : Application("testSuite") {}
-  ~TestApplication() override { shutdown(); }
-
-  void defineConnections() override {
-    findTag(".*").connectTo(cs);                              // publish everything to CS
-    findTag("MY_MONITOR").connectTo(cs["MyNiceMonitorCopy"]); // cable again, checking that the tag is applied correctly
-    findTag("MON_PARAMS")
-        .connectTo(cs["MonitorParameters"]); // cable the parameters in addition (checking that tags are set correctly)
-    findTag("MON_OUTPUT")
-        .connectTo(cs["MonitorOutput"]); // cable the parameters in addition (checking that tags are set correctly)
-  }
-  ctk::ControlSystemModule cs;
-  T monitor{this, "Monitor", "Now this is a nice monitor...", "watch", "status", ChimeraTK::HierarchyModifier::none,
-      {"MON_OUTPUT"}, {"MON_PARAMS"}, {"MY_MONITOR"}};
-};
-
-/* dummy application - for new StatusMonitor interface */
-template<typename T>
-struct TestApplicationNewInterface : public ctk::Application {
-  TestApplicationNewInterface() : Application("testSuite") {}
-  ~TestApplicationNewInterface() override { shutdown(); }
-
-  void defineConnections() override {
-    findTag(".*").connectTo(cs); // publish everything to CS
-    findTag("MON_PARAMS")
-        .connectTo(cs["MonitorParameters"]); // cable the parameters in addition (checking that tags are set correctly)
-    findTag("MON_OUTPUT")
-        .connectTo(cs["MonitorOutput"]); // cable the parameters in addition (checking that tags are set correctly)
-  }
-  ctk::ControlSystemModule cs;
-  T monitor{this, "/input/path", "/output/path", "/parameters", "Now this is a nice monitor...",
-      ctk::TAGS{"MON_OUTPUT"}, ctk::TAGS{"MON_PARAMS"}};
-};
-
-/*********************************************************************************************************************/
-
-BOOST_AUTO_TEST_CASE(testMaxMonitor) {
-  std::cout << "testMaxMonitor" << std::endl;
-  TestApplication<ctk::MaxMonitor<double_t>> app;
-
-  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
-  auto tags = ctk::VariableNetworkNode(app.monitor.status).getTags();
-  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
-
-  ctk::TestFacility test;
-  test.runApplication();
-  // app.cs.dump();
-
-  auto warning = test.getScalar<double_t>(std::string("/Monitor/upperWarningThreshold"));
-  warning = 50.0;
-  warning.write();
-  test.stepApplication();
-
-  auto fault = test.getScalar<double_t>(std::string("/Monitor/upperFaultThreshold"));
-  fault = 60.0;
-  fault.write();
-  test.stepApplication();
-
-  auto watch = test.getScalar<double_t>(std::string("/watch"));
-  watch = 40.0;
-  watch.write();
-  test.stepApplication();
-
-  auto status = test.getScalar<int32_t>(std::string("/Monitor/status"));
-  status.readLatest();
-
-  // should be in OK state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  //   //just below the warning level
-  watch = 49.99;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // drop in a disable test.
-  auto disable = test.getScalar<int>("/Monitor/disable");
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // slightly above at the upper warning threshold (exact is not good due to rounding errors in floats/doubles)
-  watch = 50.01;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // drop in a disable test.
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // just below the fault threshold,. still warning
-  watch = 59.99;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // slightly above at the upper fault threshold (exact is not good due to rounding errors in floats/doubles)
-  watch = 60.01;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // drop in a disable test.
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // increase well above the upper fault level
-  watch = 65;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // now check that changing the status is updated correctly if we change the limits
-
-  // increase fault value greater than watch
-  fault = 68;
-  fault.write();
-  test.stepApplication();
-  status.readLatest();
-  // should be in WARNING state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // increase warning value greater than watch
-  warning = 66;
-  warning.write();
-  test.stepApplication();
-  status.readLatest();
-  // should be in OK state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // Set the upper fault limit below the upper warning limit and below the current temperature. The warning is not
-  // active, but the fault. Although this is not a reasonable configuration the fault limit must superseed the warning
-  // and the status has to be fault.
-  fault = 60;
-  fault.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // check that the tags are applied correctly
-  BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MyNiceMonitorCopy/Monitor/status"));
-  BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MonitorOutput/Monitor/status"));
-  BOOST_CHECK_EQUAL(watch, test.readScalar<double>("/MyNiceMonitorCopy/watch"));
-  BOOST_CHECK_EQUAL(fault, test.readScalar<double>("/MonitorParameters/Monitor/upperFaultThreshold"));
-  BOOST_CHECK_EQUAL(warning, test.readScalar<double>("/MonitorParameters/Monitor/upperWarningThreshold"));
-}
-
-/*********************************************************************************************************************/
-BOOST_AUTO_TEST_CASE(testMinMonitor) {
-  std::cout << "testMinMonitor" << std::endl;
-
-  TestApplication<ctk::MinMonitor<uint>> app;
-
-  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
-  auto tags = ctk::VariableNetworkNode(app.monitor.status).getTags();
-  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
-
-  ctk::TestFacility test;
-  test.runApplication();
-  // app.dumpConnections();
-
-  auto warning = test.getScalar<uint>(std::string("/Monitor/lowerWarningThreshold"));
-  warning = 40;
-  warning.write();
-  test.stepApplication();
-
-  auto fault = test.getScalar<uint>(std::string("/Monitor/lowerFaultThreshold"));
-  fault = 30;
-  fault.write();
-  test.stepApplication();
-
-  auto watch = test.getScalar<uint>(std::string("/watch"));
-  watch = 45;
-  watch.write();
-  test.stepApplication();
-
-  auto status = test.getScalar<int32_t>(std::string("/Monitor/status"));
-  status.readLatest();
-
-  // should be in OK state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // just abow the lower warning limit
-  watch = 41;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // drop in a disable test.
-  auto disable = test.getScalar<int>("/Monitor/disable");
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // exactly at the lower warning limit
-  watch = 40;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // drop in a disable test.
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // just above the lower fault limit
-  watch = 31;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // exactly at the lower fault limit (only well defined for int)
-  watch = 30;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // drop in a disable test.
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // way bellow the lower fault limit
-  watch = 12;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // move the temperature back to the good range and check that the status updates correctly when changing the limits
-  watch = 41;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // change upper warning limit
-  warning = 42;
-  warning.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // rise the temperature above the lower warning limit
-  watch = 43;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // Set the lower fault limit above the lower warning limit. The warning is not active, but the fault.
-  // Although this is not a reasonable configuration the fault limit must superseed the warning and the status has to be fault.
-  fault = 44;
-  fault.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // check that the tags are applied correctly
-  BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MyNiceMonitorCopy/Monitor/status"));
-  BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MonitorOutput/Monitor/status"));
-  BOOST_CHECK_EQUAL(watch, test.readScalar<uint>("/MyNiceMonitorCopy/watch"));
-  BOOST_CHECK_EQUAL(fault, test.readScalar<uint>("/MonitorParameters/Monitor/lowerFaultThreshold"));
-  BOOST_CHECK_EQUAL(warning, test.readScalar<uint>("/MonitorParameters/Monitor/lowerWarningThreshold"));
-}
-
-/*********************************************************************************************************************/
-
-BOOST_AUTO_TEST_CASE(testRangeMonitor) {
-  std::cout << "testRangeMonitor" << std::endl;
-  TestApplication<ctk::RangeMonitor<int>> app;
-
-  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
-  auto tags = ctk::VariableNetworkNode(app.monitor.status).getTags();
-  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
-
-  ctk::TestFacility test;
-  test.runApplication();
-  // app.dumpConnections();
-
-  auto warningUpperLimit = test.getScalar<int>(std::string("/Monitor/upperWarningThreshold"));
-  warningUpperLimit = 50;
-  warningUpperLimit.write();
-  test.stepApplication();
-
-  auto warningLowerLimit = test.getScalar<int>(std::string("/Monitor/lowerWarningThreshold"));
-  warningLowerLimit = 40;
-  warningLowerLimit.write();
-  test.stepApplication();
-
-  auto faultUpperLimit = test.getScalar<int>(std::string("/Monitor/upperFaultThreshold"));
-  faultUpperLimit = 60;
-  faultUpperLimit.write();
-  test.stepApplication();
-
-  auto faultLowerLimit = test.getScalar<int>(std::string("/Monitor/lowerFaultThreshold"));
-  faultLowerLimit = 30;
-  faultLowerLimit.write();
-  test.stepApplication();
-
-  // start with a good value
-  auto watch = test.getScalar<int>(std::string("/watch"));
-  watch = 45;
-  watch.write();
-  test.stepApplication();
-
-  auto status = test.getScalar<int32_t>(std::string("/Monitor/status"));
-  status.readLatest();
-
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // just below the warning level
-  watch = 49;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // drop in a disable test.
-  auto disable = test.getScalar<int>("/Monitor/disable");
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // exactly at the upper warning threshold (only well defined for int)
-  watch = 50;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // drop in a disable test.
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // just below the fault threshold,. still warning
-  watch = 59;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // exactly at the upper warning threshold (only well defined for int)
-  watch = 60;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // drop in a disable test.
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // increase well above the upper fault level
-  watch = 65;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // back to ok, just abow the lower warning limit
-  watch = 41;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // exactly at the lower warning limit
-  watch = 40;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // just above the lower fault limit
-  watch = 31;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // exactly at the lower fault limit (only well defined for int)
-  watch = 30;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // way bellow the lower fault limit
-  watch = 12;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // Put the value back to the good range, then check that chaning the threshold also updated the status
-  watch = 49;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // change upper warning limit
-  warningUpperLimit = 48;
-  warningUpperLimit.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // lower the temperature below the upper warning limit
-  watch = 47;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // Set the upper fault limit below the upper warning limit. The warning is not active, but the fault.
-  // Although this is not a reasonable configuration the fault limit must superseed the warning and the status has to be fault.
-  faultUpperLimit = 46;
-  faultUpperLimit.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // move the temperature back to the good range and repeat for the lower limits
-  watch = 41;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // change upper warning limit
-  warningLowerLimit = 42;
-  warningLowerLimit.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
-
-  // rise the temperature above the lower warning limit
-  watch = 43;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // Set the lower fault limit above the lower warning limit. The warning is not active, but the fault.
-  // Although this is not a reasonable configuration the fault limit must superseed the warning and the status has to be fault.
-  faultLowerLimit = 44;
-  faultLowerLimit.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // check that the tags are applied correctly
-  BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MyNiceMonitorCopy/Monitor/status"));
-  BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MonitorOutput/Monitor/status"));
-  BOOST_CHECK_EQUAL(watch, test.readScalar<int>("/MyNiceMonitorCopy/watch"));
-  BOOST_CHECK_EQUAL(faultLowerLimit, test.readScalar<int>("/MonitorParameters/Monitor/lowerFaultThreshold"));
-  BOOST_CHECK_EQUAL(warningLowerLimit, test.readScalar<int>("/MonitorParameters/Monitor/lowerWarningThreshold"));
-  BOOST_CHECK_EQUAL(faultUpperLimit, test.readScalar<int>("/MonitorParameters/Monitor/upperFaultThreshold"));
-  BOOST_CHECK_EQUAL(warningUpperLimit, test.readScalar<int>("/MonitorParameters/Monitor/upperWarningThreshold"));
-}
-
-/*********************************************************************************************************************/
-
-BOOST_AUTO_TEST_CASE(testExactMonitor) {
-  std::cout << "testExactMonitor" << std::endl;
-  TestApplicationNewInterface<ctk::ExactMonitor<int64_t>> app;
-
-  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
-  auto tags = ctk::VariableNetworkNode(app.monitor.status.value).getTags();
-  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
-
-  ctk::TestFacility test;
-  test.runApplication();
-  // app.dumpConnections();
-
-  auto requiredValue = test.getScalar<int64_t>(std::string("/parameters/requiredValue"));
-  requiredValue = 409;
-  requiredValue.write();
-  test.stepApplication();
-
-  auto watch = test.getScalar<int64_t>(std::string("/input/path"));
-  watch = 409;
-  watch.write();
-  test.stepApplication();
-
-  auto status = test.getScalar<int32_t>(std::string("/output/path"));
-  status.readLatest();
-
-  // should be in OK state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // drop in a disable test.
-  auto disable = test.getScalar<int>("/parameters/disable");
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // set watch value different than required value
-  watch = 414;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  // should be in FAULT state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // drop in a disable test.
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
-
-  disable = 0;
-  disable.write();
-  test.stepApplication();
-  status.readLatest();
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  watch = 409;
-  watch.write();
-  test.stepApplication();
-  status.readLatest();
-  // should be in OK state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // set requiredValue value different than watch value
-  requiredValue = 413;
-  requiredValue.write();
-  test.stepApplication();
-  status.readLatest();
-  // should be in WARNING state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
-
-  // set requiredValue value equals to watch value
-  requiredValue = 409;
-  requiredValue.write();
-  test.stepApplication();
-  status.readLatest();
-  // should be in WARNING state.
-  BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
-
-  // check that the tags are applied correctly
-  BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MonitorOutput/output/path"));
-  BOOST_CHECK_EQUAL(requiredValue, test.readScalar<int64_t>("/MonitorParameters/parameters/requiredValue"));
-  disable = 1;
-  disable.write();
-  test.stepApplication();
-  BOOST_CHECK_EQUAL(disable, test.readScalar<int>("/MonitorParameters/parameters/disable"));
-}
-
-/*********************************************************************************************************************/
diff --git a/tests/executables_src/testStatusMonitor.cc b/tests/executables_src/testStatusMonitor.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5d13ea1c1b16baa178c366e6d500e24f1d08d88d
--- /dev/null
+++ b/tests/executables_src/testStatusMonitor.cc
@@ -0,0 +1,952 @@
+// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
+// SPDX-License-Identifier: LGPL-3.0-or-later
+#include <future>
+
+#define BOOST_TEST_MODULE testStatusMonitor
+
+#include "Application.h"
+#include "ControlSystemModule.h"
+#include "ScalarAccessor.h"
+#include "StatusMonitor.h"
+#include "TestFacility.h"
+
+#include <boost/mpl/list.hpp>
+#include <boost/test/included/unit_test.hpp>
+
+using namespace boost::unit_test_framework;
+namespace ctk = ChimeraTK;
+
+/*********************************************************************************************************************/
+/* Test dummy application for the Monitors ************************************************************************/
+/*********************************************************************************************************************/
+
+template<typename T>
+struct TestApplication : public ctk::Application {
+  TestApplication() : Application("testSuite") {}
+  ~TestApplication() override { shutdown(); }
+
+  ctk::ControlSystemModule cs;
+  T monitor{this, "/input/path", "/output/path", "/parameters", "Now this is a nice monitor...",
+      ctk::TAGS{"MON_OUTPUT"}, ctk::TAGS{"MON_PARAMS"}};
+};
+
+/*********************************************************************************************************************/
+/* Test generic functionality of the Monitors ************************************************************************/
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testMaxMonitor) {
+  std::cout << "testMaxMonitor" << std::endl;
+  TestApplication<ctk::MaxMonitor<double_t>> app;
+
+  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
+  auto tags = ctk::VariableNetworkNode(app.monitor.status.value).getTags();
+  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
+
+  ctk::TestFacility test;
+  test.runApplication();
+  // app.cs.dump();
+
+  auto warning = test.getScalar<double_t>(std::string("/parameters/upperWarningThreshold"));
+  warning = 50.0;
+  warning.write();
+  test.stepApplication();
+
+  auto fault = test.getScalar<double_t>(std::string("/parameters/upperFaultThreshold"));
+  fault = 60.0;
+  fault.write();
+  test.stepApplication();
+
+  auto watch = test.getScalar<double_t>(std::string("/input/path"));
+  watch = 40.0;
+  watch.write();
+  test.stepApplication();
+
+  auto status = test.getScalar<int32_t>(std::string("/output/path"));
+  status.readLatest();
+
+  // should be in OK state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  //   //just below the warning level
+  watch = 49.99;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // drop in a disable test.
+  auto disable = test.getScalar<ChimeraTK::Boolean>("/parameters/disable");
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // slightly above at the upper warning threshold (exact is not good due to rounding errors in floats/doubles)
+  watch = 50.01;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // drop in a disable test.
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // just below the fault threshold,. still warning
+  watch = 59.99;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // slightly above at the upper fault threshold (exact is not good due to rounding errors in floats/doubles)
+  watch = 60.01;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // drop in a disable test.
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // increase well above the upper fault level
+  watch = 65;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // now check that changing the status is updated correctly if we change the limits
+
+  // increase fault value greater than watch
+  fault = 68;
+  fault.write();
+  test.stepApplication();
+  status.readLatest();
+  // should be in WARNING state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // increase warning value greater than watch
+  warning = 66;
+  warning.write();
+  test.stepApplication();
+  status.readLatest();
+  // should be in OK state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // Set the upper fault limit below the upper warning limit and below the current temperature. The warning is not
+  // active, but the fault. Although this is not a reasonable configuration the fault limit must superseed the warning
+  // and the status has to be fault.
+  fault = 60;
+  fault.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // check that the tags are applied correctly
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("upperFaultThreshold") == app.monitor.faultThreshold.value);
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("upperWarningThreshold") == app.monitor.warningThreshold.value);
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("disable") == app.monitor.disable.value);
+  BOOST_CHECK(app.findTag("MON_OUTPUT")["output"]("path") == app.monitor.status.value);
+}
+
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testMinMonitor) {
+  std::cout << "testMinMonitor" << std::endl;
+
+  TestApplication<ctk::MinMonitor<uint>> app;
+
+  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
+  auto tags = ctk::VariableNetworkNode(app.monitor.status.value).getTags();
+  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
+
+  ctk::TestFacility test;
+  test.runApplication();
+  // app.dumpConnections();
+
+  auto warning = test.getScalar<uint>(std::string("/parameters/lowerWarningThreshold"));
+  warning = 40;
+  warning.write();
+  test.stepApplication();
+
+  auto fault = test.getScalar<uint>(std::string("/parameters/lowerFaultThreshold"));
+  fault = 30;
+  fault.write();
+  test.stepApplication();
+
+  auto watch = test.getScalar<uint>(std::string("/input/path"));
+  watch = 45;
+  watch.write();
+  test.stepApplication();
+
+  auto status = test.getScalar<int32_t>(std::string("/output/path"));
+  status.readLatest();
+
+  // should be in OK state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // just abow the lower warning limit
+  watch = 41;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // drop in a disable test.
+  auto disable = test.getScalar<ChimeraTK::Boolean>("/parameters/disable");
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // exactly at the lower warning limit
+  watch = 40;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // drop in a disable test.
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // just above the lower fault limit
+  watch = 31;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // exactly at the lower fault limit (only well defined for int)
+  watch = 30;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // drop in a disable test.
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // way bellow the lower fault limit
+  watch = 12;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // move the temperature back to the good range and check that the status updates correctly when changing the limits
+  watch = 41;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // change upper warning limit
+  warning = 42;
+  warning.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // rise the temperature above the lower warning limit
+  watch = 43;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // Set the lower fault limit above the lower warning limit. The warning is not active, but the fault.
+  // Although this is not a reasonable configuration the fault limit must superseed the warning and the status has to be fault.
+  fault = 44;
+  fault.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // check that the tags are applied correctly
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("lowerFaultThreshold") == app.monitor.faultThreshold.value);
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("lowerWarningThreshold") == app.monitor.warningThreshold.value);
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("disable") == app.monitor.disable.value);
+  BOOST_CHECK(app.findTag("MON_OUTPUT")["output"]("path") == app.monitor.status.value);
+}
+
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testRangeMonitor) {
+  std::cout << "testRangeMonitor" << std::endl;
+  TestApplication<ctk::RangeMonitor<int>> app;
+
+  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
+  auto tags = ctk::VariableNetworkNode(app.monitor.status.value).getTags();
+  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
+
+  ctk::TestFacility test;
+  test.runApplication();
+  // app.dumpConnections();
+
+  auto warningUpperLimit = test.getScalar<int>(std::string("/parameters/upperWarningThreshold"));
+  warningUpperLimit = 50;
+  warningUpperLimit.write();
+  test.stepApplication();
+
+  auto warningLowerLimit = test.getScalar<int>(std::string("/parameters/lowerWarningThreshold"));
+  warningLowerLimit = 40;
+  warningLowerLimit.write();
+  test.stepApplication();
+
+  auto faultUpperLimit = test.getScalar<int>(std::string("/parameters/upperFaultThreshold"));
+  faultUpperLimit = 60;
+  faultUpperLimit.write();
+  test.stepApplication();
+
+  auto faultLowerLimit = test.getScalar<int>(std::string("/parameters/lowerFaultThreshold"));
+  faultLowerLimit = 30;
+  faultLowerLimit.write();
+  test.stepApplication();
+
+  // start with a good value
+  auto watch = test.getScalar<int>(std::string("/input/path"));
+  watch = 45;
+  watch.write();
+  test.stepApplication();
+
+  auto status = test.getScalar<int32_t>(std::string("/output/path"));
+  status.readLatest();
+
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // just below the warning level
+  watch = 49;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // drop in a disable test.
+  auto disable = test.getScalar<ChimeraTK::Boolean>("/parameters/disable");
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // exactly at the upper warning threshold (only well defined for int)
+  watch = 50;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // drop in a disable test.
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // just below the fault threshold,. still warning
+  watch = 59;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // exactly at the upper warning threshold (only well defined for int)
+  watch = 60;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // drop in a disable test.
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // increase well above the upper fault level
+  watch = 65;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // back to ok, just abow the lower warning limit
+  watch = 41;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // exactly at the lower warning limit
+  watch = 40;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // just above the lower fault limit
+  watch = 31;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // exactly at the lower fault limit (only well defined for int)
+  watch = 30;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // way bellow the lower fault limit
+  watch = 12;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // Put the value back to the good range, then check that chaning the threshold also updated the status
+  watch = 49;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // change upper warning limit
+  warningUpperLimit = 48;
+  warningUpperLimit.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // lower the temperature below the upper warning limit
+  watch = 47;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // Set the upper fault limit below the upper warning limit. The warning is not active, but the fault.
+  // Although this is not a reasonable configuration the fault limit must superseed the warning and the status has to be fault.
+  faultUpperLimit = 46;
+  faultUpperLimit.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // move the temperature back to the good range and repeat for the lower limits
+  watch = 41;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // change upper warning limit
+  warningLowerLimit = 42;
+  warningLowerLimit.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+
+  // rise the temperature above the lower warning limit
+  watch = 43;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // Set the lower fault limit above the lower warning limit. The warning is not active, but the fault.
+  // Although this is not a reasonable configuration the fault limit must superseed the warning and the status has to be fault.
+  faultLowerLimit = 44;
+  faultLowerLimit.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // check that the tags are applied correctly
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("lowerFaultThreshold") == app.monitor.faultLowerThreshold.value);
+  BOOST_CHECK(
+      app.findTag("MON_PARAMS")["parameters"]("lowerWarningThreshold") == app.monitor.warningLowerThreshold.value);
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("upperFaultThreshold") == app.monitor.faultUpperThreshold.value);
+  BOOST_CHECK(
+      app.findTag("MON_PARAMS")["parameters"]("upperWarningThreshold") == app.monitor.warningUpperThreshold.value);
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("disable") == app.monitor.disable.value);
+  BOOST_CHECK(app.findTag("MON_OUTPUT")["output"]("path") == app.monitor.status.value);
+}
+
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testExactMonitor) {
+  std::cout << "testExactMonitor" << std::endl;
+  TestApplication<ctk::ExactMonitor<int64_t>> app;
+
+  // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
+  auto tags = ctk::VariableNetworkNode(app.monitor.status.value).getTags();
+  BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
+
+  ctk::TestFacility test;
+  test.runApplication();
+  // app.dumpConnections();
+
+  auto requiredValue = test.getScalar<int64_t>(std::string("/parameters/requiredValue"));
+  requiredValue = 409;
+  requiredValue.write();
+  test.stepApplication();
+
+  auto watch = test.getScalar<int64_t>(std::string("/input/path"));
+  watch = 409;
+  watch.write();
+  test.stepApplication();
+
+  auto status = test.getScalar<int32_t>(std::string("/output/path"));
+  status.readLatest();
+
+  // should be in OK state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // drop in a disable test.
+  auto disable = test.getScalar<ChimeraTK::Boolean>("/parameters/disable");
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // set watch value different than required value
+  watch = 414;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  // should be in FAULT state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // drop in a disable test.
+  disable = 1;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+
+  disable = 0;
+  disable.write();
+  test.stepApplication();
+  status.readLatest();
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  watch = 409;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  // should be in OK state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // set requiredValue value different than watch value
+  requiredValue = 413;
+  requiredValue.write();
+  test.stepApplication();
+  status.readLatest();
+  // should be in WARNING state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+
+  // set requiredValue value equals to watch value
+  requiredValue = 409;
+  requiredValue.write();
+  test.stepApplication();
+  status.readLatest();
+  // should be in WARNING state.
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+
+  // check that the tags are applied correctly
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("requiredValue") == app.monitor.requiredValue.value);
+  BOOST_CHECK(app.findTag("MON_PARAMS")["parameters"]("disable") == app.monitor.disable.value);
+  BOOST_CHECK(app.findTag("MON_OUTPUT")["output"]("path") == app.monitor.status.value);
+}
+
+/*********************************************************************************************************************/
+/* Test initial value propagation for the Monitors *******************************************************************/
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testMaxMonitorInitialValuePropagation) {
+  std::cout << "testMaxMonitorInitialValuePropagation" << std::endl;
+
+  {
+    TestApplication<ctk::MaxMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/upperFaultThreshold", 60.0);
+    test.setScalarDefault<float_t>("/parameters/upperWarningThreshold", 50.0);
+    test.setScalarDefault<float_t>("/input/path", 45.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+  }
+  {
+    TestApplication<ctk::MaxMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/upperFaultThreshold", 60.0);
+    test.setScalarDefault<float_t>("/parameters/upperWarningThreshold", 50.0);
+    test.setScalarDefault<float_t>("/input/path", 55.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+  }
+  {
+    TestApplication<ctk::MaxMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/upperFaultThreshold", 60.0);
+    test.setScalarDefault<float_t>("/parameters/upperWarningThreshold", 50.0);
+    test.setScalarDefault<float_t>("/input/path", 55.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", true);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+  }
+  {
+    TestApplication<ctk::MaxMonitor<double_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<double_t>("/parameters/upperFaultThreshold", 60.0);
+    test.setScalarDefault<double_t>("/parameters/upperWarningThreshold", 50.0);
+    test.setScalarDefault<double_t>("/input/path", 65.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+  }
+}
+
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testMinMonitorInitialValuePropagation) {
+  std::cout << "testMinMonitorInitialValuePropagation" << std::endl;
+
+  {
+    TestApplication<ctk::MinMonitor<double_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<double_t>("/parameters/lowerFaultThreshold", 50.0);
+    test.setScalarDefault<double_t>("/parameters/lowerWarningThreshold", 60.0);
+    test.setScalarDefault<double_t>("/input/path", 65.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+  }
+  {
+    TestApplication<ctk::MinMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/lowerFaultThreshold", 50.0);
+    test.setScalarDefault<float_t>("/parameters/lowerWarningThreshold", 60.0);
+    test.setScalarDefault<float_t>("/input/path", 55.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+  }
+  {
+    TestApplication<ctk::MinMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/lowerFaultThreshold", 50.0);
+    test.setScalarDefault<float_t>("/parameters/lowerWarningThreshold", 60.0);
+    test.setScalarDefault<float_t>("/input/path", 55.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", true);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+  }
+  {
+    TestApplication<ctk::MinMonitor<int32_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<int32_t>("/parameters/lowerFaultThreshold", 50);
+    test.setScalarDefault<int32_t>("/parameters/lowerWarningThreshold", 60);
+    test.setScalarDefault<int32_t>("/input/path", 45);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+  }
+}
+
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testRangeMonitorInitialValuePropagation) {
+  std::cout << "testRangeMonitorInitialValuePropagation" << std::endl;
+  // each {} defines new application and test facility
+  {
+    TestApplication<ctk::RangeMonitor<double_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<double_t>("/parameters/upperFaultThreshold", 80.0);
+    test.setScalarDefault<double_t>("/parameters/upperWarningThreshold", 70.0);
+    test.setScalarDefault<double_t>("/parameters/lowerWarningThreshold", 60.0);
+    test.setScalarDefault<double_t>("/parameters/lowerFaultThreshold", 50.0);
+    test.setScalarDefault<double_t>("/input/path", 65.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+  }
+  {
+    TestApplication<ctk::RangeMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/upperFaultThreshold", 80.0);
+    test.setScalarDefault<float_t>("/parameters/upperWarningThreshold", 70.0);
+    test.setScalarDefault<float_t>("/parameters/lowerWarningThreshold", 60.0);
+    test.setScalarDefault<float_t>("/parameters/lowerFaultThreshold", 50.0);
+    test.setScalarDefault<float_t>("/input/path", 75.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+  }
+  {
+    TestApplication<ctk::RangeMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/upperFaultThreshold", 80.0);
+    test.setScalarDefault<float_t>("/parameters/upperWarningThreshold", 70.0);
+    test.setScalarDefault<float_t>("/parameters/lowerWarningThreshold", 60.0);
+    test.setScalarDefault<float_t>("/parameters/lowerFaultThreshold", 50.0);
+    test.setScalarDefault<float_t>("/input/path", 55.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+  }
+  {
+    TestApplication<ctk::RangeMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/upperFaultThreshold", 80.0);
+    test.setScalarDefault<float_t>("/parameters/upperWarningThreshold", 70.0);
+    test.setScalarDefault<float_t>("/parameters/lowerWarningThreshold", 60.0);
+    test.setScalarDefault<float_t>("/parameters/lowerFaultThreshold", 50.0);
+    test.setScalarDefault<float_t>("/input/path", 55.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", true);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+  }
+  {
+    TestApplication<ctk::RangeMonitor<int32_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<int32_t>("/parameters/upperFaultThreshold", 80);
+    test.setScalarDefault<int32_t>("/parameters/upperWarningThreshold", 70);
+    test.setScalarDefault<int32_t>("/parameters/lowerWarningThreshold", 60);
+    test.setScalarDefault<int32_t>("/parameters/lowerFaultThreshold", 50);
+    test.setScalarDefault<int32_t>("/input/path", 85);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+  }
+  {
+    TestApplication<ctk::RangeMonitor<int32_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<int32_t>("/parameters/upperFaultThreshold", 80);
+    test.setScalarDefault<int32_t>("/parameters/upperWarningThreshold", 70);
+    test.setScalarDefault<int32_t>("/parameters/lowerWarningThreshold", 60);
+    test.setScalarDefault<int32_t>("/parameters/lowerFaultThreshold", 50);
+    test.setScalarDefault<int32_t>("/input/path", 45);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+  }
+}
+
+/*********************************************************************************************************************/
+
+BOOST_AUTO_TEST_CASE(testExactMonitorInitialValuePropagation) {
+  std::cout << "testExactMonitorInitialValuePropagation" << std::endl;
+
+  {
+    TestApplication<ctk::ExactMonitor<double_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<double_t>("/parameters/requiredValue", 60.0);
+    test.setScalarDefault<double_t>("/input/path", 60.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+  }
+  {
+    TestApplication<ctk::ExactMonitor<float_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<float_t>("/parameters/requiredValue", 60.0);
+    test.setScalarDefault<float_t>("/input/path", 55.0);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", true);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::OFF));
+  }
+  {
+    TestApplication<ctk::ExactMonitor<int32_t>> app;
+    ctk::TestFacility test;
+    test.setScalarDefault<int32_t>("/parameters/requiredValue", 60);
+    test.setScalarDefault<int32_t>("/input/path", 45);
+    test.setScalarDefault<ChimeraTK::Boolean>("/parameters/disable", false);
+
+    test.runApplication();
+
+    BOOST_TEST(test.readScalar<int32_t>("/output/path") == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+  }
+}
+
+/*********************************************************************************************************************/
+/* Test data validity propagation for the Monitors *******************************************************************/
+/*********************************************************************************************************************/
+
+/*
+ * Data validity is checked in base class of all monitors (MonitorBase) in method MonitorBase::setStatus only
+ * There is no need to test all types of monitors so we gonna use MaxMonitor
+ * */
+BOOST_AUTO_TEST_CASE(testMonitorDataValidityPropagation) {
+  std::cout << "testMonitorDataValidityPropagation" << std::endl;
+
+  TestApplication<ctk::MaxMonitor<double_t>> app;
+  ctk::TestFacility test;
+
+  test.runApplication();
+
+  auto fault = test.getScalar<double_t>("/parameters/upperFaultThreshold");
+  auto warning = test.getScalar<double_t>("/parameters/upperWarningThreshold");
+  auto watch = test.getScalar<double_t>("/input/path");
+  auto status = test.getScalar<int32_t>("/output/path");
+
+  fault = 60.0;
+  fault.write();
+  warning = 50.0;
+  warning.write();
+  watch = 40.0;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  // status is OK and data validity also
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+  BOOST_CHECK(status.dataValidity() == ctk::DataValidity::ok);
+
+  watch.setDataValidity(ctk::DataValidity::faulty);
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  // status is not changed as watch is the same, data validity is changed -> test condition: getDataValidity() !=
+  // lastStatusValidity
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
+  BOOST_CHECK(status.dataValidity() == ctk::DataValidity::faulty);
+
+  watch = 55.0;
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  // status is changed, data validity is not changed -> test condition: status.value != newStatus
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::WARNING));
+  BOOST_CHECK(status.dataValidity() == ctk::DataValidity::faulty);
+
+  watch = 70.0;
+  watch.setDataValidity(ctk::DataValidity::ok);
+  watch.write();
+  test.stepApplication();
+  status.readLatest();
+  // status is changed, data validity is changed -> test condition: status.value != newStatus || status.value != newStatus
+  BOOST_CHECK(status == static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
+  BOOST_CHECK(status.dataValidity() == ctk::DataValidity::ok);
+
+  watch = 75.0;
+  watch.setDataValidity(ctk::DataValidity::ok);
+  watch.write();
+  test.stepApplication();
+  // status is not changed, data validity is not changed -> test that there is no new value of status
+  BOOST_CHECK(status.readLatest() == false);
+}