blob: 070503a1a144c9627e10355113ad29ce6c237f14 [file] [log] [blame]
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <LockManager.h>
#include <AppConfig.h>
#include <AppTask.h>
#include <LockSettingsStorage.h>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <cstring>
#include <lib/support/logging/CHIPLogging.h>
LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL);
LockManager LockManager::sLock;
using namespace ::chip::DeviceLayer::Internal;
using namespace TelinkDoorLock::LockInitParams;
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
__attribute__((section(".data"))) EmberAfPluginDoorLockUserInfo mCurrentLockUsers;
__attribute__((section(".data"))) char mCurrentUserNames[DOOR_LOCK_MAX_USER_NAME_SIZE];
__attribute__((section(".data"))) uint8_t mCurrentCredentialData[kMaxCredentialSize];
__attribute__((section(".data"))) CredentialStruct mCurrentCredentials[APP_MAX_CREDENTIAL];
__attribute__((section(".data"))) HolidayScheduleInfo mHolidaySchedule;
__attribute__((section(".data"))) WeekDaysScheduleInfo mWeekdaySchedule;
__attribute__((section(".data"))) YearDayScheduleInfo mYeardaySchedule;
#endif
CHIP_ERROR LockManager::Init(chip::app::DataModel::Nullable<chip::app::Clusters::DoorLock::DlLockState> state, LockParam lockParam,
StateChangeCallback callback)
{
LockParams = lockParam;
mStateChangeCallback = callback;
if (LockParams.numberOfUsers > APP_MAX_USERS)
{
ChipLogError(Zcl,
"Max number of users %d is greater than %d, the maximum amount of users currently supported on this platform",
LockParams.numberOfUsers, APP_MAX_USERS);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfCredentialsPerUser > APP_MAX_CREDENTIAL)
{
ChipLogError(
Zcl,
"Max number of credentials per user %d is greater than %d, the maximum amount of users currently supported on this "
"platform",
LockParams.numberOfCredentialsPerUser, APP_MAX_CREDENTIAL);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfWeekdaySchedulesPerUser > APP_MAX_WEEKDAY_SCHEDULE_PER_USER)
{
ChipLogError(
Zcl,
"Max number of schedules %d is greater than %d, the maximum amount of schedules currently supported on this platform",
LockParams.numberOfWeekdaySchedulesPerUser, APP_MAX_WEEKDAY_SCHEDULE_PER_USER);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfYeardaySchedulesPerUser > APP_MAX_YEARDAY_SCHEDULE_PER_USER)
{
ChipLogError(
Zcl,
"Max number of schedules %d is greater than %d, the maximum amount of schedules currently supported on this platform",
LockParams.numberOfYeardaySchedulesPerUser, APP_MAX_YEARDAY_SCHEDULE_PER_USER);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfHolidaySchedules > APP_MAX_HOLYDAY_SCHEDULE_PER_USER)
{
ChipLogError(
Zcl,
"Max number of schedules %d is greater than %d, the maximum amount of schedules currently supported on this platform",
LockParams.numberOfHolidaySchedules, APP_MAX_HOLYDAY_SCHEDULE_PER_USER);
return APP_ERROR_ALLOCATION_FAILED;
}
k_timer_init(&mActuatorTimer, &LockManager::ActuatorTimerEventHandler, nullptr);
k_timer_user_data_set(&mActuatorTimer, this);
return CHIP_NO_ERROR;
}
/* Action related to mechanical operation. Called from button */
bool LockManager::LockAction(int32_t appSource, Action_t aAction, OperationSource source, chip::EndpointId endpointId)
{
bool status = false;
switch (aAction)
{
case LOCK_ACTION:
if (mState != kState_LockCompleted)
{
mState = kState_LockInitiated;
status = DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kLocked, source, NullNullable, NullNullable,
NullNullable, NullNullable);
if (status)
{
LOG_INF("Lock Action: Lock action initiated successfully. Waiting for actuator");
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Lock Action: Lock action failed to initiate. No action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
else
{
status = true;
LOG_INF("Lock Action: Lock is already locked. No action performed");
}
break;
case UNLOCK_ACTION:
if (mState != kState_UnlockCompleted)
{
if (DoorLockServer::Instance().SupportsUnbolt(kExampleEndpointId))
{
status = DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kUnlatched, source, NullNullable,
NullNullable, NullNullable, NullNullable);
if (status)
{
LOG_INF("Unlock Action: Step 1: Unbolt completed");
mState = kState_UnlatchInitiated;
if (mStateChangeCallback)
mStateChangeCallback(mState);
status = DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kUnlocked, source, NullNullable,
NullNullable, NullNullable, NullNullable);
if (status)
{
LOG_INF("Unlock Action: Step 2: Unlock completed");
mState = kState_UnlockInitiated;
if (mStateChangeCallback)
mStateChangeCallback(mState);
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Unlock Action: Step 2: Unlock failed. no action performed");
mState = kState_NotFulyLocked;
}
}
else
{
LOG_INF("Unlock Action: Step 1: Unbolt failed. no action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
else
{
status = DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kUnlocked, source, NullNullable,
NullNullable, NullNullable, NullNullable);
if (status)
{
LOG_INF("Unlock Action: Unlock initiated");
mState = kState_UnlockInitiated;
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Unlock Action: Unlock failed. no action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
}
else
{
status = true;
LOG_INF("Unlock Action: Lock is already unlocked. no action performed");
}
break;
case UNBOLT_ACTION:
if (mState != kState_UnlatchCompleted)
{
status = DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kUnlatched, source, NullNullable,
NullNullable, NullNullable, NullNullable);
if (status)
{
LOG_INF("Unbolt Action: Unbolt initiated");
mState = kState_UnlatchInitiated;
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Unbolt Action: Unbolt failed. no action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
else
{
status = true;
LOG_INF("Unbolt Action: Lock is already in unbolt state. no action performed");
}
break;
default:
LOG_INF("Unknown lock state. no action performed");
mState = kState_NotFulyLocked;
break;
}
return status;
}
/* Action related to mechanical operation. Called from ZCL */
bool LockManager::LockAction(int32_t appSource, Action_t aAction, OperationSource source, chip::EndpointId endpointId,
OperationErrorEnum & err, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pinCode)
{
bool status = false;
switch (aAction)
{
case LOCK_ACTION:
if (mState != kState_LockCompleted)
{
mState = kState_LockInitiated;
status = setLockState(kExampleEndpointId, DlLockState::kLocked, source, err, fabricIdx, nodeId, pinCode);
if (status)
{
LOG_INF("Lock Action: Lock action initiated successfully. Waiting for actuator");
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Lock Action: Lock action failed to initiate. No action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
else
{
status = true;
LOG_INF("Lock Action: Lock is already locked. No action performed");
}
break;
case UNLOCK_ACTION:
if (mState != kState_UnlockCompleted)
{
if (DoorLockServer::Instance().SupportsUnbolt(kExampleEndpointId))
{
status = setLockState(kExampleEndpointId, DlLockState::kUnlatched, source, err, fabricIdx, nodeId, pinCode);
if (status)
{
LOG_INF("Unlock Action: Step 1: Unbolt completed");
mState = kState_UnlatchInitiated;
if (mStateChangeCallback)
mStateChangeCallback(mState);
status = setLockState(kExampleEndpointId, DlLockState::kUnlocked, source, err, fabricIdx, nodeId, pinCode);
if (status)
{
LOG_INF("Unlock Action: Step 2: Unlock completed");
mState = kState_UnlockInitiated;
if (mStateChangeCallback)
mStateChangeCallback(mState);
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Unlock Action: Step 2: Unlock failed. no action performed");
mState = kState_NotFulyLocked;
}
}
else
{
LOG_INF("Unlock Action: Step 1: Unbolt failed. no action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
else
{
status = setLockState(kExampleEndpointId, DlLockState::kUnlocked, source, err, fabricIdx, nodeId, pinCode);
if (status)
{
LOG_INF("Unlock Action: Unlock initiated");
mState = kState_UnlockInitiated;
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Unlock Action: Unlock failed. no action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
}
else
{
status = true;
LOG_INF("Unlock Action: Lock is already unlocked. no action performed");
}
break;
case UNBOLT_ACTION:
if (mState != kState_UnlatchCompleted)
{
status = setLockState(kExampleEndpointId, DlLockState::kUnlatched, source, err, fabricIdx, nodeId, pinCode);
if (status)
{
LOG_INF("Unbolt Action: Unbolt initiated");
mState = kState_UnlatchInitiated;
k_timer_start(&mActuatorTimer, K_MSEC(LOCK_MANAGER_ACTUATOR_MOVEMENT_TIME_MS), K_NO_WAIT);
}
else
{
LOG_INF("Unbolt Action: Unbolt failed. no action performed");
mState = kState_NotFulyLocked;
}
if (mStateChangeCallback)
mStateChangeCallback(mState);
}
else
{
status = true;
LOG_INF("Unbolt Action: Lock is already in unbolt state. no action performed");
}
break;
default:
LOG_INF("Unknown lock state. no action performed");
mState = kState_NotFulyLocked;
break;
}
return status;
}
void LockManager::ActuatorTimerEventHandler(k_timer * timer)
{
// 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.
AppEvent event;
event.Type = AppEvent::kEventType_Timer;
event.TimerEvent.Context = static_cast<LockManager *>(k_timer_user_data_get(timer));
event.Handler = (EventHandler) LockManager::ActuatorAppEventHandler;
GetAppTask().PostEvent(&event);
}
void LockManager::ActuatorAppEventHandler(const AppEvent & event)
{
LockManager * lock = static_cast<LockManager *>(event.TimerEvent.Context);
if (!lock)
{
return;
}
switch (lock->mState)
{
case kState_LockInitiated:
LOG_INF("Lock action completed");
lock->mState = kState_LockCompleted;
if (lock->mStateChangeCallback)
lock->mStateChangeCallback(lock->mState);
break;
case kState_UnlockInitiated:
LOG_INF("Unlock action completed");
lock->mState = kState_UnlockCompleted;
if (lock->mStateChangeCallback)
lock->mStateChangeCallback(lock->mState);
break;
case kState_UnlatchInitiated:
LOG_INF("Unbolt action completed");
lock->mState = kState_UnlatchCompleted;
if (lock->mStateChangeCallback)
lock->mStateChangeCallback(lock->mState);
break;
default:
LOG_INF("Unexpected action occures");
break;
}
}
bool LockManager::IsValidUserIndex(uint16_t userIndex)
{
return (userIndex < APP_MAX_USERS);
}
bool LockManager::IsValidCredentialIndex(uint16_t credentialIndex, CredentialTypeEnum type)
{
if (CredentialTypeEnum::kProgrammingPIN == type)
{
return (0 == credentialIndex); // 0 is required index for Programming PIN
}
return (credentialIndex < APP_MAX_CREDENTIAL);
}
bool LockManager::IsValidCredentialType(CredentialTypeEnum type)
{
return (to_underlying(type) < kNumCredentialTypes);
}
bool LockManager::IsValidWeekdayScheduleIndex(uint8_t scheduleIndex)
{
return (scheduleIndex < APP_MAX_WEEKDAY_SCHEDULE_PER_USER);
}
bool LockManager::IsValidYeardayScheduleIndex(uint8_t scheduleIndex)
{
return (scheduleIndex < APP_MAX_YEARDAY_SCHEDULE_PER_USER);
}
bool LockManager::IsValidHolidayScheduleIndex(uint8_t scheduleIndex)
{
return (scheduleIndex < APP_MAX_HOLYDAY_SCHEDULE_PER_USER);
}
bool LockManager::GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user)
{
VerifyOrReturnValue(userIndex > 0, false); // indices are one-indexed
userIndex--;
VerifyOrReturnValue(IsValidUserIndex(userIndex), false);
ChipLogProgress(Zcl, "Door Lock App: LockManager::GetUser [endpoint=%d,userIndex=%hu]", endpointId, userIndex);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
auto & userInDb = mCurrentLockUsers;
auto & credentialsInStorage = mCurrentCredentials;
EmberAfPluginDoorLockUserInfo lockUser;
char userNames[DOOR_LOCK_MAX_USER_NAME_SIZE];
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_LockUser[userIndex], reinterpret_cast<uint8_t *>(&lockUser),
sizeof(EmberAfPluginDoorLockUserInfo), outLen);
userInDb = lockUser;
CredentialStruct credentialsUsers[APP_MAX_CREDENTIAL];
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_UserCredentials[userIndex],
reinterpret_cast<uint8_t *>(credentialsUsers),
sizeof(CredentialStruct) * LockParams.numberOfCredentialsPerUser, outLen);
for (size_t i = 0; i < userInDb.credentials.size(); ++i)
{
credentialsInStorage[i] = credentialsUsers[i];
}
userInDb.credentials = chip::Span<const CredentialStruct>(credentialsInStorage, userInDb.credentials.size());
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_LockUserName[userIndex],
reinterpret_cast<uint8_t *>(userNames), sizeof(userNames), outLen);
chip::Platform::CopyString(mCurrentUserNames, userNames);
userInDb.userName = chip::CharSpan(mCurrentUserNames, userInDb.userName.size());
#else
const auto & userInDb = mLockUsers[userIndex];
const auto & credentialsInStorage = mCredentials[userIndex];
#endif
user.userStatus = userInDb.userStatus;
if (UserStatusEnum::kAvailable == user.userStatus)
{
ChipLogProgress(Zcl, "Found unoccupied user [endpoint=%d]", endpointId);
return true;
}
user.userName = chip::CharSpan(userInDb.userName.data(), userInDb.userName.size());
user.credentials = chip::Span<const CredentialStruct>(credentialsInStorage, userInDb.credentials.size());
user.userUniqueId = userInDb.userUniqueId;
user.userType = userInDb.userType;
user.credentialRule = userInDb.credentialRule;
// So far there's no way to actually create the credential outside Matter, so here we always set the creation/modification
// source to Matter
user.creationSource = DlAssetSource::kMatterIM;
user.createdBy = userInDb.createdBy;
user.modificationSource = DlAssetSource::kMatterIM;
user.lastModifiedBy = userInDb.lastModifiedBy;
ChipLogProgress(Zcl,
"Found occupied user "
"[endpoint=%d,name=\"%.*s\",credentialsCount=%u,uniqueId=%x,type=%u,credentialRule=%u,"
"createdBy=%d,lastModifiedBy=%d]",
endpointId, static_cast<int>(user.userName.size()), user.userName.data(), user.credentials.size(),
user.userUniqueId, to_underlying(user.userType), to_underlying(user.credentialRule), user.createdBy,
user.lastModifiedBy);
return true;
}
bool LockManager::SetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier,
const chip::CharSpan & userName, uint32_t uniqueId, UserStatusEnum userStatus, UserTypeEnum usertype,
CredentialRuleEnum credentialRule, const CredentialStruct * credentials, size_t totalCredentials)
{
ChipLogProgress(Zcl,
"Door Lock App: LockManager::SetUser "
"[endpoint=%d,userIndex=%d,creator=%d,modifier=%d,userName=%s,uniqueId=%u "
"userStatus=%u,userType=%u,credentialRule=%u,credentials=%p,totalCredentials=%u]",
endpointId, userIndex, creator, modifier, userName.data(), uniqueId, to_underlying(userStatus),
to_underlying(usertype), to_underlying(credentialRule), credentials, totalCredentials);
VerifyOrReturnValue(userIndex > 0, false); // indices are one-indexed
userIndex--;
VerifyOrReturnValue(IsValidUserIndex(userIndex), false);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
EmberAfPluginDoorLockUserInfo lockUser;
CredentialStruct credentialsUsers[APP_MAX_CREDENTIAL];
char userNames[DOOR_LOCK_MAX_USER_NAME_SIZE];
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_LockUser[userIndex], reinterpret_cast<uint8_t *>(&lockUser),
sizeof(EmberAfPluginDoorLockUserInfo), outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_UserCredentials[userIndex],
reinterpret_cast<uint8_t *>(credentialsUsers),
sizeof(CredentialStruct) * LockParams.numberOfCredentialsPerUser, outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_LockUserName[userIndex],
reinterpret_cast<uint8_t *>(userNames), sizeof(userNames), outLen);
auto & userInStorage = lockUser;
auto & credentialsInStorage = credentialsUsers;
auto & userNamesInStorage = userNames;
#else
auto & userInStorage = mLockUsers[userIndex];
auto & credentialsInStorage = mCredentials[userIndex];
auto & userNamesInStorage = mUserNames[userIndex];
#endif
if (userName.size() > DOOR_LOCK_MAX_USER_NAME_SIZE)
{
ChipLogError(Zcl, "Cannot set user - user name is too long [endpoint=%d,index=%d]", endpointId, userIndex);
return false;
}
if (totalCredentials > LockParams.numberOfCredentialsPerUser)
{
ChipLogError(Zcl, "Cannot set user - total number of credentials is too big [endpoint=%d,index=%d,totalCredentials=%u]",
endpointId, userIndex, totalCredentials);
return false;
}
chip::Platform::CopyString(userNamesInStorage, userName);
userInStorage.userName = chip::CharSpan(userNamesInStorage, userName.size());
userInStorage.userUniqueId = uniqueId;
userInStorage.userStatus = userStatus;
userInStorage.userType = usertype;
userInStorage.credentialRule = credentialRule;
userInStorage.lastModifiedBy = modifier;
userInStorage.createdBy = creator;
for (size_t i = 0; i < totalCredentials; ++i)
{
credentialsInStorage[i] = credentials[i];
}
userInStorage.credentials = chip::Span<const CredentialStruct>(credentialsInStorage, totalCredentials);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
CHIP_ERROR err =
ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_LockUser[userIndex],
reinterpret_cast<const uint8_t *>(&lockUser), sizeof(EmberAfPluginDoorLockUserInfo));
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to write kConfigKey_LockUser to NVM\n");
return false;
}
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_UserCredentials[userIndex],
reinterpret_cast<const uint8_t *>(credentialsUsers),
sizeof(CredentialStruct) * LockParams.numberOfCredentialsPerUser);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to write kConfigKey_UserCredentials to NVM\n");
return false;
}
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_LockUserName[userIndex],
reinterpret_cast<const uint8_t *>(userNames), sizeof(userNames));
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to write kConfigKey_LockUserName to NVM\n");
return false;
}
#endif
ChipLogProgress(Zcl, "Successfully set the user [mEndpointId=%d,index=%d]", endpointId, userIndex);
return true;
}
bool LockManager::GetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, CredentialTypeEnum credentialType,
EmberAfPluginDoorLockCredentialInfo & credential)
{
VerifyOrReturnValue(IsValidCredentialType(credentialType), false);
if (CredentialTypeEnum::kProgrammingPIN == credentialType)
{
VerifyOrReturnValue(IsValidCredentialIndex(credentialIndex, credentialType),
false); // programming pin index is only index allowed to contain 0
}
else
{
VerifyOrReturnValue(IsValidCredentialIndex(--credentialIndex, credentialType), false); // otherwise, indices are one-indexed
}
ChipLogProgress(Zcl, "Lock App: LockManager::GetCredential [credentialType=%u], credentialIndex=%d",
to_underlying(credentialType), credentialIndex);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
EmberAfPluginDoorLockCredentialInfo lockCredentials;
uint8_t lockCredentialsData[kMaxCredentialSize] = { 0 };
auto & credentialInStorage = lockCredentials;
switch (credentialType)
{
case CredentialTypeEnum::kProgrammingPIN:
ChipLogError(Zcl, "CredentialTypeEnum::kProgrammingPIN must not be specified as a new credential\n");
break;
case CredentialTypeEnum::kPin:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialPin[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataPin[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kFace:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFace[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFace[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kFingerprint:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFingerprint[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFingerprint[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kFingerVein:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFingervein[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFingerVein[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kRfid:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialRfid[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataRfid[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
default:
ChipLogError(Zcl,
"Lock App: LockManager::GetCredential [credentialType=%u], credentialIndex=%d : Credential Type not specified",
to_underlying(credentialType), credentialIndex);
break;
}
memcpy(mCurrentCredentialData, lockCredentialsData, credentialInStorage.credentialData.size());
credentialInStorage.credentialData = chip::ByteSpan{ mCurrentCredentialData, credentialInStorage.credentialData.size() };
#else
const auto & credentialInStorage = mLockCredentials[to_underlying(credentialType)][credentialIndex];
#endif
credential.status = credentialInStorage.status;
ChipLogProgress(Zcl, "CredentialStatus: %d, CredentialIndex: %d ", (int) credential.status, credentialIndex);
if (DlCredentialStatus::kAvailable == credential.status)
{
ChipLogProgress(Zcl, "Found unoccupied credential ");
return true;
}
credential.credentialType = credentialInStorage.credentialType;
credential.credentialData = credentialInStorage.credentialData;
credential.createdBy = credentialInStorage.createdBy;
credential.lastModifiedBy = credentialInStorage.lastModifiedBy;
// So far there's no way to actually create the credential outside Matter, so here we always set the creation/modification
// source to Matter
credential.creationSource = DlAssetSource::kMatterIM;
credential.modificationSource = DlAssetSource::kMatterIM;
ChipLogProgress(Zcl, "Found occupied credential [type=%u,dataSize=%u]", to_underlying(credential.credentialType),
credential.credentialData.size());
return true;
}
bool LockManager::SetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator,
chip::FabricIndex modifier, DlCredentialStatus credentialStatus, CredentialTypeEnum credentialType,
const chip::ByteSpan & credentialData)
{
// TODO: It may be a need to perform a check, if the credential type already exists.
// The credential setter doesn't contain the user index, so the amount of 10 credentials per user can be satisfied only in case,
// if each credential type exists only once per one user. Otherwise, it can be the situation, when one user creates 10 pin
// codes, so the next one user can't create any pincode, but can create the other types of credentials.
VerifyOrReturnValue(IsValidCredentialType(credentialType), false);
if (CredentialTypeEnum::kProgrammingPIN == credentialType)
{
VerifyOrReturnValue(IsValidCredentialIndex(credentialIndex, credentialType),
false); // programming pin index is only index allowed to contain 0
}
else
{
VerifyOrReturnValue(IsValidCredentialIndex(--credentialIndex, credentialType), false); // otherwise, indices are one-indexed
}
ChipLogProgress(Zcl,
"Door Lock App: LockManager::SetCredential "
"[credentialStatus=%u,credentialType=%u,credentialDataSize=%u,creator=%d,modifier=%d]",
to_underlying(credentialStatus), to_underlying(credentialType), credentialData.size(), creator, modifier);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
EmberAfPluginDoorLockCredentialInfo lockCredentials;
uint8_t lockCredentialsData[kMaxCredentialSize] = { 0 };
switch (credentialType)
{
case CredentialTypeEnum::kProgrammingPIN:
ChipLogError(Zcl, "CredentialTypeEnum::kProgrammingPIN must not be specified as a new credential\n");
break;
case CredentialTypeEnum::kPin:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialPin[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataPin[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kFace:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFace[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFace[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kFingerprint:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFingerprint[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFingerprint[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kFingerVein:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFingervein[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFingerVein[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
case CredentialTypeEnum::kRfid:
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialRfid[credentialIndex],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataRfid[credentialIndex],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
break;
default:
ChipLogError(Zcl,
"Lock App: LockManager::GetCredential [credentialType=%u], credentialIndex=%d : Credential Type not specified",
to_underlying(credentialType), credentialIndex);
break;
}
auto & credentialInStorage = lockCredentials;
auto & credentialDataInStorage = lockCredentialsData;
#else
auto & credentialInStorage = mLockCredentials[to_underlying(credentialType)][credentialIndex];
auto & credentialDataInStorage = mCredentialData[to_underlying(credentialType)][credentialIndex];
#endif
credentialInStorage.status = credentialStatus;
credentialInStorage.credentialType = credentialType;
credentialInStorage.createdBy = creator;
credentialInStorage.lastModifiedBy = modifier;
memcpy(credentialDataInStorage, credentialData.data(), credentialData.size());
credentialInStorage.credentialData = chip::ByteSpan{ credentialDataInStorage, credentialData.size() };
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
CHIP_ERROR err = CHIP_NO_ERROR;
switch (credentialType)
{
case CredentialTypeEnum::kProgrammingPIN:
ChipLogError(Zcl, "CredentialTypeEnum::kProgrammingPIN must not be specified as a new credential\n");
break;
case CredentialTypeEnum::kPin:
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialPin[credentialIndex],
reinterpret_cast<const uint8_t *>(&lockCredentials),
sizeof(EmberAfPluginDoorLockCredentialInfo));
if (err != CHIP_NO_ERROR)
break;
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataPin[credentialIndex],
reinterpret_cast<const uint8_t *>(lockCredentialsData), kMaxCredentialSize);
break;
case CredentialTypeEnum::kFace:
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFace[credentialIndex],
reinterpret_cast<const uint8_t *>(&lockCredentials),
sizeof(EmberAfPluginDoorLockCredentialInfo));
if (err != CHIP_NO_ERROR)
break;
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFace[credentialIndex],
reinterpret_cast<const uint8_t *>(lockCredentialsData), kMaxCredentialSize);
break;
case CredentialTypeEnum::kFingerprint:
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFingerprint[credentialIndex],
reinterpret_cast<const uint8_t *>(&lockCredentials),
sizeof(EmberAfPluginDoorLockCredentialInfo));
if (err != CHIP_NO_ERROR)
break;
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFingerprint[credentialIndex],
reinterpret_cast<const uint8_t *>(lockCredentialsData), kMaxCredentialSize);
break;
case CredentialTypeEnum::kFingerVein:
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialFingervein[credentialIndex],
reinterpret_cast<const uint8_t *>(&lockCredentials),
sizeof(EmberAfPluginDoorLockCredentialInfo));
if (err != CHIP_NO_ERROR)
break;
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataFingerVein[credentialIndex],
reinterpret_cast<const uint8_t *>(lockCredentialsData), kMaxCredentialSize);
break;
case CredentialTypeEnum::kRfid:
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialRfid[credentialIndex],
reinterpret_cast<const uint8_t *>(&lockCredentials),
sizeof(EmberAfPluginDoorLockCredentialInfo));
if (err != CHIP_NO_ERROR)
break;
err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataRfid[credentialIndex],
reinterpret_cast<const uint8_t *>(lockCredentialsData), kMaxCredentialSize);
break;
default:
break;
}
if (err != CHIP_NO_ERROR)
{
ChipLogError(
Zcl,
"Failed to write kConfigKey_CredentialData. User data will be resetted during reboot. Not enough storage space \n");
return false;
}
#endif
ChipLogProgress(Zcl, "Successfully set the credential [credentialType=%u]", to_underlying(credentialType));
return true;
}
DlStatus LockManager::GetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex,
EmberAfPluginDoorLockWeekDaySchedule & schedule)
{
VerifyOrReturnValue(weekdayIndex > 0, DlStatus::kFailure); // indices are one-indexed
VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed
weekdayIndex--;
userIndex--;
VerifyOrReturnValue(IsValidWeekdayScheduleIndex(weekdayIndex), DlStatus::kFailure);
VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
CHIP_ERROR err =
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_WeekDaySchedules[userIndex][weekdayIndex],
reinterpret_cast<uint8_t *>(&mWeekdaySchedule), sizeof(WeekDaysScheduleInfo), outLen);
if (err != CHIP_NO_ERROR)
{
ChipLogProgress(Zcl, "GetWeekdaySchedule: No schedule found for user: %d at index %d\n", userIndex, weekdayIndex);
return DlStatus::kNotFound;
}
const auto & scheduleInStorage = mWeekdaySchedule;
#else
const auto & scheduleInStorage = mWeekdaySchedule[userIndex][weekdayIndex];
#endif
if (DlScheduleStatus::kAvailable == scheduleInStorage.status)
{
return DlStatus::kNotFound;
}
schedule = scheduleInStorage.schedule;
return DlStatus::kSuccess;
}
DlStatus LockManager::SetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex,
DlScheduleStatus status, DaysMaskMap daysMask, uint8_t startHour, uint8_t startMinute,
uint8_t endHour, uint8_t endMinute)
{
VerifyOrReturnValue(weekdayIndex > 0, DlStatus::kFailure); // indices are one-indexed
VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed
weekdayIndex--;
userIndex--;
VerifyOrReturnValue(IsValidWeekdayScheduleIndex(weekdayIndex), DlStatus::kFailure);
VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_WeekDaySchedules[userIndex][weekdayIndex],
reinterpret_cast<uint8_t *>(&mWeekdaySchedule), sizeof(WeekDaysScheduleInfo), outLen);
auto & scheduleInStorage = mWeekdaySchedule;
#else
auto & scheduleInStorage = mWeekdaySchedule[userIndex][weekdayIndex];
#endif
scheduleInStorage.schedule.daysMask = daysMask;
scheduleInStorage.schedule.startHour = startHour;
scheduleInStorage.schedule.startMinute = startMinute;
scheduleInStorage.schedule.endHour = endHour;
scheduleInStorage.schedule.endMinute = endMinute;
scheduleInStorage.status = status;
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
CHIP_ERROR err =
ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_WeekDaySchedules[userIndex][weekdayIndex],
reinterpret_cast<uint8_t *>(&mWeekdaySchedule), sizeof(WeekDaysScheduleInfo));
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to write kConfigKey_WeekDaySchedules to NVM\n");
return DlStatus::kFailure;
}
#endif
return DlStatus::kSuccess;
}
DlStatus LockManager::GetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex,
EmberAfPluginDoorLockYearDaySchedule & schedule)
{
VerifyOrReturnValue(yearDayIndex > 0, DlStatus::kFailure); // indices are one-indexed
VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed
yearDayIndex--;
userIndex--;
VerifyOrReturnValue(IsValidYeardayScheduleIndex(yearDayIndex), DlStatus::kFailure);
VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
CHIP_ERROR err =
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_YearDaySchedules[userIndex][yearDayIndex],
reinterpret_cast<uint8_t *>(&mYeardaySchedule), sizeof(YearDayScheduleInfo), outLen);
if (err != CHIP_NO_ERROR)
{
ChipLogProgress(Zcl, "GetYeardaySchedule: No schedule found for user: %d at index %d\n", userIndex, yearDayIndex);
return DlStatus::kNotFound;
}
const auto & scheduleInStorage = mYeardaySchedule;
#else
const auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex];
#endif
if (DlScheduleStatus::kAvailable == scheduleInStorage.status)
{
return DlStatus::kNotFound;
}
schedule = scheduleInStorage.schedule;
return DlStatus::kSuccess;
}
DlStatus LockManager::SetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex,
DlScheduleStatus status, uint32_t localStartTime, uint32_t localEndTime)
{
VerifyOrReturnValue(yearDayIndex > 0, DlStatus::kFailure); // indices are one-indexed
VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed
yearDayIndex--;
userIndex--;
VerifyOrReturnValue(IsValidYeardayScheduleIndex(yearDayIndex), DlStatus::kFailure);
VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_YearDaySchedules[userIndex][yearDayIndex],
reinterpret_cast<uint8_t *>(&mYeardaySchedule), sizeof(YearDayScheduleInfo), outLen);
auto & scheduleInStorage = mYeardaySchedule;
#else
auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex];
#endif
scheduleInStorage.schedule.localStartTime = localStartTime;
scheduleInStorage.schedule.localEndTime = localEndTime;
scheduleInStorage.status = status;
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
CHIP_ERROR err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_YearDaySchedules[userIndex][yearDayIndex],
reinterpret_cast<uint8_t *>(&mYeardaySchedule), sizeof(YearDayScheduleInfo));
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to write kConfigKey_YearDaySchedules to NVM\n");
return DlStatus::kFailure;
}
#endif
return DlStatus::kSuccess;
}
DlStatus LockManager::GetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex,
EmberAfPluginDoorLockHolidaySchedule & schedule)
{
VerifyOrReturnValue(holidayIndex > 0, DlStatus::kFailure); // indices are one-indexed
holidayIndex--;
VerifyOrReturnValue(IsValidHolidayScheduleIndex(holidayIndex), DlStatus::kFailure);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
CHIP_ERROR err =
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_HolidaySchedules[holidayIndex],
reinterpret_cast<uint8_t *>(&mHolidaySchedule), sizeof(HolidayScheduleInfo), outLen);
if (err != CHIP_NO_ERROR)
{
ChipLogProgress(Zcl, "GetHolidaySchedule: No schedule found at index %d\n", holidayIndex);
return DlStatus::kNotFound;
}
const auto & scheduleInStorage = mHolidaySchedule;
#else
const auto & scheduleInStorage = mHolidaySchedule[holidayIndex];
#endif
if (DlScheduleStatus::kAvailable == scheduleInStorage.status)
{
return DlStatus::kNotFound;
}
schedule = scheduleInStorage.schedule;
return DlStatus::kSuccess;
}
DlStatus LockManager::SetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status,
uint32_t localStartTime, uint32_t localEndTime, OperatingModeEnum operatingMode)
{
VerifyOrReturnValue(holidayIndex > 0, DlStatus::kFailure); // indices are one-indexed
holidayIndex--;
VerifyOrReturnValue(IsValidHolidayScheduleIndex(holidayIndex), DlStatus::kFailure);
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_HolidaySchedules[holidayIndex],
reinterpret_cast<uint8_t *>(&mHolidaySchedule), sizeof(HolidayScheduleInfo), outLen);
auto & scheduleInStorage = mHolidaySchedule;
#else
auto & scheduleInStorage = mHolidaySchedule[holidayIndex];
#endif
scheduleInStorage.schedule.localStartTime = localStartTime;
scheduleInStorage.schedule.localEndTime = localEndTime;
scheduleInStorage.schedule.operatingMode = operatingMode;
scheduleInStorage.status = status;
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
CHIP_ERROR err = ZephyrConfig::WriteConfigValueBin(LockSettingsStorage::kConfigKey_HolidaySchedules[holidayIndex],
reinterpret_cast<uint8_t *>(&mHolidaySchedule), sizeof(HolidayScheduleInfo));
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to write kConfigKey_HolidaySchedules to NVM\n");
return DlStatus::kFailure;
}
#endif
return DlStatus::kSuccess;
}
const char * LockManager::lockStateToString(DlLockState lockState) const
{
switch (lockState)
{
case DlLockState::kNotFullyLocked:
return "Not Fully Locked";
case DlLockState::kLocked:
return "Locked";
case DlLockState::kUnlocked:
return "Unlocked";
case DlLockState::kUnlatched:
return "Unlatched";
case DlLockState::kUnknownEnumValue:
break;
}
return "Unknown";
}
bool LockManager::setLockState(chip::EndpointId endpointId, DlLockState lockState, OperationSource source, OperationErrorEnum & err,
const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & pin)
{
// Assume pin is required until told otherwise
bool requirePin = true;
uint16_t userIndex = 0;
uint16_t credentialIndex = 0;
chip::app::Clusters::DoorLock::Attributes::RequirePINforRemoteOperation::Get(endpointId, &requirePin);
// If a pin code is not given
if (!pin.HasValue())
{
ChipLogProgress(Zcl, "Door Lock App: PIN code is not specified [endpointId=%d]", endpointId);
// If a pin code is not required
if (!requirePin)
{
ChipLogProgress(Zcl, "Door Lock App: setting door lock state to \"%s\" [endpointId=%d]", lockStateToString(lockState),
endpointId);
DoorLockServer::Instance().SetLockState(endpointId, lockState, source, NullNullable, NullNullable, fabricIdx, nodeId);
return true;
}
ChipLogError(Zcl, "Door Lock App: PIN code is not specified, but it is required [endpointId=%d]", endpointId);
err = OperationErrorEnum::kRestricted;
return false;
}
#if LOCK_MANAGER_CONFIG_USE_NVM_CREDENTIAL_STORAGE
size_t outLen;
EmberAfPluginDoorLockCredentialInfo lockCredentials;
uint8_t lockCredentialsData[kMaxCredentialSize];
for (uint8_t credentialDataIdx = 0; credentialDataIdx < APP_MAX_CREDENTIAL; credentialDataIdx++)
{
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialPin[credentialDataIdx],
reinterpret_cast<uint8_t *>(&lockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo),
outLen);
ZephyrConfig::ReadConfigValueBin(LockSettingsStorage::kConfigKey_CredentialDataPin[credentialDataIdx],
reinterpret_cast<uint8_t *>(lockCredentialsData), kMaxCredentialSize, outLen);
auto & currentCredential = lockCredentials;
auto & credentialDataInStorage = lockCredentialsData;
currentCredential.credentialData = chip::ByteSpan{ credentialDataInStorage, currentCredential.credentialData.size() };
#else
for (const auto & currentCredential : mLockCredentials[to_underlying(CredentialTypeEnum::kPin)])
{
#endif
if (currentCredential.status == DlCredentialStatus::kAvailable)
{
continue;
}
if (currentCredential.credentialData.data_equal(pin.Value()))
{
for (uint16_t i = 1; i <= APP_MAX_USERS; ++i)
{
EmberAfPluginDoorLockUserInfo user;
if (!emberAfPluginDoorLockGetUser(endpointId, i, user))
{
ChipLogError(Zcl, "[setLockState] Unable to get user: app error [userIndex=%d]", i);
err = OperationErrorEnum::kInvalidCredential;
return false;
}
// Go through occupied users only
if (UserStatusEnum::kAvailable == user.userStatus)
{
continue;
}
for (const auto & credential : user.credentials)
{
if (credential.credentialType != CredentialTypeEnum::kPin)
{
continue;
}
EmberAfPluginDoorLockCredentialInfo credentialInfo;
if (!emberAfPluginDoorLockGetCredential(endpointId, credential.credentialIndex, CredentialTypeEnum::kPin,
credentialInfo))
{
ChipLogError(Zcl,
"[setLockState] Unable to get credential: app error "
"[userIndex=%d,credentialIndex=%d,credentialType=%u]",
i, credential.credentialIndex, to_underlying(CredentialTypeEnum::kPin));
err = OperationErrorEnum::kInvalidCredential;
return false;
}
if (credentialInfo.status != DlCredentialStatus::kOccupied)
{
ChipLogError(Zcl,
"[setLockState] Users/Credentials database error: credential index attached to user is "
"not occupied "
"[userIndex=%d,credentialIndex=%d,credentialType=%u]",
i, credential.credentialIndex, to_underlying(CredentialTypeEnum::kPin));
err = OperationErrorEnum::kInvalidCredential;
return false;
}
if (credentialInfo.credentialData.data_equal(currentCredential.credentialData))
{
userIndex = i;
credentialIndex = credential.credentialIndex;
ChipLogProgress(
Zcl,
"Lock App: specified PIN code was found in the database, setting lock state to \"%s\" [endpointId=%d]",
lockStateToString(lockState), endpointId);
LockOpCredentials userCredential[] = { { CredentialTypeEnum::kPin, credentialIndex } };
auto userCredentials = chip::app::DataModel::MakeNullable<List<const LockOpCredentials>>(userCredential);
DoorLockServer::Instance().SetLockState(
endpointId, lockState, source, chip::app::DataModel::MakeNullable(static_cast<uint16_t>(userIndex)),
userCredentials, fabricIdx, nodeId);
return true;
}
}
}
}
}
ChipLogProgress(Zcl,
"Door Lock App: specified PIN code was not found in the database, ignoring command to set lock state to \"%s\" "
"[endpointId=%d]",
lockStateToString(lockState), endpointId);
err = OperationErrorEnum::kInvalidCredential;
return false;
}