/* * * 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 #include #include #include BoltLockManager BoltLockManager::sLock; TimerHandle_t sLockTimer; using namespace ::chip::DeviceLayer::Internal; using namespace ESP32DoorLock::LockInitParams; CHIP_ERROR BoltLockManager::Init(chip::app::DataModel::Nullable 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(&mLockUsers), sizeof(EmberAfPluginDoorLockUserInfo) * ArraySize(mLockUsers), outLen); ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_Credential, reinterpret_cast(&mLockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo) * ArraySize(mLockCredentials), outLen); ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_LockUserName, reinterpret_cast(mUserNames), sizeof(mUserNames), outLen); ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_CredentialData, reinterpret_cast(mCredentialData), sizeof(mCredentialData), outLen); ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_UserCredentials, reinterpret_cast(mCredentials), sizeof(CredentialStruct) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser, outLen); ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_WeekDaySchedules, reinterpret_cast(mWeekdaySchedule), sizeof(EmberAfPluginDoorLockWeekDaySchedule) * LockParams.numberOfWeekdaySchedulesPerUser * LockParams.numberOfUsers, outLen); ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_YearDaySchedules, reinterpret_cast(mYeardaySchedule), sizeof(EmberAfPluginDoorLockYearDaySchedule) * LockParams.numberOfYeardaySchedulesPerUser * LockParams.numberOfUsers, outLen); ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_HolidaySchedules, reinterpret_cast(&(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(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(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(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 & pin, OperationErrorEnum & err) { return setLockState(endpointId, DlLockState::kLocked, pin, err); } bool BoltLockManager::Unlock(chip::EndpointId endpointId, const Optional & 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(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(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(mCredentials[userIndex], totalCredentials); // Save user information in NVM flash ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_LockUser, reinterpret_cast(&mLockUsers), sizeof(EmberAfPluginDoorLockUserInfo) * LockParams.numberOfUsers); ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_UserCredentials, reinterpret_cast(mCredentials), sizeof(CredentialStruct) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser); ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_LockUserName, reinterpret_cast(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(&mLockCredentials), sizeof(EmberAfPluginDoorLockCredentialInfo) * LockParams.numberOfCredentialsPerUser); ESP32Config::WriteConfigValueBin(ESP32Config::kConfigKey_CredentialData, reinterpret_cast(&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(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(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(&(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 & 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 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; }