Added unlatch state on unlock supporting Ubolt and tdded ransition locked->unlatched->unlocked (#32023)

diff --git a/examples/lock-app/silabs/include/AppConfig.h b/examples/lock-app/silabs/include/AppConfig.h
index 2045ce3..2a98805 100644
--- a/examples/lock-app/silabs/include/AppConfig.h
+++ b/examples/lock-app/silabs/include/AppConfig.h
@@ -31,6 +31,10 @@
 // state to another.
 #define ACTUATOR_MOVEMENT_PERIOS_MS 10
 
+// Time the device will be left in the unlatched state before sending it back to the unlocked state.
+// Left at 100 ms for testing purposes.
+#define UNLATCH_TIME_MS 100
+
 #define ON_DEMO_BITMAP                                                                                                             \
     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  \
         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,    \
diff --git a/examples/lock-app/silabs/include/AppTask.h b/examples/lock-app/silabs/include/AppTask.h
index b0040ac..fb911e3 100644
--- a/examples/lock-app/silabs/include/AppTask.h
+++ b/examples/lock-app/silabs/include/AppTask.h
@@ -120,6 +120,13 @@
     static void UpdateClusterState(intptr_t context);
 
     /**
+     * @brief Update Cluster State After Unlatch
+     *
+     * @param context current context
+     */
+    static void UpdateClusterStateAfterUnlatch(intptr_t context);
+
+    /**
      * @brief Handle lock update event
      *
      * @param aEvent event received
diff --git a/examples/lock-app/silabs/include/LockManager.h b/examples/lock-app/silabs/include/LockManager.h
index fb73d1d..7345782 100644
--- a/examples/lock-app/silabs/include/LockManager.h
+++ b/examples/lock-app/silabs/include/LockManager.h
@@ -119,6 +119,7 @@
     {
         LOCK_ACTION = 0,
         UNLOCK_ACTION,
+        UNLATCH_ACTION,
 
         INVALID_ACTION
     } Action;
@@ -128,7 +129,9 @@
         kState_LockInitiated = 0,
         kState_LockCompleted,
         kState_UnlockInitiated,
+        kState_UnlatchInitiated,
         kState_UnlockCompleted,
+        kState_UnlatchCompleted,
     } State;
 
     CHIP_ERROR Init(chip::app::DataModel::Nullable<chip::app::Clusters::DoorLock::DlLockState> state,
@@ -191,7 +194,30 @@
 
     bool ReadConfigValues();
 
+    void UnlockAfterUnlatch();
+
 private:
+    struct UnlatchContext
+    {
+        chip::EndpointId mEndpointId;
+        Nullable<chip::FabricIndex> mFabricIdx;
+        Nullable<chip::NodeId> mNodeId;
+        Optional<chip::ByteSpan> mPin;
+        OperationErrorEnum mErr;
+
+        void Update(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
+                    const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err)
+        {
+            mEndpointId = endpointId;
+            mFabricIdx  = fabricIdx;
+            mNodeId     = nodeId;
+            mPin        = pin;
+            mErr        = err;
+        }
+    };
+    UnlatchContext mUnlatchContext;
+    chip::EndpointId mCurrentEndpointId;
+
     friend LockManager & LockMgr();
     State_t mState;
 
diff --git a/examples/lock-app/silabs/openthread.gni b/examples/lock-app/silabs/openthread.gni
index 3423049..970d3a0 100644
--- a/examples/lock-app/silabs/openthread.gni
+++ b/examples/lock-app/silabs/openthread.gni
@@ -28,7 +28,7 @@
 sl_enable_test_event_trigger = true
 
 # ICD Default configurations
-chip_enable_icd_server = true
+chip_enable_icd_server = false
 chip_subscription_timeout_resumption = false
 sl_use_subscription_syncing = true
 
diff --git a/examples/lock-app/silabs/src/AppTask.cpp b/examples/lock-app/silabs/src/AppTask.cpp
index 6937dbb..4e4d05a 100644
--- a/examples/lock-app/silabs/src/AppTask.cpp
+++ b/examples/lock-app/silabs/src/AppTask.cpp
@@ -70,6 +70,45 @@
 
 namespace {
 LEDWidget sLockLED;
+TimerHandle_t sUnlatchTimer;
+
+void UpdateClusterStateAfterUnlatch(intptr_t context)
+{
+    LockMgr().UnlockAfterUnlatch();
+}
+
+void UnlatchTimerCallback(TimerHandle_t xTimer)
+{
+    chip::DeviceLayer::PlatformMgr().ScheduleWork(UpdateClusterStateAfterUnlatch, reinterpret_cast<intptr_t>(nullptr));
+}
+
+void CancelUnlatchTimer(void)
+{
+    if (xTimerStop(sUnlatchTimer, pdMS_TO_TICKS(0)) == pdFAIL)
+    {
+        SILABS_LOG("sUnlatchTimer stop() failed");
+        appError(APP_ERROR_STOP_TIMER_FAILED);
+    }
+}
+
+void StartUnlatchTimer(uint32_t timeoutMs)
+{
+    if (xTimerIsTimerActive(sUnlatchTimer))
+    {
+        SILABS_LOG("app timer already started!");
+        CancelUnlatchTimer();
+    }
+
+    // timer is not active, change its period to required value (== restart).
+    // FreeRTOS- Block for a maximum of 100 ms if the change period command
+    // cannot immediately be sent to the timer command queue.
+    if (xTimerStart(sUnlatchTimer, pdMS_TO_TICKS(timeoutMs)) != pdPASS)
+    {
+        SILABS_LOG("sUnlatchTimer timer start() failed");
+        appError(APP_ERROR_START_TIMER_FAILED);
+    }
+}
+
 } // namespace
 
 using namespace chip::TLV;
@@ -180,6 +219,8 @@
     sLockLED.Init(LOCK_STATE_LED);
     sLockLED.Set(state.Value() == DlLockState::kUnlocked);
 
+    sUnlatchTimer = xTimerCreate("UnlatchTimer", pdMS_TO_TICKS(UNLATCH_TIME_MS), pdFALSE, (void *) 0, UnlatchTimerCallback);
+
     // Update the LCD with the Stored value. Show QR Code if not provisioned
 #ifdef DISPLAY_ENABLED
     GetLCD().WriteDemoUI(state.Value() != DlLockState::kUnlocked);
@@ -309,6 +350,10 @@
         sAppTask.GetLCD().WriteDemoUI(locked);
 #endif // DISPLAY_ENABLED
     }
+    else if (aAction == LockManager::UNLATCH_ACTION)
+    {
+        SILABS_LOG("Unlatch Action has been initiated");
+    }
 
     if (aActor == AppEvent::kEventType_Button)
     {
@@ -325,6 +370,11 @@
     {
         SILABS_LOG("Lock Action has been completed")
     }
+    else if (aAction == LockManager::UNLATCH_ACTION)
+    {
+        SILABS_LOG("Unlatch Action has been completed")
+        StartUnlatchTimer(UNLATCH_TIME_MS);
+    }
     else if (aAction == LockManager::UNLOCK_ACTION)
     {
         SILABS_LOG("Unlock Action has been completed")
diff --git a/examples/lock-app/silabs/src/LockManager.cpp b/examples/lock-app/silabs/src/LockManager.cpp
index 3c376a2..04527e5 100644
--- a/examples/lock-app/silabs/src/LockManager.cpp
+++ b/examples/lock-app/silabs/src/LockManager.cpp
@@ -186,22 +186,24 @@
     State_t new_state;
 
     // Initiate Turn Lock/Unlock Action only when the previous one is complete.
-    if (mState == kState_LockCompleted && aAction == UNLOCK_ACTION)
+    if ((mState == kState_LockCompleted || mState == kState_UnlatchCompleted) && (aAction == UNLOCK_ACTION))
     {
         action_initiated = true;
-
-        new_state = kState_UnlockInitiated;
+        new_state        = kState_UnlockInitiated;
+    }
+    else if ((mState == kState_LockCompleted || mState == kState_UnlockCompleted) && (aAction == UNLATCH_ACTION))
+    {
+        action_initiated = true;
+        new_state        = kState_UnlatchInitiated;
     }
     else if (mState == kState_UnlockCompleted && aAction == LOCK_ACTION)
     {
         action_initiated = true;
-
-        new_state = kState_LockInitiated;
+        new_state        = kState_LockInitiated;
     }
 
     if (action_initiated)
     {
-
         StartTimer(ACTUATOR_MOVEMENT_PERIOS_MS);
 
         // Since the timer started successfully, update the state and trigger callback
@@ -249,6 +251,23 @@
     event.Handler            = ActuatorMovementTimerEventHandler;
     AppTask::GetAppTask().PostEvent(&event);
 }
+void LockManager::UnlockAfterUnlatch()
+{
+    // write the new lock value
+    bool succes = false;
+    if (mUnlatchContext.mEndpointId != kInvalidEndpointId)
+    {
+        succes = setLockState(mUnlatchContext.mEndpointId, mUnlatchContext.mFabricIdx, mUnlatchContext.mNodeId,
+                              DlLockState::kUnlocked, mUnlatchContext.mPin, mUnlatchContext.mErr);
+    }
+
+    if (!succes)
+    {
+        SILABS_LOG("Failed to update the lock state after Unlatch");
+    }
+
+    InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION);
+}
 
 void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent)
 {
@@ -261,6 +280,11 @@
         lock->mState    = kState_LockCompleted;
         actionCompleted = LOCK_ACTION;
     }
+    else if (lock->mState == kState_UnlatchInitiated)
+    {
+        lock->mState    = kState_UnlatchCompleted;
+        actionCompleted = UNLATCH_ACTION;
+    }
     else if (lock->mState == kState_UnlockInitiated)
     {
         lock->mState    = kState_UnlockCompleted;
@@ -285,6 +309,29 @@
 bool LockManager::Unlock(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
                          const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err)
 {
+    if (DoorLockServer::Instance().SupportsUnbolt(endpointId))
+    {
+        // TODO: Our current implementation does not support multiple endpoint. This needs to be fixed in the future.
+        if (endpointId != mUnlatchContext.mEndpointId)
+        {
+            // If we get a request to unlock on a different endpoint while the current endpoint is in the middle of an action,
+            // we return false for now. This needs to be fixed in the future.
+            if (mState != kState_UnlockCompleted && mState != kState_LockCompleted)
+            {
+                ChipLogError(Zcl, "Cannot unlock while unlatch on another endpoint is in progress on  anotther endpoint");
+                return false;
+            }
+            else
+            {
+                mUnlatchContext.Update(endpointId, fabricIdx, nodeId, pin, err);
+                return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlatched, pin, err);
+            }
+        }
+        else
+        {
+            return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlatched, pin, err);
+        }
+    }
     return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlocked, pin, err);
 }
 
diff --git a/examples/lock-app/silabs/src/ZclCallbacks.cpp b/examples/lock-app/silabs/src/ZclCallbacks.cpp
index 2aa0e9d..f7e333f 100644
--- a/examples/lock-app/silabs/src/ZclCallbacks.cpp
+++ b/examples/lock-app/silabs/src/ZclCallbacks.cpp
@@ -89,7 +89,14 @@
     bool status = LockMgr().Unlock(endpointId, fabricIdx, nodeId, pinCode, err);
     if (status == true)
     {
-        LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION);
+        if (DoorLockServer::Instance().SupportsUnbolt(endpointId))
+        {
+            LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLATCH_ACTION);
+        }
+        else
+        {
+            LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION);
+        }
     }
 
     return status;