diff --git a/examples/all-clusters-app/esp32/main/AppTask.cpp b/examples/all-clusters-app/esp32/main/AppTask.cpp index 51ee806898cd10..2111d7cb32df47 100644 --- a/examples/all-clusters-app/esp32/main/AppTask.cpp +++ b/examples/all-clusters-app/esp32/main/AppTask.cpp @@ -27,7 +27,9 @@ #include "esp_spi_flash.h" #include "freertos/FreeRTOS.h" #include +#include +#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 #define APP_TASK_NAME "APP" #define APP_EVENT_QUEUE_SIZE 10 #define APP_TASK_STACK_SIZE (3072) @@ -35,6 +37,7 @@ static const char * TAG = "app-task"; namespace { +TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer QueueHandle_t sAppEventQueue; TaskHandle_t sAppTaskHandle; @@ -58,6 +61,106 @@ CHIP_ERROR AppTask::StartAppTask() return (xReturned == pdPASS) ? CHIP_NO_ERROR : APP_ERROR_CREATE_TASK_FAILED; } +void AppTask::TimerEventHandler(TimerHandle_t xTimer) +{ + AppEvent event; + event.mType = AppEvent::kEventType_Timer; + event.mTimerEvent.mContext = (void *) xTimer; + event.mHandler = FunctionTimerEventHandler; + sAppTask.PostEvent(&event); +} + +void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) +{ + if (aEvent->mType != AppEvent::kEventType_Timer) + { + return; + } + // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, + // initiate factory reset + if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) + { + // ESP_LOGI(TAG, "Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to + // cancel, if required. + sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + sAppTask.mFunction = kFunction_FactoryReset; + // Turn off all LEDs before starting blink to make sure blink is + // co-ordinated. + // sStatusLED.Set(false); + // sLockLED.Set(false); + // sStatusLED.Blink(500); + // sLockLED.Blink(500); + } + else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) + { + // Actually trigger Factory Reset + sAppTask.mFunction = kFunction_NoneSelected; + chip::Server::GetInstance().ScheduleFactoryReset(); + } +} + +void AppTask::CancelTimer() +{ + if (xTimerStop(sFunctionTimer, 0) == pdFAIL) + { + ESP_LOGI(TAG, "app timer stop() failed"); + return; + } + mFunctionTimerActive = false; +} +void AppTask::StartTimer(uint32_t aTimeoutInMs) +{ + if (xTimerIsTimerActive(sFunctionTimer)) + { + 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(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS) + { + ESP_LOGI(TAG, "app timer start() failed"); + return; + } + mFunctionTimerActive = true; +} +void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor) +{ + // If the action has been initiated by the lock, update the bolt lock trait + // and start flashing the LEDs rapidly to indicate action initiation. + if (aAction == BoltLockManager::LOCK_ACTION) + { + ESP_LOGI(TAG, "Lock Action has been initiated"); + } + else if (aAction == BoltLockManager::UNLOCK_ACTION) + { + ESP_LOGI(TAG, "Unlock Action has been initiated"); + } + if (aActor == AppEvent::kEventType_Button) + { + sAppTask.mSyncClusterToButtonAction = true; + } + // sLockLED.Blink(50, 50); +} +void AppTask::ActionCompleted(BoltLockManager::Action_t aAction) +{ + // if the action has been completed by the lock, update the bolt lock trait. + // Turn on the lock LED if in a LOCKED state OR + // Turn off the lock LED if in an UNLOCKED state. + if (aAction == BoltLockManager::LOCK_ACTION) + { + ESP_LOGI(TAG, "Lock Action has been completed"); + // sLockLED.Set(true); + } + else if (aAction == BoltLockManager::UNLOCK_ACTION) + { + ESP_LOGI(TAG, "Unlock Action has been completed"); + // sLockLED.Set(false); + } +} + CHIP_ERROR AppTask::Init() { /* Print chip information */ @@ -69,7 +172,18 @@ CHIP_ERROR AppTask::Init() ESP_LOGI(TAG, "%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); - CHIP_ERROR err = CHIP_NO_ERROR; + // Create FreeRTOS sw timer for Function Selection + sFunctionTimer = xTimerCreate("FnTmr", // 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 = app task obj context + TimerEventHandler // timer callback handler + ); + + CHIP_ERROR err = BoltLockMgr().InitLockState(); + + BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted); + statusLED1.Init(STATUS_LED_GPIO_NUM); // Our second LED doesn't map to any physical LEDs so far, just to virtual // "LED"s on devices with screens. diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt index a4003980dedde0..cb096a8c1e3262 100644 --- a/examples/all-clusters-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt @@ -20,7 +20,6 @@ set(PRIV_INCLUDE_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/all-clusters-app" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/all-clusters-app/all-clusters-common/include" "${CMAKE_CURRENT_LIST_DIR}/include" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/lock-app/linux/include" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/providers" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/third_party/nlfaultinjection/repo/include" @@ -30,11 +29,11 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/all-clusters-app/zap-generated" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/app-common/app-common/zap-generated/attributes" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/app-common/app-common/zap-generated" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/lock-app/linux/src" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/providers" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/ota" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/common" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/shell_extension" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/lock" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/util" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/reporting" @@ -94,10 +93,6 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/all-clusters-app/all-clusters-common/src" ) -set(EXCLUDE_SRCS_LIST - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/lock-app/linux/src/LockAppCommandDelegate.cpp" -) - if (CONFIG_ENABLE_PW_RPC) # Append additional directories for RPC build set(PRIV_INCLUDE_DIRS_LIST "${PRIV_INCLUDE_DIRS_LIST}" diff --git a/examples/all-clusters-app/esp32/main/include/AppTask.h b/examples/all-clusters-app/esp32/main/include/AppTask.h index 3dd8b0bf121a5c..8a72b2d47e5777 100644 --- a/examples/all-clusters-app/esp32/main/include/AppTask.h +++ b/examples/all-clusters-app/esp32/main/include/AppTask.h @@ -18,7 +18,8 @@ */ #pragma once -#include "AppEvent.h" +#include +#include #include // Application-defined error codes in the CHIP_ERROR space. @@ -28,6 +29,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 { @@ -40,7 +42,30 @@ class AppTask private: CHIP_ERROR Init(); + + static void ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor); + static void ActionCompleted(BoltLockManager::Action_t aAction); + + void StartTimer(uint32_t aTimeoutMs); + void CancelTimer(void); + + static void FunctionTimerEventHandler(AppEvent * aEvent); + static void TimerEventHandler(TimerHandle_t xTimer); + void DispatchEvent(AppEvent * event); + + enum Function_t + { + kFunction_NoneSelected = 0, + kFunction_SoftwareUpdate = 0, + kFunction_StartBleAdv = 1, + kFunction_FactoryReset = 2, + kFunction_Invalid + } Function; + Function_t mFunction; + bool mFunctionTimerActive; + bool mSyncClusterToButtonAction; + static AppTask sAppTask; friend AppTask & GetAppTask(void); }; diff --git a/examples/lock-app/esp32/main/AppTask.cpp b/examples/lock-app/esp32/main/AppTask.cpp index c7ebacf0f334f5..9014982eba5975 100644 --- a/examples/lock-app/esp32/main/AppTask.cpp +++ b/examples/lock-app/esp32/main/AppTask.cpp @@ -16,13 +16,14 @@ */ #include "AppTask.h" -#include "AppConfig.h" -#include "AppEvent.h" #include "Button.h" #include "LEDWidget.h" #include "esp_log.h" #include #include +#include +#include +//#include #include #include #include @@ -60,6 +61,7 @@ StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; using namespace ::chip::DeviceLayer; using namespace ::chip::System; +// using namespace ESP32DoorLock::LockInitParams; AppTask AppTask::sAppTask; @@ -79,19 +81,14 @@ CHIP_ERROR AppTask::StartAppTask() CHIP_ERROR AppTask::Init() { - // Create FreeRTOS sw timer for Function Selection. + // Create FreeRTOS sw timer for Function Selection sFunctionTimer = xTimerCreate("FnTmr", // 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 = app task obj context TimerEventHandler // timer callback handler ); - CHIP_ERROR err = BoltLockMgr().Init(); - if (err != CHIP_NO_ERROR) - { - ESP_LOGI(TAG, "BoltLockMgr().Init() failed"); - return err; - } + CHIP_ERROR err = BoltLockMgr().InitLockState(); BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted); @@ -201,12 +198,12 @@ void AppTask::LockActionEventHandler(AppEvent * aEvent) int32_t actor; CHIP_ERROR err = CHIP_NO_ERROR; - if (aEvent->Type == AppEvent::kEventType_Lock) + if (aEvent->mType == AppEvent::kEventType_Lock) { - action = static_cast(aEvent->LockEvent.Action); - actor = aEvent->LockEvent.Actor; + action = static_cast(aEvent->mLockEvent.mAction); + actor = aEvent->mLockEvent.mActor; } - else if (aEvent->Type == AppEvent::kEventType_Button) + else if (aEvent->mType == AppEvent::kEventType_Button) { if (BoltLockMgr().IsUnlocked()) { @@ -241,19 +238,19 @@ void AppTask::ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction) return; } - AppEvent button_event = {}; - button_event.Type = AppEvent::kEventType_Button; - button_event.ButtonEvent.PinNo = btnIdx; - button_event.ButtonEvent.Action = btnAction; + AppEvent button_event = {}; + button_event.mType = AppEvent::kEventType_Button; + button_event.mButtonEvent.mPinNo = btnIdx; + button_event.mButtonEvent.mAction = btnAction; if (btnIdx == APP_LOCK_BUTTON && btnAction == APP_BUTTON_PRESSED) { - button_event.Handler = LockActionEventHandler; + button_event.mHandler = LockActionEventHandler; sAppTask.PostEvent(&button_event); } else if (btnIdx == APP_FUNCTION_BUTTON) { - button_event.Handler = FunctionHandler; + button_event.mHandler = FunctionHandler; sAppTask.PostEvent(&button_event); } } @@ -261,15 +258,15 @@ void AppTask::ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction) void AppTask::TimerEventHandler(TimerHandle_t xTimer) { AppEvent event; - event.Type = AppEvent::kEventType_Timer; - event.TimerEvent.Context = (void *) xTimer; - event.Handler = FunctionTimerEventHandler; + event.mType = AppEvent::kEventType_Timer; + event.mTimerEvent.mContext = (void *) xTimer; + event.mHandler = FunctionTimerEventHandler; sAppTask.PostEvent(&event); } void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) { - if (aEvent->Type != AppEvent::kEventType_Timer) + if (aEvent->mType != AppEvent::kEventType_Timer) { return; } @@ -304,7 +301,7 @@ void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) void AppTask::FunctionHandler(AppEvent * aEvent) { - if (aEvent->ButtonEvent.PinNo != APP_FUNCTION_BUTTON) + if (aEvent->mButtonEvent.mPinNo != APP_FUNCTION_BUTTON) { return; } @@ -316,7 +313,7 @@ void AppTask::FunctionHandler(AppEvent * aEvent) // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT - if (aEvent->ButtonEvent.Action == APP_BUTTON_PRESSED) + if (aEvent->mButtonEvent.mAction == APP_BUTTON_PRESSED) { if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) { @@ -431,10 +428,10 @@ void AppTask::ActionCompleted(BoltLockManager::Action_t aAction) void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction) { AppEvent event; - event.Type = AppEvent::kEventType_Lock; - event.LockEvent.Actor = aActor; - event.LockEvent.Action = aAction; - event.Handler = LockActionEventHandler; + event.mType = AppEvent::kEventType_Lock; + event.mLockEvent.mActor = aActor; + event.mLockEvent.mAction = aAction; + event.mHandler = LockActionEventHandler; PostEvent(&event); } @@ -451,9 +448,9 @@ void AppTask::PostEvent(const AppEvent * aEvent) void AppTask::DispatchEvent(AppEvent * aEvent) { - if (aEvent->Handler) + if (aEvent->mHandler) { - aEvent->Handler(aEvent); + aEvent->mHandler(aEvent); } else { diff --git a/examples/lock-app/esp32/main/BoltLockManager.cpp b/examples/lock-app/esp32/main/BoltLockManager.cpp deleted file mode 100644 index d6cb59aaf54a3e..00000000000000 --- a/examples/lock-app/esp32/main/BoltLockManager.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - * - * 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 "esp_log.h" -#include -static const char * TAG = "BoltLockManager"; -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) - { - ESP_LOGE(TAG, "sLockTimer timer create failed"); - return APP_ERROR_CREATE_TIMER_FAILED; - } - - mState = kState_LockingCompleted; - 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 == kState_LockingInitiated || mState == kState_UnlockingInitiated); -} - -bool BoltLockManager::IsUnlocked() -{ - return (mState == kState_UnlockingCompleted); -} - -void BoltLockManager::SetAutoLockDuration(uint32_t aDurationInSecs) -{ - mAutoLockDuration = aDurationInSecs; -} - -bool BoltLockManager::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_LockingCompleted && aAction == UNLOCK_ACTION) - { - action_initiated = true; - - new_state = kState_UnlockingInitiated; - } - else if (mState == kState_UnlockingCompleted && aAction == LOCK_ACTION) - { - action_initiated = true; - - new_state = kState_LockingInitiated; - } - - if (action_initiated) - { - if (mAutoLockTimerArmed && new_state == kState_LockingInitiated) - { - // 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"); - return; - } -} - -void BoltLockManager::CancelTimer(void) -{ - if (xTimerStop(sLockTimer, 0) == pdFAIL) - { - ESP_LOGI(TAG, "Lock timer timer stop() failed"); - return; - } -} -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; - - 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->TimerEvent.Context); - - if (lock->mState == kState_LockingInitiated) - { - lock->mState = kState_LockingCompleted; - actionCompleted = LOCK_ACTION; - } - else if (lock->mState == kState_UnlockingInitiated) - { - lock->mState = kState_UnlockingCompleted; - 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 %u seconds", lock->mAutoLockDuration); - } - } -} diff --git a/examples/lock-app/esp32/main/Button.cpp b/examples/lock-app/esp32/main/Button.cpp index 698888440ecb65..301e62f92d7e08 100644 --- a/examples/lock-app/esp32/main/Button.cpp +++ b/examples/lock-app/esp32/main/Button.cpp @@ -18,7 +18,7 @@ #include "Button.h" #include "AppTask.h" -#include "AppConfig.h" +//#include esp_err_t Button::Init(gpio_num_t gpioNum, uint16_t debouncePeriod) { diff --git a/examples/lock-app/esp32/main/CMakeLists.txt b/examples/lock-app/esp32/main/CMakeLists.txt index 8dd4bc1dc0a774..8947b92a95c617 100644 --- a/examples/lock-app/esp32/main/CMakeLists.txt +++ b/examples/lock-app/esp32/main/CMakeLists.txt @@ -23,6 +23,7 @@ idf_component_register(INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/pw_sys_io/public" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/lock" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/providers" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/common" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/common/pigweed" @@ -185,6 +186,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/door-lock-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/identify-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/groups-server" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/lock" PRIV_REQUIRES chip QRCode bt) add_dependencies(${COMPONENT_LIB} app-codegen) diff --git a/examples/lock-app/esp32/main/DeviceCallbacks.cpp b/examples/lock-app/esp32/main/DeviceCallbacks.cpp index 7a073be32e1e53..576bbb7b8edf4f 100644 --- a/examples/lock-app/esp32/main/DeviceCallbacks.cpp +++ b/examples/lock-app/esp32/main/DeviceCallbacks.cpp @@ -24,8 +24,8 @@ **/ #include "DeviceCallbacks.h" -#include "AppConfig.h" -#include "BoltLockManager.h" +#include +#include #include #include @@ -59,7 +59,8 @@ void AppDeviceCallbacks::PostAttributeChangeCallback(EndpointId endpointId, Clus void AppDeviceCallbacks::OnOnOffPostAttributeChangeCallback(EndpointId endpointId, AttributeId attributeId, uint8_t * value) { - VerifyOrExit(attributeId == ZCL_ON_OFF_ATTRIBUTE_ID, ESP_LOGI(TAG, "Unhandled Attribute ID: '0x%04x", attributeId)); + VerifyOrExit(attributeId == app::Clusters::OnOff::Attributes::OnOff::Id, + ESP_LOGI(TAG, "Unhandled Attribute ID: '0x%04x", attributeId)); VerifyOrExit(endpointId == 1 || endpointId == 2, ESP_LOGE(TAG, "Unexpected EndPoint ID: `0x%02x'", endpointId)); if (*value) { diff --git a/examples/lock-app/esp32/main/include/AppEvent.h b/examples/lock-app/esp32/main/include/AppEvent.h deleted file mode 100644 index 783d29ca6feb5d..00000000000000 --- a/examples/lock-app/esp32/main/include/AppEvent.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * 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. - */ - -#pragma once - -struct AppEvent; -typedef void (*EventHandler)(AppEvent *); - -struct AppEvent -{ - enum AppEventTypes - { - kEventType_Button = 0, - kEventType_Timer, - kEventType_Lock, - kEventType_Install, - }; - - uint16_t Type; - - union - { - struct - { - uint8_t PinNo; - uint8_t Action; - } ButtonEvent; - struct - { - void * Context; - } TimerEvent; - struct - { - uint8_t Action; - int32_t Actor; - } LockEvent; - }; - - EventHandler Handler; -}; diff --git a/examples/lock-app/esp32/main/include/AppTask.h b/examples/lock-app/esp32/main/include/AppTask.h index ba7121b1554495..7a1aad3e221800 100644 --- a/examples/lock-app/esp32/main/include/AppTask.h +++ b/examples/lock-app/esp32/main/include/AppTask.h @@ -20,8 +20,8 @@ #include #include -#include "AppEvent.h" -#include "BoltLockManager.h" +#include +#include #include "freertos/FreeRTOS.h" #include @@ -35,6 +35,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 { diff --git a/examples/lock-app/esp32/main/include/BoltLockManager.h b/examples/lock-app/esp32/main/include/BoltLockManager.h deleted file mode 100644 index 654da89ece435b..00000000000000 --- a/examples/lock-app/esp32/main/include/BoltLockManager.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * 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. - */ - -#ifndef LOCK_MANAGER_H -#define LOCK_MANAGER_H - -#include -#include - -#include "AppEvent.h" - -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/timers.h" // provides FreeRTOS timer support - -class BoltLockManager -{ -public: - enum Action_t - { - LOCK_ACTION = 0, - UNLOCK_ACTION, - - INVALID_ACTION - } Action; - - enum State_t - { - kState_LockingInitiated = 0, - kState_LockingCompleted, - kState_UnlockingInitiated, - kState_UnlockingCompleted, - } State; - - CHIP_ERROR Init(); - bool IsUnlocked(); - void EnableAutoRelock(bool aOn); - void SetAutoLockDuration(uint32_t aDurationInSecs); - 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); - -private: - friend BoltLockManager & BoltLockMgr(void); - State_t mState; - - Callback_fn_initiated mActionInitiated_CB; - Callback_fn_completed mActionCompleted_CB; - - bool mAutoRelock; - uint32_t mAutoLockDuration; - bool mAutoLockTimerArmed; - - 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; -} - -#endif // LOCK_MANAGER_H diff --git a/examples/lock-app/esp32/main/include/Button.h b/examples/lock-app/esp32/main/include/Button.h index 53ae87884b2865..fc838f806a1061 100644 --- a/examples/lock-app/esp32/main/include/Button.h +++ b/examples/lock-app/esp32/main/include/Button.h @@ -17,11 +17,10 @@ #pragma once -#include - -#include "AppConfig.h" #include "AppTask.h" +#include "driver/gpio.h" #include "freertos/FreeRTOS.h" +#include class Button { diff --git a/examples/lock-app/esp32/main/include/AppConfig.h b/examples/platform/esp32/lock/AppConfig.h similarity index 98% rename from examples/lock-app/esp32/main/include/AppConfig.h rename to examples/platform/esp32/lock/AppConfig.h index ec883599d56627..e90d9a16336725 100644 --- a/examples/lock-app/esp32/main/include/AppConfig.h +++ b/examples/platform/esp32/lock/AppConfig.h @@ -17,8 +17,6 @@ #pragma once -#include "driver/gpio.h" - // ---- Lock Example App Config ---- #define APP_TASK_NAME "LOCK-APP" diff --git a/examples/all-clusters-app/esp32/main/include/AppEvent.h b/examples/platform/esp32/lock/AppEvent.h similarity index 90% rename from examples/all-clusters-app/esp32/main/include/AppEvent.h rename to examples/platform/esp32/lock/AppEvent.h index 26b97ab86d5018..114b228b078f01 100644 --- a/examples/all-clusters-app/esp32/main/include/AppEvent.h +++ b/examples/platform/esp32/lock/AppEvent.h @@ -29,6 +29,7 @@ struct AppEvent kEventType_Button = 0, kEventType_Timer, kEventType_Light, + kEventType_Lock, kEventType_Install, }; @@ -45,6 +46,11 @@ struct AppEvent { void * mContext; } mTimerEvent; + struct + { + uint8_t mAction; + int32_t mActor; + } mLockEvent; }; EventHandler mHandler; diff --git a/examples/platform/esp32/lock/BoltLockManager.cpp b/examples/platform/esp32/lock/BoltLockManager.cpp new file mode 100644 index 00000000000000..48c355f24eaca4 --- /dev/null +++ b/examples/platform/esp32/lock/BoltLockManager.cpp @@ -0,0 +1,836 @@ +/* + * + * 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 + +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 %u 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]; + mCredentials[userIndex][i].CredentialType = 1; + mCredentials[userIndex][i].CredentialIndex = i + 1; + } + + 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::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::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(); + + 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; +} diff --git a/examples/platform/esp32/lock/BoltLockManager.h b/examples/platform/esp32/lock/BoltLockManager.h new file mode 100644 index 00000000000000..5e3c2de9f35a9a --- /dev/null +++ b/examples/platform/esp32/lock/BoltLockManager.h @@ -0,0 +1,226 @@ +/* + * + * 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. + */ + +#pragma once +#include + +#include +#include + +#include "AppEvent.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/timers.h" // provides FreeRTOS timer support + +#include + +struct WeekDaysScheduleInfo +{ + DlScheduleStatus status; + EmberAfPluginDoorLockWeekDaySchedule schedule; +}; + +struct YearDayScheduleInfo +{ + DlScheduleStatus status; + EmberAfPluginDoorLockYearDaySchedule schedule; +}; + +struct HolidayScheduleInfo +{ + DlScheduleStatus status; + EmberAfPluginDoorLockHolidaySchedule schedule; +}; + +namespace ESP32DoorLock { +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; + +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 ESP32DoorLock + +using namespace ::chip; +using namespace ESP32DoorLock::ResourceRanges; + +class BoltLockManager +{ +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 InitLockState(); + CHIP_ERROR Init(chip::app::DataModel::Nullable state, + ESP32DoorLock::LockInitParams::LockParam lockParam); + bool IsUnlocked(); + void EnableAutoRelock(bool aOn); + void SetAutoLockDuration(uint32_t aDurationInSecs); + 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, OperationErrorEnum & err); + bool Unlock(chip::EndpointId endpointId, const Optional & pin, OperationErrorEnum & 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, UserStatusEnum userStatus, UserTypeEnum usertype, + CredentialRuleEnum credentialRule, const CredentialStruct * credentials, size_t totalCredentials); + + bool GetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, CredentialTypeEnum credentialType, + EmberAfPluginDoorLockCredentialInfo & credential); + + bool SetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + DlCredentialStatus credentialStatus, CredentialTypeEnum 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, + DaysMaskMap 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, OperatingModeEnum operatingMode); + + bool IsValidUserIndex(uint16_t userIndex); + bool IsValidCredentialIndex(uint16_t credentialIndex, CredentialTypeEnum 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, + OperationErrorEnum & err); + const char * lockStateToString(DlLockState lockState) const; + + bool ReadConfigValues(); + +private: + friend BoltLockManager & BoltLockMgr(); + State_t mState; + + Callback_fn_initiated mActionInitiated_CB; + Callback_fn_completed mActionCompleted_CB; + + bool mAutoRelock; + uint32_t mAutoLockDuration; + bool mAutoLockTimerArmed; + + void CancelTimer(void); + void StartTimer(uint32_t aTimeoutMs); + + static void TimerEventHandler(TimerHandle_t xTimer); + static void AutoReLockTimerEventHandler(AppEvent * aEvent); + static void ActuatorMovementTimerEventHandler(AppEvent * aEvent); + + EmberAfPluginDoorLockUserInfo mLockUsers[kMaxUsers]; + EmberAfPluginDoorLockCredentialInfo mLockCredentials[kMaxCredentials]; + WeekDaysScheduleInfo mWeekdaySchedule[kMaxUsers][kMaxWeekdaySchedulesPerUser]; + YearDayScheduleInfo mYeardaySchedule[kMaxUsers][kMaxYeardaySchedulesPerUser]; + HolidayScheduleInfo mHolidaySchedule[kMaxHolidaySchedules]; + + char mUserNames[ArraySize(mLockUsers)][DOOR_LOCK_MAX_USER_NAME_SIZE]; + uint8_t mCredentialData[kMaxCredentials][kMaxCredentialSize]; + CredentialStruct mCredentials[kMaxUsers][kMaxCredentialsPerUser]; + + static BoltLockManager sLock; + ESP32DoorLock::LockInitParams::LockParam LockParams; +}; + +inline BoltLockManager & BoltLockMgr() +{ + return BoltLockManager::sLock; +} diff --git a/examples/platform/esp32/lock/ZclCallbacks.cpp b/examples/platform/esp32/lock/ZclCallbacks.cpp new file mode 100644 index 00000000000000..02c2a2a75f58a0 --- /dev/null +++ b/examples/platform/esp32/lock/ZclCallbacks.cpp @@ -0,0 +1,142 @@ +/* + * + * 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. + */ + +/** + * @file + * This file implements the handler for data model messages. + */ + +//#include "AppConfig.h" +#include "BoltLockManager.h" +#include + +#include +#include +#include +#include + +using namespace ::chip::app::Clusters; +using namespace ::chip::DeviceLayer::Internal; + +/** @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. + * It is called exactly once for each endpoint where cluster is present. + * + * @param endpoint Ver.: always + * + */ +void emberAfDoorLockClusterInitCallback(EndpointId endpoint) {} + +bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, + OperationErrorEnum & err) +{ + ChipLogProgress(Zcl, "Door Lock App: Lock Command endpoint=%d", endpointId); + bool status = BoltLockMgr().Lock(endpointId, pinCode, err); + if (status == true) + { + BoltLockMgr().InitiateAction(AppEvent::kEventType_Lock, BoltLockManager::LOCK_ACTION); + } + return status; +} + +bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, + OperationErrorEnum & err) +{ + ChipLogProgress(Zcl, "Door Lock App: Unlock Command endpoint=%d", endpointId); + bool status = BoltLockMgr().Unlock(endpointId, pinCode, err); + if (status == true) + { + BoltLockMgr().InitiateAction(AppEvent::kEventType_Lock, BoltLockManager::UNLOCK_ACTION); + } + + return status; +} + +bool emberAfPluginDoorLockGetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, CredentialTypeEnum credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) +{ + return BoltLockMgr().GetCredential(endpointId, credentialIndex, credentialType, credential); +} + +bool emberAfPluginDoorLockSetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, + chip::FabricIndex modifier, DlCredentialStatus credentialStatus, + CredentialTypeEnum credentialType, const chip::ByteSpan & credentialData) +{ + return BoltLockMgr().SetCredential(endpointId, credentialIndex, creator, modifier, credentialStatus, credentialType, + credentialData); +} + +bool emberAfPluginDoorLockGetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) +{ + return BoltLockMgr().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, + UserStatusEnum userStatus, UserTypeEnum usertype, CredentialRuleEnum credentialRule, + const CredentialStruct * credentials, size_t totalCredentials) +{ + + return BoltLockMgr().SetUser(endpointId, userIndex, creator, modifier, userName, uniqueId, userStatus, usertype, credentialRule, + credentials, totalCredentials); +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + EmberAfPluginDoorLockWeekDaySchedule & schedule) +{ + return BoltLockMgr().GetWeekdaySchedule(endpointId, weekdayIndex, userIndex, schedule); +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + EmberAfPluginDoorLockYearDaySchedule & schedule) +{ + return BoltLockMgr().GetYeardaySchedule(endpointId, yearDayIndex, userIndex, schedule); +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t holidayIndex, + EmberAfPluginDoorLockHolidaySchedule & holidaySchedule) +{ + return BoltLockMgr().GetHolidaySchedule(endpointId, holidayIndex, holidaySchedule); +} + +DlStatus emberAfPluginDoorLockSetSchedule(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) +{ + return BoltLockMgr().SetWeekdaySchedule(endpointId, weekdayIndex, userIndex, status, daysMask, startHour, startMinute, endHour, + endMinute); +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + DlScheduleStatus status, uint32_t localStartTime, uint32_t localEndTime) +{ + return BoltLockMgr().SetYeardaySchedule(endpointId, yearDayIndex, userIndex, status, localStartTime, localEndTime); +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status, + uint32_t localStartTime, uint32_t localEndTime, OperatingModeEnum operatingMode) +{ + return BoltLockMgr().SetHolidaySchedule(endpointId, holidayIndex, status, localStartTime, localEndTime, operatingMode); +} + +void emberAfPluginDoorLockOnAutoRelock(chip::EndpointId endpointId) +{ + // Apply the relock state in the application control + BoltLockMgr().InitiateAction(AppEvent::kEventType_Lock, BoltLockManager::LOCK_ACTION); +} diff --git a/src/platform/ESP32/ESP32Config.cpp b/src/platform/ESP32/ESP32Config.cpp index 6d91c0d2566ed0..d47af60778e155 100644 --- a/src/platform/ESP32/ESP32Config.cpp +++ b/src/platform/ESP32/ESP32Config.cpp @@ -89,6 +89,14 @@ const ESP32Config::Key ESP32Config::kConfigKey_WiFiStationSecType = { kConfigNam const ESP32Config::Key ESP32Config::kConfigKey_RegulatoryLocation = { kConfigNamespace_ChipConfig, "reg-location" }; const ESP32Config::Key ESP32Config::kConfigKey_CountryCode = { kConfigNamespace_ChipConfig, "country-code" }; const ESP32Config::Key ESP32Config::kConfigKey_UniqueId = { kConfigNamespace_ChipConfig, "unique-id" }; +const ESP32Config::Key ESP32Config::kConfigKey_LockUser = { kConfigNamespace_ChipConfig, "lock-user" }; +const ESP32Config::Key ESP32Config::kConfigKey_Credential = { kConfigNamespace_ChipConfig, "credential" }; +const ESP32Config::Key ESP32Config::kConfigKey_LockUserName = { kConfigNamespace_ChipConfig, "lock-user-name" }; +const ESP32Config::Key ESP32Config::kConfigKey_CredentialData = { kConfigNamespace_ChipConfig, "credential-data" }; +const ESP32Config::Key ESP32Config::kConfigKey_UserCredentials = { kConfigNamespace_ChipConfig, "user-credential" }; +const ESP32Config::Key ESP32Config::kConfigKey_WeekDaySchedules = { kConfigNamespace_ChipConfig, "week-day-sched" }; +const ESP32Config::Key ESP32Config::kConfigKey_YearDaySchedules = { kConfigNamespace_ChipConfig, "year-day-sched" }; +const ESP32Config::Key ESP32Config::kConfigKey_HolidaySchedules = { kConfigNamespace_ChipConfig, "holiday-sched" }; // Keys stored in the Chip-counters namespace const ESP32Config::Key ESP32Config::kCounterKey_RebootCount = { kConfigNamespace_ChipCounters, "reboot-count" }; diff --git a/src/platform/ESP32/ESP32Config.h b/src/platform/ESP32/ESP32Config.h index 6a008dbb795cf7..c85303c2197555 100644 --- a/src/platform/ESP32/ESP32Config.h +++ b/src/platform/ESP32/ESP32Config.h @@ -91,6 +91,14 @@ class ESP32Config static const Key kConfigKey_RegulatoryLocation; static const Key kConfigKey_CountryCode; static const Key kConfigKey_UniqueId; + 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;