Make the timing inside on-off server cluster more accurate (#21755)

* Make the timing inside on-off server cluster more accurate by compensating the latency between scheduling and firing a timer callback

* debug output line removed

* even simpler: nextDesiredOnWithTimedOffTimestamp reflects the desired time stamp for the next state machine step. This variable gets adjusted by UPDATE_TIME_MS and the wait time gets calculated by UPDATE_TIME_MS - last latency

* Fixing style

Co-authored-by: Justin Wood <woody@apple.com>
diff --git a/src/app/clusters/on-off-server/on-off-server.cpp b/src/app/clusters/on-off-server/on-off-server.cpp
index c0006c7..b24ee63 100644
--- a/src/app/clusters/on-off-server/on-off-server.cpp
+++ b/src/app/clusters/on-off-server/on-off-server.cpp
@@ -452,6 +452,26 @@
     return true;
 }
 
+uint32_t OnOffServer::calculateNextWaitTimeMS(void)
+{
+    const chip::System::Clock::Timestamp currentTime = chip::System::SystemClock().GetMonotonicTimestamp();
+    chip::System::Clock::Timestamp waitTime          = UPDATE_TIME_MS;
+    chip::System::Clock::Timestamp latency;
+
+    if (currentTime > nextDesiredOnWithTimedOffTimestamp)
+    {
+        latency = currentTime - nextDesiredOnWithTimedOffTimestamp;
+        if (latency >= UPDATE_TIME_MS)
+            waitTime = chip::System::Clock::Milliseconds32(1);
+        else
+            waitTime -= latency;
+    }
+
+    nextDesiredOnWithTimedOffTimestamp += UPDATE_TIME_MS;
+
+    return (uint32_t) waitTime.count();
+}
+
 bool OnOffServer::OnWithTimedOffCommand(const app::ConcreteCommandPath & commandPath,
                                         const Commands::OnWithTimedOff::DecodableType & commandData)
 {
@@ -503,7 +523,8 @@
 
     if (currentOnTime < MAX_TIME_VALUE && currentOffWaitTime < MAX_TIME_VALUE)
     {
-        emberEventControlSetDelayMS(configureEventControl(endpoint), UPDATE_TIME_MS);
+        nextDesiredOnWithTimedOffTimestamp = chip::System::SystemClock().GetMonotonicTimestamp() + UPDATE_TIME_MS;
+        emberEventControlSetDelayMS(configureEventControl(endpoint), (uint32_t) UPDATE_TIME_MS.count());
     }
 
 exit:
@@ -526,7 +547,7 @@
     if (isOn) // OnOff On case
     {
         // Restart Timer
-        emberEventControlSetDelayMS(configureEventControl(endpoint), UPDATE_TIME_MS);
+        emberEventControlSetDelayMS(configureEventControl(endpoint), calculateNextWaitTimeMS());
 
         // Update onTime values
         uint16_t onTime = MIN_TIME_VALUE;
@@ -565,7 +586,7 @@
         if (offWaitTime > 0)
         {
             // Restart Timer
-            emberEventControlSetDelayMS(configureEventControl(endpoint), UPDATE_TIME_MS);
+            emberEventControlSetDelayMS(configureEventControl(endpoint), calculateNextWaitTimeMS());
         }
         else
         {
diff --git a/src/app/clusters/on-off-server/on-off-server.h b/src/app/clusters/on-off-server/on-off-server.h
index a915880..61c9527 100644
--- a/src/app/clusters/on-off-server/on-off-server.h
+++ b/src/app/clusters/on-off-server/on-off-server.h
@@ -29,8 +29,8 @@
  * Defines and Macros
  *********************************************************/
 
-static constexpr uint8_t UPDATE_TIME_MS      = 100;
-static constexpr uint16_t TRANSITION_TIME_1S = 10;
+static constexpr chip::System::Clock::Milliseconds32 UPDATE_TIME_MS = chip::System::Clock::Milliseconds32(100);
+static constexpr uint16_t TRANSITION_TIME_1S                        = 10;
 
 static constexpr uint16_t MAX_TIME_VALUE = 0xFFFF;
 static constexpr uint8_t MIN_TIME_VALUE  = 1;
@@ -79,12 +79,14 @@
     EmberEventControl * getEventControl(chip::EndpointId endpoint);
     EmberEventControl * configureEventControl(chip::EndpointId endpoint);
 
+    uint32_t calculateNextWaitTimeMS(void);
     /**********************************************************
      * Attributes Declaration
      *********************************************************/
 
     static OnOffServer instance;
     EmberEventControl eventControls[EMBER_AF_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT];
+    chip::System::Clock::Timestamp nextDesiredOnWithTimedOffTimestamp;
 };
 
 struct OnOffEffect