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

samples: matter: lock: Support all fields of LockOperation event #21078

Merged
merged 7 commits into from
Mar 24, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ Matter samples
* :ref:`matter_lock_sample` sample:

* Removed support for nRF54H20 devices.
* Updated the API of ``AppTask``, ``BoltLockManager``, and ``AccessManager`` to provide additional information for the ``LockOperation`` event.

Networking samples
------------------
Expand Down
51 changes: 34 additions & 17 deletions samples/matter/lock/src/access/access_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ using namespace DoorLockData;

template <CredentialsBits CRED_BIT_MASK>
void AccessManager<CRED_BIT_MASK>::Init(SetCredentialCallback setCredentialClbk,
ClearCredentialCallback clearCredentialClbk,
ValidateCredentialCallback validateCredentialClbk)
ClearCredentialCallback clearCredentialClbk,
ValidateCredentialCallback validateCredentialClbk)
{
InitializeAllCredentials();
mSetCredentialCallback = setCredentialClbk;
Expand Down Expand Up @@ -54,7 +54,8 @@ bool AccessManager<CRED_BIT_MASK>::ValidateCustom(CredentialTypeEnum type, chip:
}

template <CredentialsBits CRED_BIT_MASK>
bool AccessManager<CRED_BIT_MASK>::ValidatePIN(const Optional<ByteSpan> &pinCode, OperationErrorEnum &err)
bool AccessManager<CRED_BIT_MASK>::ValidatePIN(const Optional<ByteSpan> &pinCode, OperationErrorEnum &err,
Nullable<ValidatePINResult> &result)
{
/* Optionality of the PIN code is validated by the caller, so assume it is OK not to provide the PIN
* code. */
Expand All @@ -66,17 +67,34 @@ bool AccessManager<CRED_BIT_MASK>::ValidatePIN(const Optional<ByteSpan> &pinCode

/* Check the PIN code */
for (size_t index = 1; index <= CONFIG_LOCK_MAX_NUM_CREDENTIALS_PER_TYPE; ++index) {
if (CHIP_NO_ERROR == mCredentials.GetCredentials(CredentialTypeEnum::kPin, credential, index)) {
if (credential.status == DlCredentialStatus::kAvailable) {
continue;
}

if (credential.credentialData.data_equal(pinCode.Value())) {
LOG_DBG("Valid lock PIN code provided");
return true;
}
if (CHIP_NO_ERROR != mCredentials.GetCredentials(CredentialTypeEnum::kPin, credential, index)) {
err = OperationErrorEnum::kInvalidCredential;
continue;
}
err = OperationErrorEnum::kInvalidCredential;

if (credential.status == DlCredentialStatus::kAvailable) {
continue;
}

if (!credential.credentialData.data_equal(pinCode.Value())) {
err = OperationErrorEnum::kInvalidCredential;
continue;
}

uint32_t credentialUserId;
if (GetCredentialUserId(index, CredentialTypeEnum::kPin, credentialUserId) == CHIP_NO_ERROR) {
result = ValidatePINResult{
.mUserId = static_cast<uint16_t>(credentialUserId),
.mCredential =
LockOpCredentials{ CredentialTypeEnum::kPin, static_cast<uint16_t>(index) },
};
} else {
result = {};
}

LOG_DBG("Valid lock PIN code provided");
err = OperationErrorEnum::kUnspecified;
return true;
}
LOG_DBG("Invalid lock PIN code provided");
return false;
Expand Down Expand Up @@ -134,9 +152,8 @@ template <CredentialsBits CRED_BIT_MASK> void AccessManager<CRED_BIT_MASK>::SetR
{
if (mRequirePINForRemoteOperation != require) {
mRequirePINForRemoteOperation = require;
if (!AccessStorage::Instance().Store(AccessStorage::Type::RequirePIN,
&mRequirePINForRemoteOperation,
sizeof(mRequirePINForRemoteOperation))) {
if (!AccessStorage::Instance().Store(AccessStorage::Type::RequirePIN, &mRequirePINForRemoteOperation,
sizeof(mRequirePINForRemoteOperation))) {
LOG_ERR("Cannot store RequirePINforRemoteOperation.");
}
}
Expand All @@ -148,4 +165,4 @@ template class AccessManager<DoorLockData::PIN | DoorLockData::RFID>;
template class AccessManager<DoorLockData::PIN | DoorLockData::RFID | DoorLockData::FINGER>;
template class AccessManager<DoorLockData::PIN | DoorLockData::RFID | DoorLockData::FINGER | DoorLockData::VEIN>;
template class AccessManager<DoorLockData::PIN | DoorLockData::RFID | DoorLockData::FINGER | DoorLockData::VEIN |
DoorLockData::FACE>;
DoorLockData::FACE>;
9 changes: 8 additions & 1 deletion samples/matter/lock/src/access/access_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

template <DoorLockData::CredentialsBits CRED_BIT_MASK> class AccessManager {
public:
struct ValidatePINResult {
uint16_t mUserId;
LockOpCredentials mCredential;
};

/**
* @brief Signature of the callback fired when the credential is set.
*
Expand Down Expand Up @@ -234,9 +239,11 @@ template <DoorLockData::CredentialsBits CRED_BIT_MASK> class AccessManager {
*
* @param pinCode PIN code data.
* @param err specific error code enumeration.
* @param result user and credential data set on success.
* @return true on success, false otherwise.
*/
bool ValidatePIN(const Optional<chip::ByteSpan> &pinCode, OperationErrorEnum &err);
bool ValidatePIN(const Optional<chip::ByteSpan> &pinCode, OperationErrorEnum &err,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@markaj-nordic just FYI, the doorlock access manager API has been changed.

Nullable<ValidatePINResult> &result);
bool ValidateCustom(CredentialTypeEnum type, chip::MutableByteSpan &secret);

/**
Expand Down
78 changes: 57 additions & 21 deletions samples/matter/lock/src/app_task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ void AppTask::SwitchTransportTriggerHandler(const SwitchButtonAction &action)
}
#endif

void AppTask::LockStateChanged(BoltLockManager::State state, BoltLockManager::OperationSource source)
void AppTask::LockStateChanged(const BoltLockManager::StateData &stateData)
{
switch (state) {
switch (stateData.mState) {
case BoltLockManager::State::kLockingInitiated:
LOG_INF("Lock action initiated");
Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Blink(50, 50);
Expand Down Expand Up @@ -164,14 +164,36 @@ void AppTask::LockStateChanged(BoltLockManager::State state, BoltLockManager::Op
}

/* Handle changing attribute state in the application */
Instance().UpdateClusterState(state, source);
Instance().UpdateClusterState(stateData);
}

void AppTask::UpdateClusterState(BoltLockManager::State state, BoltLockManager::OperationSource source)
void AppTask::UpdateClusterState(const BoltLockManager::StateData &stateData)
{
BoltLockManager::StateData *stateDataCopy = Platform::New<BoltLockManager::StateData>(stateData);

if (stateDataCopy == nullptr) {
LOG_ERR("Failed to allocate memory for BoltLockManager::StateData");
return;
}

CHIP_ERROR err = SystemLayer().ScheduleLambda([stateDataCopy]() {
UpdateClusterStateHandler(*stateDataCopy);
Platform::Delete(stateDataCopy);
});

if (err != CHIP_NO_ERROR) {
LOG_ERR("Failed to schedule lambda: %" CHIP_ERROR_FORMAT, err.Format());
Platform::Delete(stateDataCopy);
}
}

void AppTask::UpdateClusterStateHandler(const BoltLockManager::StateData &stateData)
{
using namespace chip::app::Clusters::DoorLock::Attributes;

DlLockState newLockState;

switch (state) {
switch (stateData.mState) {
case BoltLockManager::State::kLockingCompleted:
newLockState = DlLockState::kLocked;
break;
Expand All @@ -183,29 +205,43 @@ void AppTask::UpdateClusterState(BoltLockManager::State state, BoltLockManager::
break;
}

SystemLayer().ScheduleLambda([newLockState, source] {
chip::app::DataModel::Nullable<chip::app::Clusters::DoorLock::DlLockState> currentLockState;
chip::app::Clusters::DoorLock::Attributes::LockState::Get(kLockEndpointId, currentLockState);
Nullable<DlLockState> currentLockState;
LockState::Get(kLockEndpointId, currentLockState);

if (currentLockState.IsNull()) {
/* Initialize lock state with start value, but not invoke lock/unlock. */
LockState::Set(kLockEndpointId, newLockState);
} else {
LOG_INF("Updating LockState attribute");

Nullable<uint16_t> userId;
Nullable<List<const LockOpCredentials>> credentials;
List<const LockOpCredentials> credentialList;

if (currentLockState.IsNull()) {
/* Initialize lock state with start value, but not invoke lock/unlock. */
chip::app::Clusters::DoorLock::Attributes::LockState::Set(kLockEndpointId, newLockState);
} else {
LOG_INF("Updating LockState attribute");
if (!stateData.mValidatePINResult.IsNull()) {
userId = { stateData.mValidatePINResult.Value().mUserId };

if (!DoorLockServer::Instance().SetLockState(kLockEndpointId, newLockState, source)) {
LOG_ERR("Failed to update LockState attribute");
}
/* `DoorLockServer::SetLockState` exptects list of `LockOpCredentials`,
however in case of PIN validation it makes no sense to have more than one
credential corresponding to validation result. For simplicity we wrap single
credential in list here. */
credentialList = { &stateData.mValidatePINResult.Value().mCredential, 1 };
credentials = { credentialList };
}
});

if (!DoorLockServer::Instance().SetLockState(kLockEndpointId, newLockState, stateData.mSource, userId,
credentials, stateData.mFabricIdx, stateData.mNodeId)) {
LOG_ERR("Failed to update LockState attribute");
}
}
}

#ifdef CONFIG_CHIP_NUS
void AppTask::NUSLockCallback(void *context)
{
LOG_DBG("Received LOCK command from NUS");
if (BoltLockMgr().mState == BoltLockManager::State::kLockingCompleted ||
BoltLockMgr().mState == BoltLockManager::State::kLockingInitiated) {
if (BoltLockMgr().GetState().mState == BoltLockManager::State::kLockingCompleted ||
BoltLockMgr().GetState().mState == BoltLockManager::State::kLockingInitiated) {
LOG_INF("Device is already locked");
} else {
Nrf::PostTask([] { LockActionEventHandler(); });
Expand All @@ -215,8 +251,8 @@ void AppTask::NUSLockCallback(void *context)
void AppTask::NUSUnlockCallback(void *context)
{
LOG_DBG("Received UNLOCK command from NUS");
if (BoltLockMgr().mState == BoltLockManager::State::kUnlockingCompleted ||
BoltLockMgr().mState == BoltLockManager::State::kUnlockingInitiated) {
if (BoltLockMgr().GetState().mState == BoltLockManager::State::kUnlockingCompleted ||
BoltLockMgr().GetState().mState == BoltLockManager::State::kUnlockingInitiated) {
LOG_INF("Device is already unlocked");
} else {
Nrf::PostTask([] { LockActionEventHandler(); });
Expand Down
8 changes: 5 additions & 3 deletions samples/matter/lock/src/app_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class AppTask {

CHIP_ERROR StartApp();

void UpdateClusterState(BoltLockManager::State state, BoltLockManager::OperationSource source);
void UpdateClusterState(const BoltLockManager::StateData &stateData);
static void IdentifyStartHandler(Identify *);
static void IdentifyStopHandler(Identify *);

Expand All @@ -40,7 +40,8 @@ class AppTask {

static void LockActionEventHandler();
static void ButtonEventHandler(Nrf::ButtonState state, Nrf::ButtonMask hasChanged);
static void LockStateChanged(BoltLockManager::State state, BoltLockManager::OperationSource source);
static void LockStateChanged(const BoltLockManager::StateData &stateData);
static void UpdateClusterStateHandler(const BoltLockManager::StateData &stateData);

#ifdef CONFIG_THREAD_WIFI_SWITCHING
static void SwitchTransportEventHandler();
Expand All @@ -54,7 +55,8 @@ class AppTask {
#endif

#ifdef CONFIG_NCS_SAMPLE_MATTER_TEST_EVENT_TRIGGERS
constexpr static Nrf::Matter::TestEventTrigger::EventTriggerId kDoorLockJammedEventTriggerId = 0xFFFF'FFFF'3277'4000;
constexpr static Nrf::Matter::TestEventTrigger::EventTriggerId kDoorLockJammedEventTriggerId =
0xFFFF'FFFF'3277'4000;
static CHIP_ERROR DoorLockJammedEventCallback(Nrf::Matter::TestEventTrigger::TriggerValue);
#endif
};
55 changes: 33 additions & 22 deletions samples/matter/lock/src/bolt_lock_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ bool BoltLockManager::SetUser(uint16_t userIndex, FabricIndex creator, FabricInd
size_t totalCredentials)
{
return AccessMgr::Instance().SetUser(userIndex, creator, modifier, userName, uniqueId, userStatus, userType,
credentialRule, credentials, totalCredentials);
credentialRule, credentials, totalCredentials);
}

bool BoltLockManager::GetCredential(uint16_t credentialIndex, CredentialTypeEnum credentialType,
Expand All @@ -50,8 +50,8 @@ bool BoltLockManager::SetCredential(uint16_t credentialIndex, FabricIndex creato
DlCredentialStatus credentialStatus, CredentialTypeEnum credentialType,
const ByteSpan &secret)
{
return AccessMgr::Instance().SetCredential(credentialIndex, creator, modifier, credentialStatus,
credentialType, secret);
return AccessMgr::Instance().SetCredential(credentialIndex, creator, modifier, credentialStatus, credentialType,
secret);
}

#ifdef CONFIG_LOCK_SCHEDULES
Expand All @@ -67,7 +67,7 @@ DlStatus BoltLockManager::SetWeekDaySchedule(uint8_t weekdayIndex, uint16_t user
uint8_t endHour, uint8_t endMinute)
{
return AccessMgr::Instance().SetWeekDaySchedule(weekdayIndex, userIndex, status, daysMask, startHour,
startMinute, endHour, endMinute);
startMinute, endHour, endMinute);
}

DlStatus BoltLockManager::GetYearDaySchedule(uint8_t yearDayIndex, uint16_t userIndex,
Expand All @@ -91,15 +91,15 @@ DlStatus BoltLockManager::SetHolidaySchedule(uint8_t holidayIndex, DlScheduleSta
uint32_t localEndTime, OperatingModeEnum operatingMode)
{
return AccessMgr::Instance().SetHolidaySchedule(holidayIndex, status, localStartTime, localEndTime,
operatingMode);
operatingMode);
}

#endif /* CONFIG_LOCK_SCHEDULES */


bool BoltLockManager::ValidatePIN(const Optional<ByteSpan> &pinCode, OperationErrorEnum &err)
bool BoltLockManager::ValidatePIN(const Optional<chip::ByteSpan> &pinCode, OperationErrorEnum &err,
Nullable<ValidatePINResult> &result)
{
return AccessMgr::Instance().ValidatePIN(pinCode, err);
return AccessMgr::Instance().ValidatePIN(pinCode, err, result);
}

void BoltLockManager::SetRequirePIN(bool require)
Expand All @@ -111,21 +111,23 @@ bool BoltLockManager::GetRequirePIN()
return AccessMgr::Instance().GetRequirePIN();
}

void BoltLockManager::Lock(OperationSource source)
void BoltLockManager::Lock(const OperationSource source, const Nullable<chip::FabricIndex> &fabricIdx,
const Nullable<chip::NodeId> &nodeId, const Nullable<ValidatePINResult> &validatePINResult)
{
VerifyOrReturn(mState != State::kLockingCompleted);
SetState(State::kLockingInitiated, source);
VerifyOrReturn(mStateData.mState != State::kLockingCompleted);
StateData newStateData{ State::kLockingInitiated, source, fabricIdx, nodeId, validatePINResult };
SetStateData(newStateData);

mActuatorOperationSource = source;
k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT);
}

void BoltLockManager::Unlock(OperationSource source)
void BoltLockManager::Unlock(const OperationSource source, const Nullable<chip::FabricIndex> &fabricIdx,
const Nullable<chip::NodeId> &nodeId, const Nullable<ValidatePINResult> &validatePINResult)
{
VerifyOrReturn(mState != State::kUnlockingCompleted);
SetState(State::kUnlockingInitiated, source);
VerifyOrReturn(mStateData.mState != State::kUnlockingCompleted);
StateData newStateData{ State::kUnlockingInitiated, source, fabricIdx, nodeId, validatePINResult };
SetStateData(newStateData);

mActuatorOperationSource = source;
k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT);
}

Expand All @@ -146,24 +148,33 @@ void BoltLockManager::ActuatorAppEventHandler(const BoltLockManagerEvent &event)
{
BoltLockManager *lock = reinterpret_cast<BoltLockManager *>(event.manager);

switch (lock->mState) {
switch (lock->mStateData.mState) {
case State::kLockingInitiated:
lock->SetState(State::kLockingCompleted, lock->mActuatorOperationSource);
lock->SetState(State::kLockingCompleted);
break;
case State::kUnlockingInitiated:
lock->SetState(State::kUnlockingCompleted, lock->mActuatorOperationSource);
lock->SetState(State::kUnlockingCompleted);
break;
default:
break;
}
}

void BoltLockManager::SetState(State state, OperationSource source)
void BoltLockManager::SetState(State state)
{
mStateData.mState = state;

if (mStateChangeCallback != nullptr) {
mStateChangeCallback(mStateData);
}
}

void BoltLockManager::SetStateData(const StateData &stateData)
{
mState = state;
mStateData = stateData;

if (mStateChangeCallback != nullptr) {
mStateChangeCallback(state, source);
mStateChangeCallback(mStateData);
}
}

Expand Down
Loading