Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nrfconnect] Adapt lock-app to Door Lock cluster #17593

Merged
merged 1 commit into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 48 additions & 61 deletions examples/lock-app/nrfconnect/main/AppTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <app-common/zap-generated/attribute-type.h>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-id.h>
#include <app/clusters/door-lock-server/door-lock-server.h>
#include <app/server/OnboardingCodesUtil.h>
#include <app/server/Server.h>
#include <credentials/DeviceAttestationCredsProvider.h>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<BoltLockManager::Action_t>(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)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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))
Expand All @@ -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");
}
});
}
171 changes: 37 additions & 134 deletions examples/lock-app/nrfconnect/main/BoltLockManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,170 +20,73 @@
#include "BoltLockManager.h"

#include "AppConfig.h"
#include "AppEvent.h"
#include "AppTask.h"

#include <logging/log.h>
#include <zephyr.h>

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<BoltLockManager *>(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<BoltLockManager *>(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<BoltLockManager *>(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<BoltLockManager *>(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);
}
}
Loading