From 7eb1228403bd51ea8a4b481600b47a3b04d2c817 Mon Sep 17 00:00:00 2001
From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com>
Date: Thu, 25 Jul 2024 13:58:25 -0400
Subject: [PATCH 1/4] WIP

---
 .../color-control-server.cpp                  | 157 ++++++++++++------
 .../color-control-server.h                    |  57 ++++++-
 2 files changed, 164 insertions(+), 50 deletions(-)

diff --git a/src/app/clusters/color-control-server/color-control-server.cpp b/src/app/clusters/color-control-server/color-control-server.cpp
index 55c8cd9e7a77de..c57fe2d05eb16f 100644
--- a/src/app/clusters/color-control-server/color-control-server.cpp
+++ b/src/app/clusters/color-control-server/color-control-server.cpp
@@ -204,22 +204,24 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl
         // The color control cluster should have a maximum of 9 scenable attributes
         ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount));
         VerifyOrReturnError(attributeCount <= kColorControlScenableAttributesCount, CHIP_ERROR_BUFFER_TOO_SMALL);
+
+        uint16_t epIndex = ColorControlServer::Instance().getEndpointIndex(endpoint);
         // Retrieve the buffers for different modes
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_HSV
         ColorControlServer::ColorHueTransitionState * colorHueTransitionState =
-            ColorControlServer::Instance().getColorHueTransitionState(endpoint);
+            ColorControlServer::Instance().getColorHueTransitionStateByIndex(epIndex);
         ColorControlServer::Color16uTransitionState * colorSaturationTransitionState =
-            ColorControlServer::Instance().getSaturationTransitionState(endpoint);
+            ColorControlServer::Instance().getSaturationTransitionStateByIndex(epIndex);
 #endif
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
         ColorControlServer::Color16uTransitionState * colorXTransitionState =
-            ColorControlServer::Instance().getXTransitionState(endpoint);
+            ColorControlServer::Instance().getXTransitionStateByIndex(epIndex);
         ColorControlServer::Color16uTransitionState * colorYTransitionState =
-            ColorControlServer::Instance().getYTransitionState(endpoint);
+            ColorControlServer::Instance().getYTransitionStateByIndex(epIndex);
 #endif
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
         ColorControlServer::Color16uTransitionState * colorTempTransitionState =
-            ColorControlServer::Instance().getTempTransitionState(endpoint);
+            ColorControlServer::Instance().getTempTransitionStateByIndex(epIndex);
 #endif
 
         // Initialize action attributes to default values in case they are not in the scene
@@ -456,6 +458,11 @@ ColorControlServer & ColorControlServer::Instance()
     return instance;
 }
 
+uint16_t ColorControlServer::getEndpointIndex(EndpointId endpoint)
+{
+    return emberAfGetClusterServerEndpointIndex(endpoint, ColorControl::Id, MATTER_DM_COLOR_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT);
+}
+
 #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
 chip::scenes::SceneHandler * ColorControlServer::GetSceneHandler()
 {
@@ -513,8 +520,9 @@ bool ColorControlServer::stopMoveStepCommand(app::CommandHandler * commandObj, c
         // Init both transition states on stop command to prevent that.
         if (status == Status::Success)
         {
-            ColorHueTransitionState * hueState        = getColorHueTransitionState(endpoint);
-            Color16uTransitionState * saturationState = getSaturationTransitionState(endpoint);
+            uint16_t epIndex                          = getEndpointIndex(endpoint);
+            ColorHueTransitionState * hueState        = getColorHueTransitionStateByIndex(epIndex);
+            Color16uTransitionState * saturationState = getSaturationTransitionStateByIndex(epIndex);
             initHueTransitionState(endpoint, hueState, false /*isEnhancedHue don't care*/);
             initSaturationTransitionState(endpoint, saturationState);
         }
@@ -702,8 +710,7 @@ uint16_t ColorControlServer::computeTransitionTimeFromStateAndRate(ColorControlS
  */
 EmberEventControl * ColorControlServer::getEventControl(EndpointId endpoint)
 {
-    uint16_t index =
-        emberAfGetClusterServerEndpointIndex(endpoint, ColorControl::Id, MATTER_DM_COLOR_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT);
+    uint16_t index            = getEndpointIndex(endpoint);
     EmberEventControl * event = nullptr;
 
     if (index < ArraySize(eventControls))
@@ -825,15 +832,13 @@ bool ColorControlServer::computeNewColor16uValue(ColorControlServer::Color16uTra
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_HSV
 
 /**
- * @brief Returns ColorHueTransititionState associated to an endpoint
+ * @brief Returns ColorHueTransititionState associated to an endpoint index
  *
  * @param[in] endpoint
  * @return ColorControlServer::ColorHueTransitionState*
  */
-ColorControlServer::ColorHueTransitionState * ColorControlServer::getColorHueTransitionState(EndpointId endpoint)
+ColorControlServer::ColorHueTransitionState * ColorControlServer::getColorHueTransitionStateByIndex(uint16_t index)
 {
-    uint16_t index =
-        emberAfGetClusterServerEndpointIndex(endpoint, ColorControl::Id, MATTER_DM_COLOR_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT);
     ColorHueTransitionState * state = nullptr;
 
     if (index < ArraySize(colorHueTransitionStates))
@@ -844,15 +849,24 @@ ColorControlServer::ColorHueTransitionState * ColorControlServer::getColorHueTra
 }
 
 /**
- * @brief Returns Color16uTransitionState for saturation associated to an endpoint
+ * @brief Returns ColorHueTransititionState associated to an endpoint
+ *
+ * @param[in] endpoint
+ * @return ColorControlServer::ColorHueTransitionState*
+ */
+ColorControlServer::ColorHueTransitionState * ColorControlServer::getColorHueTransitionState(EndpointId endpoint)
+{
+    return getColorHueTransitionStateByIndex(getEndpointIndex(endpoint));
+}
+
+/**
+ * @brief Returns the saturation Color16uTransitionState associated to an endpoint index
  *
  * @param[in] endpoint
  * @return ColorControlServer::Color16uTransitionState*
  */
-ColorControlServer::Color16uTransitionState * ColorControlServer::getSaturationTransitionState(EndpointId endpoint)
+ColorControlServer::Color16uTransitionState * ColorControlServer::getSaturationTransitionStateByIndex(uint16_t index)
 {
-    uint16_t index =
-        emberAfGetClusterServerEndpointIndex(endpoint, ColorControl::Id, MATTER_DM_COLOR_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT);
     Color16uTransitionState * state = nullptr;
 
     if (index < ArraySize(colorSatTransitionStates))
@@ -862,6 +876,17 @@ ColorControlServer::Color16uTransitionState * ColorControlServer::getSaturationT
     return state;
 }
 
+/**
+ * @brief Returns the saturation Color16uTransitionState associated to an endpoint
+ *
+ * @param[in] endpoint
+ * @return ColorControlServer::Color16uTransitionState*
+ */
+ColorControlServer::Color16uTransitionState * ColorControlServer::getSaturationTransitionState(EndpointId endpoint)
+{
+    return getSaturationTransitionStateByIndex(getEndpointIndex(endpoint));
+}
+
 /**
  * @brief Returns current saturation for a specified endpoint
  *
@@ -1079,8 +1104,9 @@ void ColorControlServer::initSaturationTransitionState(chip::EndpointId endpoint
 
 void ColorControlServer::SetHSVRemainingTime(chip::EndpointId endpoint)
 {
-    ColorHueTransitionState * hueTransitionState        = getColorHueTransitionState(endpoint);
-    Color16uTransitionState * saturationTransitionState = getSaturationTransitionState(endpoint);
+    uint16_t epIndex                                    = getEndpointIndex(endpoint);
+    ColorHueTransitionState * hueTransitionState        = getColorHueTransitionStateByIndex(epIndex);
+    Color16uTransitionState * saturationTransitionState = getSaturationTransitionStateByIndex(epIndex);
 
     // When the hue transition is loop, RemainingTime stays at MAX_INT16
     if (hueTransitionState->repeat == false)
@@ -1308,8 +1334,9 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati
     uint16_t halfWay    = isEnhanced ? HALF_MAX_UINT16T : HALF_MAX_UINT8T;
     bool moveUp;
 
-    Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint);
-    ColorHueTransitionState * colorHueTransitionState        = getColorHueTransitionState(endpoint);
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionStateByIndex(epIndex);
+    ColorHueTransitionState * colorHueTransitionState        = getColorHueTransitionStateByIndex(epIndex);
 
     VerifyOrReturnError(nullptr != colorSaturationTransitionState, Status::UnsupportedEndpoint);
     VerifyOrReturnError(nullptr != colorHueTransitionState, Status::UnsupportedEndpoint);
@@ -1395,7 +1422,9 @@ bool ColorControlServer::moveHueCommand(app::CommandHandler * commandObj, const
     MATTER_TRACE_SCOPE("moveHue", "ColorControl");
     EndpointId endpoint                               = commandPath.mEndpointId;
     Status status                                     = Status::Success;
-    ColorHueTransitionState * colorHueTransitionState = getColorHueTransitionState(endpoint);
+
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    ColorHueTransitionState * colorHueTransitionState = getColorHueTransitionStateByIndex(epIndex);
 
     VerifyOrExit(colorHueTransitionState != nullptr, status = Status::UnsupportedEndpoint);
 
@@ -1421,7 +1450,7 @@ bool ColorControlServer::moveHueCommand(app::CommandHandler * commandObj, const
     if (moveMode == HueMoveMode::kStop)
     {
         // Per spec any saturation transition must also be cancelled.
-        Color16uTransitionState * saturationState = getSaturationTransitionState(endpoint);
+        Color16uTransitionState * saturationState = getSaturationTransitionStateByIndex(epIndex);
         initSaturationTransitionState(endpoint, saturationState);
         commandObj->AddStatus(commandPath, Status::Success);
         return true;
@@ -1766,7 +1795,8 @@ bool ColorControlServer::moveSaturationCommand(app::CommandHandler * commandObj,
     EndpointId endpoint    = commandPath.mEndpointId;
     Status status          = Status::Success;
 
-    Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint);
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionStateByIndex(epIndex);
     VerifyOrExit(colorSaturationTransitionState != nullptr, status = Status::UnsupportedEndpoint);
 
     // check moveMode and rate before any operation is done on the transition states
@@ -1794,7 +1824,7 @@ bool ColorControlServer::moveSaturationCommand(app::CommandHandler * commandObj,
     if (moveMode == SaturationMoveMode::kStop)
     {
         // Per spec any hue transition must also be cancelled.
-        ColorHueTransitionState * hueState = getColorHueTransitionState(endpoint);
+        ColorHueTransitionState * hueState = getColorHueTransitionStateByIndex(epIndex);
         initHueTransitionState(endpoint, hueState, false /*isEnhancedHue don't care*/);
         commandObj->AddStatus(commandPath, Status::Success);
         return true;
@@ -2063,8 +2093,9 @@ bool ColorControlServer::colorLoopCommand(app::CommandHandler * commandObj, cons
 void ColorControlServer::updateHueSatCommand(EndpointId endpoint)
 {
     MATTER_TRACE_SCOPE("updateHueSat", "ColorControl");
-    ColorHueTransitionState * colorHueTransitionState        = getColorHueTransitionState(endpoint);
-    Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint);
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    ColorHueTransitionState * colorHueTransitionState        = getColorHueTransitionStateByIndex(epIndex);
+    Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionStateByIndex(epIndex);
 
     uint8_t previousHue          = colorHueTransitionState->currentHue;
     uint16_t previousSaturation  = colorSaturationTransitionState->currentValue;
@@ -2118,16 +2149,13 @@ void ColorControlServer::updateHueSatCommand(EndpointId endpoint)
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
 
 /**
- * @brief Returns Color16uTransitionState for X color associated to an endpoint
+ * @brief Returns Color16uTransitionState for X color associated to an endpoint index
  *
  * @param endpoint
  * @return ColorControlServer::Color16uTransitionState*
  */
-ColorControlServer::Color16uTransitionState * ColorControlServer::getXTransitionState(EndpointId endpoint)
+ColorControlServer::Color16uTransitionState * ColorControlServer::getXTransitionStateByIndex(uint16_t index)
 {
-    uint16_t index =
-        emberAfGetClusterServerEndpointIndex(endpoint, ColorControl::Id, MATTER_DM_COLOR_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT);
-
     Color16uTransitionState * state = nullptr;
     if (index < ArraySize(colorXtransitionStates))
     {
@@ -2138,16 +2166,24 @@ ColorControlServer::Color16uTransitionState * ColorControlServer::getXTransition
 }
 
 /**
- * @brief Returns Color16uTransitionState for Y color associated to an endpoint
+ * @brief Returns Color16uTransitionState for X color associated to an endpoint
  *
  * @param endpoint
  * @return ColorControlServer::Color16uTransitionState*
  */
-ColorControlServer::Color16uTransitionState * ColorControlServer::getYTransitionState(EndpointId endpoint)
+ColorControlServer::Color16uTransitionState * ColorControlServer::getXTransitionState(EndpointId endpoint)
 {
-    uint16_t index =
-        emberAfGetClusterServerEndpointIndex(endpoint, ColorControl::Id, MATTER_DM_COLOR_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT);
+    return getXTransitionStateByIndex(getEndpointIndex(endpoint));
+}
 
+/**
+ * @brief Returns Color16uTransitionState for Y color associated to an endpoint index
+ *
+ * @param endpoint
+ * @return ColorControlServer::Color16uTransitionState*
+ */
+ColorControlServer::Color16uTransitionState * ColorControlServer::getYTransitionStateByIndex(uint16_t index)
+{
     Color16uTransitionState * state = nullptr;
     if (index < ArraySize(colorYtransitionStates))
     {
@@ -2157,6 +2193,17 @@ ColorControlServer::Color16uTransitionState * ColorControlServer::getYTransition
     return state;
 }
 
+/**
+ * @brief Returns Color16uTransitionState for Y color associated to an endpoint
+ *
+ * @param endpoint
+ * @return ColorControlServer::Color16uTransitionState*
+ */
+ColorControlServer::Color16uTransitionState * ColorControlServer::getYTransitionState(EndpointId endpoint)
+{
+    return getYTransitionStateByIndex(getEndpointIndex(endpoint));
+}
+
 uint16_t ColorControlServer::findNewColorValueFromStep(uint16_t oldValue, int16_t step)
 {
     uint16_t newValue;
@@ -2207,8 +2254,9 @@ EmberEventControl * ColorControlServer::configureXYEventControl(EndpointId endpo
  */
 Status ColorControlServer::moveToColor(uint16_t colorX, uint16_t colorY, uint16_t transitionTime, EndpointId endpoint)
 {
-    Color16uTransitionState * colorXTransitionState = getXTransitionState(endpoint);
-    Color16uTransitionState * colorYTransitionState = getYTransitionState(endpoint);
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
+    Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
 
     VerifyOrReturnError(nullptr != colorXTransitionState, Status::UnsupportedEndpoint);
     VerifyOrReturnError(nullptr != colorYTransitionState, Status::UnsupportedEndpoint);
@@ -2275,8 +2323,9 @@ bool ColorControlServer::moveColorCommand(app::CommandHandler * commandObj, cons
     EndpointId endpoint     = commandPath.mEndpointId;
     Status status           = Status::Success;
 
-    Color16uTransitionState * colorXTransitionState = getXTransitionState(endpoint);
-    Color16uTransitionState * colorYTransitionState = getYTransitionState(endpoint);
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
+    Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
 
     VerifyOrExit(colorXTransitionState != nullptr, status = Status::UnsupportedEndpoint);
     VerifyOrExit(colorYTransitionState != nullptr, status = Status::UnsupportedEndpoint);
@@ -2377,8 +2426,9 @@ bool ColorControlServer::stepColorCommand(app::CommandHandler * commandObj, cons
 
     Status status = Status::Success;
 
-    Color16uTransitionState * colorXTransitionState = getXTransitionState(endpoint);
-    Color16uTransitionState * colorYTransitionState = getYTransitionState(endpoint);
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
+    Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
 
     VerifyOrExit(colorXTransitionState != nullptr, status = Status::UnsupportedEndpoint);
     VerifyOrExit(colorYTransitionState != nullptr, status = Status::UnsupportedEndpoint);
@@ -2445,8 +2495,9 @@ bool ColorControlServer::stepColorCommand(app::CommandHandler * commandObj, cons
  */
 void ColorControlServer::updateXYCommand(EndpointId endpoint)
 {
-    Color16uTransitionState * colorXTransitionState = getXTransitionState(endpoint);
-    Color16uTransitionState * colorYTransitionState = getYTransitionState(endpoint);
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
+    Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
     bool isXTransitionDone, isYTransitionDone;
 
     // compute new values for X and Y.
@@ -2477,16 +2528,13 @@ void ColorControlServer::updateXYCommand(EndpointId endpoint)
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
 /**
- * @brief Get the Temp Transition State object associated to the endpoint
+ * @brief Get the Temp Transition State object associated to the endpoint index
  *
  * @param endpoint
  * @return Color16uTransitionState*
  */
-ColorControlServer::Color16uTransitionState * ColorControlServer::getTempTransitionState(EndpointId endpoint)
+ColorControlServer::Color16uTransitionState * ColorControlServer::getTempTransitionStateByIndex(uint16_t index)
 {
-    uint16_t index =
-        emberAfGetClusterServerEndpointIndex(endpoint, ColorControl::Id, MATTER_DM_COLOR_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT);
-
     Color16uTransitionState * state = nullptr;
     if (index < ArraySize(colorTempTransitionStates))
     {
@@ -2496,6 +2544,17 @@ ColorControlServer::Color16uTransitionState * ColorControlServer::getTempTransit
     return state;
 }
 
+/**
+ * @brief Get the Temp Transition State object associated to the endpoint
+ *
+ * @param endpoint
+ * @return Color16uTransitionState*
+ */
+ColorControlServer::Color16uTransitionState * ColorControlServer::getTempTransitionState(EndpointId endpoint)
+{
+    return getTempTransitionStateByIndex(getEndpointIndex(endpoint));
+}
+
 /**
  * @brief executes move to color temp logic
  *
diff --git a/src/app/clusters/color-control-server/color-control-server.h b/src/app/clusters/color-control-server/color-control-server.h
index f453c6f02872e4..8016db9df7a78a 100644
--- a/src/app/clusters/color-control-server/color-control-server.h
+++ b/src/app/clusters/color-control-server/color-control-server.h
@@ -20,6 +20,8 @@
 #include <app-common/zap-generated/cluster-objects.h>
 #include <app/CommandHandler.h>
 #include <app/ConcreteCommandPath.h>
+#include <app/cluster-building-blocks/QuieterReporting.h>
+#include <app/data-model/Nullable.h>
 #include <app/util/af-types.h>
 #include <app/util/attribute-storage.h>
 #include <app/util/basic-types.h>
@@ -199,7 +201,37 @@ class ColorControlServer
      * Functions Definitions
      *********************************************************/
 
-    ColorControlServer() {}
+    ColorControlServer()
+    {
+        // #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_HSV
+        //         quietHue = new chip::app::QuieterReportingAttribute<uint8_t>[kColorControlClusterServerMaxEndpointCount]
+        //         {
+        //             chip::app::QuieterReportingAttribute<uint8_t>(0)
+        //         };
+        //         quietSaturation = new
+        //         chip::app::QuieterReportingAttribute<uint8_t>[kColorControlClusterServerMaxEndpointCount] {
+        //             chip::app::QuieterReportingAttribute<uint8_t>(0)
+        //         };
+        //         quietEnhancedHue = new
+        //         chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
+        //             chip::app::QuieterReportingAttribute<uint16_t>(0)
+        //         };
+        // #endif
+        // #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
+        //         quietColorX = new
+        //         chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
+        //             chip::app::QuieterReportingAttribute<uint16_t>(0)
+        //         };
+        //         quietColorY = new
+        //         chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
+        //             chip::app::QuieterReportingAttribute<uint16_t>(0)
+        //         };
+        // #endif
+        //         quietRemainingTime =
+        //             new chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
+        //                 chip::app::QuieterReportingAttribute<uint16_t>(0)
+        //             };
+    }
     bool shouldExecuteIfOff(chip::EndpointId endpoint, uint8_t optionMask, uint8_t optionOverride);
     void handleModeSwitch(chip::EndpointId endpoint, uint8_t newColorMode);
     uint16_t computeTransitionTimeFromStateAndRate(Color16uTransitionState * p, uint16_t rate);
@@ -213,6 +245,7 @@ class ColorControlServer
     static void timerCallback(chip::System::Layer *, void * callbackContext);
     void scheduleTimerCallbackMs(EmberEventControl * control, uint32_t delayMs);
     void cancelEndpointTimerCallback(EmberEventControl * control);
+    uint16_t getEndpointIndex(chip::EndpointId);
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_HSV
     chip::Protocols::InteractionModel::Status moveToSaturation(uint8_t saturation, uint16_t transitionTime,
@@ -221,6 +254,8 @@ class ColorControlServer
                                                                      bool isEnhanced, chip::EndpointId endpoint);
     ColorHueTransitionState * getColorHueTransitionState(chip::EndpointId endpoint);
     Color16uTransitionState * getSaturationTransitionState(chip::EndpointId endpoint);
+    ColorHueTransitionState * getColorHueTransitionStateByIndex(uint16_t index);
+    Color16uTransitionState * getSaturationTransitionStateByIndex(uint16_t index);
     uint8_t getSaturation(chip::EndpointId endpoint);
     uint8_t addHue(uint8_t hue1, uint8_t hue2);
     uint8_t subtractHue(uint8_t hue1, uint8_t hue2);
@@ -241,12 +276,15 @@ class ColorControlServer
                                                           chip::EndpointId endpoint);
     Color16uTransitionState * getXTransitionState(chip::EndpointId endpoint);
     Color16uTransitionState * getYTransitionState(chip::EndpointId endpoint);
+    Color16uTransitionState * getXTransitionStateByIndex(uint16_t index);
+    Color16uTransitionState * getYTransitionStateByIndex(uint16_t index);
     uint16_t findNewColorValueFromStep(uint16_t oldValue, int16_t step);
     EmberEventControl * configureXYEventControl(chip::EndpointId);
 #endif // #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
     Color16uTransitionState * getTempTransitionState(chip::EndpointId endpoint);
+    Color16uTransitionState * getTempTransitionStateByIndex(uint16_t index);
     chip::Protocols::InteractionModel::Status moveToColorTemp(chip::EndpointId aEndpoint, uint16_t colorTemperature,
                                                               uint16_t transitionTime);
     uint16_t getTemperatureCoupleToLevelMin(chip::EndpointId endpoint);
@@ -264,11 +302,26 @@ class ColorControlServer
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_HSV
     ColorHueTransitionState colorHueTransitionStates[kColorControlClusterServerMaxEndpointCount];
     Color16uTransitionState colorSatTransitionStates[kColorControlClusterServerMaxEndpointCount];
+
+    chip::app::QuieterReportingAttribute<uint8_t> quietHue[kColorControlClusterServerMaxEndpointCount]{
+        chip::app::QuieterReportingAttribute<uint8_t>(0)
+    };
+    chip::app::QuieterReportingAttribute<uint8_t> quietSaturation[kColorControlClusterServerMaxEndpointCount]{
+        chip::app::QuieterReportingAttribute<uint8_t>(0)
+    };
+    chip::app::QuieterReportingAttribute<uint16_t> quietEnhancedHue[kColorControlClusterServerMaxEndpointCount]{
+        chip::app::QuieterReportingAttribute<uint16_t>(0)
+    };
 #endif
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
     Color16uTransitionState colorXtransitionStates[kColorControlClusterServerMaxEndpointCount];
     Color16uTransitionState colorYtransitionStates[kColorControlClusterServerMaxEndpointCount];
+
+    chip::app::QuieterReportingAttribute<uint16_t> quietColorX[kColorControlClusterServerMaxEndpointCount]{
+        chip::app::QuieterReportingAttribute<uint16_t>(0)};
+    chip::app::QuieterReportingAttribute<uint16_t> quietColorY[kColorControlClusterServerMaxEndpointCount]{
+        chip::app::QuieterReportingAttribute<uint16_t>(0)};
 #endif // MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
@@ -276,6 +329,8 @@ class ColorControlServer
 #endif // MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
 
     EmberEventControl eventControls[kColorControlClusterServerMaxEndpointCount];
+    chip::app::QuieterReportingAttribute<uint16_t> quietRemainingTime[kColorControlClusterServerMaxEndpointCount]{
+        chip::app::QuieterReportingAttribute<uint16_t>(0)};
 
 #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
     friend class DefaultColorControlSceneHandler;

From 4f0dcc42b34be87e50aee1b6e540abd7dcb664fd Mon Sep 17 00:00:00 2001
From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com>
Date: Fri, 26 Jul 2024 15:47:57 -0400
Subject: [PATCH 2/4] Add Quiet reporting to the CurrentHue, CurrentSaturation,
 EnhancedCurrentHue, CurrentX, CurrentY and RemainingTime attributes of the
 colorcontrol cluster server implementation

---
 .../color-control-server.cpp                  | 203 ++++++++++++++----
 .../color-control-server.h                    |  57 +++--
 2 files changed, 188 insertions(+), 72 deletions(-)

diff --git a/src/app/clusters/color-control-server/color-control-server.cpp b/src/app/clusters/color-control-server/color-control-server.cpp
index c57fe2d05eb16f..c4de9f9d9461ef 100644
--- a/src/app/clusters/color-control-server/color-control-server.cpp
+++ b/src/app/clusters/color-control-server/color-control-server.cpp
@@ -30,6 +30,7 @@
 #endif
 
 using namespace chip;
+using namespace chip::app;
 using namespace chip::app::Clusters;
 using namespace chip::app::Clusters::ColorControl;
 using chip::Protocols::InteractionModel::Status;
@@ -1058,9 +1059,10 @@ void ColorControlServer::startColorLoop(EndpointId endpoint, uint8_t startFromSt
     colorHueTransitionState->stepsRemaining = static_cast<uint16_t>(time * TRANSITION_STEPS_PER_1S);
     colorHueTransitionState->stepsTotal     = static_cast<uint16_t>(time * TRANSITION_STEPS_PER_1S);
     colorHueTransitionState->timeRemaining  = MAX_INT16U_VALUE;
+    colorHueTransitionState->transitionTime = MAX_INT16U_VALUE;
     colorHueTransitionState->endpoint       = endpoint;
 
-    Attributes::RemainingTime::Set(endpoint, MAX_INT16U_VALUE);
+    SetQuietReportRemainingTime(endpoint, MAX_INT16U_VALUE);
 
     scheduleTimerCallbackMs(configureHSVEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count());
 }
@@ -1111,7 +1113,7 @@ void ColorControlServer::SetHSVRemainingTime(chip::EndpointId endpoint)
     // When the hue transition is loop, RemainingTime stays at MAX_INT16
     if (hueTransitionState->repeat == false)
     {
-        Attributes::RemainingTime::Set(endpoint, max(hueTransitionState->timeRemaining, saturationTransitionState->timeRemaining));
+        SetQuietReportRemainingTime(endpoint, max(hueTransitionState->timeRemaining, saturationTransitionState->timeRemaining));
     }
 }
 
@@ -1303,6 +1305,7 @@ Status ColorControlServer::moveToSaturation(uint8_t saturation, uint16_t transit
     colorSaturationTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorSaturationTransitionState->stepsTotal     = colorSaturationTransitionState->stepsRemaining;
     colorSaturationTransitionState->timeRemaining  = transitionTime;
+    colorSaturationTransitionState->transitionTime = transitionTime;
     colorSaturationTransitionState->endpoint       = endpoint;
     colorSaturationTransitionState->lowLimit       = MIN_SATURATION_VALUE;
     colorSaturationTransitionState->highLimit      = MAX_SATURATION_VALUE;
@@ -1334,7 +1337,7 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati
     uint16_t halfWay    = isEnhanced ? HALF_MAX_UINT16T : HALF_MAX_UINT8T;
     bool moveUp;
 
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                         = getEndpointIndex(endpoint);
     Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionStateByIndex(epIndex);
     ColorHueTransitionState * colorHueTransitionState        = getColorHueTransitionStateByIndex(epIndex);
 
@@ -1382,6 +1385,7 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati
     colorHueTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorHueTransitionState->stepsTotal     = colorHueTransitionState->stepsRemaining;
     colorHueTransitionState->timeRemaining  = transitionTime;
+    colorHueTransitionState->transitionTime = transitionTime;
     colorHueTransitionState->endpoint       = endpoint;
     colorHueTransitionState->repeat         = false;
 
@@ -1390,6 +1394,7 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati
     colorSaturationTransitionState->stepsRemaining = colorHueTransitionState->stepsRemaining;
     colorSaturationTransitionState->stepsTotal     = colorHueTransitionState->stepsRemaining;
     colorSaturationTransitionState->timeRemaining  = transitionTime;
+    colorSaturationTransitionState->transitionTime = transitionTime;
     colorSaturationTransitionState->endpoint       = endpoint;
     colorSaturationTransitionState->lowLimit       = MIN_SATURATION_VALUE;
     colorSaturationTransitionState->highLimit      = MAX_SATURATION_VALUE;
@@ -1420,10 +1425,10 @@ bool ColorControlServer::moveHueCommand(app::CommandHandler * commandObj, const
                                         bool isEnhanced)
 {
     MATTER_TRACE_SCOPE("moveHue", "ColorControl");
-    EndpointId endpoint                               = commandPath.mEndpointId;
-    Status status                                     = Status::Success;
+    EndpointId endpoint = commandPath.mEndpointId;
+    Status status       = Status::Success;
 
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                  = getEndpointIndex(endpoint);
     ColorHueTransitionState * colorHueTransitionState = getColorHueTransitionStateByIndex(epIndex);
 
     VerifyOrExit(colorHueTransitionState != nullptr, status = Status::UnsupportedEndpoint);
@@ -1496,11 +1501,12 @@ bool ColorControlServer::moveHueCommand(app::CommandHandler * commandObj, const
     colorHueTransitionState->stepsRemaining = TRANSITION_STEPS_PER_1S;
     colorHueTransitionState->stepsTotal     = TRANSITION_STEPS_PER_1S;
     colorHueTransitionState->timeRemaining  = MAX_INT16U_VALUE;
+    colorHueTransitionState->transitionTime = MAX_INT16U_VALUE;
     colorHueTransitionState->endpoint       = endpoint;
     colorHueTransitionState->repeat         = true;
 
     // hue movement can last forever. Indicate this with a remaining time of maxint
-    Attributes::RemainingTime::Set(endpoint, MAX_INT16U_VALUE);
+    SetQuietReportRemainingTime(endpoint, MAX_INT16U_VALUE);
 
     // kick off the state machine:
     scheduleTimerCallbackMs(configureHSVEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count());
@@ -1629,6 +1635,7 @@ bool ColorControlServer::moveToHueCommand(app::CommandHandler * commandObj, cons
     colorHueTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorHueTransitionState->stepsTotal     = colorHueTransitionState->stepsRemaining;
     colorHueTransitionState->timeRemaining  = transitionTime;
+    colorHueTransitionState->transitionTime = transitionTime;
     colorHueTransitionState->endpoint       = endpoint;
     colorHueTransitionState->up             = (direction == HueDirection::kUp);
     colorHueTransitionState->repeat         = false;
@@ -1771,6 +1778,7 @@ bool ColorControlServer::stepHueCommand(app::CommandHandler * commandObj, const
     colorHueTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorHueTransitionState->stepsTotal     = colorHueTransitionState->stepsRemaining;
     colorHueTransitionState->timeRemaining  = transitionTime;
+    colorHueTransitionState->transitionTime = transitionTime;
     colorHueTransitionState->endpoint       = endpoint;
     colorHueTransitionState->repeat         = false;
 
@@ -1795,7 +1803,7 @@ bool ColorControlServer::moveSaturationCommand(app::CommandHandler * commandObj,
     EndpointId endpoint    = commandPath.mEndpointId;
     Status status          = Status::Success;
 
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                         = getEndpointIndex(endpoint);
     Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionStateByIndex(epIndex);
     VerifyOrExit(colorSaturationTransitionState != nullptr, status = Status::UnsupportedEndpoint);
 
@@ -1847,6 +1855,7 @@ bool ColorControlServer::moveSaturationCommand(app::CommandHandler * commandObj,
     colorSaturationTransitionState->stepsRemaining = transitionTime;
     colorSaturationTransitionState->stepsTotal     = transitionTime;
     colorSaturationTransitionState->timeRemaining  = transitionTime;
+    colorSaturationTransitionState->transitionTime = transitionTime;
     colorSaturationTransitionState->endpoint       = endpoint;
     colorSaturationTransitionState->lowLimit       = MIN_SATURATION_VALUE;
     colorSaturationTransitionState->highLimit      = MAX_SATURATION_VALUE;
@@ -1945,6 +1954,7 @@ bool ColorControlServer::stepSaturationCommand(app::CommandHandler * commandObj,
     colorSaturationTransitionState->stepsRemaining = max<uint8_t>(transitionTime, 1);
     colorSaturationTransitionState->stepsTotal     = colorSaturationTransitionState->stepsRemaining;
     colorSaturationTransitionState->timeRemaining  = transitionTime;
+    colorSaturationTransitionState->transitionTime = transitionTime;
     colorSaturationTransitionState->endpoint       = endpoint;
     colorSaturationTransitionState->lowLimit       = MIN_SATURATION_VALUE;
     colorSaturationTransitionState->highLimit      = MAX_SATURATION_VALUE;
@@ -1975,7 +1985,8 @@ bool ColorControlServer::colorLoopCommand(app::CommandHandler * commandObj, cons
     uint8_t isColorLoopActive = 0;
     uint8_t deactiveColorLoop = 0;
 
-    ColorHueTransitionState * colorHueTransitionState = getColorHueTransitionState(endpoint);
+    uint16_t epIndex                                  = getEndpointIndex(endpoint);
+    ColorHueTransitionState * colorHueTransitionState = getColorHueTransitionStateByIndex(epIndex);
     VerifyOrExit(colorHueTransitionState != nullptr, status = Status::UnsupportedEndpoint);
 
     // Validate the action and direction parameters of the command
@@ -2060,7 +2071,9 @@ bool ColorControlServer::colorLoopCommand(app::CommandHandler * commandObj, cons
 
                 uint16_t storedEnhancedHue = 0;
                 Attributes::ColorLoopStoredEnhancedHue::Get(endpoint, &storedEnhancedHue);
-                Attributes::EnhancedCurrentHue::Set(endpoint, storedEnhancedHue);
+                MarkAttributeDirty markDirty =
+                    SetQuietReportAttribute(quietEnhancedHue[epIndex], storedEnhancedHue, true /*isStartOrEndOfTransition*/, 0);
+                Attributes::EnhancedCurrentHue::Set(endpoint, quietEnhancedHue[epIndex].value().Value(), markDirty);
             }
             else
             {
@@ -2093,7 +2106,7 @@ bool ColorControlServer::colorLoopCommand(app::CommandHandler * commandObj, cons
 void ColorControlServer::updateHueSatCommand(EndpointId endpoint)
 {
     MATTER_TRACE_SCOPE("updateHueSat", "ColorControl");
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                         = getEndpointIndex(endpoint);
     ColorHueTransitionState * colorHueTransitionState        = getColorHueTransitionStateByIndex(epIndex);
     Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionStateByIndex(epIndex);
 
@@ -2101,6 +2114,10 @@ void ColorControlServer::updateHueSatCommand(EndpointId endpoint)
     uint16_t previousSaturation  = colorSaturationTransitionState->currentValue;
     uint16_t previousEnhancedhue = colorHueTransitionState->currentEnhancedHue;
 
+    bool isHueTansitionStart = (colorHueTransitionState->stepsRemaining == colorHueTransitionState->stepsTotal);
+    bool isSaturationTransitionStart =
+        (colorSaturationTransitionState->stepsRemaining == colorSaturationTransitionState->stepsTotal);
+
     bool isHueTansitionDone         = computeNewHueValue(colorHueTransitionState);
     bool isSaturationTransitionDone = computeNewColor16uValue(colorSaturationTransitionState);
 
@@ -2115,32 +2132,43 @@ void ColorControlServer::updateHueSatCommand(EndpointId endpoint)
         scheduleTimerCallbackMs(configureHSVEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count());
     }
 
+    uint8_t currentHue;
+    MarkAttributeDirty markDirty;
     if (colorHueTransitionState->isEnhancedHue)
     {
+        markDirty = SetQuietReportAttribute(quietEnhancedHue[epIndex], colorHueTransitionState->currentEnhancedHue,
+                                            (isHueTansitionStart || isHueTansitionDone), colorHueTransitionState->transitionTime);
+        Attributes::EnhancedCurrentHue::Set(endpoint, quietEnhancedHue[epIndex].value().Value(), markDirty);
+        currentHue = static_cast<uint8_t>(colorHueTransitionState->currentEnhancedHue >> 8);
+
         if (previousEnhancedhue != colorHueTransitionState->currentEnhancedHue)
         {
-            Attributes::EnhancedCurrentHue::Set(endpoint, colorHueTransitionState->currentEnhancedHue);
-            Attributes::CurrentHue::Set(endpoint, static_cast<uint8_t>(colorHueTransitionState->currentEnhancedHue >> 8));
-
             ChipLogProgress(Zcl, "Enhanced Hue %d endpoint %d", colorHueTransitionState->currentEnhancedHue, endpoint);
         }
     }
     else
     {
+        currentHue = colorHueTransitionState->currentHue;
         if (previousHue != colorHueTransitionState->currentHue)
         {
-            Attributes::CurrentHue::Set(colorHueTransitionState->endpoint, colorHueTransitionState->currentHue);
             ChipLogProgress(Zcl, "Hue %d endpoint %d", colorHueTransitionState->currentHue, endpoint);
         }
     }
 
+    markDirty = SetQuietReportAttribute(quietHue[epIndex], currentHue, (isHueTansitionStart || isHueTansitionDone),
+                                        colorHueTransitionState->transitionTime);
+    Attributes::CurrentHue::Set(endpoint, quietHue[epIndex].value().Value(), markDirty);
+
     if (previousSaturation != colorSaturationTransitionState->currentValue)
     {
-        Attributes::CurrentSaturation::Set(colorSaturationTransitionState->endpoint,
-                                           (uint8_t) colorSaturationTransitionState->currentValue);
         ChipLogProgress(Zcl, "Saturation %d endpoint %d", colorSaturationTransitionState->currentValue, endpoint);
     }
 
+    markDirty = SetQuietReportAttribute(quietSaturation[epIndex], colorSaturationTransitionState->currentValue,
+                                        (isSaturationTransitionStart || isSaturationTransitionDone),
+                                        colorSaturationTransitionState->transitionTime);
+    Attributes::CurrentSaturation::Set(endpoint, quietSaturation[epIndex].value().Value(), markDirty);
+
     computePwmFromHsv(endpoint);
 }
 
@@ -2254,7 +2282,7 @@ EmberEventControl * ColorControlServer::configureXYEventControl(EndpointId endpo
  */
 Status ColorControlServer::moveToColor(uint16_t colorX, uint16_t colorY, uint16_t transitionTime, EndpointId endpoint)
 {
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                = getEndpointIndex(endpoint);
     Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
     Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
 
@@ -2274,6 +2302,7 @@ Status ColorControlServer::moveToColor(uint16_t colorX, uint16_t colorY, uint16_
     colorXTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorXTransitionState->stepsTotal     = colorXTransitionState->stepsRemaining;
     colorXTransitionState->timeRemaining  = transitionTime;
+    colorXTransitionState->transitionTime = transitionTime;
     colorXTransitionState->endpoint       = endpoint;
     colorXTransitionState->lowLimit       = MIN_CIE_XY_VALUE;
     colorXTransitionState->highLimit      = MAX_CIE_XY_VALUE;
@@ -2284,11 +2313,12 @@ Status ColorControlServer::moveToColor(uint16_t colorX, uint16_t colorY, uint16_
     colorYTransitionState->stepsRemaining = colorXTransitionState->stepsRemaining;
     colorYTransitionState->stepsTotal     = colorXTransitionState->stepsRemaining;
     colorYTransitionState->timeRemaining  = transitionTime;
+    colorYTransitionState->transitionTime = transitionTime;
     colorYTransitionState->endpoint       = endpoint;
     colorYTransitionState->lowLimit       = MIN_CIE_XY_VALUE;
     colorYTransitionState->highLimit      = MAX_CIE_XY_VALUE;
 
-    Attributes::RemainingTime::Set(endpoint, transitionTime);
+    SetQuietReportRemainingTime(endpoint, transitionTime);
 
     // kick off the state machine:
     scheduleTimerCallbackMs(configureXYEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0);
@@ -2323,7 +2353,7 @@ bool ColorControlServer::moveColorCommand(app::CommandHandler * commandObj, cons
     EndpointId endpoint     = commandPath.mEndpointId;
     Status status           = Status::Success;
 
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                = getEndpointIndex(endpoint);
     Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
     Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
 
@@ -2369,6 +2399,7 @@ bool ColorControlServer::moveColorCommand(app::CommandHandler * commandObj, cons
     colorXTransitionState->stepsRemaining = transitionTimeX;
     colorXTransitionState->stepsTotal     = transitionTimeX;
     colorXTransitionState->timeRemaining  = transitionTimeX;
+    colorXTransitionState->transitionTime = transitionTimeX;
     colorXTransitionState->endpoint       = endpoint;
     colorXTransitionState->lowLimit       = MIN_CIE_XY_VALUE;
     colorXTransitionState->highLimit      = MAX_CIE_XY_VALUE;
@@ -2389,18 +2420,12 @@ bool ColorControlServer::moveColorCommand(app::CommandHandler * commandObj, cons
     colorYTransitionState->stepsRemaining = transitionTimeY;
     colorYTransitionState->stepsTotal     = transitionTimeY;
     colorYTransitionState->timeRemaining  = transitionTimeY;
+    colorYTransitionState->transitionTime = transitionTimeY;
     colorYTransitionState->endpoint       = endpoint;
     colorYTransitionState->lowLimit       = MIN_CIE_XY_VALUE;
     colorYTransitionState->highLimit      = MAX_CIE_XY_VALUE;
 
-    if (transitionTimeX < transitionTimeY)
-    {
-        Attributes::RemainingTime::Set(endpoint, transitionTimeX);
-    }
-    else
-    {
-        Attributes::RemainingTime::Set(endpoint, transitionTimeY);
-    }
+    SetQuietReportRemainingTime(endpoint, max(transitionTimeX, transitionTimeY));
 
     // kick off the state machine:
     scheduleTimerCallbackMs(configureXYEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count());
@@ -2426,7 +2451,7 @@ bool ColorControlServer::stepColorCommand(app::CommandHandler * commandObj, cons
 
     Status status = Status::Success;
 
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                = getEndpointIndex(endpoint);
     Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
     Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
 
@@ -2464,6 +2489,7 @@ bool ColorControlServer::stepColorCommand(app::CommandHandler * commandObj, cons
     colorXTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorXTransitionState->stepsTotal     = colorXTransitionState->stepsRemaining;
     colorXTransitionState->timeRemaining  = transitionTime;
+    colorXTransitionState->transitionTime = transitionTime;
     colorXTransitionState->endpoint       = endpoint;
     colorXTransitionState->lowLimit       = MIN_CIE_XY_VALUE;
     colorXTransitionState->highLimit      = MAX_CIE_XY_VALUE;
@@ -2474,11 +2500,12 @@ bool ColorControlServer::stepColorCommand(app::CommandHandler * commandObj, cons
     colorYTransitionState->stepsRemaining = colorXTransitionState->stepsRemaining;
     colorYTransitionState->stepsTotal     = colorXTransitionState->stepsRemaining;
     colorYTransitionState->timeRemaining  = transitionTime;
+    colorYTransitionState->transitionTime = transitionTime;
     colorYTransitionState->endpoint       = endpoint;
     colorYTransitionState->lowLimit       = MIN_CIE_XY_VALUE;
     colorYTransitionState->highLimit      = MAX_CIE_XY_VALUE;
 
-    Attributes::RemainingTime::Set(endpoint, transitionTime);
+    SetQuietReportRemainingTime(endpoint, transitionTime);
 
     // kick off the state machine:
     scheduleTimerCallbackMs(configureXYEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0);
@@ -2495,16 +2522,15 @@ bool ColorControlServer::stepColorCommand(app::CommandHandler * commandObj, cons
  */
 void ColorControlServer::updateXYCommand(EndpointId endpoint)
 {
-    uint16_t epIndex = getEndpointIndex(endpoint);
+    uint16_t epIndex                                = getEndpointIndex(endpoint);
     Color16uTransitionState * colorXTransitionState = getXTransitionStateByIndex(epIndex);
     Color16uTransitionState * colorYTransitionState = getYTransitionStateByIndex(epIndex);
-    bool isXTransitionDone, isYTransitionDone;
 
     // compute new values for X and Y.
-    isXTransitionDone = computeNewColor16uValue(colorXTransitionState);
-    isYTransitionDone = computeNewColor16uValue(colorYTransitionState);
+    bool isXTransitionDone = computeNewColor16uValue(colorXTransitionState);
+    bool isYTransitionDone = computeNewColor16uValue(colorYTransitionState);
 
-    Attributes::RemainingTime::Set(endpoint, max(colorXTransitionState->timeRemaining, colorYTransitionState->timeRemaining));
+    SetQuietReportRemainingTime(endpoint, max(colorXTransitionState->timeRemaining, colorYTransitionState->timeRemaining));
 
     if (isXTransitionDone && isYTransitionDone)
     {
@@ -2515,9 +2541,18 @@ void ColorControlServer::updateXYCommand(EndpointId endpoint)
         scheduleTimerCallbackMs(configureXYEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count());
     }
 
-    // update the attributes
-    Attributes::CurrentX::Set(endpoint, colorXTransitionState->currentValue);
-    Attributes::CurrentY::Set(endpoint, colorYTransitionState->currentValue);
+    bool isXTransitionStart = (colorXTransitionState->stepsRemaining == colorXTransitionState->stepsTotal);
+    bool isYTransitionStart = (colorYTransitionState->stepsRemaining == colorYTransitionState->stepsTotal);
+
+    MarkAttributeDirty markXDirty =
+        SetQuietReportAttribute(quietColorX[epIndex], colorXTransitionState->currentValue,
+                                (isXTransitionStart || isXTransitionDone), colorXTransitionState->transitionTime);
+    MarkAttributeDirty markYDirty =
+        SetQuietReportAttribute(quietColorY[epIndex], colorYTransitionState->currentValue,
+                                (isYTransitionStart || isYTransitionDone), colorYTransitionState->transitionTime);
+
+    Attributes::CurrentX::Set(endpoint, quietColorX[epIndex].value().Value(), markXDirty);
+    Attributes::CurrentY::Set(endpoint, quietColorY[epIndex].value().Value(), markYDirty);
 
     ChipLogProgress(Zcl, "Color X %d Color Y %d", colorXTransitionState->currentValue, colorYTransitionState->currentValue);
 
@@ -2600,6 +2635,7 @@ Status ColorControlServer::moveToColorTemp(EndpointId aEndpoint, uint16_t colorT
     colorTempTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorTempTransitionState->stepsTotal     = colorTempTransitionState->stepsRemaining;
     colorTempTransitionState->timeRemaining  = transitionTime;
+    colorTempTransitionState->transitionTime = transitionTime;
     colorTempTransitionState->endpoint       = endpoint;
     colorTempTransitionState->lowLimit       = temperatureMin;
     colorTempTransitionState->highLimit      = temperatureMax;
@@ -2729,7 +2765,7 @@ void ColorControlServer::updateTempCommand(EndpointId endpoint)
         }
     }
 
-    Attributes::RemainingTime::Set(endpoint, colorTempTransitionState->timeRemaining);
+    SetQuietReportRemainingTime(endpoint, colorTempTransitionState->timeRemaining);
 
     if (isColorTempTransitionDone)
     {
@@ -2851,11 +2887,12 @@ bool ColorControlServer::moveColorTempCommand(app::CommandHandler * commandObj,
     colorTempTransitionState->stepsRemaining = transitionTime;
     colorTempTransitionState->stepsTotal     = transitionTime;
     colorTempTransitionState->timeRemaining  = transitionTime;
+    colorTempTransitionState->transitionTime = transitionTime;
     colorTempTransitionState->endpoint       = endpoint;
     colorTempTransitionState->lowLimit       = colorTemperatureMinimum;
     colorTempTransitionState->highLimit      = colorTemperatureMaximum;
 
-    Attributes::RemainingTime::Set(endpoint, transitionTime);
+    SetQuietReportRemainingTime(endpoint, transitionTime);
 
     // kick off the state machine:
     scheduleTimerCallbackMs(configureTempEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count());
@@ -2968,11 +3005,12 @@ bool ColorControlServer::stepColorTempCommand(app::CommandHandler * commandObj,
     colorTempTransitionState->stepsRemaining = max<uint16_t>(transitionTime, 1);
     colorTempTransitionState->stepsTotal     = colorTempTransitionState->stepsRemaining;
     colorTempTransitionState->timeRemaining  = transitionTime;
+    colorTempTransitionState->transitionTime = transitionTime;
     colorTempTransitionState->endpoint       = endpoint;
     colorTempTransitionState->lowLimit       = colorTemperatureMinimum;
     colorTempTransitionState->highLimit      = colorTemperatureMaximum;
 
-    Attributes::RemainingTime::Set(endpoint, transitionTime);
+    SetQuietReportRemainingTime(endpoint, transitionTime);
 
     // kick off the state machine:
     scheduleTimerCallbackMs(configureTempEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0);
@@ -3065,6 +3103,89 @@ void ColorControlServer::levelControlColorTempChangeCommand(EndpointId endpoint)
 
 #endif // MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
 
+/*
+ * @brief
+ * Utility function used to update a color control attribute which has the quiet reporting quality.
+ * matching the following report conditions:
+ * - At most once per second, or
+ * - At the start of the movement/transition, or
+ * - At the end of the movement/transition, or
+ * - When it changes from null to any other value and vice versa. (Implicit to the QuieterReportingAttribute class)
+ *
+ * The QuietReportAttribute class is updated with the new value and when the report conditions are met,
+ * this function will return MarkAttributeDirty::kIfChanged.
+ * It is expected that the user will use this return value to trigger a reporting mechanism for the attribute with the new value
+ * (Which was updated in the quietReporter)
+ *
+ * @param quietReporter: The QuieterReportingAttribute<TYPE> object for the attribute to update.
+ * @param newValue: Value to update the attribute with
+ * @param isStartOrEndOfTransition: Boolean that indicatse whether the update is occurring at the start or end of a level transition
+ * @return MarkAttributeDirty::kIfChanged when the attribute must be maredk dirty and be reported. MarkAttributeDirty::kNo when it
+ * when it no report is needed.
+ */
+template <typename Q, typename V>
+MarkAttributeDirty ColorControlServer::SetQuietReportAttribute(QuieterReportingAttribute<Q> & quietReporter, V newValue,
+                                                               bool isStartOrEndOfTransition, uint16_t transitionTime)
+{
+    AttributeDirtyState dirtyState;
+    auto now = System::SystemClock().GetMonotonicTimestamp();
+
+    if (isStartOrEndOfTransition)
+    {
+        // At the start or end of the movement/transition we must report
+        auto predicate = [](const typename QuieterReportingAttribute<Q>::SufficientChangePredicateCandidate &) -> bool {
+            return true;
+        };
+        dirtyState = quietReporter.SetValue(newValue, now, predicate);
+    }
+    else
+    {
+        // During transitions, reports should be at most once per second
+
+        // For "infinite" transition, default reports interval to 10s (100 1/10ths of a second )
+        if (transitionTime == MAX_INT16U_VALUE)
+        {
+            transitionTime = 100;
+        }
+
+        // Opt for the longest interval between reports, 1s or (transitionTime / 4).
+        // Since transitionTime is in 1/10th of a second, convert it to ms (x 100), thus * 100/4 -> * 25
+        System::Clock::Milliseconds64 reportInterval = System::Clock::Milliseconds64(std::max(1000, transitionTime * 25));
+        auto predicate                               = quietReporter.GetPredicateForSufficientTimeSinceLastDirty(reportInterval);
+        dirtyState                                   = quietReporter.SetValue(newValue, now, predicate);
+    }
+
+    return (dirtyState == AttributeDirtyState::kMustReport) ? MarkAttributeDirty::kIfChanged : MarkAttributeDirty::kNo;
+}
+
+/*
+ * @brief
+ * Function used to set the remaining time based on quiet reporting conditions.
+ * It will update the attribute storage and report the attribute if it is determined dirty.
+ * The condition on which the attribute must be reported are defined by the set QuieterReportingPolicyFlags
+ * of the quietRemainingTime object and the implicit conditions of the QuieterReportingAttribute class
+ *
+ * @param endpoint: Endpoint of the RemainingTime attribute to set
+ * @param newRemainingTime: Value to update the RemainingTime attribute with
+ * @return Success in setting the attribute value or the IM error code for the failure.
+ */
+Status ColorControlServer::SetQuietReportRemainingTime(EndpointId endpoint, uint16_t newRemainingTime)
+{
+    uint16_t epIndex = getEndpointIndex(endpoint);
+    auto markDirty   = MarkAttributeDirty::kNo;
+    auto now         = System::SystemClock().GetMonotonicTimestamp();
+    // Establish the quiet report condition for the RemainingTime Attribute
+    // The quiet report is by the previously set policies :
+    // - kMarkDirtyOnChangeToFromZero : When the value changes from 0 to any other value and vice versa, or
+    // - kMarkDirtyOnIncrement : When the value increases.
+    if (quietRemainingTime[epIndex].SetValue(newRemainingTime, now) == AttributeDirtyState::kMustReport)
+    {
+        markDirty = MarkAttributeDirty::kIfChanged;
+    }
+
+    return Attributes::RemainingTime::Set(endpoint, quietRemainingTime[epIndex].value().Value(), markDirty);
+}
+
 /**********************************************************
  * Callbacks Implementation
  *********************************************************/
diff --git a/src/app/clusters/color-control-server/color-control-server.h b/src/app/clusters/color-control-server/color-control-server.h
index 8016db9df7a78a..4c2b13483f3a4c 100644
--- a/src/app/clusters/color-control-server/color-control-server.h
+++ b/src/app/clusters/color-control-server/color-control-server.h
@@ -111,6 +111,8 @@ class ColorControlServer
         // The amount of time remaining until the transition completes. Measured in tenths of a second.
         // When the transition repeats indefinitely, this will hold the maximum value possible.
         uint16_t timeRemaining;
+        // The total transitionTime in 1/10th of a seconds
+        uint16_t transitionTime;
         uint16_t initialEnhancedHue;
         uint16_t currentEnhancedHue;
         uint16_t finalEnhancedHue;
@@ -129,6 +131,8 @@ class ColorControlServer
         uint16_t stepsTotal;
         // The amount of time remaining until the transition completes. Measured in tenths of a second.
         uint16_t timeRemaining;
+        // The total transitionTime in 1/10th of a seconds
+        uint16_t transitionTime;
         uint16_t lowLimit;
         uint16_t highLimit;
         chip::EndpointId endpoint;
@@ -196,6 +200,11 @@ class ColorControlServer
 
     void cancelEndpointTimerCallback(chip::EndpointId endpoint);
 
+    template <typename Q, typename V>
+    chip::app::MarkAttributeDirty SetQuietReportAttribute(chip::app::QuieterReportingAttribute<Q> & quietReporter, V newValue,
+                                                          bool isStartOrEndOfTransition, uint16_t transitionTime);
+    chip::Protocols::InteractionModel::Status SetQuietReportRemainingTime(chip::EndpointId endpoint, uint16_t newRemainingTime);
+
 private:
     /**********************************************************
      * Functions Definitions
@@ -203,35 +212,18 @@ class ColorControlServer
 
     ColorControlServer()
     {
-        // #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_HSV
-        //         quietHue = new chip::app::QuieterReportingAttribute<uint8_t>[kColorControlClusterServerMaxEndpointCount]
-        //         {
-        //             chip::app::QuieterReportingAttribute<uint8_t>(0)
-        //         };
-        //         quietSaturation = new
-        //         chip::app::QuieterReportingAttribute<uint8_t>[kColorControlClusterServerMaxEndpointCount] {
-        //             chip::app::QuieterReportingAttribute<uint8_t>(0)
-        //         };
-        //         quietEnhancedHue = new
-        //         chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
-        //             chip::app::QuieterReportingAttribute<uint16_t>(0)
-        //         };
-        // #endif
-        // #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
-        //         quietColorX = new
-        //         chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
-        //             chip::app::QuieterReportingAttribute<uint16_t>(0)
-        //         };
-        //         quietColorY = new
-        //         chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
-        //             chip::app::QuieterReportingAttribute<uint16_t>(0)
-        //         };
-        // #endif
-        //         quietRemainingTime =
-        //             new chip::app::QuieterReportingAttribute<uint16_t>[kColorControlClusterServerMaxEndpointCount] {
-        //                 chip::app::QuieterReportingAttribute<uint16_t>(0)
-        //             };
+        for (size_t i = 0; i < kColorControlClusterServerMaxEndpointCount; i++)
+        {
+            // Set the quiet report policies for the RemaininTime Attribute on all endpoint
+            // - kMarkDirtyOnChangeToFromZero : When the value changes from 0 to any other value and vice versa, or
+            // - kMarkDirtyOnIncrement : When the value increases.
+            quietRemainingTime[i]
+                .policy()
+                .Set(chip::app::QuieterReportingPolicyEnum::kMarkDirtyOnIncrement)
+                .Set(chip::app::QuieterReportingPolicyEnum::kMarkDirtyOnChangeToFromZero);
+        }
     }
+
     bool shouldExecuteIfOff(chip::EndpointId endpoint, uint8_t optionMask, uint8_t optionOverride);
     void handleModeSwitch(chip::EndpointId endpoint, uint8_t newColorMode);
     uint16_t computeTransitionTimeFromStateAndRate(Color16uTransitionState * p, uint16_t rate);
@@ -319,9 +311,11 @@ class ColorControlServer
     Color16uTransitionState colorYtransitionStates[kColorControlClusterServerMaxEndpointCount];
 
     chip::app::QuieterReportingAttribute<uint16_t> quietColorX[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint16_t>(0)};
+        chip::app::QuieterReportingAttribute<uint16_t>(0)
+    };
     chip::app::QuieterReportingAttribute<uint16_t> quietColorY[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint16_t>(0)};
+        chip::app::QuieterReportingAttribute<uint16_t>(0)
+    };
 #endif // MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
@@ -330,7 +324,8 @@ class ColorControlServer
 
     EmberEventControl eventControls[kColorControlClusterServerMaxEndpointCount];
     chip::app::QuieterReportingAttribute<uint16_t> quietRemainingTime[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint16_t>(0)};
+        chip::app::QuieterReportingAttribute<uint16_t>(0)
+    };
 
 #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
     friend class DefaultColorControlSceneHandler;

From b5521d27abdd165add001ea3421c117104baf88e Mon Sep 17 00:00:00 2001
From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com>
Date: Fri, 26 Jul 2024 17:37:10 -0400
Subject: [PATCH 3/4] Add a constructor in QuieterReporting that works with
 arrays

---
 .../QuieterReporting.h                        |  2 ++
 .../color-control-server.h                    | 24 +++++--------------
 2 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/src/app/cluster-building-blocks/QuieterReporting.h b/src/app/cluster-building-blocks/QuieterReporting.h
index bf6b4fe36346d2..f038dce26f859a 100644
--- a/src/app/cluster-building-blocks/QuieterReporting.h
+++ b/src/app/cluster-building-blocks/QuieterReporting.h
@@ -112,6 +112,8 @@ class QuieterReportingAttribute
 {
 public:
     explicit QuieterReportingAttribute(const Nullable<T> & initialValue) : mValue(initialValue), mLastDirtyValue(initialValue) {}
+    // constructor that works with arrays of QuieterReportingAttribute
+    explicit QuieterReportingAttribute() : mValue(Nullable<T>(0)), mLastDirtyValue(Nullable<T>(0)) {}
 
     struct SufficientChangePredicateCandidate
     {
diff --git a/src/app/clusters/color-control-server/color-control-server.h b/src/app/clusters/color-control-server/color-control-server.h
index 4c2b13483f3a4c..4238fed1dd8b2d 100644
--- a/src/app/clusters/color-control-server/color-control-server.h
+++ b/src/app/clusters/color-control-server/color-control-server.h
@@ -295,27 +295,17 @@ class ColorControlServer
     ColorHueTransitionState colorHueTransitionStates[kColorControlClusterServerMaxEndpointCount];
     Color16uTransitionState colorSatTransitionStates[kColorControlClusterServerMaxEndpointCount];
 
-    chip::app::QuieterReportingAttribute<uint8_t> quietHue[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint8_t>(0)
-    };
-    chip::app::QuieterReportingAttribute<uint8_t> quietSaturation[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint8_t>(0)
-    };
-    chip::app::QuieterReportingAttribute<uint16_t> quietEnhancedHue[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint16_t>(0)
-    };
+    chip::app::QuieterReportingAttribute<uint8_t> quietHue[kColorControlClusterServerMaxEndpointCount];
+    chip::app::QuieterReportingAttribute<uint8_t> quietSaturation[kColorControlClusterServerMaxEndpointCount];
+    chip::app::QuieterReportingAttribute<uint16_t> quietEnhancedHue[kColorControlClusterServerMaxEndpointCount];
 #endif
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
     Color16uTransitionState colorXtransitionStates[kColorControlClusterServerMaxEndpointCount];
     Color16uTransitionState colorYtransitionStates[kColorControlClusterServerMaxEndpointCount];
 
-    chip::app::QuieterReportingAttribute<uint16_t> quietColorX[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint16_t>(0)
-    };
-    chip::app::QuieterReportingAttribute<uint16_t> quietColorY[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint16_t>(0)
-    };
+    chip::app::QuieterReportingAttribute<uint16_t> quietColorX[kColorControlClusterServerMaxEndpointCount];
+    chip::app::QuieterReportingAttribute<uint16_t> quietColorY[kColorControlClusterServerMaxEndpointCount];
 #endif // MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_XY
 
 #ifdef MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
@@ -323,9 +313,7 @@ class ColorControlServer
 #endif // MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP
 
     EmberEventControl eventControls[kColorControlClusterServerMaxEndpointCount];
-    chip::app::QuieterReportingAttribute<uint16_t> quietRemainingTime[kColorControlClusterServerMaxEndpointCount]{
-        chip::app::QuieterReportingAttribute<uint16_t>(0)
-    };
+    chip::app::QuieterReportingAttribute<uint16_t> quietRemainingTime[kColorControlClusterServerMaxEndpointCount];
 
 #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
     friend class DefaultColorControlSceneHandler;

From 5b6cd8338d769b7aaa4b5ebef4d11895cd3aa67b Mon Sep 17 00:00:00 2001
From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com>
Date: Sat, 27 Jul 2024 20:25:20 -0400
Subject: [PATCH 4/4] address comment

---
 src/app/cluster-building-blocks/QuieterReporting.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/cluster-building-blocks/QuieterReporting.h b/src/app/cluster-building-blocks/QuieterReporting.h
index f038dce26f859a..e64d56cd2b1c72 100644
--- a/src/app/cluster-building-blocks/QuieterReporting.h
+++ b/src/app/cluster-building-blocks/QuieterReporting.h
@@ -113,7 +113,7 @@ class QuieterReportingAttribute
 public:
     explicit QuieterReportingAttribute(const Nullable<T> & initialValue) : mValue(initialValue), mLastDirtyValue(initialValue) {}
     // constructor that works with arrays of QuieterReportingAttribute
-    explicit QuieterReportingAttribute() : mValue(Nullable<T>(0)), mLastDirtyValue(Nullable<T>(0)) {}
+    explicit QuieterReportingAttribute() : mValue(DataModel::NullNullable), mLastDirtyValue(DataModel::NullNullable) {}
 
     struct SufficientChangePredicateCandidate
     {