From d311644fdce439b8eddb063c0e01efc2d921dde7 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> Date: Thu, 23 May 2024 14:44:43 -0400 Subject: [PATCH] Added unlatch state on unlock supporting Ubolt and tdded ransition locked->unlatched->unlocked (#32023) --- examples/lock-app/silabs/include/AppConfig.h | 4 ++ examples/lock-app/silabs/include/AppTask.h | 7 +++ .../lock-app/silabs/include/LockManager.h | 26 ++++++++ examples/lock-app/silabs/openthread.gni | 2 +- examples/lock-app/silabs/src/AppTask.cpp | 50 ++++++++++++++++ examples/lock-app/silabs/src/LockManager.cpp | 59 +++++++++++++++++-- examples/lock-app/silabs/src/ZclCallbacks.cpp | 9 ++- 7 files changed, 149 insertions(+), 8 deletions(-) diff --git a/examples/lock-app/silabs/include/AppConfig.h b/examples/lock-app/silabs/include/AppConfig.h index 2045ce3ef4d947..2a98805fa0c8d3 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 b0040ac5bf6586..fb911e3ff02b5c 100644 --- a/examples/lock-app/silabs/include/AppTask.h +++ b/examples/lock-app/silabs/include/AppTask.h @@ -119,6 +119,13 @@ class AppTask : public BaseApplication */ 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 * diff --git a/examples/lock-app/silabs/include/LockManager.h b/examples/lock-app/silabs/include/LockManager.h index fb73d1d1218d23..7345782d695194 100644 --- a/examples/lock-app/silabs/include/LockManager.h +++ b/examples/lock-app/silabs/include/LockManager.h @@ -119,6 +119,7 @@ class LockManager { LOCK_ACTION = 0, UNLOCK_ACTION, + UNLATCH_ACTION, INVALID_ACTION } Action; @@ -128,7 +129,9 @@ class LockManager kState_LockInitiated = 0, kState_LockCompleted, kState_UnlockInitiated, + kState_UnlatchInitiated, kState_UnlockCompleted, + kState_UnlatchCompleted, } State; CHIP_ERROR Init(chip::app::DataModel::Nullable state, @@ -191,7 +194,30 @@ class LockManager bool ReadConfigValues(); + void UnlockAfterUnlatch(); + private: + struct UnlatchContext + { + chip::EndpointId mEndpointId; + Nullable mFabricIdx; + Nullable mNodeId; + Optional mPin; + OperationErrorEnum mErr; + + void Update(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & 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 3423049a9a8f79..970d3a05c3aefc 100644 --- a/examples/lock-app/silabs/openthread.gni +++ b/examples/lock-app/silabs/openthread.gni @@ -28,7 +28,7 @@ openthread_external_platform = 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 6937dbb78e8aa5..4e4d05a810acb8 100644 --- a/examples/lock-app/silabs/src/AppTask.cpp +++ b/examples/lock-app/silabs/src/AppTask.cpp @@ -70,6 +70,45 @@ using namespace EFR32DoorLock::LockInitParams; 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(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 @@ CHIP_ERROR AppTask::Init() 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 @@ void AppTask::ActionInitiated(LockManager::Action_t aAction, int32_t aActor) 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 @@ void AppTask::ActionCompleted(LockManager::Action_t aAction) { 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 3c376a237ad81f..04527e53ac7ab1 100644 --- a/examples/lock-app/silabs/src/LockManager.cpp +++ b/examples/lock-app/silabs/src/LockManager.cpp @@ -186,22 +186,24 @@ bool LockManager::InitiateAction(int32_t aActor, Action_t aAction) 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 @@ void LockManager::TimerEventHandler(void * timerCbArg) 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 @@ void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) 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::Lock(chip::EndpointId endpointId, const Nullable & fabricIdx, const Nullable & nodeId, const Optional & 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 2aa0e9d0f6cef2..f7e333f3acb350 100644 --- a/examples/lock-app/silabs/src/ZclCallbacks.cpp +++ b/examples/lock-app/silabs/src/ZclCallbacks.cpp @@ -89,7 +89,14 @@ bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const 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;