Trigger: make dbus properties writable

This change allows to modify 'Sensors', 'ReportNames' and 'Thresholds'
dbus properties of Trigger interface. They are required by Redfish to
implement PATCH functionality for Trigger schema.

Some backend changes were required to enable this functionality, and as
such few improvements were made for existing code:
- NumericThreshold and DiscreteThreshold now have common implementation
  where it was possible.
- Internal sensor info structure for Trigger is now the same as the one
  used for Report. This resulted in breaking compatibility with previous
  Trigger persistency data.
- Added getInfo / getParams methods for Sensor and Threshold interfaces.
  They are used by Trigger dbus getters and persistency mechanism now,
  instead of storing this data in Trigger object.

Testing done:
- Unit tests were expanded and are passing
- dbus setters for Sensors and Thresholds are working and modifications
  are reflected by calling appropriate getters.

Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
Change-Id: I7a14c15a30d78ce872342b5f938aba43c77be9c0
diff --git a/src/discrete_threshold.cpp b/src/discrete_threshold.cpp
index 3c98c93..a2073b7 100644
--- a/src/discrete_threshold.cpp
+++ b/src/discrete_threshold.cpp
@@ -4,37 +4,39 @@
 
 DiscreteThreshold::DiscreteThreshold(
     boost::asio::io_context& ioc, Sensors sensorsIn,
-    std::vector<std::string> sensorNames,
     std::vector<std::unique_ptr<interfaces::TriggerAction>> actionsIn,
     Milliseconds dwellTimeIn, double thresholdValueIn,
-    const std::string& name) :
+    const std::string& nameIn, const discrete::Severity severityIn) :
     ioc(ioc),
-    sensors(std::move(sensorsIn)), actions(std::move(actionsIn)),
-    dwellTime(dwellTimeIn), thresholdValue(thresholdValueIn), name(name)
+    actions(std::move(actionsIn)), dwellTime(dwellTimeIn),
+    thresholdValue(thresholdValueIn), name(nameIn), severity(severityIn)
 {
-    details.reserve(sensors.size());
-    for (size_t i = 0; i < sensors.size(); i++)
+    for (const auto& sensor : sensorsIn)
     {
-        details.emplace_back(sensorNames[i], false, ioc);
+        sensorDetails.emplace(sensor, makeDetails(sensor->getName()));
     }
 }
 
 void DiscreteThreshold::initialize()
 {
-    for (auto& sensor : sensors)
-    {
-        sensor->registerForUpdates(weak_from_this());
-    }
+    ThresholdOperations().initialize(this);
+}
+
+void DiscreteThreshold::updateSensors(Sensors newSensors)
+{
+    ThresholdOperations().updateSensors(this, std::move(newSensors));
 }
 
 DiscreteThreshold::ThresholdDetail&
-    DiscreteThreshold::getDetails(interfaces::Sensor& sensor)
+    DiscreteThreshold::getDetails(const interfaces::Sensor& sensor)
 {
-    auto it =
-        std::find_if(sensors.begin(), sensors.end(),
-                     [&sensor](const auto& x) { return &sensor == x.get(); });
-    auto index = std::distance(sensors.begin(), it);
-    return details.at(index);
+    return ThresholdOperations().getDetails(this, sensor);
+}
+
+std::shared_ptr<DiscreteThreshold::ThresholdDetail>
+    DiscreteThreshold::makeDetails(const std::string& sensorName)
+{
+    return std::make_shared<ThresholdDetail>(sensorName, false, ioc);
 }
 
 void DiscreteThreshold::sensorUpdated(interfaces::Sensor& sensor,
@@ -44,7 +46,8 @@
 void DiscreteThreshold::sensorUpdated(interfaces::Sensor& sensor,
                                       Milliseconds timestamp, double value)
 {
-    auto& [sensorName, dwell, timer] = getDetails(sensor);
+    auto& details = getDetails(sensor);
+    auto& [sensorName, dwell, timer] = details;
 
     if (thresholdValue)
     {
@@ -55,15 +58,18 @@
         }
         else if (value == thresholdValue)
         {
-            startTimer(sensor, timestamp, value);
+            startTimer(details, timestamp, value);
         }
     }
 }
 
-void DiscreteThreshold::startTimer(interfaces::Sensor& sensor,
+void DiscreteThreshold::startTimer(DiscreteThreshold::ThresholdDetail& details,
                                    Milliseconds timestamp, double value)
 {
-    auto& [sensorName, dwell, timer] = getDetails(sensor);
+    const auto& sensorName = details.sensorName;
+    auto& dwell = details.dwell;
+    auto& timer = details.timer;
+
     if (dwellTime == Milliseconds::zero())
     {
         commit(sensorName, timestamp, value);
@@ -72,7 +78,7 @@
     {
         dwell = true;
         timer.expires_after(dwellTime);
-        timer.async_wait([this, &sensor, timestamp,
+        timer.async_wait([this, &sensorName, &dwell, timestamp,
                           value](const boost::system::error_code ec) {
             if (ec)
             {
@@ -80,7 +86,6 @@
                     "Timer has been canceled");
                 return;
             }
-            auto& [sensorName, dwell, timer] = getDetails(sensor);
             commit(sensorName, timestamp, value);
             dwell = false;
         });
@@ -95,3 +100,9 @@
         action->commit(sensorName, timestamp, value);
     }
 }
+
+LabeledThresholdParam DiscreteThreshold::getThresholdParam() const
+{
+    return discrete::LabeledThresholdParam(name, severity, dwellTime.count(),
+                                           std::to_string(thresholdValue));
+}
diff --git a/src/discrete_threshold.hpp b/src/discrete_threshold.hpp
index c9bca4d..1f145f4 100644
--- a/src/discrete_threshold.hpp
+++ b/src/discrete_threshold.hpp
@@ -6,10 +6,12 @@
 #include "interfaces/trigger_action.hpp"
 #include "types/duration_types.hpp"
 #include "types/trigger_types.hpp"
+#include "utils/threshold_operations.hpp"
 
 #include <boost/asio/steady_timer.hpp>
 
 #include <chrono>
+#include <map>
 #include <memory>
 #include <vector>
 
@@ -21,23 +23,26 @@
   public:
     DiscreteThreshold(
         boost::asio::io_context& ioc, Sensors sensors,
-        std::vector<std::string> sensorNames,
         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions,
-        Milliseconds dwellTime, double thresholdValue, const std::string& name);
+        Milliseconds dwellTime, double thresholdValue, const std::string& name,
+        const discrete::Severity severity);
     DiscreteThreshold(const DiscreteThreshold&) = delete;
     DiscreteThreshold(DiscreteThreshold&&) = delete;
 
     void initialize() override;
     void sensorUpdated(interfaces::Sensor&, Milliseconds) override;
     void sensorUpdated(interfaces::Sensor&, Milliseconds, double) override;
+    LabeledThresholdParam getThresholdParam() const override;
+    void updateSensors(Sensors newSensors) override;
 
   private:
     boost::asio::io_context& ioc;
-    const Sensors sensors;
     const std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
     const Milliseconds dwellTime;
     const double thresholdValue;
     const std::string name;
+    const discrete::Severity severity;
+    bool initialized = false;
 
     struct ThresholdDetail
     {
@@ -51,9 +56,15 @@
             dwell(dwell), timer(ioc)
         {}
     };
-    std::vector<ThresholdDetail> details;
+    using SensorDetails =
+        std::unordered_map<std::shared_ptr<interfaces::Sensor>,
+                           std::shared_ptr<ThresholdDetail>>;
+    SensorDetails sensorDetails;
 
-    void startTimer(interfaces::Sensor&, Milliseconds, double);
+    friend ThresholdOperations;
+
+    void startTimer(ThresholdDetail&, Milliseconds, double);
     void commit(const std::string&, Milliseconds, double);
-    ThresholdDetail& getDetails(interfaces::Sensor& sensor);
+    ThresholdDetail& getDetails(const interfaces::Sensor& sensor);
+    std::shared_ptr<ThresholdDetail> makeDetails(const std::string& sensorName);
 };
diff --git a/src/interfaces/sensor.hpp b/src/interfaces/sensor.hpp
index 6a6bcf7..7764b37 100644
--- a/src/interfaces/sensor.hpp
+++ b/src/interfaces/sensor.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "types/sensor_types.hpp"
+
 #include <chrono>
 #include <memory>
 #include <ostream>
@@ -44,9 +46,12 @@
 
     virtual Id id() const = 0;
     virtual std::string metadata() const = 0;
+    virtual std::string getName() const = 0;
     virtual void registerForUpdates(const std::weak_ptr<SensorListener>&) = 0;
     virtual void
         unregisterFromUpdates(const std::weak_ptr<SensorListener>&) = 0;
+
+    virtual LabeledSensorInfo getLabeledSensorInfo() const = 0;
 };
 
 } // namespace interfaces
diff --git a/src/interfaces/threshold.hpp b/src/interfaces/threshold.hpp
index 23ff9d9..1efee2e 100644
--- a/src/interfaces/threshold.hpp
+++ b/src/interfaces/threshold.hpp
@@ -1,5 +1,8 @@
 #pragma once
 
+#include "interfaces/sensor.hpp"
+#include "types/trigger_types.hpp"
+
 namespace interfaces
 {
 
@@ -9,6 +12,8 @@
     virtual ~Threshold() = default;
 
     virtual void initialize() = 0;
+    virtual LabeledThresholdParam getThresholdParam() const = 0;
+    virtual void updateSensors(Sensors newSensors) = 0;
 };
 
 } // namespace interfaces
diff --git a/src/interfaces/trigger_factory.hpp b/src/interfaces/trigger_factory.hpp
index ee85063..0e7ac7f 100644
--- a/src/interfaces/trigger_factory.hpp
+++ b/src/interfaces/trigger_factory.hpp
@@ -1,8 +1,11 @@
 #pragma once
 
 #include "interfaces/json_storage.hpp"
+#include "interfaces/sensor.hpp"
+#include "interfaces/threshold.hpp"
 #include "interfaces/trigger.hpp"
 #include "interfaces/trigger_manager.hpp"
+#include "sensor.hpp"
 #include "types/trigger_types.hpp"
 
 #include <boost/asio/spawn.hpp>
@@ -31,6 +34,20 @@
     virtual std::vector<LabeledSensorInfo>
         getLabeledSensorsInfo(boost::asio::yield_context& yield,
                               const SensorsInfo& sensorsInfo) const = 0;
+
+    virtual std::vector<LabeledSensorInfo>
+        getLabeledSensorsInfo(const SensorsInfo& sensorsInfo) const = 0;
+
+    virtual void updateThresholds(
+        std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
+        const std::vector<::TriggerAction>& triggerActions,
+        const std::shared_ptr<std::vector<std::string>>& reportIds,
+        const Sensors& sensors,
+        const LabeledTriggerThresholdParams& newParams) const = 0;
+
+    virtual void updateSensors(
+        Sensors& currentSensors,
+        const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const = 0;
 };
 
 } // namespace interfaces
diff --git a/src/metric.cpp b/src/metric.cpp
index 582e506..3e450d6 100644
--- a/src/metric.cpp
+++ b/src/metric.cpp
@@ -2,6 +2,7 @@
 
 #include "details/collection_function.hpp"
 #include "types/report_types.hpp"
+#include "types/sensor_types.hpp"
 #include "utils/labeled_tuple.hpp"
 #include "utils/transform.hpp"
 
@@ -213,8 +214,8 @@
 LabeledMetricParameters Metric::dumpConfiguration() const
 {
     auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
-        return LabeledSensorParameters(sensor->id().service, sensor->id().path,
-                                       sensor->metadata());
+        return LabeledSensorInfo(sensor->id().service, sensor->id().path,
+                                 sensor->metadata());
     });
 
     return LabeledMetricParameters(std::move(sensorPath), operationType, id,
diff --git a/src/numeric_threshold.cpp b/src/numeric_threshold.cpp
index 3bec427..f152e76 100644
--- a/src/numeric_threshold.cpp
+++ b/src/numeric_threshold.cpp
@@ -4,41 +4,40 @@
 
 NumericThreshold::NumericThreshold(
     boost::asio::io_context& ioc, Sensors sensorsIn,
-    std::vector<std::string> sensorNames,
     std::vector<std::unique_ptr<interfaces::TriggerAction>> actionsIn,
-    Milliseconds dwellTimeIn, numeric::Direction direction,
-    double thresholdValueIn) :
+    Milliseconds dwellTimeIn, numeric::Direction directionIn,
+    double thresholdValueIn, numeric::Type typeIn) :
     ioc(ioc),
-    sensors(std::move(sensorsIn)), actions(std::move(actionsIn)),
-    dwellTime(dwellTimeIn), direction(direction),
-    thresholdValue(thresholdValueIn)
+    actions(std::move(actionsIn)), dwellTime(dwellTimeIn),
+    direction(directionIn), thresholdValue(thresholdValueIn), type(typeIn)
 {
-    details.reserve(sensors.size());
-    for (size_t i = 0; i < sensors.size(); i++)
+    for (const auto& sensor : sensorsIn)
     {
-        details.emplace_back(sensorNames[i], thresholdValue, false, ioc);
+        sensorDetails.emplace(sensor, makeDetails(sensor->getName()));
     }
 }
 
-NumericThreshold::~NumericThreshold()
-{}
-
 void NumericThreshold::initialize()
 {
-    for (auto& sensor : sensors)
-    {
-        sensor->registerForUpdates(weak_from_this());
-    }
+    ThresholdOperations().initialize(this);
+}
+
+void NumericThreshold::updateSensors(Sensors newSensors)
+{
+    ThresholdOperations().updateSensors(this, std::move(newSensors));
 }
 
 NumericThreshold::ThresholdDetail&
-    NumericThreshold::getDetails(interfaces::Sensor& sensor)
+    NumericThreshold::getDetails(const interfaces::Sensor& sensor)
 {
-    auto it =
-        std::find_if(sensors.begin(), sensors.end(),
-                     [&sensor](const auto& x) { return &sensor == x.get(); });
-    auto index = std::distance(sensors.begin(), it);
-    return details.at(index);
+    return ThresholdOperations().getDetails(this, sensor);
+}
+
+std::shared_ptr<NumericThreshold::ThresholdDetail>
+    NumericThreshold::makeDetails(const std::string& sensorName)
+{
+    return std::make_shared<ThresholdDetail>(sensorName, thresholdValue, false,
+                                             ioc);
 }
 
 void NumericThreshold::sensorUpdated(interfaces::Sensor& sensor,
@@ -48,7 +47,8 @@
 void NumericThreshold::sensorUpdated(interfaces::Sensor& sensor,
                                      Milliseconds timestamp, double value)
 {
-    auto& [sensorName, prevValue, dwell, timer] = getDetails(sensor);
+    auto& details = getDetails(sensor);
+    auto& [sensorName, prevValue, dwell, timer] = details;
     bool decreasing = thresholdValue < prevValue && thresholdValue > value;
     bool increasing = thresholdValue > prevValue && thresholdValue < value;
 
@@ -61,16 +61,19 @@
         (direction == numeric::Direction::increasing && increasing) ||
         (direction == numeric::Direction::either && (increasing || decreasing)))
     {
-        startTimer(sensorName, timestamp, value, dwell, timer);
+        startTimer(details, timestamp, value);
     }
 
     prevValue = value;
 }
 
-void NumericThreshold::startTimer(const std::string& sensorName,
-                                  Milliseconds timestamp, double value,
-                                  bool& dwell, boost::asio::steady_timer& timer)
+void NumericThreshold::startTimer(NumericThreshold::ThresholdDetail& details,
+                                  Milliseconds timestamp, double value)
 {
+    const auto& sensorName = details.sensorName;
+    auto& dwell = details.dwell;
+    auto& timer = details.timer;
+
     if (dwellTime == Milliseconds::zero())
     {
         commit(sensorName, timestamp, value);
@@ -79,8 +82,8 @@
     {
         dwell = true;
         timer.expires_after(dwellTime);
-        timer.async_wait([this, sensorName, timestamp, value,
-                          &dwell](const boost::system::error_code ec) {
+        timer.async_wait([this, &sensorName, &dwell, timestamp,
+                          value](const boost::system::error_code ec) {
             if (ec)
             {
                 phosphor::logging::log<phosphor::logging::level::DEBUG>(
@@ -101,3 +104,9 @@
         action->commit(sensorName, timestamp, value);
     }
 }
+
+LabeledThresholdParam NumericThreshold::getThresholdParam() const
+{
+    return numeric::LabeledThresholdParam(type, dwellTime.count(), direction,
+                                          thresholdValue);
+}
diff --git a/src/numeric_threshold.hpp b/src/numeric_threshold.hpp
index 2aff15c..02480a4 100644
--- a/src/numeric_threshold.hpp
+++ b/src/numeric_threshold.hpp
@@ -6,10 +6,12 @@
 #include "interfaces/trigger_action.hpp"
 #include "types/duration_types.hpp"
 #include "types/trigger_types.hpp"
+#include "utils/threshold_operations.hpp"
 
 #include <boost/asio/steady_timer.hpp>
 
 #include <chrono>
+#include <map>
 #include <memory>
 #include <vector>
 
@@ -21,23 +23,26 @@
   public:
     NumericThreshold(
         boost::asio::io_context& ioc, Sensors sensors,
-        std::vector<std::string> sensorNames,
         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions,
         Milliseconds dwellTime, numeric::Direction direction,
-        double thresholdValue);
-    ~NumericThreshold();
+        double thresholdValue, numeric::Type type);
+    ~NumericThreshold()
+    {}
 
     void initialize() override;
     void sensorUpdated(interfaces::Sensor&, Milliseconds) override;
     void sensorUpdated(interfaces::Sensor&, Milliseconds, double) override;
+    LabeledThresholdParam getThresholdParam() const override;
+    void updateSensors(Sensors newSensors) override;
 
   private:
     boost::asio::io_context& ioc;
-    const Sensors sensors;
     const std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
     const Milliseconds dwellTime;
     const numeric::Direction direction;
     const double thresholdValue;
+    const numeric::Type type;
+    bool initialized = false;
 
     struct ThresholdDetail
     {
@@ -52,10 +57,15 @@
             prevValue(prevValue), dwell(dwell), timer(ioc)
         {}
     };
-    std::vector<ThresholdDetail> details;
+    using SensorDetails =
+        std::unordered_map<std::shared_ptr<interfaces::Sensor>,
+                           std::shared_ptr<ThresholdDetail>>;
+    SensorDetails sensorDetails;
 
-    void startTimer(const std::string&, Milliseconds, double, bool&,
-                    boost::asio::steady_timer&);
+    friend ThresholdOperations;
+
+    void startTimer(ThresholdDetail&, Milliseconds, double);
     void commit(const std::string&, Milliseconds, double);
-    ThresholdDetail& getDetails(interfaces::Sensor& sensor);
+    ThresholdDetail& getDetails(const interfaces::Sensor& sensor);
+    std::shared_ptr<ThresholdDetail> makeDetails(const std::string& sensorName);
 };
diff --git a/src/on_change_threshold.cpp b/src/on_change_threshold.cpp
index e278c7f..fe3d1dd 100644
--- a/src/on_change_threshold.cpp
+++ b/src/on_change_threshold.cpp
@@ -3,10 +3,10 @@
 #include <phosphor-logging/log.hpp>
 
 OnChangeThreshold::OnChangeThreshold(
-    Sensors sensorsIn, std::vector<std::string> sensorNamesIn,
+    Sensors sensorsIn,
     std::vector<std::unique_ptr<interfaces::TriggerAction>> actionsIn) :
     sensors(std::move(sensorsIn)),
-    sensorNames(std::move(sensorNamesIn)), actions(std::move(actionsIn))
+    actions(std::move(actionsIn))
 {}
 
 void OnChangeThreshold::initialize()
@@ -15,6 +15,39 @@
     {
         sensor->registerForUpdates(weak_from_this());
     }
+    initialized = true;
+}
+
+void OnChangeThreshold::updateSensors(Sensors newSensors)
+{
+    Sensors oldSensors = sensors;
+
+    for (const auto& sensor : newSensors)
+    {
+        auto it =
+            std::find_if(oldSensors.begin(), oldSensors.end(),
+                         [&sensor](const auto& s) { return sensor == s; });
+        if (it != oldSensors.end())
+        {
+            oldSensors.erase(it);
+            continue;
+        }
+
+        if (initialized)
+        {
+            sensor->registerForUpdates(weak_from_this());
+        }
+    }
+
+    if (initialized)
+    {
+        for (auto& sensor : oldSensors)
+        {
+            sensor->unregisterFromUpdates(weak_from_this());
+        }
+    }
+
+    sensors = std::move(newSensors);
 }
 
 void OnChangeThreshold::sensorUpdated(interfaces::Sensor& sensor,
@@ -24,11 +57,7 @@
 void OnChangeThreshold::sensorUpdated(interfaces::Sensor& sensor,
                                       Milliseconds timestamp, double value)
 {
-    auto it =
-        std::find_if(sensors.begin(), sensors.end(),
-                     [&sensor](const auto& x) { return &sensor == x.get(); });
-    auto index = std::distance(sensors.begin(), it);
-    commit(sensorNames.at(index), timestamp, value);
+    commit(sensor.getName(), timestamp, value);
 }
 
 void OnChangeThreshold::commit(const std::string& sensorName,
@@ -39,3 +68,8 @@
         action->commit(sensorName, timestamp, value);
     }
 }
+
+LabeledThresholdParam OnChangeThreshold::getThresholdParam() const
+{
+    return {};
+}
diff --git a/src/on_change_threshold.hpp b/src/on_change_threshold.hpp
index 61c75d5..32a5f65 100644
--- a/src/on_change_threshold.hpp
+++ b/src/on_change_threshold.hpp
@@ -19,7 +19,7 @@
 {
   public:
     OnChangeThreshold(
-        Sensors sensors, std::vector<std::string> sensorNames,
+        Sensors sensors,
         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions);
     ~OnChangeThreshold()
     {}
@@ -27,11 +27,13 @@
     void initialize() override;
     void sensorUpdated(interfaces::Sensor&, Milliseconds) override;
     void sensorUpdated(interfaces::Sensor&, Milliseconds, double) override;
+    LabeledThresholdParam getThresholdParam() const override;
+    void updateSensors(Sensors newSensors) override;
 
   private:
-    const Sensors sensors;
-    const std::vector<std::string> sensorNames;
+    Sensors sensors;
     const std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
+    bool initialized = false;
 
     void commit(const std::string&, Milliseconds, double);
 };
diff --git a/src/report_factory.cpp b/src/report_factory.cpp
index 952b4f2..df71fa0 100644
--- a/src/report_factory.cpp
+++ b/src/report_factory.cpp
@@ -47,13 +47,13 @@
 }
 
 Sensors ReportFactory::getSensors(
-    const std::vector<LabeledSensorParameters>& sensorPaths) const
+    const std::vector<LabeledSensorInfo>& sensorPaths) const
 {
     using namespace utils::tstring;
 
     return utils::transform(
         sensorPaths,
-        [this](const LabeledSensorParameters& sensorPath)
+        [this](const LabeledSensorInfo& sensorPath)
             -> std::shared_ptr<interfaces::Sensor> {
             return sensorCache.makeSensor<Sensor>(
                 sensorPath.at_label<Service>(), sensorPath.at_label<Path>(),
@@ -71,7 +71,7 @@
         auto [sensorPaths, operationType, id, collectionTimeScope,
               collectionDuration] = item;
 
-        std::vector<LabeledSensorParameters> sensorParameters;
+        std::vector<LabeledSensorInfo> sensorParameters;
 
         for (const auto& [sensorPath, metadata] : sensorPaths)
         {
diff --git a/src/report_factory.hpp b/src/report_factory.hpp
index c9209ef..14f9f39 100644
--- a/src/report_factory.hpp
+++ b/src/report_factory.hpp
@@ -3,6 +3,7 @@
 #include "interfaces/report_factory.hpp"
 #include "interfaces/sensor.hpp"
 #include "sensor_cache.hpp"
+#include "types/sensor_types.hpp"
 
 #include <boost/asio/io_context.hpp>
 #include <sdbusplus/asio/object_server.hpp>
@@ -31,8 +32,7 @@
              bool enabled) const override;
 
   private:
-    Sensors getSensors(
-        const std::vector<LabeledSensorParameters>& sensorPaths) const;
+    Sensors getSensors(const std::vector<LabeledSensorInfo>& sensorPaths) const;
 
     std::shared_ptr<sdbusplus::asio::connection> bus;
     std::shared_ptr<sdbusplus::asio::object_server> objServer;
diff --git a/src/sensor.cpp b/src/sensor.cpp
index d81e1a1..2551562 100644
--- a/src/sensor.cpp
+++ b/src/sensor.cpp
@@ -30,6 +30,11 @@
     return sensorMetadata;
 }
 
+std::string Sensor::getName() const
+{
+    return sensorMetadata.empty() ? sensorId.path : sensorMetadata;
+}
+
 void Sensor::async_read()
 {
     uniqueCall([this](auto lock) { async_read(std::move(lock)); });
@@ -179,3 +184,8 @@
         }
     }
 }
+
+LabeledSensorInfo Sensor::getLabeledSensorInfo() const
+{
+    return LabeledSensorInfo(sensorId.service, sensorId.path, sensorMetadata);
+}
diff --git a/src/sensor.hpp b/src/sensor.hpp
index d2c3ec2..92ff02c 100644
--- a/src/sensor.hpp
+++ b/src/sensor.hpp
@@ -29,11 +29,14 @@
 
     Id id() const override;
     std::string metadata() const override;
+    std::string getName() const override;
     void registerForUpdates(
         const std::weak_ptr<interfaces::SensorListener>& weakListener) override;
     void unregisterFromUpdates(
         const std::weak_ptr<interfaces::SensorListener>& weakListener) override;
 
+    LabeledSensorInfo getLabeledSensorInfo() const override;
+
   private:
     static std::optional<double> readValue(const ValueVariant& v);
     static void signalProc(const std::weak_ptr<Sensor>& weakSelf,
diff --git a/src/trigger.cpp b/src/trigger.cpp
index e84a389..db988ff 100644
--- a/src/trigger.cpp
+++ b/src/trigger.cpp
@@ -12,21 +12,18 @@
     boost::asio::io_context& ioc,
     const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
     const std::string& idIn, const std::string& nameIn,
-    const std::vector<std::string>& triggerActionsIn,
-    const std::vector<std::string>& reportIdsIn,
-    const std::vector<LabeledSensorInfo>& LabeledSensorsInfoIn,
-    const LabeledTriggerThresholdParams& labeledThresholdParamsIn,
+    const std::vector<TriggerAction>& triggerActionsIn,
+    const std::shared_ptr<std::vector<std::string>> reportIdsIn,
     std::vector<std::shared_ptr<interfaces::Threshold>>&& thresholdsIn,
     interfaces::TriggerManager& triggerManager,
-    interfaces::JsonStorage& triggerStorageIn) :
+    interfaces::JsonStorage& triggerStorageIn,
+    const interfaces::TriggerFactory& triggerFactory, Sensors sensorsIn) :
     id(idIn),
     name(nameIn), triggerActions(std::move(triggerActionsIn)),
-    path(triggerDir + id), reportIds(reportIdsIn),
-    labeledSensorsInfo(LabeledSensorsInfoIn),
-    labeledThresholdParams(labeledThresholdParamsIn),
+    path(triggerDir + id), reportIds(std::move(reportIdsIn)),
     thresholds(std::move(thresholdsIn)),
     fileName(std::to_string(std::hash<std::string>{}(id))),
-    triggerStorage(triggerStorageIn)
+    triggerStorage(triggerStorageIn), sensors(std::move(sensorsIn))
 {
     deleteIface = objServer->add_unique_interface(
         path, deleteIfaceName, [this, &ioc, &triggerManager](auto& dbusIface) {
@@ -42,7 +39,7 @@
         });
 
     triggerIface = objServer->add_unique_interface(
-        path, triggerIfaceName, [this](auto& dbusIface) {
+        path, triggerIfaceName, [this, &triggerFactory](auto& dbusIface) {
             persistent = storeConfiguration();
             dbusIface.register_property_rw(
                 "Persistent", persistent,
@@ -65,31 +62,64 @@
                 },
                 [this](const auto&) { return persistent; });
 
-            dbusIface.register_property_r(
+            dbusIface.register_property_rw(
                 "Thresholds", TriggerThresholdParams{},
                 sdbusplus::vtable::property_::emits_change,
+                [this, &triggerFactory](auto newVal, auto& oldVal) {
+                    auto newThresholdParams = std::visit(
+                        utils::ToLabeledThresholdParamConversion(), newVal);
+                    triggerFactory.updateThresholds(thresholds, triggerActions,
+                                                    reportIds, sensors,
+                                                    newThresholdParams);
+                    oldVal = std::move(newVal);
+                    return 1;
+                },
                 [this](const auto&) {
-                    return std::visit(
-                        utils::FromLabeledThresholdParamConversion(),
-                        labeledThresholdParams);
+                    return fromLabeledThresholdParam(
+                        utils::transform(thresholds, [](const auto& threshold) {
+                            return threshold->getThresholdParam();
+                        }));
                 });
 
-            dbusIface.register_property_r(
+            dbusIface.register_property_rw(
                 "Sensors", SensorsInfo{},
                 sdbusplus::vtable::property_::emits_change,
+                [this, &triggerFactory](auto newVal, auto& oldVal) {
+                    auto labeledSensorInfo =
+                        triggerFactory.getLabeledSensorsInfo(newVal);
+                    triggerFactory.updateSensors(sensors, labeledSensorInfo);
+                    for (const auto& threshold : thresholds)
+                    {
+                        threshold->updateSensors(sensors);
+                    }
+                    oldVal = std::move(newVal);
+                    return 1;
+                },
                 [this](const auto&) {
-                    return utils::fromLabeledSensorsInfo(labeledSensorsInfo);
+                    return utils::fromLabeledSensorsInfo(
+                        utils::transform(sensors, [](const auto& sensor) {
+                            return sensor->getLabeledSensorInfo();
+                        }));
                 });
 
-            dbusIface.register_property_r(
-                "ReportNames", reportIds,
+            dbusIface.register_property_rw(
+                "ReportNames", std::vector<std::string>{},
                 sdbusplus::vtable::property_::emits_change,
-                [](const auto& x) { return x; });
+                [this](auto newVal, auto& oldVal) {
+                    reportIds->clear();
+                    std::copy(newVal.begin(), newVal.end(),
+                              std::back_inserter(*reportIds));
+                    oldVal = std::move(newVal);
+                    return 1;
+                },
+                [this](const auto&) { return *reportIds; });
 
             dbusIface.register_property_r(
                 "Discrete", false, sdbusplus::vtable::property_::const_,
                 [this](const auto& x) {
-                    return isTriggerThresholdDiscrete(labeledThresholdParams);
+                    return !std::holds_alternative<
+                        numeric::LabeledThresholdParam>(
+                        thresholds.back()->getThresholdParam());
                 });
 
             dbusIface.register_property_rw(
@@ -101,9 +131,14 @@
                 },
                 [this](const auto&) { return name; });
 
-            dbusIface.register_property_r("TriggerActions", triggerActions,
-                                          sdbusplus::vtable::property_::const_,
-                                          [this](const auto& x) { return x; });
+            dbusIface.register_property_r(
+                "TriggerActions", std::vector<std::string>(),
+                sdbusplus::vtable::property_::const_, [this](const auto&) {
+                    return utils::transform(triggerActions,
+                                            [](const auto& action) {
+                                                return actionToString(action);
+                                            });
+                });
         });
 
     for (const auto& threshold : thresholds)
@@ -118,15 +153,27 @@
     {
         nlohmann::json data;
 
+        auto labeledThresholdParams =
+            std::visit(utils::ToLabeledThresholdParamConversion(),
+                       fromLabeledThresholdParam(utils::transform(
+                           thresholds, [](const auto& threshold) {
+                               return threshold->getThresholdParam();
+                           })));
+
         data["Version"] = triggerVersion;
         data["Id"] = id;
         data["Name"] = name;
         data["ThresholdParamsDiscriminator"] = labeledThresholdParams.index();
-        data["TriggerActions"] = triggerActions;
+        data["TriggerActions"] =
+            utils::transform(triggerActions, [](const auto& action) {
+                return actionToString(action);
+            });
         data["ThresholdParams"] =
             utils::labeledThresholdParamsToJson(labeledThresholdParams);
-        data["ReportIds"] = reportIds;
-        data["Sensors"] = labeledSensorsInfo;
+        data["ReportIds"] = *reportIds;
+        data["Sensors"] = utils::transform(sensors, [](const auto& sensor) {
+            return sensor->getLabeledSensorInfo();
+        });
 
         triggerStorage.store(fileName, data);
     }
diff --git a/src/trigger.hpp b/src/trigger.hpp
index 54f67c5..a9348bd 100644
--- a/src/trigger.hpp
+++ b/src/trigger.hpp
@@ -3,6 +3,7 @@
 #include "interfaces/json_storage.hpp"
 #include "interfaces/threshold.hpp"
 #include "interfaces/trigger.hpp"
+#include "interfaces/trigger_factory.hpp"
 #include "interfaces/trigger_manager.hpp"
 #include "types/trigger_types.hpp"
 
@@ -17,13 +18,13 @@
     Trigger(boost::asio::io_context& ioc,
             const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
             const std::string& id, const std::string& name,
-            const std::vector<std::string>& triggerActions,
-            const std::vector<std::string>& reportIds,
-            const std::vector<LabeledSensorInfo>& LabeledSensorsInfoIn,
-            const LabeledTriggerThresholdParams& labeledThresholdParamsIn,
+            const std::vector<TriggerAction>& triggerActions,
+            const std::shared_ptr<std::vector<std::string>> reportIds,
             std::vector<std::shared_ptr<interfaces::Threshold>>&& thresholds,
             interfaces::TriggerManager& triggerManager,
-            interfaces::JsonStorage& triggerStorage);
+            interfaces::JsonStorage& triggerStorage,
+            const interfaces::TriggerFactory& triggerFactory,
+            Sensors sensorsIn);
 
     Trigger(const Trigger&) = delete;
     Trigger(Trigger&&) = delete;
@@ -45,18 +46,17 @@
   private:
     std::string id;
     std::string name;
-    std::vector<std::string> triggerActions;
+    std::vector<TriggerAction> triggerActions;
     std::string path;
     bool persistent = false;
-    std::vector<std::string> reportIds;
-    std::vector<LabeledSensorInfo> labeledSensorsInfo;
-    LabeledTriggerThresholdParams labeledThresholdParams;
+    std::shared_ptr<std::vector<std::string>> reportIds;
     std::unique_ptr<sdbusplus::asio::dbus_interface> deleteIface;
     std::unique_ptr<sdbusplus::asio::dbus_interface> triggerIface;
     std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
 
     interfaces::JsonStorage::FilePath fileName;
     interfaces::JsonStorage& triggerStorage;
+    Sensors sensors;
 
   public:
     static constexpr const char* triggerIfaceName =
@@ -65,5 +65,5 @@
         "/xyz/openbmc_project/Telemetry/Triggers/";
     static constexpr const char* deleteIfaceName =
         "xyz.openbmc_project.Object.Delete";
-    static constexpr size_t triggerVersion = 0;
+    static constexpr size_t triggerVersion = 1;
 };
diff --git a/src/trigger_actions.cpp b/src/trigger_actions.cpp
index 22c81a3..0a627e6 100644
--- a/src/trigger_actions.cpp
+++ b/src/trigger_actions.cpp
@@ -99,7 +99,7 @@
     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
     const std::vector<TriggerAction>& ActionsEnum, ::numeric::Type type,
     double thresholdValue, interfaces::ReportManager& reportManager,
-    const std::vector<std::string>& reportIds)
+    const std::shared_ptr<std::vector<std::string>>& reportIds)
 {
     actionsIf.reserve(ActionsEnum.size());
     for (auto actionType : ActionsEnum)
@@ -185,7 +185,7 @@
     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
     const std::vector<TriggerAction>& ActionsEnum,
     ::discrete::Severity severity, interfaces::ReportManager& reportManager,
-    const std::vector<std::string>& reportIds)
+    const std::shared_ptr<std::vector<std::string>>& reportIds)
 {
     actionsIf.reserve(ActionsEnum.size());
     for (auto actionType : ActionsEnum)
@@ -241,7 +241,7 @@
     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
     const std::vector<TriggerAction>& ActionsEnum,
     interfaces::ReportManager& reportManager,
-    const std::vector<std::string>& reportIds)
+    const std::shared_ptr<std::vector<std::string>>& reportIds)
 {
     actionsIf.reserve(ActionsEnum.size());
     for (auto actionType : ActionsEnum)
@@ -272,7 +272,7 @@
 
 void UpdateReport::commit(const std::string&, Milliseconds, double)
 {
-    for (const auto& name : reportIds)
+    for (const auto& name : *reportIds)
     {
         reportManager.updateReport(name);
     }
diff --git a/src/trigger_actions.hpp b/src/trigger_actions.hpp
index 6a5f7c2..99a1baa 100644
--- a/src/trigger_actions.hpp
+++ b/src/trigger_actions.hpp
@@ -45,7 +45,7 @@
     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
     const std::vector<TriggerAction>& ActionsEnum, ::numeric::Type type,
     double thresholdValue, interfaces::ReportManager& reportManager,
-    const std::vector<std::string>& reportIds);
+    const std::shared_ptr<std::vector<std::string>>& reportIds);
 } // namespace numeric
 
 namespace discrete
@@ -84,7 +84,7 @@
     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
     const std::vector<TriggerAction>& ActionsEnum,
     ::discrete::Severity severity, interfaces::ReportManager& reportManager,
-    const std::vector<std::string>& reportIds);
+    const std::shared_ptr<std::vector<std::string>>& reportIds);
 
 namespace onChange
 {
@@ -112,7 +112,7 @@
     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
     const std::vector<TriggerAction>& ActionsEnum,
     interfaces::ReportManager& reportManager,
-    const std::vector<std::string>& reportIds);
+    const std::shared_ptr<std::vector<std::string>>& reportIds);
 } // namespace onChange
 
 } // namespace discrete
@@ -121,7 +121,7 @@
 {
   public:
     UpdateReport(interfaces::ReportManager& reportManager,
-                 std::vector<std::string> ids) :
+                 std::shared_ptr<std::vector<std::string>> ids) :
         reportManager(reportManager),
         reportIds(std::move(ids))
     {}
@@ -131,6 +131,6 @@
 
   private:
     interfaces::ReportManager& reportManager;
-    std::vector<std::string> reportIds;
+    std::shared_ptr<std::vector<std::string>> reportIds;
 };
 } // namespace action
diff --git a/src/trigger_factory.cpp b/src/trigger_factory.cpp
index 493fe32..c8b464e 100644
--- a/src/trigger_factory.cpp
+++ b/src/trigger_factory.cpp
@@ -20,126 +20,284 @@
     reportManager(reportManager)
 {}
 
+void TriggerFactory::updateDiscreteThresholds(
+    std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
+    const std::vector<TriggerAction>& triggerActions,
+    const std::shared_ptr<std::vector<std::string>>& reportIds,
+    const Sensors& sensors,
+    const std::vector<discrete::LabeledThresholdParam>& newParams) const
+{
+    auto oldThresholds = currentThresholds;
+    std::vector<std::shared_ptr<interfaces::Threshold>> newThresholds;
+
+    bool isCurrentOnChange = false;
+    if (oldThresholds.size() == 1 &&
+        std::holds_alternative<std::monostate>(
+            oldThresholds.back()->getThresholdParam()))
+    {
+        isCurrentOnChange = true;
+    }
+
+    newThresholds.reserve(newParams.size());
+
+    if (!isCurrentOnChange)
+    {
+        for (const auto& labeledThresholdParam : newParams)
+        {
+            auto paramChecker = [labeledThresholdParam](auto threshold) {
+                return labeledThresholdParam ==
+                       std::get<discrete::LabeledThresholdParam>(
+                           threshold->getThresholdParam());
+            };
+            if (auto existing = std::find_if(oldThresholds.begin(),
+                                             oldThresholds.end(), paramChecker);
+                existing != oldThresholds.end())
+            {
+                newThresholds.emplace_back(*existing);
+                oldThresholds.erase(existing);
+                continue;
+            }
+
+            makeDiscreteThreshold(newThresholds, triggerActions, reportIds,
+                                  sensors, labeledThresholdParam);
+        }
+    }
+    else
+    {
+        for (const auto& labeledThresholdParam : newParams)
+        {
+            makeDiscreteThreshold(newThresholds, triggerActions, reportIds,
+                                  sensors, labeledThresholdParam);
+        }
+    }
+    if (newParams.empty())
+    {
+        if (isCurrentOnChange)
+        {
+            newThresholds.emplace_back(*oldThresholds.begin());
+        }
+        else
+        {
+            makeOnChangeThreshold(newThresholds, triggerActions, reportIds,
+                                  sensors);
+        }
+    }
+    currentThresholds = std::move(newThresholds);
+}
+
+void TriggerFactory::updateNumericThresholds(
+    std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
+    const std::vector<TriggerAction>& triggerActions,
+    const std::shared_ptr<std::vector<std::string>>& reportIds,
+    const Sensors& sensors,
+    const std::vector<numeric::LabeledThresholdParam>& newParams) const
+{
+    auto oldThresholds = currentThresholds;
+    std::vector<std::shared_ptr<interfaces::Threshold>> newThresholds;
+
+    newThresholds.reserve(newParams.size());
+
+    for (const auto& labeledThresholdParam : newParams)
+    {
+        auto paramChecker = [labeledThresholdParam](auto threshold) {
+            return labeledThresholdParam ==
+                   std::get<numeric::LabeledThresholdParam>(
+                       threshold->getThresholdParam());
+        };
+        if (auto existing = std::find_if(oldThresholds.begin(),
+                                         oldThresholds.end(), paramChecker);
+            existing != oldThresholds.end())
+        {
+            newThresholds.emplace_back(*existing);
+            oldThresholds.erase(existing);
+            continue;
+        }
+
+        makeNumericThreshold(newThresholds, triggerActions, reportIds, sensors,
+                             labeledThresholdParam);
+    }
+    currentThresholds = std::move(newThresholds);
+}
+
+void TriggerFactory::updateThresholds(
+    std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
+    const std::vector<TriggerAction>& triggerActions,
+    const std::shared_ptr<std::vector<std::string>>& reportIds,
+    const Sensors& sensors,
+    const LabeledTriggerThresholdParams& newParams) const
+{
+    if (isTriggerThresholdDiscrete(newParams))
+    {
+        const auto& labeledDiscreteThresholdParams =
+            std::get<std::vector<discrete::LabeledThresholdParam>>(newParams);
+
+        updateDiscreteThresholds(currentThresholds, triggerActions, reportIds,
+                                 sensors, labeledDiscreteThresholdParams);
+    }
+    else
+    {
+        const auto& labeledNumericThresholdParams =
+            std::get<std::vector<numeric::LabeledThresholdParam>>(newParams);
+
+        updateNumericThresholds(currentThresholds, triggerActions, reportIds,
+                                sensors, labeledNumericThresholdParams);
+    }
+}
+
+void TriggerFactory::makeDiscreteThreshold(
+    std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
+    const std::vector<TriggerAction>& triggerActions,
+    const std::shared_ptr<std::vector<std::string>>& reportIds,
+    const Sensors& sensors,
+    const discrete::LabeledThresholdParam& thresholdParam) const
+{
+    std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
+
+    std::string thresholdName = thresholdParam.at_label<ts::UserId>();
+    discrete::Severity severity = thresholdParam.at_label<ts::Severity>();
+    auto dwellTime = Milliseconds(thresholdParam.at_label<ts::DwellTime>());
+    std::string thresholdValue = thresholdParam.at_label<ts::ThresholdValue>();
+
+    action::discrete::fillActions(actions, triggerActions, severity,
+                                  reportManager, reportIds);
+
+    thresholds.emplace_back(std::make_shared<DiscreteThreshold>(
+        bus->get_io_context(), sensors, std::move(actions),
+        Milliseconds(dwellTime), std::stod(thresholdValue), thresholdName,
+        severity));
+}
+
+void TriggerFactory::makeNumericThreshold(
+    std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
+    const std::vector<TriggerAction>& triggerActions,
+    const std::shared_ptr<std::vector<std::string>>& reportIds,
+    const Sensors& sensors,
+    const numeric::LabeledThresholdParam& thresholdParam) const
+{
+    std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
+
+    auto type = thresholdParam.at_label<ts::Type>();
+    auto dwellTime = Milliseconds(thresholdParam.at_label<ts::DwellTime>());
+    auto direction = thresholdParam.at_label<ts::Direction>();
+    auto thresholdValue = double{thresholdParam.at_label<ts::ThresholdValue>()};
+
+    action::numeric::fillActions(actions, triggerActions, type, thresholdValue,
+                                 reportManager, reportIds);
+
+    thresholds.emplace_back(std::make_shared<NumericThreshold>(
+        bus->get_io_context(), sensors, std::move(actions), dwellTime,
+        direction, thresholdValue, type));
+}
+
+void TriggerFactory::makeOnChangeThreshold(
+    std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
+    const std::vector<TriggerAction>& triggerActions,
+    const std::shared_ptr<std::vector<std::string>>& reportIds,
+    const Sensors& sensors) const
+{
+    std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
+
+    action::discrete::onChange::fillActions(actions, triggerActions,
+                                            reportManager, reportIds);
+
+    thresholds.emplace_back(
+        std::make_shared<OnChangeThreshold>(sensors, std::move(actions)));
+}
+
 std::unique_ptr<interfaces::Trigger> TriggerFactory::make(
     const std::string& id, const std::string& name,
     const std::vector<std::string>& triggerActionsIn,
-    const std::vector<std::string>& reportIds,
+    const std::vector<std::string>& reportIdsIn,
     interfaces::TriggerManager& triggerManager,
     interfaces::JsonStorage& triggerStorage,
     const LabeledTriggerThresholdParams& labeledThresholdParams,
     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
 {
-    const auto& [sensors, sensorNames] = getSensors(labeledSensorsInfo);
-    std::vector<TriggerAction> triggerActions;
-    std::transform(triggerActionsIn.begin(), triggerActionsIn.end(),
-                   std::back_inserter(triggerActions),
-                   [](const auto& triggerActionStr) {
-                       return toTriggerAction(triggerActionStr);
-                   });
+    const auto& sensors = getSensors(labeledSensorsInfo);
+    auto triggerActions =
+        utils::transform(triggerActionsIn, [](const auto& triggerActionStr) {
+            return toTriggerAction(triggerActionStr);
+        });
     std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
 
-    if (isTriggerThresholdDiscrete(labeledThresholdParams))
-    {
-        const auto& labeledDiscreteThresholdParams =
-            std::get<std::vector<discrete::LabeledThresholdParam>>(
-                labeledThresholdParams);
-        for (const auto& labeledThresholdParam : labeledDiscreteThresholdParams)
-        {
-            std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
+    auto reportIds = std::make_shared<std::vector<std::string>>(reportIdsIn);
 
-            std::string thresholdName =
-                labeledThresholdParam.at_label<ts::UserId>();
-            discrete::Severity severity =
-                labeledThresholdParam.at_label<ts::Severity>();
-            auto dwellTime =
-                Milliseconds(labeledThresholdParam.at_label<ts::DwellTime>());
-            std::string thresholdValue =
-                labeledThresholdParam.at_label<ts::ThresholdValue>();
-
-            action::discrete::fillActions(actions, triggerActions, severity,
-                                          reportManager, reportIds);
-
-            thresholds.emplace_back(std::make_shared<DiscreteThreshold>(
-                bus->get_io_context(), sensors, sensorNames, std::move(actions),
-                Milliseconds(dwellTime), std::stod(thresholdValue),
-                thresholdName));
-        }
-        if (labeledDiscreteThresholdParams.empty())
-        {
-            std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
-            action::discrete::onChange::fillActions(actions, triggerActions,
-                                                    reportManager, reportIds);
-
-            thresholds.emplace_back(std::make_shared<OnChangeThreshold>(
-                sensors, sensorNames, std::move(actions)));
-        }
-    }
-    else
-    {
-        const auto& labeledNumericThresholdParams =
-            std::get<std::vector<numeric::LabeledThresholdParam>>(
-                labeledThresholdParams);
-
-        for (const auto& labeledThresholdParam : labeledNumericThresholdParams)
-        {
-            std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
-            auto type = labeledThresholdParam.at_label<ts::Type>();
-            auto dwellTime =
-                Milliseconds(labeledThresholdParam.at_label<ts::DwellTime>());
-            auto direction = labeledThresholdParam.at_label<ts::Direction>();
-            auto thresholdValue =
-                double{labeledThresholdParam.at_label<ts::ThresholdValue>()};
-
-            action::numeric::fillActions(actions, triggerActions, type,
-                                         thresholdValue, reportManager,
-                                         reportIds);
-
-            thresholds.emplace_back(std::make_shared<NumericThreshold>(
-                bus->get_io_context(), sensors, sensorNames, std::move(actions),
-                dwellTime, direction, thresholdValue));
-        }
-    }
+    updateThresholds(thresholds, triggerActions, reportIds, sensors,
+                     labeledThresholdParams);
 
     return std::make_unique<Trigger>(
-        bus->get_io_context(), objServer, id, name, triggerActionsIn, reportIds,
-        labeledSensorsInfo, labeledThresholdParams, std::move(thresholds),
-        triggerManager, triggerStorage);
+        bus->get_io_context(), objServer, id, name, triggerActions, reportIds,
+        std::move(thresholds), triggerManager, triggerStorage, *this, sensors);
 }
 
-std::pair<Sensors, std::vector<std::string>> TriggerFactory::getSensors(
+Sensors TriggerFactory::getSensors(
     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
 {
     Sensors sensors;
-    std::vector<std::string> sensorNames;
+    updateSensors(sensors, labeledSensorsInfo);
+    return sensors;
+}
+
+void TriggerFactory::updateSensors(
+    Sensors& currentSensors,
+    const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
+{
+    Sensors oldSensors = currentSensors;
+    Sensors newSensors;
 
     for (const auto& labeledSensorInfo : labeledSensorsInfo)
     {
+        auto existing = std::find_if(oldSensors.begin(), oldSensors.end(),
+                                     [labeledSensorInfo](auto sensor) {
+                                         return labeledSensorInfo ==
+                                                sensor->getLabeledSensorInfo();
+                                     });
+
+        if (existing != oldSensors.end())
+        {
+            newSensors.emplace_back(*existing);
+            oldSensors.erase(existing);
+            continue;
+        }
+
         const auto& service = labeledSensorInfo.at_label<ts::Service>();
-        const auto& sensorPath = labeledSensorInfo.at_label<ts::SensorPath>();
+        const auto& sensorPath = labeledSensorInfo.at_label<ts::Path>();
         const auto& metadata = labeledSensorInfo.at_label<ts::Metadata>();
 
-        sensors.emplace_back(sensorCache.makeSensor<Sensor>(
+        newSensors.emplace_back(sensorCache.makeSensor<Sensor>(
             service, sensorPath, metadata, bus->get_io_context(), bus));
-
-        if (metadata.empty())
-        {
-            sensorNames.emplace_back(sensorPath);
-        }
-        else
-        {
-            sensorNames.emplace_back(metadata);
-        }
     }
 
-    return {sensors, sensorNames};
+    currentSensors = std::move(newSensors);
 }
 
 std::vector<LabeledSensorInfo>
     TriggerFactory::getLabeledSensorsInfo(boost::asio::yield_context& yield,
                                           const SensorsInfo& sensorsInfo) const
 {
+    if (sensorsInfo.empty())
+    {
+        return {};
+    }
     auto tree = utils::getSubTreeSensors(yield, bus);
+    return parseSensorTree(tree, sensorsInfo);
+}
 
+std::vector<LabeledSensorInfo>
+    TriggerFactory::getLabeledSensorsInfo(const SensorsInfo& sensorsInfo) const
+{
+    if (sensorsInfo.empty())
+    {
+        return {};
+    }
+    auto tree = utils::getSubTreeSensors(bus);
+    return parseSensorTree(tree, sensorsInfo);
+}
+
+std::vector<LabeledSensorInfo>
+    TriggerFactory::parseSensorTree(const std::vector<utils::SensorTree>& tree,
+                                    const SensorsInfo& sensorsInfo)
+{
     return utils::transform(sensorsInfo, [&tree](const auto& item) {
         const auto& [sensorPath, metadata] = item;
         auto found = std::find_if(
diff --git a/src/trigger_factory.hpp b/src/trigger_factory.hpp
index 494f5ab..223efe0 100644
--- a/src/trigger_factory.hpp
+++ b/src/trigger_factory.hpp
@@ -2,8 +2,10 @@
 
 #include "interfaces/report_manager.hpp"
 #include "interfaces/sensor.hpp"
+#include "interfaces/threshold.hpp"
 #include "interfaces/trigger_factory.hpp"
 #include "sensor_cache.hpp"
+#include "utils/dbus_mapper.hpp"
 
 #include <sdbusplus/asio/object_server.hpp>
 
@@ -28,6 +30,19 @@
     std::vector<LabeledSensorInfo>
         getLabeledSensorsInfo(boost::asio::yield_context& yield,
                               const SensorsInfo& sensorsInfo) const override;
+    std::vector<LabeledSensorInfo>
+        getLabeledSensorsInfo(const SensorsInfo& sensorsInfo) const override;
+
+    void updateThresholds(
+        std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
+        const std::vector<TriggerAction>& triggerActions,
+        const std::shared_ptr<std::vector<std::string>>& reportIds,
+        const Sensors& sensors,
+        const LabeledTriggerThresholdParams& newParams) const override;
+
+    void updateSensors(Sensors& currentSensors,
+                       const std::vector<LabeledSensorInfo>& labeledSensorsInfo)
+        const override;
 
   private:
     std::shared_ptr<sdbusplus::asio::connection> bus;
@@ -35,6 +50,44 @@
     SensorCache& sensorCache;
     interfaces::ReportManager& reportManager;
 
-    std::pair<Sensors, std::vector<std::string>> getSensors(
+    Sensors getSensors(
         const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const;
+
+    static std::vector<LabeledSensorInfo>
+        parseSensorTree(const std::vector<utils::SensorTree>& tree,
+                        const SensorsInfo& sensorsInfo);
+
+    void updateDiscreteThresholds(
+        std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
+        const std::vector<TriggerAction>& triggerActions,
+        const std::shared_ptr<std::vector<std::string>>& reportIds,
+        const Sensors& sensors,
+        const std::vector<discrete::LabeledThresholdParam>& newParams) const;
+
+    void updateNumericThresholds(
+        std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
+        const std::vector<TriggerAction>& triggerActions,
+        const std::shared_ptr<std::vector<std::string>>& reportIds,
+        const Sensors& sensors,
+        const std::vector<numeric::LabeledThresholdParam>& newParams) const;
+
+    void makeDiscreteThreshold(
+        std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
+        const std::vector<TriggerAction>& triggerActions,
+        const std::shared_ptr<std::vector<std::string>>& reportIds,
+        const Sensors& sensors,
+        const discrete::LabeledThresholdParam& thresholdParam) const;
+
+    void makeNumericThreshold(
+        std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
+        const std::vector<TriggerAction>& triggerActions,
+        const std::shared_ptr<std::vector<std::string>>& reportIds,
+        const Sensors& sensors,
+        const numeric::LabeledThresholdParam& thresholdParam) const;
+
+    void makeOnChangeThreshold(
+        std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
+        const std::vector<TriggerAction>& triggerActions,
+        const std::shared_ptr<std::vector<std::string>>& reportIds,
+        const Sensors& sensors) const;
 };
diff --git a/src/types/report_types.cpp b/src/types/report_types.cpp
index 47add24..72bce4b 100644
--- a/src/types/report_types.cpp
+++ b/src/types/report_types.cpp
@@ -12,7 +12,7 @@
             return ReadingParameters::value_type(
                 utils::transform(
                     metricParams.at_label<ts::SensorPath>(),
-                    [](const LabeledSensorParameters& sensorParameters) {
+                    [](const LabeledSensorInfo& sensorParameters) {
                         return std::tuple<sdbusplus::message::object_path,
                                           std::string>(
                             sensorParameters.at_label<ts::Path>(),
diff --git a/src/types/report_types.hpp b/src/types/report_types.hpp
index 978fd18..c0f7b39 100644
--- a/src/types/report_types.hpp
+++ b/src/types/report_types.hpp
@@ -3,6 +3,7 @@
 #include "types/collection_duration.hpp"
 #include "types/collection_time_scope.hpp"
 #include "types/operation_type.hpp"
+#include "types/sensor_types.hpp"
 #include "utils/labeled_tuple.hpp"
 #include "utils/tstring.hpp"
 
@@ -22,13 +23,8 @@
     std::vector<std::tuple<sdbusplus::message::object_path, std::string>>,
     std::string, std::string, std::string, uint64_t>>;
 
-using LabeledSensorParameters =
-    utils::LabeledTuple<std::tuple<std::string, std::string, std::string>,
-                        utils::tstring::Service, utils::tstring::Path,
-                        utils::tstring::Metadata>;
-
 using LabeledMetricParameters = utils::LabeledTuple<
-    std::tuple<std::vector<LabeledSensorParameters>, OperationType, std::string,
+    std::tuple<std::vector<LabeledSensorInfo>, OperationType, std::string,
                CollectionTimeScope, CollectionDuration>,
     utils::tstring::SensorPath, utils::tstring::OperationType,
     utils::tstring::Id, utils::tstring::CollectionTimeScope,
diff --git a/src/types/sensor_types.hpp b/src/types/sensor_types.hpp
new file mode 100644
index 0000000..e76b345
--- /dev/null
+++ b/src/types/sensor_types.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "utils/labeled_tuple.hpp"
+#include "utils/tstring.hpp"
+
+#include <tuple>
+#include <vector>
+
+using SensorsInfo =
+    std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
+
+using LabeledSensorInfo =
+    utils::LabeledTuple<std::tuple<std::string, std::string, std::string>,
+                        utils::tstring::Service, utils::tstring::Path,
+                        utils::tstring::Metadata>;
diff --git a/src/types/trigger_types.hpp b/src/types/trigger_types.hpp
index b21e8eb..778a9a1 100644
--- a/src/types/trigger_types.hpp
+++ b/src/types/trigger_types.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "types/sensor_types.hpp"
 #include "utils/conversion.hpp"
 #include "utils/labeled_tuple.hpp"
 #include "utils/tstring.hpp"
@@ -31,6 +32,11 @@
     return utils::toEnum(details::convDataTriggerAction, str);
 }
 
+inline std::string actionToString(TriggerAction v)
+{
+    return std::string(utils::enumToString(details::convDataTriggerAction, v));
+}
+
 namespace discrete
 {
 
@@ -132,14 +138,6 @@
                         utils::tstring::ThresholdValue>;
 } // namespace numeric
 
-using SensorsInfo =
-    std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
-
-using LabeledSensorInfo =
-    utils::LabeledTuple<std::tuple<std::string, std::string, std::string>,
-                        utils::tstring::Service, utils::tstring::SensorPath,
-                        utils::tstring::Metadata>;
-
 using TriggerThresholdParamsExt =
     std::variant<std::monostate, std::vector<numeric::ThresholdParam>,
                  std::vector<discrete::ThresholdParam>>;
@@ -152,6 +150,10 @@
     std::variant<std::vector<numeric::LabeledThresholdParam>,
                  std::vector<discrete::LabeledThresholdParam>>;
 
+using LabeledThresholdParam =
+    std::variant<std::monostate, numeric::LabeledThresholdParam,
+                 discrete::LabeledThresholdParam>;
+
 inline bool
     isTriggerThresholdDiscrete(const LabeledTriggerThresholdParams& params)
 {
diff --git a/src/utils/conversion_trigger.cpp b/src/utils/conversion_trigger.cpp
index ab06a48..e1b835a 100644
--- a/src/utils/conversion_trigger.cpp
+++ b/src/utils/conversion_trigger.cpp
@@ -1,6 +1,7 @@
 #include "utils/conversion_trigger.hpp"
 
 #include "utils/transform.hpp"
+#include "utils/tstring.hpp"
 
 #include <sdbusplus/exception.hpp>
 
@@ -72,11 +73,61 @@
 {
     return utils::transform(infos, [](const LabeledSensorInfo& val) {
         return SensorsInfo::value_type(
-            sdbusplus::message::object_path(val.at_label<ts::SensorPath>()),
+            sdbusplus::message::object_path(val.at_label<ts::Path>()),
             val.at_label<ts::Metadata>());
     });
 }
 
+TriggerThresholdParams
+    fromLabeledThresholdParam(const std::vector<LabeledThresholdParam>& params)
+{
+    namespace ts = utils::tstring;
+    if (isFirstElementOfType<std::monostate>(params))
+    {
+        return std::vector<discrete::ThresholdParam>();
+    }
+
+    if (isFirstElementOfType<discrete::LabeledThresholdParam>(params))
+    {
+        return utils::transform(params, [](const auto& param) {
+            const discrete::LabeledThresholdParam* paramUnpacked =
+                std::get_if<discrete::LabeledThresholdParam>(&param);
+            if (!paramUnpacked)
+            {
+                throw std::runtime_error(
+                    "Mixing threshold types is not allowed");
+            }
+            return discrete::ThresholdParam(
+                paramUnpacked->at_label<ts::UserId>(),
+                discrete::severityToString(
+                    paramUnpacked->at_label<ts::Severity>()),
+                paramUnpacked->at_label<ts::DwellTime>(),
+                paramUnpacked->at_label<ts::ThresholdValue>());
+        });
+    }
+
+    if (isFirstElementOfType<numeric::LabeledThresholdParam>(params))
+    {
+        return utils::transform(params, [](const auto& param) {
+            const numeric::LabeledThresholdParam* paramUnpacked =
+                std::get_if<numeric::LabeledThresholdParam>(&param);
+            if (!paramUnpacked)
+            {
+                throw std::runtime_error(
+                    "Mixing threshold types is not allowed");
+            }
+            return numeric::ThresholdParam(
+                numeric::typeToString(paramUnpacked->at_label<ts::Type>()),
+                paramUnpacked->at_label<ts::DwellTime>(),
+                numeric::directionToString(
+                    paramUnpacked->at_label<ts::Direction>()),
+                paramUnpacked->at_label<ts::ThresholdValue>());
+        });
+    }
+
+    throw std::runtime_error("Incorrect threshold params");
+}
+
 nlohmann::json labeledThresholdParamsToJson(
     const LabeledTriggerThresholdParams& labeledThresholdParams)
 {
diff --git a/src/utils/conversion_trigger.hpp b/src/utils/conversion_trigger.hpp
index 5229e5e..a749962 100644
--- a/src/utils/conversion_trigger.hpp
+++ b/src/utils/conversion_trigger.hpp
@@ -27,7 +27,32 @@
 
 SensorsInfo fromLabeledSensorsInfo(const std::vector<LabeledSensorInfo>& infos);
 
+TriggerThresholdParams
+    fromLabeledThresholdParam(const std::vector<LabeledThresholdParam>& params);
+
 nlohmann::json labeledThresholdParamsToJson(
     const LabeledTriggerThresholdParams& labeledThresholdParams);
 
+template <typename T>
+struct is_variant : std::false_type
+{};
+
+template <typename... Args>
+struct is_variant<std::variant<Args...>> : std::true_type
+{};
+
+template <typename T>
+inline constexpr bool is_variant_v = is_variant<T>::value;
+
+template <typename AlternativeT, typename VariantT>
+requires is_variant_v<VariantT>
+bool isFirstElementOfType(const std::vector<VariantT>& collection)
+{
+    if (collection.empty())
+    {
+        return false;
+    }
+    return std::holds_alternative<AlternativeT>(*collection.begin());
+}
+
 } // namespace utils
diff --git a/src/utils/dbus_mapper.hpp b/src/utils/dbus_mapper.hpp
index 78cf7b2..5f07445 100644
--- a/src/utils/dbus_mapper.hpp
+++ b/src/utils/dbus_mapper.hpp
@@ -17,19 +17,20 @@
 using SensorIfaces = std::vector<std::pair<ServiceName, Ifaces>>;
 using SensorTree = std::pair<SensorPath, SensorIfaces>;
 
+constexpr std::array<const char*, 1> sensorInterfaces = {
+    "xyz.openbmc_project.Sensor.Value"};
+
 inline std::vector<SensorTree>
     getSubTreeSensors(boost::asio::yield_context& yield,
                       const std::shared_ptr<sdbusplus::asio::connection>& bus)
 {
-    std::array<const char*, 1> interfaces = {
-        "xyz.openbmc_project.Sensor.Value"};
     boost::system::error_code ec;
 
     auto tree = bus->yield_method_call<std::vector<SensorTree>>(
         yield, ec, "xyz.openbmc_project.ObjectMapper",
         "/xyz/openbmc_project/object_mapper",
         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
-        "/xyz/openbmc_project/sensors", 2, interfaces);
+        "/xyz/openbmc_project/sensors", 2, sensorInterfaces);
     if (ec)
     {
         throw std::runtime_error("Failed to query ObjectMapper!");
@@ -37,4 +38,20 @@
     return tree;
 }
 
+inline std::vector<SensorTree>
+    getSubTreeSensors(const std::shared_ptr<sdbusplus::asio::connection>& bus)
+{
+    auto method_call =
+        bus->new_method_call("xyz.openbmc_project.ObjectMapper",
+                             "/xyz/openbmc_project/object_mapper",
+                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
+    method_call.append("/xyz/openbmc_project/sensors/", 2, sensorInterfaces);
+    auto reply = bus->call(method_call);
+
+    std::vector<SensorTree> tree;
+    reply.read(tree);
+
+    return tree;
+}
+
 } // namespace utils
diff --git a/src/utils/threshold_operations.hpp b/src/utils/threshold_operations.hpp
new file mode 100644
index 0000000..04b0195
--- /dev/null
+++ b/src/utils/threshold_operations.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "interfaces/sensor.hpp"
+
+#include <boost/asio/io_context.hpp>
+
+struct ThresholdOperations
+{
+    template <typename ThresholdType>
+    void initialize(ThresholdType* thresholdPtr)
+    {
+        for ([[maybe_unused]] auto& [sensor, detail] :
+             thresholdPtr->sensorDetails)
+        {
+            sensor->registerForUpdates(thresholdPtr->weak_from_this());
+        }
+        thresholdPtr->initialized = true;
+    }
+
+    template <typename ThresholdType>
+    typename ThresholdType::ThresholdDetail&
+        getDetails(ThresholdType* thresholdPtr,
+                   const interfaces::Sensor& sensor)
+    {
+        auto it = std::find_if(
+            thresholdPtr->sensorDetails.begin(),
+            thresholdPtr->sensorDetails.end(),
+            [&sensor](const auto& x) { return &sensor == x.first.get(); });
+        return *it->second;
+    }
+
+    template <typename ThresholdType>
+    void updateSensors(ThresholdType* thresholdPtr, Sensors newSensors)
+    {
+        typename ThresholdType::SensorDetails newSensorDetails;
+        typename ThresholdType::SensorDetails oldSensorDetails =
+            thresholdPtr->sensorDetails;
+
+        for (const auto& sensor : newSensors)
+        {
+            auto it = std::find_if(
+                oldSensorDetails.begin(), oldSensorDetails.end(),
+                [&sensor](const auto& sd) { return sensor == sd.first; });
+            if (it != oldSensorDetails.end())
+            {
+                newSensorDetails.emplace(*it);
+                oldSensorDetails.erase(it);
+                continue;
+            }
+
+            newSensorDetails.emplace(
+                sensor, thresholdPtr->makeDetails(sensor->getName()));
+            if (thresholdPtr->initialized)
+            {
+                sensor->registerForUpdates(thresholdPtr->weak_from_this());
+            }
+        }
+
+        if (thresholdPtr->initialized)
+        {
+            for ([[maybe_unused]] auto& [sensor, detail] : oldSensorDetails)
+            {
+                sensor->unregisterFromUpdates(thresholdPtr->weak_from_this());
+            }
+        }
+
+        thresholdPtr->sensorDetails = std::move(newSensorDetails);
+    }
+};