diff --git a/examples/lock-app/p6/BUILD.gn b/examples/lock-app/p6/BUILD.gn index c37e453c3d8644..1468498c2e093b 100644 --- a/examples/lock-app/p6/BUILD.gn +++ b/examples/lock-app/p6/BUILD.gn @@ -139,8 +139,8 @@ p6_executable("lock_app") { "${examples_plat_dir}/init_p6Platform.cpp", "${p6_project_dir}/include/CHIPProjectConfig.h", "src/AppTask.cpp", - "src/BoltLockManager.cpp", "src/ButtonHandler.cpp", + "src/LockManager.cpp", "src/ZclCallbacks.cpp", "src/main.cpp", ] diff --git a/examples/lock-app/p6/include/AppTask.h b/examples/lock-app/p6/include/AppTask.h index 3e50921a76cff2..8974d83523baa9 100644 --- a/examples/lock-app/p6/include/AppTask.h +++ b/examples/lock-app/p6/include/AppTask.h @@ -23,7 +23,7 @@ #include #include "AppEvent.h" -#include "BoltLockManager.h" +#include "LockManager.h" #include "FreeRTOS.h" #include "timers.h" // provides FreeRTOS timer support @@ -37,6 +37,7 @@ #define APP_ERROR_CREATE_TIMER_FAILED CHIP_APPLICATION_ERROR(0x04) #define APP_ERROR_START_TIMER_FAILED CHIP_APPLICATION_ERROR(0x05) #define APP_ERROR_STOP_TIMER_FAILED CHIP_APPLICATION_ERROR(0x06) +#define APP_ERROR_ALLOCATION_FAILED CHIP_APPLICATION_ERROR(0x07) class AppTask { @@ -45,7 +46,7 @@ class AppTask CHIP_ERROR StartAppTask(); static void AppTaskMain(void * pvParameter); - void PostLockActionRequest(int32_t actor, BoltLockManager::Action action); + void ActionRequest(int32_t aActor, LockManager::Action_t aAction); void PostEvent(const AppEvent * event); void ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction); @@ -57,8 +58,8 @@ class AppTask CHIP_ERROR Init(); - static void ActionInitiated(BoltLockManager::Action action, int32_t actor); - static void ActionCompleted(BoltLockManager::Action action); + static void ActionInitiated(LockManager::Action_t aAction, int32_t aActor); + static void ActionCompleted(LockManager::Action_t aAction); void CancelTimer(void); diff --git a/examples/lock-app/p6/include/BoltLockManager.h b/examples/lock-app/p6/include/BoltLockManager.h deleted file mode 100644 index 4c0093b3efd592..00000000000000 --- a/examples/lock-app/p6/include/BoltLockManager.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors - * Copyright (c) 2019 Google LLC. - * 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. - */ - -#pragma once - -#include - -#include "AppEvent.h" - -#include "FreeRTOS.h" -#include "timers.h" // provides FreeRTOS timer support - -#include - -class BoltLockManager -{ -public: - enum class Action - { - kLock = 0, - kUnlock, - KInvalid - }; - - enum class State - { - kLockingInitiated = 0, - kLockingCompleted, - kUnlockingInitiated, - kUnlockingCompleted, - }; - - CHIP_ERROR Init(); - bool IsUnlocked(); - void EnableAutoRelock(bool aOn); - void SetAutoLockDuration(uint32_t aDurationInSecs); - bool IsActionInProgress(); - bool InitiateAction(int32_t aActor, Action aAction); - - typedef void (*Callback_fn_initiated)(Action, int32_t aActor); - typedef void (*Callback_fn_completed)(Action); - void SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB); - -private: - friend BoltLockManager & BoltLockMgr(void); - State mState = State::kUnlockingCompleted; - - Callback_fn_initiated mActionInitiated_CB; - Callback_fn_completed mActionCompleted_CB; - - bool mAutoRelock = false; - uint32_t mAutoLockDuration = 0; - bool mAutoLockTimerArmed = false; - - void CancelTimer(void); - void StartTimer(uint32_t aTimeoutMs); - - static void TimerEventHandler(TimerHandle_t xTimer); - static void AutoReLockTimerEventHandler(AppEvent * aEvent); - static void ActuatorMovementTimerEventHandler(AppEvent * aEvent); - - static BoltLockManager sLock; -}; - -inline BoltLockManager & BoltLockMgr(void) -{ - return BoltLockManager::sLock; -} diff --git a/examples/lock-app/p6/include/LockManager.h b/examples/lock-app/p6/include/LockManager.h new file mode 100644 index 00000000000000..9fab16ec1e1cd4 --- /dev/null +++ b/examples/lock-app/p6/include/LockManager.h @@ -0,0 +1,205 @@ +/* + * + * Copyright (c) 2019 Google LLC. + * 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. + */ + +#pragma once + +#include + +#include +#include + +#include "AppEvent.h" + +#include "FreeRTOS.h" +#include "timers.h" // provides FreeRTOS timer support + +#include + +namespace P6DoorLock { +namespace ResourceRanges { +// Used to size arrays +static constexpr uint16_t kMaxUsers = 10; +static constexpr uint8_t kMaxCredentialsPerUser = 10; +static constexpr uint8_t kMaxWeekdaySchedulesPerUser = 10; +static constexpr uint8_t kMaxYeardaySchedulesPerUser = 10; +static constexpr uint8_t kMaxHolidaySchedules = 10; +static constexpr uint8_t kMaxCredentialSize = 8; + +// Indices received for user/credential/schedules are 1-indexed +static constexpr uint8_t kStartIndexValue = 1; + +static constexpr uint8_t kMaxCredentials = kMaxUsers * kMaxCredentialsPerUser; +} // namespace ResourceRanges + +namespace LockInitParams { + +struct LockParam +{ + // Read from zap attributes + uint16_t numberOfUsers = 0; + uint8_t numberOfCredentialsPerUser = 0; + uint8_t numberOfWeekdaySchedulesPerUser = 0; + uint8_t numberOfYeardaySchedulesPerUser = 0; + uint8_t numberOfHolidaySchedules = 0; +}; + +class ParamBuilder +{ +public: + ParamBuilder & SetNumberOfUsers(uint16_t numberOfUsers) + { + lockParam_.numberOfUsers = numberOfUsers; + return *this; + } + ParamBuilder & SetNumberOfCredentialsPerUser(uint8_t numberOfCredentialsPerUser) + { + lockParam_.numberOfCredentialsPerUser = numberOfCredentialsPerUser; + return *this; + } + ParamBuilder & SetNumberOfWeekdaySchedulesPerUser(uint8_t numberOfWeekdaySchedulesPerUser) + { + lockParam_.numberOfWeekdaySchedulesPerUser = numberOfWeekdaySchedulesPerUser; + return *this; + } + ParamBuilder & SetNumberOfYeardaySchedulesPerUser(uint8_t numberOfYeardaySchedulesPerUser) + { + lockParam_.numberOfYeardaySchedulesPerUser = numberOfYeardaySchedulesPerUser; + return *this; + } + ParamBuilder & SetNumberOfHolidaySchedules(uint8_t numberOfHolidaySchedules) + { + lockParam_.numberOfHolidaySchedules = numberOfHolidaySchedules; + return *this; + } + LockParam GetLockParam() { return lockParam_; } + +private: + LockParam lockParam_; +}; + +} // namespace LockInitParams +} // namespace P6DoorLock + +using namespace ::chip; +using namespace P6DoorLock::ResourceRanges; + +class LockManager +{ +public: + enum Action_t + { + LOCK_ACTION = 0, + UNLOCK_ACTION, + + INVALID_ACTION + } Action; + + enum State_t + { + kState_LockInitiated = 0, + kState_LockCompleted, + kState_UnlockInitiated, + kState_UnlockCompleted, + } State; + + CHIP_ERROR Init(chip::app::DataModel::Nullable state, + P6DoorLock::LockInitParams::LockParam lockParam); + bool NextState(); + bool IsActionInProgress(); + bool InitiateAction(int32_t aActor, Action_t aAction); + + typedef void (*Callback_fn_initiated)(Action_t, int32_t aActor); + typedef void (*Callback_fn_completed)(Action_t); + void SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB); + + bool Lock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err); + bool Unlock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err); + + bool GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user); + bool SetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + const chip::CharSpan & userName, uint32_t uniqueId, DlUserStatus userStatus, DlUserType usertype, + DlCredentialRule credentialRule, const DlCredential * credentials, size_t totalCredentials); + + bool GetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential); + + bool SetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + DlCredentialStatus credentialStatus, DlCredentialType credentialType, const chip::ByteSpan & credentialData); + + DlStatus GetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + EmberAfPluginDoorLockWeekDaySchedule & schedule); + + DlStatus SetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, DlScheduleStatus status, + DlDaysMaskMap daysMask, uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute); + + DlStatus GetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + EmberAfPluginDoorLockYearDaySchedule & schedule); + + DlStatus SetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, DlScheduleStatus status, + uint32_t localStartTime, uint32_t localEndTime); + + DlStatus GetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, EmberAfPluginDoorLockHolidaySchedule & schedule); + + DlStatus SetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status, uint32_t localStartTime, + uint32_t localEndTime, DlOperatingMode operatingMode); + + bool IsValidUserIndex(uint16_t userIndex); + bool IsValidCredentialIndex(uint16_t credentialIndex, DlCredentialType type); + bool IsValidWeekdayScheduleIndex(uint8_t scheduleIndex); + bool IsValidYeardayScheduleIndex(uint8_t scheduleIndex); + bool IsValidHolidayScheduleIndex(uint8_t scheduleIndex); + + bool setLockState(chip::EndpointId endpointId, DlLockState lockState, const Optional & pin, + DlOperationError & err); + const char * lockStateToString(DlLockState lockState) const; + + bool ReadConfigValues(); + +private: + friend LockManager & LockMgr(); + chip::EndpointId mEndpointId; + State_t mState; + + Callback_fn_initiated mActionInitiated_CB; + Callback_fn_completed mActionCompleted_CB; + + void CancelTimer(void); + void StartTimer(uint32_t aTimeoutMs); + + static void TimerEventHandler(TimerHandle_t xTimer); + static void AutoLockTimerEventHandler(AppEvent * aEvent); + static void ActuatorMovementTimerEventHandler(AppEvent * aEvent); + + EmberAfPluginDoorLockUserInfo mLockUsers[kMaxUsers]; + EmberAfPluginDoorLockCredentialInfo mLockCredentials[kMaxCredentials]; + EmberAfPluginDoorLockWeekDaySchedule mWeekdaySchedule[kMaxUsers][kMaxWeekdaySchedulesPerUser]; + EmberAfPluginDoorLockYearDaySchedule mYeardaySchedule[kMaxUsers][kMaxYeardaySchedulesPerUser]; + EmberAfPluginDoorLockHolidaySchedule mHolidaySchedule[kMaxHolidaySchedules]; + + char mUserNames[ArraySize(mLockUsers)][DOOR_LOCK_MAX_USER_NAME_SIZE]; + uint8_t mCredentialData[kMaxCredentials][kMaxCredentialSize]; + DlCredential mCredentials[kMaxUsers][kMaxCredentialsPerUser]; + + static LockManager sLock; + P6DoorLock::LockInitParams::LockParam LockParams; +}; + +inline LockManager & LockMgr() +{ + return LockManager::sLock; +} diff --git a/examples/lock-app/p6/src/AppTask.cpp b/examples/lock-app/p6/src/AppTask.cpp index fdd6052c39de59..6f62d3c3d852c8 100644 --- a/examples/lock-app/p6/src/AppTask.cpp +++ b/examples/lock-app/p6/src/AppTask.cpp @@ -22,9 +22,15 @@ #include "ButtonHandler.h" #include "LEDWidget.h" #include "qrcodegen.h" +#include #include #include +#include #include +#include + +#include +#include #include #include #include @@ -40,6 +46,9 @@ #include #include +#include +#include + /* OTA related includes */ #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR #include @@ -67,12 +76,16 @@ using namespace ::chip::DeviceLayer; using namespace ::chip::System; #endif + #define FACTORY_RESET_TRIGGER_TIMEOUT 3000 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 #define APP_TASK_STACK_SIZE (4096) #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 +using chip::app::Clusters::DoorLock::DlLockState; +using chip::app::Clusters::DoorLock::DlOperationError; +using chip::app::Clusters::DoorLock::DlOperationSource; namespace { TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer. @@ -106,6 +119,7 @@ using namespace chip::TLV; using namespace ::chip::Credentials; using namespace ::chip::DeviceLayer; using namespace ::chip::System; +using namespace P6DoorLock::LockInitParams; AppTask AppTask::sAppTask; @@ -192,19 +206,90 @@ CHIP_ERROR AppTask::Init() } NetWorkCommissioningInstInit(); P6_LOG("Current Software Version: %d", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION); - err = BoltLockMgr().Init(); + // Initial lock state + chip::app::DataModel::Nullable state; + chip::EndpointId endpointId{ 1 }; + chip::DeviceLayer::PlatformMgr().LockChipStack(); + 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::DeviceLayer::PlatformMgr().UnlockChipStack(); + + // err = LockMgr().Init(state, maxCredentialsPerUser, numberOfSupportedUsers); + err = LockMgr().Init(state, + ParamBuilder() + .SetNumberOfUsers(numberOfUsers) + .SetNumberOfCredentialsPerUser(numberOfCredentialsPerUser) + .SetNumberOfWeekdaySchedulesPerUser(numberOfWeekdaySchedulesPerUser) + .SetNumberOfYeardaySchedulesPerUser(numberOfYeardaySchedulesPerUser) + .SetNumberOfHolidaySchedules(numberOfHolidaySchedules) + .GetLockParam()); if (err != CHIP_NO_ERROR) { - P6_LOG("BoltLockMgr().Init() failed"); + P6_LOG("LockMgr().Init() failed"); appError(err); } - - BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted); + LockMgr().SetCallbacks(ActionInitiated, ActionCompleted); // Initialize LEDs sStatusLED.Init(SYSTEM_STATE_LED); sLockLED.Init(LOCK_STATE_LED); - sLockLED.Set(!BoltLockMgr().IsUnlocked()); + if (state.Value() == DlLockState::kUnlocked) + { + sLockLED.Set(true); + } + else + { + sLockLED.Set(false); + } ConfigurationMgr().LogDeviceConfig(); @@ -282,25 +367,25 @@ void AppTask::AppTaskMain(void * pvParameter) void AppTask::LockActionEventHandler(AppEvent * event) { - bool initiated = false; - BoltLockManager::Action action = BoltLockManager::Action::KInvalid; - int32_t actor = 0; - CHIP_ERROR err = CHIP_NO_ERROR; + bool initiated = false; + LockManager::Action_t action; + int32_t actor; + CHIP_ERROR err = CHIP_NO_ERROR; if (event->Type == AppEvent::kEventType_Lock) { - action = static_cast(event->LockEvent.Action); + action = static_cast(event->LockEvent.Action); actor = event->LockEvent.Actor; } else if (event->Type == AppEvent::kEventType_Button) { - if (BoltLockMgr().IsUnlocked()) + if (LockMgr().NextState() == true) { - action = BoltLockManager::Action::kLock; + action = LockManager::LOCK_ACTION; } else { - action = BoltLockManager::Action::kUnlock; + action = LockManager::UNLOCK_ACTION; } actor = AppEvent::kEventType_Button; } @@ -311,7 +396,7 @@ void AppTask::LockActionEventHandler(AppEvent * event) if (err == CHIP_NO_ERROR) { - initiated = BoltLockMgr().InitiateAction(actor, action); + initiated = LockMgr().InitiateAction(actor, action); if (!initiated) { @@ -420,7 +505,7 @@ void AppTask::FunctionHandler(AppEvent * event) else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == Function::kFactoryReset) { // Set lock status LED back to show state of lock. - sLockLED.Set(!BoltLockMgr().IsUnlocked()); + sLockLED.Set(!LockMgr().NextState()); sAppTask.CancelTimer(); @@ -464,20 +549,20 @@ void AppTask::StartTimer(uint32_t aTimeoutInMs) mFunctionTimerActive = true; } -void AppTask::ActionInitiated(BoltLockManager::Action action, int32_t actor) +void AppTask::ActionInitiated(LockManager::Action_t aAction, int32_t aActor) { - // If the action has been initiated by the lock, update the bolt lock trait + // If the action has been initiated by the lock, update the lock trait // and start flashing the LEDs rapidly to indicate action initiation. - if (action == BoltLockManager::Action::kLock) + if (aAction == LockManager::LOCK_ACTION) { P6_LOG("Lock Action has been initiated"); } - else if (action == BoltLockManager::Action::kUnlock) + else if (aAction == LockManager::UNLOCK_ACTION) { P6_LOG("Unlock Action has been initiated"); } - if (actor == AppEvent::kEventType_Button) + if (aActor == AppEvent::kEventType_Button) { sAppTask.mSyncClusterToButtonAction = true; } @@ -485,18 +570,18 @@ void AppTask::ActionInitiated(BoltLockManager::Action action, int32_t actor) sLockLED.Blink(50, 50); } -void AppTask::ActionCompleted(BoltLockManager::Action action) +void AppTask::ActionCompleted(LockManager::Action_t aAction) { - // if the action has been completed by the lock, update the bolt lock trait. + // if the action has been completed by the lock, update the lock trait. // Turn on the lock LED if in a LOCKED state OR // Turn off the lock LED if in an UNLOCKED state. - if (action == BoltLockManager::Action::kLock) + if (aAction == LockManager::LOCK_ACTION) { P6_LOG("Lock Action has been completed"); sLockLED.Set(true); } - else if (action == BoltLockManager::Action::kUnlock) + else if (aAction == LockManager::UNLOCK_ACTION) { P6_LOG("Unlock Action has been completed"); @@ -510,12 +595,12 @@ void AppTask::ActionCompleted(BoltLockManager::Action action) } } -void AppTask::PostLockActionRequest(int32_t actor, BoltLockManager::Action action) +void AppTask::ActionRequest(int32_t aActor, LockManager::Action_t aAction) { AppEvent event; event.Type = AppEvent::kEventType_Lock; - event.LockEvent.Actor = actor; - event.LockEvent.Action = static_cast(action); + event.LockEvent.Actor = aActor; + event.LockEvent.Action = aAction; event.Handler = LockActionEventHandler; PostEvent(&event); } @@ -545,14 +630,17 @@ void AppTask::DispatchEvent(AppEvent * event) void AppTask::UpdateCluster(intptr_t context) { - uint8_t newValue = !BoltLockMgr().IsUnlocked(); + bool unlocked = LockMgr().NextState(); + DlLockState newState = unlocked ? DlLockState::kUnlocked : DlLockState::kLocked; + + DlOperationSource source = DlOperationSource::kUnspecified; - // write the new on/off value + // write the new lock value EmberAfStatus status = - emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE); + DoorLockServer::Instance().SetLockState(1, newState, source) ? EMBER_ZCL_STATUS_SUCCESS : EMBER_ZCL_STATUS_FAILURE; if (status != EMBER_ZCL_STATUS_SUCCESS) { - P6_LOG("ERR: updating on/off %x", status); + P6_LOG("ERR: updating lock state %x", status); } } diff --git a/examples/lock-app/p6/src/BoltLockManager.cpp b/examples/lock-app/p6/src/BoltLockManager.cpp deleted file mode 100644 index 8f7ee9dd5f1fdf..00000000000000 --- a/examples/lock-app/p6/src/BoltLockManager.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors - * Copyright (c) 2019 Google LLC. - * 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 "BoltLockManager.h" - -#include "AppConfig.h" -#include "AppTask.h" -#include - -BoltLockManager BoltLockManager::sLock; - -TimerHandle_t sLockTimer; - -CHIP_ERROR BoltLockManager::Init() -{ - // 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) - { - P6_LOG("sLockTimer timer create failed"); - appError(APP_ERROR_CREATE_TIMER_FAILED); - } - - mState = State::kUnlockingCompleted; - mAutoLockTimerArmed = false; - mAutoRelock = false; - mAutoLockDuration = 0; - - return CHIP_NO_ERROR; -} - -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 == State::kLockingInitiated || mState == State::kUnlockingInitiated); -} - -bool BoltLockManager::IsUnlocked() -{ - return (mState == State::kUnlockingCompleted); -} - -void BoltLockManager::EnableAutoRelock(bool aOn) -{ - mAutoRelock = aOn; -} - -void BoltLockManager::SetAutoLockDuration(uint32_t aDurationInSecs) -{ - mAutoLockDuration = aDurationInSecs; -} - -bool BoltLockManager::InitiateAction(int32_t aActor, Action aAction) -{ - bool action_initiated = false; - State new_state; - - // Initiate Lock/Unlock Action only when the previous one is complete. - if (mState == State::kLockingCompleted && aAction == Action::kUnlock) - { - action_initiated = true; - - new_state = State::kUnlockingInitiated; - } - else if (mState == State::kUnlockingCompleted && aAction == Action::kLock) - { - action_initiated = true; - - new_state = State::kLockingInitiated; - } - - if (action_initiated) - { - if (mAutoLockTimerArmed && new_state == State::kLockingInitiated) - { - // 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)) - { - P6_LOG("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) - { - P6_LOG("sLockTimer timer start() failed"); - appError(APP_ERROR_START_TIMER_FAILED); - } -} - -void BoltLockManager::CancelTimer(void) -{ - if (xTimerStop(sLockTimer, 0) == pdFAIL) - { - P6_LOG("Lock timer timer stop() failed"); - appError(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.Type = AppEvent::kEventType_Timer; - event.TimerEvent.Context = lock; - if (lock->mAutoLockTimerArmed) - { - event.Handler = AutoReLockTimerEventHandler; - } - else - { - event.Handler = ActuatorMovementTimerEventHandler; - } - GetAppTask().PostEvent(&event); -} - -void BoltLockManager::AutoReLockTimerEventHandler(AppEvent * aEvent) -{ - BoltLockManager * lock = static_cast(aEvent->TimerEvent.Context); - int32_t actor = 0; - - // Make sure auto lock timer is still armed. - if (!lock->mAutoLockTimerArmed) - { - return; - } - - lock->mAutoLockTimerArmed = false; - - P6_LOG("Auto Re-Lock has been triggered!"); - - lock->InitiateAction(actor, Action::kLock); -} - -void BoltLockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) -{ - Action actionCompleted = Action::KInvalid; - - BoltLockManager * lock = static_cast(aEvent->TimerEvent.Context); - - if (lock->mState == State::kLockingInitiated) - { - lock->mState = State::kLockingCompleted; - actionCompleted = Action::kLock; - } - else if (lock->mState == State::kUnlockingInitiated) - { - lock->mState = State::kUnlockingCompleted; - actionCompleted = Action::kUnlock; - } - - if (actionCompleted != Action::KInvalid) - { - if (lock->mActionCompleted_CB) - { - lock->mActionCompleted_CB(actionCompleted); - } - - if (lock->mAutoRelock && actionCompleted == Action::kUnlock) - { - // Start the timer for auto relock - lock->StartTimer(lock->mAutoLockDuration * 1000); - - lock->mAutoLockTimerArmed = true; - - P6_LOG("Auto Re-lock enabled. Will be triggered in %lu seconds", lock->mAutoLockDuration); - } - } -} diff --git a/examples/lock-app/p6/src/LockManager.cpp b/examples/lock-app/p6/src/LockManager.cpp new file mode 100644 index 00000000000000..d83b984b789779 --- /dev/null +++ b/examples/lock-app/p6/src/LockManager.cpp @@ -0,0 +1,709 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 +#include +#include +#include +#include + +LockManager LockManager::sLock; + +TimerHandle_t sLockTimer; + +using namespace ::chip::DeviceLayer::Internal; +using namespace P6DoorLock::LockInitParams; + +CHIP_ERROR LockManager::Init(chip::app::DataModel::Nullable state, LockParam lockParam) +{ + LockParams = lockParam; + + if (LockParams.numberOfUsers > kMaxUsers) + { + ChipLogError(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) + { + ChipLogError( + 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) + { + ChipLogError( + 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) + { + ChipLogError( + 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) + { + ChipLogError( + 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) + { + P6_LOG("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 LockManager::IsValidUserIndex(uint16_t userIndex) +{ + return (userIndex < kMaxUsers); +} + +bool LockManager::IsValidCredentialIndex(uint16_t credentialIndex, DlCredentialType type) +{ + // appclusters, 5.2.6.3.1: 0 is allowed index for Programming PIN credential only + if (DlCredentialType::kProgrammingPIN == type) + { + return (0 == credentialIndex); + } + + return (credentialIndex < kMaxCredentialsPerUser); +} + +bool LockManager::IsValidWeekdayScheduleIndex(uint8_t scheduleIndex) +{ + return (scheduleIndex < kMaxWeekdaySchedulesPerUser); +} + +bool LockManager::IsValidYeardayScheduleIndex(uint8_t scheduleIndex) +{ + return (scheduleIndex < kMaxYeardaySchedulesPerUser); +} + +bool LockManager::IsValidHolidayScheduleIndex(uint8_t scheduleIndex) +{ + return (scheduleIndex < kMaxHolidaySchedules); +} + +bool LockManager::ReadConfigValues() +{ + size_t outLen; + P6Config::ReadConfigValueBin(P6Config::kConfigKey_LockUser, reinterpret_cast(&mLockUsers), + sizeof(EmberAfPluginDoorLockUserInfo) * ArraySize(mLockUsers), outLen); + + P6Config::ReadConfigValueBin(P6Config::kConfigKey_Credential, reinterpret_cast(&mLockCredentials), + sizeof(EmberAfPluginDoorLockCredentialInfo) * ArraySize(mLockCredentials), outLen); + + P6Config::ReadConfigValueBin(P6Config::kConfigKey_LockUserName, reinterpret_cast(mUserNames), sizeof(mUserNames), + outLen); + + P6Config::ReadConfigValueBin(P6Config::kConfigKey_CredentialData, reinterpret_cast(mCredentialData), + sizeof(mCredentialData), outLen); + + P6Config::ReadConfigValueBin(P6Config::kConfigKey_UserCredentials, reinterpret_cast(mCredentials), + sizeof(DlCredential) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser, outLen); + + P6Config::ReadConfigValueBin(P6Config::kConfigKey_WeekDaySchedules, reinterpret_cast(mWeekdaySchedule), + sizeof(EmberAfPluginDoorLockWeekDaySchedule) * LockParams.numberOfWeekdaySchedulesPerUser * + LockParams.numberOfUsers, + outLen); + + P6Config::ReadConfigValueBin(P6Config::kConfigKey_YearDaySchedules, reinterpret_cast(mYeardaySchedule), + sizeof(EmberAfPluginDoorLockYearDaySchedule) * LockParams.numberOfYeardaySchedulesPerUser * + LockParams.numberOfUsers, + outLen); + + P6Config::ReadConfigValueBin(P6Config::kConfigKey_HolidaySchedules, reinterpret_cast(&(mHolidaySchedule)), + sizeof(EmberAfPluginDoorLockHolidaySchedule) * LockParams.numberOfHolidaySchedules, outLen); + + return true; +} + +void LockManager::SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB) +{ + mActionInitiated_CB = aActionInitiated_CB; + mActionCompleted_CB = aActionCompleted_CB; +} + +bool LockManager::IsActionInProgress() +{ + return (mState == kState_LockInitiated || mState == kState_UnlockInitiated); +} + +bool LockManager::NextState() +{ + return (mState == kState_UnlockCompleted); +} + +bool LockManager::InitiateAction(int32_t aActor, Action_t aAction) +{ + bool action_initiated = false; + State_t new_state; + + // Initiate 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) + { + + 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 LockManager::StartTimer(uint32_t aTimeoutMs) +{ + if (xTimerIsTimerActive(sLockTimer)) + { + P6_LOG("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) + { + P6_LOG("sLockTimer timer start() failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } +} + +void LockManager::CancelTimer(void) +{ + if (xTimerStop(sLockTimer, 0) == pdFAIL) + { + P6_LOG("sLockTimer stop() failed"); + appError(APP_ERROR_STOP_TIMER_FAILED); + } +} + +void LockManager::TimerEventHandler(TimerHandle_t xTimer) +{ + // Get lock obj context from timer id. + LockManager * 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.Type = AppEvent::kEventType_Timer; + event.TimerEvent.Context = lock; + event.Handler = ActuatorMovementTimerEventHandler; + GetAppTask().PostEvent(&event); +} + +void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) +{ + Action_t actionCompleted = INVALID_ACTION; + + LockManager * lock = static_cast(aEvent->TimerEvent.Context); + + 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); + } + } +} + +bool LockManager::Lock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err) +{ + return setLockState(endpointId, DlLockState::kLocked, pin, err); +} + +bool LockManager::Unlock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err) +{ + return setLockState(endpointId, DlLockState::kUnlocked, pin, err); +} + +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); + + const auto & userInDb = mLockUsers[userIndex]; + + user.userStatus = userInDb.userStatus; + if (DlUserStatus::kAvailable == user.userStatus) + { + ChipLogDetail(Zcl, "Found unoccupied user [endpoint=%d]", mEndpointId); + 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; + + ChipLogDetail(Zcl, + "Found occupied user " + "[endpoint=%d,name=\"%.*s\",credentialsCount=%u,uniqueId=%lx,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 LockManager::SetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + const chip::CharSpan & userName, uint32_t uniqueId, DlUserStatus userStatus, DlUserType usertype, + DlCredentialRule credentialRule, const DlCredential * credentials, size_t totalCredentials) +{ + ChipLogProgress(Zcl, + "Door Lock App: LockManager::SetUser " + "[endpoint=%d,userIndex=%d,creator=%d,modifier=%d,userName=%s,uniqueId=%ld " + "userStatus=%u,userType=%u,credentialRule=%u,credentials=%p,totalCredentials=%u]", + mEndpointId, 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) + { + ChipLogError(Zcl, "Cannot set user - user name is too long [endpoint=%d,index=%d]", mEndpointId, 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(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]; + mCredentials[userIndex][i].CredentialType = 1; + mCredentials[userIndex][i].CredentialIndex = i + 1; + } + + userInStorage.credentials = chip::Span(mCredentials[userIndex], totalCredentials); + + // Save user information in NVM flash + P6Config::WriteConfigValueBin(P6Config::kConfigKey_LockUser, reinterpret_cast(&mLockUsers), + sizeof(EmberAfPluginDoorLockUserInfo) * LockParams.numberOfUsers); + + P6Config::WriteConfigValueBin(P6Config::kConfigKey_UserCredentials, reinterpret_cast(mCredentials), + sizeof(DlCredential) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser); + + P6Config::WriteConfigValueBin(P6Config::kConfigKey_LockUserName, reinterpret_cast(mUserNames), + sizeof(mUserNames)); + + ChipLogProgress(Zcl, "Successfully set the user [mEndpointId=%d,index=%d]", endpointId, userIndex); + + return true; +} + +bool LockManager::GetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) +{ + + VerifyOrReturnValue(credentialIndex > 0, false); // indices are one-indexed + + credentialIndex--; + + VerifyOrReturnValue(IsValidCredentialIndex(credentialIndex, credentialType), false); + + ChipLogProgress(Zcl, "Lock App: LockManager::GetCredential [credentialType=%u], credentialIndex=%d", + to_underlying(credentialType), credentialIndex); + + if (credentialType == DlCredentialType::kProgrammingPIN) + { + ChipLogError(Zcl, "Programming user not supported [credentialType=%u], credentialIndex=%d", to_underlying(credentialType), + credentialIndex); + + return true; + } + + const auto & credentialInStorage = mLockCredentials[credentialIndex]; + + credential.status = credentialInStorage.status; + ChipLogDetail(Zcl, "CredentialStatus: %d, CredentialIndex: %d ", (int) credential.status, credentialIndex); + + if (DlCredentialStatus::kAvailable == credential.status) + { + ChipLogDetail(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; + + ChipLogDetail(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, DlCredentialType credentialType, + const chip::ByteSpan & credentialData) +{ + + VerifyOrReturnValue(credentialIndex > 0, false); // indices are one-indexed + + credentialIndex--; + + VerifyOrReturnValue(IsValidCredentialIndex(credentialIndex, credentialType), false); + + 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); + + 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 + P6Config::WriteConfigValueBin(P6Config::kConfigKey_Credential, reinterpret_cast(&mLockCredentials), + sizeof(EmberAfPluginDoorLockCredentialInfo) * LockParams.numberOfCredentialsPerUser); + + P6Config::WriteConfigValueBin(P6Config::kConfigKey_CredentialData, reinterpret_cast(&mCredentialData), + sizeof(mCredentialData)); + + 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); + + schedule = mWeekdaySchedule[userIndex][weekdayIndex]; + + return DlStatus::kSuccess; +} + +DlStatus LockManager::SetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + DlScheduleStatus status, DlDaysMaskMap 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.daysMask = daysMask; + scheduleInStorage.startHour = startHour; + scheduleInStorage.startMinute = startMinute; + scheduleInStorage.endHour = endHour; + scheduleInStorage.endMinute = endMinute; + + // Save schedule information in NVM flash + P6Config::WriteConfigValueBin(P6Config::kConfigKey_WeekDaySchedules, reinterpret_cast(mWeekdaySchedule), + sizeof(EmberAfPluginDoorLockWeekDaySchedule) * LockParams.numberOfWeekdaySchedulesPerUser * + LockParams.numberOfUsers); + + 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); + + auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex]; + + schedule = scheduleInStorage; + + 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); + + auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex]; + + scheduleInStorage.localStartTime = localStartTime; + scheduleInStorage.localEndTime = localEndTime; + + // Save schedule information in NVM flash + P6Config::WriteConfigValueBin(P6Config::kConfigKey_YearDaySchedules, reinterpret_cast(mYeardaySchedule), + sizeof(EmberAfPluginDoorLockYearDaySchedule) * LockParams.numberOfYeardaySchedulesPerUser * + LockParams.numberOfUsers); + + 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); + + auto & scheduleInStorage = mHolidaySchedule[holidayIndex]; + + schedule = scheduleInStorage; + + return DlStatus::kSuccess; +} + +DlStatus LockManager::SetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status, + uint32_t localStartTime, uint32_t localEndTime, DlOperatingMode operatingMode) +{ + VerifyOrReturnValue(holidayIndex > 0, DlStatus::kFailure); // indices are one-indexed + + holidayIndex--; + + VerifyOrReturnValue(IsValidHolidayScheduleIndex(holidayIndex), DlStatus::kFailure); + + auto & scheduleInStorage = mHolidaySchedule[holidayIndex]; + + scheduleInStorage.localStartTime = localStartTime; + scheduleInStorage.localEndTime = localEndTime; + scheduleInStorage.operatingMode = operatingMode; + + // Save schedule information in NVM flash + P6Config::WriteConfigValueBin(P6Config::kConfigKey_HolidaySchedules, reinterpret_cast(&(mHolidaySchedule)), + sizeof(EmberAfPluginDoorLockHolidaySchedule) * LockParams.numberOfHolidaySchedules); + + 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::kUnknownEnumValue: + break; + } + + return "Unknown"; +} + +bool LockManager::setLockState(chip::EndpointId endpointId, DlLockState lockState, const Optional & pin, + DlOperationError & err) +{ + DlLockState curState = DlLockState::kLocked; + if (mState == kState_UnlockCompleted) + curState = DlLockState::kUnlocked; + + if ((curState == lockState) && (curState == DlLockState::kLocked)) + { + ChipLogDetail(Zcl, "Door Lock App: door is already locked, ignoring command to set lock state to \"%s\" [endpointId=%d]", + lockStateToString(lockState), endpointId); + return true; + } + else if ((curState == lockState) && (curState == DlLockState::kUnlocked)) + { + ChipLogDetail(Zcl, + "Door Lock App: door is already unlocked, ignoring command to set unlock state to \"%s\" [endpointId=%d]", + lockStateToString(lockState), endpointId); + return true; + } + + // Check the RequirePINforRemoteOperation attribute + bool requirePin = false; + // chip::app::Clusters::DoorLock::Attributes::RequirePINforRemoteOperation::Get(endpointId, &requirePin); + + // If a pin code is not given + if (!pin.HasValue()) + { + ChipLogDetail(Zcl, "Door Lock App: PIN code is not specified, but it is required [endpointId=%d]", mEndpointId); + curState = lockState; + + // If a pin code is not required + if (!requirePin) + { + ChipLogDetail(Zcl, "Door Lock App: setting door lock state to \"%s\" [endpointId=%d]", lockStateToString(lockState), + endpointId); + curState = lockState; + return true; + } + + return false; + } + + // Check the PIN code + for (uint8_t i = 0; i < kMaxCredentials; i++) + { + if (mLockCredentials[i].credentialType != DlCredentialType::kPin || + mLockCredentials[i].status == DlCredentialStatus::kAvailable) + { + continue; + } + + if (mLockCredentials[i].credentialData.data_equal(pin.Value())) + { + ChipLogDetail(Zcl, + "Lock App: specified PIN code was found in the database, setting lock state to \"%s\" [endpointId=%d]", + lockStateToString(lockState), mEndpointId); + + curState = lockState; + + return true; + } + } + + ChipLogDetail(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), mEndpointId); + + err = DlOperationError::kInvalidCredential; + return false; +} diff --git a/examples/lock-app/p6/src/ZclCallbacks.cpp b/examples/lock-app/p6/src/ZclCallbacks.cpp index dd90025ccfe234..cdfd3a8d27e883 100644 --- a/examples/lock-app/p6/src/ZclCallbacks.cpp +++ b/examples/lock-app/p6/src/ZclCallbacks.cpp @@ -22,7 +22,8 @@ #include "AppConfig.h" #include "AppTask.h" -#include "BoltLockManager.h" +#include "LockManager.h" +#include #include #include @@ -31,18 +32,22 @@ using namespace ::chip; using namespace ::chip::app::Clusters; +using namespace ::chip::DeviceLayer::Internal; void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size, uint8_t * value) { - if (attributePath.mClusterId == OnOff::Id && attributePath.mAttributeId == OnOff::Attributes::OnOff::Id) + ClusterId clusterId = attributePath.mClusterId; + AttributeId attributeId = attributePath.mAttributeId; + ChipLogProgress(Zcl, "Cluster callback: " ChipLogFormatMEI, ChipLogValueMEI(clusterId)); + + if (clusterId == DoorLock::Id && attributeId == DoorLock::Attributes::LockState::Id) { - BoltLockMgr().InitiateAction(AppEvent::kEventType_Lock, - *value ? BoltLockManager::Action::kLock : BoltLockManager::Action::kUnlock); + ChipLogProgress(Zcl, "Door lock cluster: " ChipLogFormatMEI, ChipLogValueMEI(clusterId)); } } -/** @brief OnOff Cluster Init +/** @brief DoorLock Cluster Init * * This function is called when a specific cluster is initialized. It gives the * application an opportunity to take care of cluster initialization procedures. @@ -50,14 +55,103 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & * * @param endpoint Ver.: always * - * TODO Issue #3841 - * emberAfOnOffClusterInitCallback happens before the stack initialize the cluster - * attributes to the default value. - * The logic here expects something similar to the deprecated Plugins callback - * emberAfPluginOnOffClusterServerPostInitCallback. - * */ -void emberAfOnOffClusterInitCallback(EndpointId endpoint) +void emberAfOnOffClusterInitCallback(EndpointId endpoint) {} + +bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, DlOperationError & err) +{ + ChipLogProgress(Zcl, "Door Lock App: Lock Command endpoint=%d", endpointId); + bool status = LockMgr().Lock(endpointId, pinCode, err); + if (status == true) + { + LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::LOCK_ACTION); + } + return status; +} + +bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, + DlOperationError & err) +{ + ChipLogProgress(Zcl, "Door Lock App: Unlock Command endpoint=%d", endpointId); + bool status = LockMgr().Unlock(endpointId, pinCode, err); + if (status == true) + { + LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION); + } + + return status; +} + +bool emberAfPluginDoorLockGetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) +{ + return LockMgr().GetCredential(endpointId, credentialIndex, credentialType, credential); +} + +bool emberAfPluginDoorLockSetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, + chip::FabricIndex modifier, DlCredentialStatus credentialStatus, + DlCredentialType credentialType, const chip::ByteSpan & credentialData) +{ + return LockMgr().SetCredential(endpointId, credentialIndex, creator, modifier, credentialStatus, credentialType, + credentialData); +} + +bool emberAfPluginDoorLockGetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) +{ + return LockMgr().GetUser(endpointId, userIndex, user); +} + +bool emberAfPluginDoorLockSetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, + chip::FabricIndex modifier, const chip::CharSpan & userName, uint32_t uniqueId, + DlUserStatus userStatus, DlUserType usertype, DlCredentialRule credentialRule, + const DlCredential * credentials, size_t totalCredentials) +{ + + return LockMgr().SetUser(endpointId, userIndex, creator, modifier, userName, uniqueId, userStatus, usertype, credentialRule, + credentials, totalCredentials); +} + +// TODO: These functions will be supported by door-lock-server in the future. These are set to return failure until implemented. +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + EmberAfPluginDoorLockWeekDaySchedule & schedule) // +{ + return LockMgr().GetWeekdaySchedule(endpointId, weekdayIndex, userIndex, schedule); + // return DlStatus::kFailure; +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + EmberAfPluginDoorLockYearDaySchedule & schedule) +{ + return LockMgr().GetYeardaySchedule(endpointId, yearDayIndex, userIndex, schedule); + // return DlStatus::kFailure; +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + DlScheduleStatus status, DlDaysMaskMap daysMask, uint8_t startHour, uint8_t startMinute, + uint8_t endHour, uint8_t endMinute) +{ + return LockMgr().SetWeekdaySchedule(endpointId, weekdayIndex, userIndex, status, daysMask, startHour, startMinute, endHour, + endMinute); + // return DlStatus::kFailure; +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + DlScheduleStatus status, uint32_t localStartTime, uint32_t localEndTime) +{ + return LockMgr().SetYeardaySchedule(endpointId, yearDayIndex, userIndex, status, localStartTime, localEndTime); + // return DlStatus::kFailure; +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t holidayIndex, + EmberAfPluginDoorLockHolidaySchedule & schedule) +{ + return LockMgr().GetHolidaySchedule(endpointId, holidayIndex, schedule); + // return DlStatus::kFailure; +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status, + uint32_t localStartTime, uint32_t localEndTime, DlOperatingMode operatingMode) { - GetAppTask().UpdateClusterState(); + return LockMgr().SetHolidaySchedule(endpointId, holidayIndex, status, localStartTime, localEndTime, operatingMode); + // return DlStatus::kFailure; } diff --git a/src/platform/P6/P6Config.cpp b/src/platform/P6/P6Config.cpp index ecaa219979e65f..e15d56c5583b6e 100644 --- a/src/platform/P6/P6Config.cpp +++ b/src/platform/P6/P6Config.cpp @@ -76,6 +76,17 @@ const P6Config::Key P6Config::kConfigKey_WiFiPassword = { kConfigNamespace const P6Config::Key P6Config::kConfigKey_WiFiSecurity = { kConfigNamespace_ChipConfig, "wifi-security" }; const P6Config::Key P6Config::kConfigKey_WiFiMode = { kConfigNamespace_ChipConfig, "wifimode" }; const P6Config::Key P6Config::kConfigKey_UniqueId = { kConfigNamespace_ChipConfig, "unique-id" }; +const P6Config::Key P6Config::kConfigKey_LockUser = { kConfigNamespace_ChipConfig, "lock-user" }; +const P6Config::Key P6Config::kConfigKey_Credential = { kConfigNamespace_ChipConfig, "credential" }; +const P6Config::Key P6Config::kConfigKey_LockUserName = { kConfigNamespace_ChipConfig, "lock-user-name" }; +const P6Config::Key P6Config::kConfigKey_CredentialData = { kConfigNamespace_ChipConfig, "credential-data" }; +const P6Config::Key P6Config::kConfigKey_UserCredentials = { kConfigNamespace_ChipConfig, "user-credentials" }; +const P6Config::Key P6Config::kConfigKey_WeekDaySchedules = { kConfigNamespace_ChipConfig, "weekday-schedules" }; +; +const P6Config::Key P6Config::kConfigKey_YearDaySchedules = { kConfigNamespace_ChipConfig, "yearday-schedules" }; +; +const P6Config::Key P6Config::kConfigKey_HolidaySchedules = { kConfigNamespace_ChipConfig, "holiday-schedules" }; +; // Keys stored in the Chip-counters namespace const P6Config::Key P6Config::kCounterKey_RebootCount = { kConfigNamespace_ChipCounters, "reboot-count" }; diff --git a/src/platform/P6/P6Config.h b/src/platform/P6/P6Config.h index 0be7aa5abb7696..7e62fd6e66b772 100644 --- a/src/platform/P6/P6Config.h +++ b/src/platform/P6/P6Config.h @@ -80,6 +80,14 @@ class P6Config static const Key kConfigKey_Spake2pIterationCount; static const Key kConfigKey_Spake2pSalt; static const Key kConfigKey_Spake2pVerifier; + static const Key kConfigKey_LockUser; + static const Key kConfigKey_Credential; + static const Key kConfigKey_LockUserName; + static const Key kConfigKey_CredentialData; + static const Key kConfigKey_UserCredentials; + static const Key kConfigKey_WeekDaySchedules; + static const Key kConfigKey_YearDaySchedules; + static const Key kConfigKey_HolidaySchedules; // CHIP Counter keys static const Key kCounterKey_RebootCount;