From 03d14b2813c24c194cfb9c7513791590f015f11e Mon Sep 17 00:00:00 2001 From: Damian Krolik Date: Thu, 21 Apr 2022 10:52:52 +0200 Subject: [PATCH] [nrfconnect] Adapt lock-app to Door Lock cluster 1. Listen to lock-state attribute changes rather than lock/unlock commands to support the auto-relock feature. 2. Remove obsolete code from BoltLockManager as now the auto-relock feature is handled in the common cluster code. --- examples/lock-app/nrfconnect/main/AppTask.cpp | 109 +++++------ .../nrfconnect/main/BoltLockManager.cpp | 171 ++++-------------- .../lock-app/nrfconnect/main/ZclCallbacks.cpp | 34 +++- .../nrfconnect/main/include/AppConfig.h | 3 - .../nrfconnect/main/include/AppTask.h | 6 +- .../nrfconnect/main/include/BoltLockManager.h | 67 +++---- 6 files changed, 141 insertions(+), 249 deletions(-) diff --git a/examples/lock-app/nrfconnect/main/AppTask.cpp b/examples/lock-app/nrfconnect/main/AppTask.cpp index 16ab053b7244cc..4c26d4e339bbb2 100644 --- a/examples/lock-app/nrfconnect/main/AppTask.cpp +++ b/examples/lock-app/nrfconnect/main/AppTask.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -120,15 +121,14 @@ CHIP_ERROR AppTask::Init() sStatusLED.Init(SYSTEM_STATE_LED); sLockLED.Init(LOCK_STATE_LED); - sLockLED.Set(!BoltLockMgr().IsUnlocked()); + sLockLED.Set(BoltLockMgr().IsLocked()); sUnusedLED.Init(DK_LED3); sUnusedLED_1.Init(DK_LED4); UpdateStatusLED(); - BoltLockMgr().Init(); - BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted); + BoltLockMgr().Init(LockStateChanged); // Initialize buttons int ret = dk_buttons_init(ButtonEventHandler); @@ -191,22 +191,14 @@ CHIP_ERROR AppTask::StartApp() void AppTask::LockActionEventHandler(AppEvent * aEvent) { - BoltLockManager::Action_t action = BoltLockManager::INVALID_ACTION; - int32_t actor = 0; - - if (aEvent->Type == AppEvent::kEventType_Lock) + if (BoltLockMgr().IsLocked()) { - action = static_cast(aEvent->LockEvent.Action); - actor = aEvent->LockEvent.Actor; + BoltLockMgr().Unlock(BoltLockManager::OperationSource::kButton); } - else if (aEvent->Type == AppEvent::kEventType_Button) + else { - action = BoltLockMgr().IsUnlocked() ? BoltLockManager::LOCK_ACTION : BoltLockManager::UNLOCK_ACTION; - actor = AppEvent::kEventType_Button; + BoltLockMgr().Lock(BoltLockManager::OperationSource::kButton); } - - if (action != BoltLockManager::INVALID_ACTION && !BoltLockMgr().InitiateAction(actor, action)) - LOG_INF("Action is already in progress or active."); } void AppTask::ButtonEventHandler(uint32_t button_state, uint32_t has_changed) @@ -341,7 +333,7 @@ void AppTask::FunctionHandler(AppEvent * aEvent) sUnusedLED_1.Set(false); // Set lock status LED back to show state of lock. - sLockLED.Set(!BoltLockMgr().IsUnlocked()); + sLockLED.Set(BoltLockMgr().IsLocked()); UpdateStatusLED(); sAppTask.CancelTimer(); @@ -480,54 +472,34 @@ void AppTask::StartTimer(uint32_t aTimeoutInMs) mFunctionTimerActive = true; } -void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor) +void AppTask::LockStateChanged(BoltLockManager::State state, BoltLockManager::OperationSource source) { - // If the action has been initiated by the lock, update the bolt lock trait - // and start flashing the LEDs rapidly to indicate action initiation. - if (aAction == BoltLockManager::LOCK_ACTION) - { - LOG_INF("Lock Action has been initiated"); - } - else if (aAction == BoltLockManager::UNLOCK_ACTION) + switch (state) { - LOG_INF("Unlock Action has been initiated"); - } - - sLockLED.Blink(50, 50); -} - -void AppTask::ActionCompleted(BoltLockManager::Action_t aAction, int32_t aActor) -{ - // if the action has been completed by the lock, update the bolt lock trait. - // Turn on the lock LED if in a LOCKED state OR - // Turn off the lock LED if in an UNLOCKED state. - if (aAction == BoltLockManager::LOCK_ACTION) - { - LOG_INF("Lock Action has been completed"); + case BoltLockManager::State::kLockingInitiated: + LOG_INF("Lock action initiated"); + sLockLED.Blink(50, 50); + break; + case BoltLockManager::State::kLockingCompleted: + LOG_INF("Lock action completed"); sLockLED.Set(true); - } - else if (aAction == BoltLockManager::UNLOCK_ACTION) - { - LOG_INF("Unlock Action has been completed"); + break; + case BoltLockManager::State::kUnlockingInitiated: + LOG_INF("Unlock action initiated"); + sLockLED.Blink(50, 50); + break; + case BoltLockManager::State::kUnlockingCompleted: + LOG_INF("Unlock action completed"); sLockLED.Set(false); + break; } - if (aActor == AppEvent::kEventType_Button) + if (source != BoltLockManager::OperationSource::kRemote) { - sAppTask.UpdateClusterState(); + sAppTask.UpdateClusterState(state, source); } } -void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction) -{ - AppEvent event; - event.Type = AppEvent::kEventType_Lock; - event.LockEvent.Actor = aActor; - event.LockEvent.Action = aAction; - event.Handler = LockActionEventHandler; - PostEvent(&event); -} - void AppTask::PostEvent(AppEvent * aEvent) { if (k_msgq_put(&sAppEventQueue, aEvent, K_NO_WAIT)) @@ -548,14 +520,29 @@ void AppTask::DispatchEvent(AppEvent * aEvent) } } -void AppTask::UpdateClusterState() +void AppTask::UpdateClusterState(BoltLockManager::State state, BoltLockManager::OperationSource source) { - EmberAfStatus status; - LOG_INF("Updating door lock state"); - status = Clusters::DoorLock::Attributes::LockState::Set( - kLockEndpointId, BoltLockMgr().IsUnlocked() ? DlLockState::kUnlocked : DlLockState::kLocked); - if (status != EMBER_ZCL_STATUS_SUCCESS) + DlLockState lockState; + + switch (state) { - LOG_ERR("Updating door lock state %x", status); + case BoltLockManager::State::kLockingCompleted: + lockState = DlLockState::kLocked; + break; + case BoltLockManager::State::kUnlockingCompleted: + lockState = DlLockState::kUnlocked; + break; + default: + lockState = DlLockState::kNotFullyLocked; + break; } + + SystemLayer().ScheduleLambda([lockState, source] { + LOG_INF("Updating LockState attribute"); + + if (!DoorLockServer::Instance().SetLockState(kLockEndpointId, lockState, source)) + { + LOG_ERR("Failed to update LockState attribute"); + } + }); } diff --git a/examples/lock-app/nrfconnect/main/BoltLockManager.cpp b/examples/lock-app/nrfconnect/main/BoltLockManager.cpp index 73d3d447b417eb..a220dae31ece86 100644 --- a/examples/lock-app/nrfconnect/main/BoltLockManager.cpp +++ b/examples/lock-app/nrfconnect/main/BoltLockManager.cpp @@ -20,170 +20,73 @@ #include "BoltLockManager.h" #include "AppConfig.h" +#include "AppEvent.h" #include "AppTask.h" -#include -#include - -LOG_MODULE_DECLARE(app, CONFIG_MATTER_LOG_LEVEL); - -static k_timer sLockTimer; - BoltLockManager BoltLockManager::sLock; -void BoltLockManager::Init() +void BoltLockManager::Init(StateChangeCallback callback) { - k_timer_init(&sLockTimer, &BoltLockManager::TimerEventHandler, nullptr); - k_timer_user_data_set(&sLockTimer, this); + mStateChangeCallback = callback; - mState = kState_LockingCompleted; - mAutoLockTimerArmed = false; - mAutoRelock = false; - mAutoLockDuration = 0; -} - -void BoltLockManager::SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB) -{ - mActionInitiated_CB = aActionInitiated_CB; - mActionCompleted_CB = aActionCompleted_CB; + k_timer_init(&mActuatorTimer, &BoltLockManager::ActuatorTimerEventHandler, nullptr); + k_timer_user_data_set(&mActuatorTimer, this); } -bool BoltLockManager::IsActionInProgress() +void BoltLockManager::Lock(OperationSource source) { - return (mState == kState_LockingInitiated || mState == kState_UnlockingInitiated) ? true : false; -} + VerifyOrReturn(mState != State::kLockingCompleted); + SetState(State::kLockingInitiated, source); -bool BoltLockManager::IsUnlocked() -{ - return (mState == kState_UnlockingCompleted) ? true : false; + mActuatorOperationSource = source; + k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT); } -void BoltLockManager::EnableAutoRelock(bool aOn) +void BoltLockManager::Unlock(OperationSource source) { - mAutoRelock = aOn; -} + VerifyOrReturn(mState != State::kUnlockingCompleted); + SetState(State::kUnlockingInitiated, source); -void BoltLockManager::SetAutoLockDuration(uint32_t aDurationInSecs) -{ - mAutoLockDuration = aDurationInSecs; + mActuatorOperationSource = source; + k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT); } -bool BoltLockManager::InitiateAction(int32_t aActor, Action_t aAction) +void BoltLockManager::ActuatorTimerEventHandler(k_timer * timer) { - bool action_initiated = false; - State_t new_state; - - // Initiate Lock/Unlock Action only when the previous one is complete. - if (mState == kState_LockingCompleted && aAction == UNLOCK_ACTION) - { - action_initiated = true; - mCurrentActor = aActor; - new_state = kState_UnlockingInitiated; - } - else if (mState == kState_UnlockingCompleted && aAction == LOCK_ACTION) - { - action_initiated = true; - mCurrentActor = aActor; - new_state = kState_LockingInitiated; - } - - if (action_initiated) - { - if (mAutoLockTimerArmed && new_state == kState_LockingInitiated) - { - // If auto lock timer has been armed and someone initiates locking, - // cancel the timer and continue as normal. - mAutoLockTimerArmed = false; - - CancelTimer(); - } - - StartTimer(ACTUATOR_MOVEMENT_PERIOS_MS); - - // Since the timer started successfully, update the state and trigger callback - mState = new_state; - - if (mActionInitiated_CB) - { - mActionInitiated_CB(aAction, aActor); - } - } - - return action_initiated; -} + // The timer event handler is called in the context of the system clock ISR. + // Post an event to the application task queue to process the event in the + // context of the application thread. -void BoltLockManager::StartTimer(uint32_t aTimeoutMs) -{ - k_timer_start(&sLockTimer, K_MSEC(aTimeoutMs), K_NO_WAIT); -} - -void BoltLockManager::CancelTimer(void) -{ - k_timer_stop(&sLockTimer); -} - -void BoltLockManager::TimerEventHandler(k_timer * timer) -{ - BoltLockManager * lock = static_cast(k_timer_user_data_get(timer)); - - // The timer event handler will be called in the context of the timer task - // once sLockTimer expires. Post an event to apptask queue with the actual handler - // so that the event can be handled in the context of the apptask. AppEvent event; event.Type = AppEvent::kEventType_Timer; - event.TimerEvent.Context = lock; - event.Handler = lock->mAutoLockTimerArmed ? AutoReLockTimerEventHandler : ActuatorMovementTimerEventHandler; + event.TimerEvent.Context = static_cast(k_timer_user_data_get(timer)); + event.Handler = BoltLockManager::ActuatorAppEventHandler; GetAppTask().PostEvent(&event); } -void BoltLockManager::AutoReLockTimerEventHandler(AppEvent * aEvent) +void BoltLockManager::ActuatorAppEventHandler(AppEvent * aEvent) { BoltLockManager * lock = static_cast(aEvent->TimerEvent.Context); - int32_t actor = 0; - - // Make sure auto lock timer is still armed. - if (!lock->mAutoLockTimerArmed) - return; - lock->mAutoLockTimerArmed = false; - - LOG_INF("Auto Re-Lock has been triggered!"); - - lock->InitiateAction(actor, LOCK_ACTION); + switch (lock->mState) + { + case State::kLockingInitiated: + lock->SetState(State::kLockingCompleted, lock->mActuatorOperationSource); + break; + case State::kUnlockingInitiated: + lock->SetState(State::kUnlockingCompleted, lock->mActuatorOperationSource); + break; + default: + break; + } } -void BoltLockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) +void BoltLockManager::SetState(State state, OperationSource source) { - Action_t actionCompleted = INVALID_ACTION; - - BoltLockManager * lock = static_cast(aEvent->TimerEvent.Context); + mState = state; - if (lock->mState == kState_LockingInitiated) + if (mStateChangeCallback != nullptr) { - lock->mState = kState_LockingCompleted; - actionCompleted = LOCK_ACTION; - } - else if (lock->mState == kState_UnlockingInitiated) - { - lock->mState = kState_UnlockingCompleted; - actionCompleted = UNLOCK_ACTION; - } - - if (actionCompleted != INVALID_ACTION) - { - if (lock->mActionCompleted_CB) - { - lock->mActionCompleted_CB(actionCompleted, lock->mCurrentActor); - } - - if (lock->mAutoRelock && actionCompleted == UNLOCK_ACTION) - { - // Start the timer for auto relock - lock->StartTimer(lock->mAutoLockDuration * 1000); - - lock->mAutoLockTimerArmed = true; - - LOG_INF("Auto Re-lock enabled. Will be triggered in %u seconds", lock->mAutoLockDuration); - } + mStateChangeCallback(state, source); } } diff --git a/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp b/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp index d9a65ff918f2d8..db0bd22a6dd5fb 100644 --- a/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp +++ b/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace ::chip; using namespace ::chip::app::Clusters; @@ -30,35 +31,52 @@ using namespace ::chip::app::Clusters::DoorLock; LOG_MODULE_DECLARE(app, CONFIG_MATTER_LOG_LEVEL); +void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t mask, uint8_t type, + uint16_t size, uint8_t * value) +{ + VerifyOrReturn(attributePath.mClusterId == DoorLock::Id && attributePath.mAttributeId == DoorLock::Attributes::LockState::Id); + + switch (*value) + { + case to_underlying(DlLockState::kLocked): + BoltLockMgr().Lock(BoltLockManager::OperationSource::kRemote); + break; + case to_underlying(DlLockState::kUnlocked): + BoltLockMgr().Unlock(BoltLockManager::OperationSource::kRemote); + break; + default: + break; + } +} + bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, DlOperationError & err) { - LOG_INF("Locking"); - BoltLockMgr().InitiateAction(0, BoltLockManager::LOCK_ACTION); return true; } bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, DlOperationError & err) { - LOG_INF("Unlocking"); - BoltLockMgr().InitiateAction(0, BoltLockManager::UNLOCK_ACTION); return true; } void emberAfDoorLockClusterInitCallback(EndpointId endpoint) { DoorLockServer::Instance().InitServer(endpoint); + EmberAfStatus status = DoorLock::Attributes::LockType::Set(endpoint, DlLockType::kDeadBolt); if (status != EMBER_ZCL_STATUS_SUCCESS) { LOG_ERR("Updating type %x", status); } - // Set FeatureMap to 0, defaults is - //(kUsersManagement|kAccessSchedules|kRFIDCredentials|kPINCredentials) 0x113 + + // Set FeatureMap to 0, default is: + // (kUsersManagement|kAccessSchedules|kRFIDCredentials|kPINCredentials) 0x113 status = DoorLock::Attributes::FeatureMap::Set(endpoint, 0); if (status != EMBER_ZCL_STATUS_SUCCESS) { - LOG_ERR("Updating type %x", status); + LOG_ERR("Updating feature map %x", status); } - GetAppTask().UpdateClusterState(); + + GetAppTask().UpdateClusterState(BoltLockMgr().GetState(), BoltLockManager::OperationSource::kUnspecified); } diff --git a/examples/lock-app/nrfconnect/main/include/AppConfig.h b/examples/lock-app/nrfconnect/main/include/AppConfig.h index e62f471559c466..397c50230e5f62 100644 --- a/examples/lock-app/nrfconnect/main/include/AppConfig.h +++ b/examples/lock-app/nrfconnect/main/include/AppConfig.h @@ -31,6 +31,3 @@ #define SYSTEM_STATE_LED DK_LED1 #define LOCK_STATE_LED DK_LED2 - -// Time it takes in ms for the simulated actuator to move from one state to another. -#define ACTUATOR_MOVEMENT_PERIOS_MS 2000 diff --git a/examples/lock-app/nrfconnect/main/include/AppTask.h b/examples/lock-app/nrfconnect/main/include/AppTask.h index 2789d15a41e229..7fb211df3ed248 100644 --- a/examples/lock-app/nrfconnect/main/include/AppTask.h +++ b/examples/lock-app/nrfconnect/main/include/AppTask.h @@ -36,17 +36,15 @@ class AppTask public: CHIP_ERROR StartApp(); - void PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction); void PostEvent(AppEvent * event); - void UpdateClusterState(); + void UpdateClusterState(BoltLockManager::State state, BoltLockManager::OperationSource source); private: friend AppTask & GetAppTask(void); CHIP_ERROR Init(); - static void ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor); - static void ActionCompleted(BoltLockManager::Action_t aAction, int32_t aActor); + static void LockStateChanged(BoltLockManager::State state, BoltLockManager::OperationSource source); void CancelTimer(void); diff --git a/examples/lock-app/nrfconnect/main/include/BoltLockManager.h b/examples/lock-app/nrfconnect/main/include/BoltLockManager.h index 982092a959ef80..feb320b4e70ae1 100644 --- a/examples/lock-app/nrfconnect/main/include/BoltLockManager.h +++ b/examples/lock-app/nrfconnect/main/include/BoltLockManager.h @@ -19,65 +19,54 @@ #pragma once -#include +#include + +#include -#include "AppEvent.h" +#include -struct k_timer; +class AppEvent; class BoltLockManager { public: - enum Action_t + enum class State : uint8_t { - LOCK_ACTION = 0, - UNLOCK_ACTION, - - INVALID_ACTION + kLockingInitiated = 0, + kLockingCompleted, + kUnlockingInitiated, + kUnlockingCompleted, }; - enum State_t - { - kState_LockingInitiated = 0, - kState_LockingCompleted, - kState_UnlockingInitiated, - kState_UnlockingCompleted, - }; + using OperationSource = chip::app::Clusters::DoorLock::DlOperationSource; + using StateChangeCallback = void (*)(State, OperationSource); - void Init(); - bool IsUnlocked(); - void EnableAutoRelock(bool aOn); - void SetAutoLockDuration(uint32_t aDurationInSecs); - bool IsActionInProgress(); - bool InitiateAction(int32_t aActor, Action_t aAction); + static constexpr uint32_t kActuatorMovementTimeMs = 2000; - typedef void (*Callback_fn_initiated)(Action_t, int32_t aActor); - typedef void (*Callback_fn_completed)(Action_t, int32_t aActor); - void SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB); + void Init(StateChangeCallback callback); -private: - friend BoltLockManager & BoltLockMgr(void); - State_t mState; + State GetState() const { return mState; } + bool IsLocked() const { return mState == State::kLockingCompleted; } - Callback_fn_initiated mActionInitiated_CB; - Callback_fn_completed mActionCompleted_CB; + void Lock(OperationSource source); + void Unlock(OperationSource source); - bool mAutoRelock; - uint32_t mAutoLockDuration; - bool mAutoLockTimerArmed; - int32_t mCurrentActor; +private: + void SetState(State state, OperationSource source); - void CancelTimer(void); - void StartTimer(uint32_t aTimeoutMs); + static void ActuatorTimerEventHandler(k_timer * timer); + static void ActuatorAppEventHandler(AppEvent * aEvent); + friend BoltLockManager & BoltLockMgr(); - static void TimerEventHandler(k_timer * timer); - static void AutoReLockTimerEventHandler(AppEvent * aEvent); - static void ActuatorMovementTimerEventHandler(AppEvent * aEvent); + State mState = State::kLockingCompleted; + StateChangeCallback mStateChangeCallback = nullptr; + OperationSource mActuatorOperationSource = OperationSource::kButton; + k_timer mActuatorTimer = {}; static BoltLockManager sLock; }; -inline BoltLockManager & BoltLockMgr(void) +inline BoltLockManager & BoltLockMgr() { return BoltLockManager::sLock; }