blob: 262a5991ff0be983c06577073384283793907d2f [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
*
* 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 "BoltLockManager.h"
#include "AppConfig.h"
#include "AppTask.h"
#include <app-common/zap-generated/attributes/Accessors.h>
#include <cstring>
#include <esp_log.h>
#include <lib/support/logging/CHIPLogging.h>
BoltLockManager BoltLockManager::sLock;
TimerHandle_t sLockTimer;
using namespace ::chip::DeviceLayer::Internal;
using namespace ESP32DoorLock::LockInitParams;
CHIP_ERROR BoltLockManager::Init(chip::app::DataModel::Nullable<chip::app::Clusters::DoorLock::DlLockState> state,
LockParam lockParam)
{
LockParams = lockParam;
if (LockParams.numberOfUsers > kMaxUsers)
{
ChipLogDetail(Zcl,
"Max number of users is greater than %d, the maximum amount of users currently supported on this platform",
kMaxUsers);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfCredentialsPerUser > kMaxCredentialsPerUser)
{
ChipLogDetail(Zcl,
"Max number of credentials per user is greater than %d, the maximum amount of users currently supported on "
"this platform",
kMaxCredentialsPerUser);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfWeekdaySchedulesPerUser > kMaxWeekdaySchedulesPerUser)
{
ChipLogDetail(
Zcl,
" Max number of schedules is greater than %d, the maximum amount of schedules currently supported on this platform",
kMaxWeekdaySchedulesPerUser);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfYeardaySchedulesPerUser > kMaxYeardaySchedulesPerUser)
{
ChipLogDetail(
Zcl, "Max number of schedules is greater than %d, the maximum amount of schedules currently supported on this platform",
kMaxYeardaySchedulesPerUser);
return APP_ERROR_ALLOCATION_FAILED;
}
if (LockParams.numberOfHolidaySchedules > kMaxHolidaySchedules)
{
ChipLogDetail(
Zcl, "Max number of schedules is greater than %d, the maximum amount of schedules currently supported on this platform",
kMaxHolidaySchedules);
return APP_ERROR_ALLOCATION_FAILED;
}
// Create FreeRTOS sw timer for lock timer.
sLockTimer = xTimerCreate("lockTmr", // Just a text name, not used by the RTOS kernel
1, // == default timer period (mS)
false, // no timer reload (==one-shot)
(void *) this, // init timer id = lock obj context
TimerEventHandler // timer callback handler
);
if (sLockTimer == NULL)
{
ChipLogDetail(Zcl, "sLockTimer timer create failed");
return APP_ERROR_CREATE_TIMER_FAILED;
}
if (state.Value() == DlLockState::kUnlocked)
mState = kState_UnlockCompleted;
else
mState = kState_LockCompleted;
return CHIP_NO_ERROR;
}
bool BoltLockManager::IsValidUserIndex(uint16_t userIndex)
{
return (userIndex < kMaxUsers);
}
bool BoltLockManager::IsValidCredentialIndex(uint16_t credentialIndex, CredentialTypeEnum type)
{
if (CredentialTypeEnum::kProgrammingPIN == type)
{
return (0 == credentialIndex); // 0 is required index for Programming PIN
}
return (credentialIndex < kMaxCredentialsPerUser);
}
bool BoltLockManager::IsValidWeekdayScheduleIndex(uint8_t scheduleIndex)
{
return (scheduleIndex < kMaxWeekdaySchedulesPerUser);
}
bool BoltLockManager::IsValidYeardayScheduleIndex(uint8_t scheduleIndex)
{
return (scheduleIndex < kMaxYeardaySchedulesPerUser);
}
bool BoltLockManager::IsValidHolidayScheduleIndex(uint8_t scheduleIndex)
{
return (scheduleIndex < kMaxHolidaySchedules);
}
bool BoltLockManager::ReadConfigValues()
{
size_t outLen;
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_LockUser, reinterpret_cast<uint8_t *>(&mLockUsers),
sizeof(EmberAfPluginDoorLockUserInfo) * ArraySize(mLockUsers), outLen);
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_Credential, reinterpret_cast<uint8_t *>(&mLockCredentials),
sizeof(EmberAfPluginDoorLockCredentialInfo) * ArraySize(mLockCredentials), outLen);
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_LockUserName, reinterpret_cast<uint8_t *>(mUserNames),
sizeof(mUserNames), outLen);
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_CredentialData, reinterpret_cast<uint8_t *>(mCredentialData),
sizeof(mCredentialData), outLen);
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_UserCredentials, reinterpret_cast<uint8_t *>(mCredentials),
sizeof(CredentialStruct) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser,
outLen);
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_WeekDaySchedules, reinterpret_cast<uint8_t *>(mWeekdaySchedule),
sizeof(EmberAfPluginDoorLockWeekDaySchedule) * LockParams.numberOfWeekdaySchedulesPerUser *
LockParams.numberOfUsers,
outLen);
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_YearDaySchedules, reinterpret_cast<uint8_t *>(mYeardaySchedule),
sizeof(EmberAfPluginDoorLockYearDaySchedule) * LockParams.numberOfYeardaySchedulesPerUser *
LockParams.numberOfUsers,
outLen);
ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_HolidaySchedules, reinterpret_cast<uint8_t *>(&(mHolidaySchedule)),
sizeof(EmberAfPluginDoorLockHolidaySchedule) * LockParams.numberOfHolidaySchedules, outLen);
return true;
}
void BoltLockManager::SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB)
{
mActionInitiated_CB = aActionInitiated_CB;
mActionCompleted_CB = aActionCompleted_CB;
}
bool BoltLockManager::IsActionInProgress()
{
return (mState == kState_LockInitiated || mState == kState_UnlockInitiated);
}
bool BoltLockManager::IsUnlocked()
{
return (mState == kState_UnlockCompleted);
}
void BoltLockManager::SetAutoLockDuration(uint32_t aDurationInSecs)
{
mAutoLockDuration = aDurationInSecs;
}
bool BoltLockManager::NextState()
{
return (mState == kState_UnlockCompleted);
}
bool BoltLockManager::InitiateAction(int32_t aActor, Action_t aAction)
{
bool action_initiated = false;
State_t new_state;
// Initiate Turn Lock/Unlock Action only when the previous one is complete.
if (mState == kState_LockCompleted && aAction == UNLOCK_ACTION)
{
action_initiated = true;
new_state = kState_UnlockInitiated;
}
else if (mState == kState_UnlockCompleted && aAction == LOCK_ACTION)
{
action_initiated = true;
new_state = kState_LockInitiated;
}
if (action_initiated)
{
if (mAutoLockTimerArmed && new_state == kState_LockInitiated)
{
// 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;
}
void BoltLockManager::StartTimer(uint32_t aTimeoutMs)
{
if (xTimerIsTimerActive(sLockTimer))
{
ESP_LOGI(TAG, "app timer already started!");
CancelTimer();
}
// timer is not active, change its period to required value (== restart).
// FreeRTOS- Block for a maximum of 100 ticks if the change period command
// cannot immediately be sent to the timer command queue.
if (xTimerChangePeriod(sLockTimer, (aTimeoutMs / portTICK_PERIOD_MS), 100) != pdPASS)
{
ESP_LOGI(TAG, "sLockTimer timer start() failed : %s", ErrorStr(APP_ERROR_START_TIMER_FAILED));
}
}
void BoltLockManager::CancelTimer(void)
{
if (xTimerStop(sLockTimer, 0) == pdFAIL)
{
ESP_LOGI(TAG, "sLockTimer stop() failed : %s", ErrorStr(APP_ERROR_STOP_TIMER_FAILED));
}
}
void BoltLockManager::TimerEventHandler(TimerHandle_t xTimer)
{
// Get lock obj context from timer id.
BoltLockManager * lock = static_cast<BoltLockManager *>(pvTimerGetTimerID(xTimer));
// 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.mType = AppEvent::kEventType_Timer;
event.mTimerEvent.mContext = lock;
if (lock->mAutoLockTimerArmed)
{
event.mHandler = AutoReLockTimerEventHandler;
}
else
{
event.mHandler = ActuatorMovementTimerEventHandler;
}
GetAppTask().PostEvent(&event);
}
void BoltLockManager::AutoReLockTimerEventHandler(AppEvent * aEvent)
{
BoltLockManager * lock = static_cast<BoltLockManager *>(aEvent->mTimerEvent.mContext);
int32_t actor = 0;
// Make sure auto lock timer is still armed.
if (!lock->mAutoLockTimerArmed)
{
return;
}
lock->mAutoLockTimerArmed = false;
ESP_LOGI(TAG, "Auto Re-Lock has been triggered!");
lock->InitiateAction(actor, LOCK_ACTION);
}
void BoltLockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent)
{
Action_t actionCompleted = INVALID_ACTION;
BoltLockManager * lock = static_cast<BoltLockManager *>(aEvent->mTimerEvent.mContext);
if (lock->mState == kState_LockInitiated)
{
lock->mState = kState_LockCompleted;
actionCompleted = LOCK_ACTION;
}
else if (lock->mState == kState_UnlockInitiated)
{
lock->mState = kState_UnlockCompleted;
actionCompleted = UNLOCK_ACTION;
}
if (actionCompleted != INVALID_ACTION)
{
if (lock->mActionCompleted_CB)
{
lock->mActionCompleted_CB(actionCompleted);
}
if (lock->mAutoRelock && actionCompleted == UNLOCK_ACTION)
{
// Start the timer for auto relock
lock->StartTimer(lock->mAutoLockDuration * 1000);
lock->mAutoLockTimerArmed = true;
ESP_LOGI(TAG, "Auto Re-lock enabled. Will be triggered in %" PRIu32 " seconds", lock->mAutoLockDuration);
}
}
}
bool BoltLockManager::Lock(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err)
{
return setLockState(endpointId, DlLockState::kLocked, pin, err);
}
bool BoltLockManager::Unlock(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err)
{
return setLockState(endpointId, DlLockState::kUnlocked, pin, err);
}
bool BoltLockManager::GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user)
{
VerifyOrReturnValue(userIndex > 0, false); // indices are one-indexed
userIndex--;
VerifyOrReturnValue(IsValidUserIndex(userIndex), false);
ESP_LOGI(TAG, "Door Lock App: BoltLockManager::GetUser [endpoint=%d,userIndex=%hu]", endpointId, userIndex);
const auto & userInDb = mLockUsers[userIndex];
user.userStatus = userInDb.userStatus;
if (UserStatusEnum::kAvailable == user.userStatus)
{
ESP_LOGI(TAG, "Found unoccupied user [endpoint=%d]", endpointId);
return true;
}
user.userName = chip::CharSpan(userInDb.userName.data(), userInDb.userName.size());
user.credentials = chip::Span<const CredentialStruct>(mCredentials[userIndex], 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;
ESP_LOGI(TAG,
"Found occupied user [endpoint=%d,name=\"%.*s\",credentialsCount=%u,uniqueId=%" PRIu32
",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 BoltLockManager::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)
{
ESP_LOGI(TAG,
"Door Lock App: BoltLockManager::SetUser "
"[endpoint=%d,userIndex=%d,creator=%d,modifier=%d,userName=%s,uniqueId=%" PRIu32 ""
"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);
auto & userInStorage = mLockUsers[userIndex];
if (userName.size() > DOOR_LOCK_MAX_USER_NAME_SIZE)
{
ESP_LOGE(TAG, "Cannot set user - user name is too long [endpoint=%d,index=%d]", endpointId, userIndex);
return false;
}
if (totalCredentials > LockParams.numberOfCredentialsPerUser)
{
ESP_LOGE(TAG, "Cannot set user - total number of credentials is too big [endpoint=%d,index=%d,totalCredentials=%u]",
endpointId, userIndex, totalCredentials);
return false;
}
chip::Platform::CopyString(mUserNames[userIndex], userName);
userInStorage.userName = chip::CharSpan(mUserNames[userIndex], 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)
{
mCredentials[userIndex][i] = credentials[i];
}
userInStorage.credentials = chip::Span<const CredentialStruct>(mCredentials[userIndex], totalCredentials);
// Save user information in NVM flash
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_LockUser, reinterpret_cast<const uint8_t *>(&mLockUsers),
sizeof(EmberAfPluginDoorLockUserInfo) * LockParams.numberOfUsers);
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_UserCredentials, reinterpret_cast<const uint8_t *>(mCredentials),
sizeof(CredentialStruct) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser);
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_LockUserName, reinterpret_cast<const uint8_t *>(mUserNames),
sizeof(mUserNames));
ESP_LOGI(TAG, "Successfully set the user [mEndpointId=%d,index=%d]", endpointId, userIndex);
return true;
}
bool BoltLockManager::GetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, CredentialTypeEnum credentialType,
EmberAfPluginDoorLockCredentialInfo & credential)
{
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
}
ESP_LOGI(TAG, "Lock App: BoltLockManager::GetCredential [credentialType=%u], credentialIndex=%d", to_underlying(credentialType),
credentialIndex);
const auto & credentialInStorage = mLockCredentials[credentialIndex];
credential.status = credentialInStorage.status;
ESP_LOGI(TAG, "CredentialStatus: %d, CredentialIndex: %d ", (int) credential.status, credentialIndex);
if (DlCredentialStatus::kAvailable == credential.status)
{
ESP_LOGI(TAG, "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;
ESP_LOGI(TAG, "Found occupied credential [type=%u,dataSize=%u]", to_underlying(credential.credentialType),
credential.credentialData.size());
return true;
}
bool BoltLockManager::SetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator,
chip::FabricIndex modifier, DlCredentialStatus credentialStatus,
CredentialTypeEnum credentialType, const chip::ByteSpan & credentialData)
{
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
}
ESP_LOGI(TAG,
"Door Lock App: BoltLockManager::SetCredential "
"[credentialStatus=%u,credentialType=%u,credentialDataSize=%u,creator=%d,modifier=%d]",
to_underlying(credentialStatus), to_underlying(credentialType), credentialData.size(), creator, modifier);
auto & credentialInStorage = mLockCredentials[credentialIndex];
credentialInStorage.status = credentialStatus;
credentialInStorage.credentialType = credentialType;
credentialInStorage.createdBy = creator;
credentialInStorage.lastModifiedBy = modifier;
memcpy(mCredentialData[credentialIndex], credentialData.data(), credentialData.size());
credentialInStorage.credentialData = chip::ByteSpan{ mCredentialData[credentialIndex], credentialData.size() };
// Save credential information in NVM flash
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_Credential, reinterpret_cast<const uint8_t *>(&mLockCredentials),
sizeof(EmberAfPluginDoorLockCredentialInfo) * LockParams.numberOfCredentialsPerUser);
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_CredentialData, reinterpret_cast<const uint8_t *>(&mCredentialData),
sizeof(mCredentialData));
ESP_LOGI(TAG, "Successfully set the credential [credentialType=%u]", to_underlying(credentialType));
return true;
}
DlStatus BoltLockManager::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);
const auto & scheduleInStorage = mWeekdaySchedule[userIndex][weekdayIndex];
if (DlScheduleStatus::kAvailable == scheduleInStorage.status)
{
return DlStatus::kNotFound;
}
schedule = scheduleInStorage.schedule;
return DlStatus::kSuccess;
}
DlStatus BoltLockManager::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);
auto & scheduleInStorage = mWeekdaySchedule[userIndex][weekdayIndex];
scheduleInStorage.schedule.daysMask = daysMask;
scheduleInStorage.schedule.startHour = startHour;
scheduleInStorage.schedule.startMinute = startMinute;
scheduleInStorage.schedule.endHour = endHour;
scheduleInStorage.schedule.endMinute = endMinute;
scheduleInStorage.status = status;
// Save schedule information in NVM flash
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_WeekDaySchedules, reinterpret_cast<const uint8_t *>(mWeekdaySchedule),
sizeof(EmberAfPluginDoorLockWeekDaySchedule) * LockParams.numberOfWeekdaySchedulesPerUser *
LockParams.numberOfUsers);
return DlStatus::kSuccess;
}
DlStatus BoltLockManager::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);
const auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex];
if (DlScheduleStatus::kAvailable == scheduleInStorage.status)
{
return DlStatus::kNotFound;
}
schedule = scheduleInStorage.schedule;
return DlStatus::kSuccess;
}
DlStatus BoltLockManager::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);
auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex];
scheduleInStorage.schedule.localStartTime = localStartTime;
scheduleInStorage.schedule.localEndTime = localEndTime;
scheduleInStorage.status = status;
// Save schedule information in NVM flash
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_YearDaySchedules, reinterpret_cast<const uint8_t *>(mYeardaySchedule),
sizeof(EmberAfPluginDoorLockYearDaySchedule) * LockParams.numberOfYeardaySchedulesPerUser *
LockParams.numberOfUsers);
return DlStatus::kSuccess;
}
DlStatus BoltLockManager::GetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex,
EmberAfPluginDoorLockHolidaySchedule & schedule)
{
VerifyOrReturnValue(holidayIndex > 0, DlStatus::kFailure); // indices are one-indexed
holidayIndex--;
VerifyOrReturnValue(IsValidHolidayScheduleIndex(holidayIndex), DlStatus::kFailure);
const auto & scheduleInStorage = mHolidaySchedule[holidayIndex];
if (DlScheduleStatus::kAvailable == scheduleInStorage.status)
{
return DlStatus::kNotFound;
}
schedule = scheduleInStorage.schedule;
return DlStatus::kSuccess;
}
DlStatus BoltLockManager::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);
auto & scheduleInStorage = mHolidaySchedule[holidayIndex];
scheduleInStorage.schedule.localStartTime = localStartTime;
scheduleInStorage.schedule.localEndTime = localEndTime;
scheduleInStorage.schedule.operatingMode = operatingMode;
scheduleInStorage.status = status;
// Save schedule information in NVM flash
ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_HolidaySchedules,
reinterpret_cast<const uint8_t *>(&(mHolidaySchedule)),
sizeof(EmberAfPluginDoorLockHolidaySchedule) * LockParams.numberOfHolidaySchedules);
return DlStatus::kSuccess;
}
const char * BoltLockManager::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 BoltLockManager::setLockState(chip::EndpointId endpointId, DlLockState lockState, const Optional<chip::ByteSpan> & pin,
OperationErrorEnum & err)
{
// Assume pin is required until told otherwise
bool requirePin = true;
chip::app::Clusters::DoorLock::Attributes::RequirePINforRemoteOperation::Get(endpointId, &requirePin);
// If a pin code is not given
if (!pin.HasValue())
{
ESP_LOGI(TAG, "Door Lock App: PIN code is not specified [endpointId=%d]", endpointId);
// If a pin code is not required
if (!requirePin)
{
ESP_LOGI(TAG, "Door Lock App: setting door lock state to \"%s\" [endpointId=%d]", lockStateToString(lockState),
endpointId);
DoorLockServer::Instance().SetLockState(endpointId, lockState);
return true;
}
ESP_LOGI(TAG, "Door Lock App: PIN code is not specified, but it is required [endpointId=%d]", endpointId);
return false;
}
// Check the PIN code
for (uint8_t i = 0; i < kMaxCredentials; i++)
{
if (mLockCredentials[i].credentialType != CredentialTypeEnum::kPin ||
mLockCredentials[i].status == DlCredentialStatus::kAvailable)
{
continue;
}
if (mLockCredentials[i].credentialData.data_equal(pin.Value()))
{
ESP_LOGI(TAG, "Lock App: specified PIN code was found in the database, setting lock state to \"%s\" [endpointId=%d]",
lockStateToString(lockState), endpointId);
DoorLockServer::Instance().SetLockState(endpointId, lockState);
return true;
}
}
ESP_LOGI(TAG,
"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;
}
CHIP_ERROR BoltLockManager::InitLockState()
{
// Initial lock state
chip::app::DataModel::Nullable<chip::app::Clusters::DoorLock::DlLockState> state;
chip::EndpointId endpointId{ 1 };
chip::app::Clusters::DoorLock::Attributes::LockState::Get(endpointId, state);
uint8_t numberOfCredentialsPerUser = 0;
if (!DoorLockServer::Instance().GetNumberOfCredentialsSupportedPerUser(endpointId, numberOfCredentialsPerUser))
{
ChipLogError(Zcl,
"Unable to get number of credentials supported per user when initializing lock endpoint, defaulting to 5 "
"[endpointId=%d]",
endpointId);
numberOfCredentialsPerUser = 5;
}
uint16_t numberOfUsers = 0;
if (!DoorLockServer::Instance().GetNumberOfUserSupported(endpointId, numberOfUsers))
{
ChipLogError(Zcl,
"Unable to get number of supported users when initializing lock endpoint, defaulting to 10 [endpointId=%d]",
endpointId);
numberOfUsers = 10;
}
uint8_t numberOfWeekdaySchedulesPerUser = 0;
if (!DoorLockServer::Instance().GetNumberOfWeekDaySchedulesPerUserSupported(endpointId, numberOfWeekdaySchedulesPerUser))
{
ChipLogError(
Zcl,
"Unable to get number of supported weekday schedules when initializing lock endpoint, defaulting to 10 [endpointId=%d]",
endpointId);
numberOfWeekdaySchedulesPerUser = 10;
}
uint8_t numberOfYeardaySchedulesPerUser = 0;
if (!DoorLockServer::Instance().GetNumberOfYearDaySchedulesPerUserSupported(endpointId, numberOfYeardaySchedulesPerUser))
{
ChipLogError(
Zcl,
"Unable to get number of supported yearday schedules when initializing lock endpoint, defaulting to 10 [endpointId=%d]",
endpointId);
numberOfYeardaySchedulesPerUser = 10;
}
uint8_t numberOfHolidaySchedules = 0;
if (!DoorLockServer::Instance().GetNumberOfHolidaySchedulesSupported(endpointId, numberOfHolidaySchedules))
{
ChipLogError(
Zcl,
"Unable to get number of supported holiday schedules when initializing lock endpoint, defaulting to 10 [endpointId=%d]",
endpointId);
numberOfHolidaySchedules = 10;
}
CHIP_ERROR err = BoltLockMgr().Init(state,
ParamBuilder()
.SetNumberOfUsers(numberOfUsers)
.SetNumberOfCredentialsPerUser(numberOfCredentialsPerUser)
.SetNumberOfWeekdaySchedulesPerUser(numberOfWeekdaySchedulesPerUser)
.SetNumberOfYeardaySchedulesPerUser(numberOfYeardaySchedulesPerUser)
.SetNumberOfHolidaySchedules(numberOfHolidaySchedules)
.GetLockParam());
if (err != CHIP_NO_ERROR)
{
ESP_LOGI(TAG, "BoltLockMgr().Init() failed");
return err;
}
return err;
}