From c902b7227427e3b730eed9f78c26eb2896a070b8 Mon Sep 17 00:00:00 2001 From: Suryanshu Prakash Date: Tue, 10 Sep 2019 19:10:41 -0700 Subject: [PATCH 1/3] Minor tweak to LEDWidget --- main/LEDWidget.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main/LEDWidget.cpp b/main/LEDWidget.cpp index 38a696de..8049860c 100644 --- a/main/LEDWidget.cpp +++ b/main/LEDWidget.cpp @@ -28,7 +28,7 @@ void LEDWidget::Init(uint32_t gpioNum) mBlinkOnTimeMS = 0; mBlinkOffTimeMS = 0; mGPIONum = gpioNum; - mState = false; + mState = true; nrf_gpio_cfg_output(gpioNum); Set(false); @@ -75,6 +75,9 @@ void LEDWidget::Animate() void LEDWidget::DoSet(bool state) { + if (mState == state) + return; + mState = state; if (state) From b2ade456dd85585a7e8271273d758990edee6784 Mon Sep 17 00:00:00 2001 From: Suryanshu Prakash Date: Tue, 10 Sep 2019 19:11:17 -0700 Subject: [PATCH 2/3] Add new code-generated schema for OpenCloseTrait and SecurityOpenCloseTrait --- main/schema/OpenCloseTrait.cpp | 124 +++++++++++++++++ main/schema/SecurityOpenCloseTrait.cpp | 129 ++++++++++++++++++ .../nest/trait/detector/OpenCloseTrait.h | 105 ++++++++++++++ .../trait/security/SecurityOpenCloseTrait.h | 101 ++++++++++++++ 4 files changed, 459 insertions(+) create mode 100644 main/schema/OpenCloseTrait.cpp create mode 100644 main/schema/SecurityOpenCloseTrait.cpp create mode 100644 main/schema/include/nest/trait/detector/OpenCloseTrait.h create mode 100644 main/schema/include/nest/trait/security/SecurityOpenCloseTrait.h diff --git a/main/schema/OpenCloseTrait.cpp b/main/schema/OpenCloseTrait.cpp new file mode 100644 index 00000000..e17db909 --- /dev/null +++ b/main/schema/OpenCloseTrait.cpp @@ -0,0 +1,124 @@ + +/** + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * THIS FILE IS GENERATED. DO NOT MODIFY. + * + * SOURCE TEMPLATE: trait.cpp + * SOURCE PROTO: nest/trait/detector/open_close_trait.proto + * + */ + +#include + +namespace Schema { +namespace Nest { +namespace Trait { +namespace Detector { +namespace OpenCloseTrait { + +using namespace ::nl::Weave::Profiles::DataManagement; + +// +// Property Table +// + +const TraitSchemaEngine::PropertyInfo PropertyMap[] = { + { kPropertyHandle_Root, 1 }, // open_close_state + { kPropertyHandle_Root, 2 }, // first_observed_at + { kPropertyHandle_Root, 3 }, // first_observed_at_ms +}; + +// +// IsOptional Table +// + +uint8_t IsOptionalHandleBitfield[] = { + 0x6 +}; + +// +// IsNullable Table +// + +uint8_t IsNullableHandleBitfield[] = { + 0x6 +}; + +// +// IsEphemeral Table +// + +uint8_t IsEphemeralHandleBitfield[] = { + 0x6 +}; + +// +// Supported version +// +const ConstSchemaVersionRange traitVersion = { .mMinVersion = 1, .mMaxVersion = 2 }; + +// +// Schema +// + +const TraitSchemaEngine TraitSchema = { + { + kWeaveProfileId, + PropertyMap, + sizeof(PropertyMap) / sizeof(PropertyMap[0]), + 1, +#if (TDM_EXTENSION_SUPPORT) || (TDM_VERSIONING_SUPPORT) + 2, +#endif + NULL, + &IsOptionalHandleBitfield[0], + NULL, + &IsNullableHandleBitfield[0], + &IsEphemeralHandleBitfield[0], +#if (TDM_EXTENSION_SUPPORT) + NULL, +#endif +#if (TDM_VERSIONING_SUPPORT) + &traitVersion, +#endif + } +}; + + // + // Events + // + +const nl::FieldDescriptor OpenCloseEventFieldDescriptors[] = +{ + { + NULL, offsetof(OpenCloseEvent, openCloseState), SET_TYPE_AND_FLAGS(nl::SerializedFieldTypeInt32, 0), 1 + }, + + { + NULL, offsetof(OpenCloseEvent, priorOpenCloseState), SET_TYPE_AND_FLAGS(nl::SerializedFieldTypeInt32, 0), 2 + }, + +}; + +const nl::SchemaFieldDescriptor OpenCloseEvent::FieldSchema = +{ + .mNumFieldDescriptorElements = sizeof(OpenCloseEventFieldDescriptors)/sizeof(OpenCloseEventFieldDescriptors[0]), + .mFields = OpenCloseEventFieldDescriptors, + .mSize = sizeof(OpenCloseEvent) +}; +const nl::Weave::Profiles::DataManagement::EventSchema OpenCloseEvent::Schema = +{ + .mProfileId = kWeaveProfileId, + .mStructureType = 0x1, + .mImportance = nl::Weave::Profiles::DataManagement::Production, + .mDataSchemaVersion = 2, + .mMinCompatibleDataSchemaVersion = 1, +}; + +} // namespace OpenCloseTrait +} // namespace Detector +} // namespace Trait +} // namespace Nest +} // namespace Schema diff --git a/main/schema/SecurityOpenCloseTrait.cpp b/main/schema/SecurityOpenCloseTrait.cpp new file mode 100644 index 00000000..3d201f5e --- /dev/null +++ b/main/schema/SecurityOpenCloseTrait.cpp @@ -0,0 +1,129 @@ + +/** + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * THIS FILE IS GENERATED. DO NOT MODIFY. + * + * SOURCE TEMPLATE: trait.cpp + * SOURCE PROTO: nest/trait/security/security_open_close_trait.proto + * + */ + +#include + +namespace Schema { +namespace Nest { +namespace Trait { +namespace Security { +namespace SecurityOpenCloseTrait { + +using namespace ::nl::Weave::Profiles::DataManagement; + +// +// Property Table +// + +const TraitSchemaEngine::PropertyInfo PropertyMap[] = { + { kPropertyHandle_Root, 1 }, // open_close_state + { kPropertyHandle_Root, 2 }, // first_observed_at + { kPropertyHandle_Root, 3 }, // first_observed_at_ms + { kPropertyHandle_Root, 32 }, // bypass_requested +}; + +// +// IsOptional Table +// + +uint8_t IsOptionalHandleBitfield[] = { + 0x6 +}; + +// +// IsNullable Table +// + +uint8_t IsNullableHandleBitfield[] = { + 0x6 +}; + +// +// IsEphemeral Table +// + +uint8_t IsEphemeralHandleBitfield[] = { + 0x6 +}; + +// +// Supported version +// +const ConstSchemaVersionRange traitVersion = { .mMinVersion = 1, .mMaxVersion = 2 }; + +// +// Schema +// + +const TraitSchemaEngine TraitSchema = { + { + kWeaveProfileId, + PropertyMap, + sizeof(PropertyMap) / sizeof(PropertyMap[0]), + 1, +#if (TDM_EXTENSION_SUPPORT) || (TDM_VERSIONING_SUPPORT) + 5, +#endif + NULL, + &IsOptionalHandleBitfield[0], + NULL, + &IsNullableHandleBitfield[0], + &IsEphemeralHandleBitfield[0], +#if (TDM_EXTENSION_SUPPORT) + &Nest::Trait::Detector::OpenCloseTrait::TraitSchema, +#endif +#if (TDM_VERSIONING_SUPPORT) + &traitVersion, +#endif + } +}; + + // + // Events + // + +const nl::FieldDescriptor SecurityOpenCloseEventFieldDescriptors[] = +{ + { + NULL, offsetof(SecurityOpenCloseEvent, openCloseState), SET_TYPE_AND_FLAGS(nl::SerializedFieldTypeInt32, 0), 1 + }, + + { + NULL, offsetof(SecurityOpenCloseEvent, priorOpenCloseState), SET_TYPE_AND_FLAGS(nl::SerializedFieldTypeInt32, 0), 2 + }, + + { + NULL, offsetof(SecurityOpenCloseEvent, bypassRequested), SET_TYPE_AND_FLAGS(nl::SerializedFieldTypeBoolean, 0), 32 + }, + +}; + +const nl::SchemaFieldDescriptor SecurityOpenCloseEvent::FieldSchema = +{ + .mNumFieldDescriptorElements = sizeof(SecurityOpenCloseEventFieldDescriptors)/sizeof(SecurityOpenCloseEventFieldDescriptors[0]), + .mFields = SecurityOpenCloseEventFieldDescriptors, + .mSize = sizeof(SecurityOpenCloseEvent) +}; +const nl::Weave::Profiles::DataManagement::EventSchema SecurityOpenCloseEvent::Schema = +{ + .mProfileId = kWeaveProfileId, + .mStructureType = 0x1, + .mImportance = nl::Weave::Profiles::DataManagement::ProductionCritical, + .mDataSchemaVersion = 2, + .mMinCompatibleDataSchemaVersion = 1, +}; + +} // namespace SecurityOpenCloseTrait +} // namespace Security +} // namespace Trait +} // namespace Nest +} // namespace Schema diff --git a/main/schema/include/nest/trait/detector/OpenCloseTrait.h b/main/schema/include/nest/trait/detector/OpenCloseTrait.h new file mode 100644 index 00000000..f26bcce1 --- /dev/null +++ b/main/schema/include/nest/trait/detector/OpenCloseTrait.h @@ -0,0 +1,105 @@ + +/** + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * THIS FILE IS GENERATED. DO NOT MODIFY. + * + * SOURCE TEMPLATE: trait.cpp.h + * SOURCE PROTO: nest/trait/detector/open_close_trait.proto + * + */ +#ifndef _NEST_TRAIT_DETECTOR__OPEN_CLOSE_TRAIT_H_ +#define _NEST_TRAIT_DETECTOR__OPEN_CLOSE_TRAIT_H_ + +#include +#include + + + +namespace Schema { +namespace Nest { +namespace Trait { +namespace Detector { +namespace OpenCloseTrait { + +extern const nl::Weave::Profiles::DataManagement::TraitSchemaEngine TraitSchema; + +enum { + kWeaveProfileId = (0x235aU << 16) | 0x208U +}; + +// +// Properties +// + +enum { + kPropertyHandle_Root = 1, + + //---------------------------------------------------------------------------------------------------------------------------// + // Name IDL Type TLV Type Optional? Nullable? // + //---------------------------------------------------------------------------------------------------------------------------// + + // + // open_close_state OpenCloseState int NO NO + // + kPropertyHandle_OpenCloseState = 2, + + // + // first_observed_at google.protobuf.Timestamp uint32 seconds YES YES + // + kPropertyHandle_FirstObservedAt = 3, + + // + // first_observed_at_ms google.protobuf.Timestamp int64 millisecondsYES YES + // + kPropertyHandle_FirstObservedAtMs = 4, + + // + // Enum for last handle + // + kLastSchemaHandle = 4, +}; + +// +// Events +// +struct OpenCloseEvent +{ + int32_t openCloseState; + int32_t priorOpenCloseState; + + static const nl::SchemaFieldDescriptor FieldSchema; + + // Statically-known Event Struct Attributes: + enum { + kWeaveProfileId = (0x235aU << 16) | 0x208U, + kEventTypeId = 0x1U + }; + + static const nl::Weave::Profiles::DataManagement::EventSchema Schema; +}; + +struct OpenCloseEvent_array { + uint32_t num; + OpenCloseEvent *buf; +}; + + +// +// Enums +// + +enum OpenCloseState { + OPEN_CLOSE_STATE_CLOSED = 1, + OPEN_CLOSE_STATE_OPEN = 2, + OPEN_CLOSE_STATE_UNKNOWN = 3, + OPEN_CLOSE_STATE_INVALID_CALIBRATION = 4, +}; + +} // namespace OpenCloseTrait +} // namespace Detector +} // namespace Trait +} // namespace Nest +} // namespace Schema +#endif // _NEST_TRAIT_DETECTOR__OPEN_CLOSE_TRAIT_H_ diff --git a/main/schema/include/nest/trait/security/SecurityOpenCloseTrait.h b/main/schema/include/nest/trait/security/SecurityOpenCloseTrait.h new file mode 100644 index 00000000..a8057f17 --- /dev/null +++ b/main/schema/include/nest/trait/security/SecurityOpenCloseTrait.h @@ -0,0 +1,101 @@ + +/** + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * THIS FILE IS GENERATED. DO NOT MODIFY. + * + * SOURCE TEMPLATE: trait.cpp.h + * SOURCE PROTO: nest/trait/security/security_open_close_trait.proto + * + */ +#ifndef _NEST_TRAIT_SECURITY__SECURITY_OPEN_CLOSE_TRAIT_H_ +#define _NEST_TRAIT_SECURITY__SECURITY_OPEN_CLOSE_TRAIT_H_ + +#include +#include + +#include + + +namespace Schema { +namespace Nest { +namespace Trait { +namespace Security { +namespace SecurityOpenCloseTrait { + +extern const nl::Weave::Profiles::DataManagement::TraitSchemaEngine TraitSchema; + +enum { + kWeaveProfileId = (0x235aU << 16) | 0x20aU +}; + +// +// Properties +// + +enum { + kPropertyHandle_Root = 1, + + //---------------------------------------------------------------------------------------------------------------------------// + // Name IDL Type TLV Type Optional? Nullable? // + //---------------------------------------------------------------------------------------------------------------------------// + + // + // open_close_state nest.trait.detector.OpenCloseTrait.OpenCloseState int NO NO + // + kPropertyHandle_OpenCloseState = 2, + + // + // first_observed_at google.protobuf.Timestamp uint32 seconds YES YES + // + kPropertyHandle_FirstObservedAt = 3, + + // + // first_observed_at_ms google.protobuf.Timestamp int64 millisecondsYES YES + // + kPropertyHandle_FirstObservedAtMs = 4, + + // + // bypass_requested bool bool NO NO + // + kPropertyHandle_BypassRequested = 5, + + // + // Enum for last handle + // + kLastSchemaHandle = 5, +}; + +// +// Events +// +struct SecurityOpenCloseEvent +{ + int32_t openCloseState; + int32_t priorOpenCloseState; + bool bypassRequested; + + static const nl::SchemaFieldDescriptor FieldSchema; + + // Statically-known Event Struct Attributes: + enum { + kWeaveProfileId = (0x235aU << 16) | 0x20aU, + kEventTypeId = 0x1U + }; + + static const nl::Weave::Profiles::DataManagement::EventSchema Schema; +}; + +struct SecurityOpenCloseEvent_array { + uint32_t num; + SecurityOpenCloseEvent *buf; +}; + + +} // namespace SecurityOpenCloseTrait +} // namespace Security +} // namespace Trait +} // namespace Nest +} // namespace Schema +#endif // _NEST_TRAIT_SECURITY__SECURITY_OPEN_CLOSE_TRAIT_H_ From a585266a30ccd613742c9138508f8ec3474cf700 Mon Sep 17 00:00:00 2001 From: Suryanshu Prakash Date: Mon, 16 Sep 2019 12:50:57 -0700 Subject: [PATCH 3/3] Multiple Improvements and support for subscription to device -- Add AppEventDispatcher class which implements logic to add and remove event handlers and dispatch app events. It is meant to be inherited by other classes that require notifying interested parties of app events related to that class. -- Add ButtonManager which implements logic to detect long press, release from long press along with regular press/release and sends an app event to registered handlers containing information on the button press. -- Re-work the button handlers implemented in AppTask to now use the new ButtonManager events for long press detection. All old logic to detect long press for the function button was removed from AppTask. -- In Bolt Lock Manager, use two app timers for instead of one for auto relock timer and actuator movement timer since the nordic app timer APIs (which internally use FreeRTOS Timers) only post to the timer queue when the timer start and stop APIs are called. This is because a timer that is re-started right after being cancelled/stopped get's ignored since the event in the timer queue is not processed quickly enough. -- BoltLockManager now inherits AppEventDispatcher class. Interested parties can register handlers to receive BoltLock Change events such as kEventType_LockAction_Initiated, kEventType_LockAction_Completed. -- AppTask now registers a handler to receive lock events and drives the Lock LED based of the state in the receiveing events. -- Add ability to enable user selected mode when BUTTON3 is pressed briefly. This feature can be enabled by enabling SERVICELESS_DEVICE_DISCOVERY -- Add ability to send a multicast Device Identify Request over link local to devices in the fabric. The Device Identify Request is targeted to devices that are of the type vendor Id Google and product type OpenWeave SDK Open Close Resource with UserSelectedMode enabled. -- Added logic to make a 1 way subscription request to a discovered device for the SecurityOpenCloseTrait and display the subscription status on LED3. If SERVICELESS_DEVICE_DISCOVERY is enabled, the subscription is attempted right after the local device is discovered. Since the node id of the discovered device is not stored in persistent storage, the device will have to be rediscovered after a reboot for the subscription attempt to be made. -- Rework auto relock logic to incorporate lock when closed feature. If SERVICELESS_DEVICE_DISCOVERY is enabled, defaults are provided in app_config.h for door check enable, auto relock duration and auto relock enable. -- When SERVICELESS_DEVICE_DISCOVERY is enabled, the lock example will be configured as a Thread Router type instead of a sleepy end node. -- Other minor code improvements and refactor --- Makefile | 6 + main/AppEvent.cpp | 83 +++++ main/AppTask.cpp | 351 ++++++++---------- main/BoltLockManager.cpp | 190 ++++++++-- main/ButtonManager.cpp | 267 +++++++++++++ main/DeviceDiscovery.cpp | 139 +++++++ main/WDMFeature.cpp | 230 ++++++++---- main/include/AppEvent.h | 46 ++- main/include/AppTask.h | 22 +- main/include/BoltLockManager.h | 30 +- main/include/ButtonManager.h | 87 +++++ main/include/DeviceDiscovery.h | 64 ++++ main/include/WDMFeature.h | 46 ++- main/include/app_config.h | 92 ++++- main/main.cpp | 5 +- main/traits/BoltLockTraitDataSource.cpp | 35 +- .../traits/SecurityOpenCloseTraitDataSink.cpp | 93 +++++ main/traits/include/BoltLockTraitDataSource.h | 3 + .../include/SecurityOpenCloseTraitDataSink.h | 38 ++ 19 files changed, 1473 insertions(+), 354 deletions(-) create mode 100644 main/AppEvent.cpp create mode 100644 main/ButtonManager.cpp create mode 100644 main/DeviceDiscovery.cpp create mode 100644 main/include/ButtonManager.h create mode 100644 main/include/DeviceDiscovery.h create mode 100644 main/traits/SecurityOpenCloseTraitDataSink.cpp create mode 100644 main/traits/include/SecurityOpenCloseTraitDataSink.h diff --git a/Makefile b/Makefile index 9571a7c5..8e36b346 100644 --- a/Makefile +++ b/Makefile @@ -35,16 +35,22 @@ APP = openweave-nrf52840-lock-example SRCS = \ $(PROJECT_ROOT)/main/main.cpp \ + $(PROJECT_ROOT)/main/AppEvent.cpp \ $(PROJECT_ROOT)/main/AppTask.cpp \ + $(PROJECT_ROOT)/main/DeviceDiscovery.cpp \ $(PROJECT_ROOT)/main/LEDWidget.cpp \ $(PROJECT_ROOT)/main/BoltLockManager.cpp \ + $(PROJECT_ROOT)/main/ButtonManager.cpp \ $(PROJECT_ROOT)/main/WDMFeature.cpp \ $(PROJECT_ROOT)/main/traits/BoltLockTraitDataSource.cpp \ $(PROJECT_ROOT)/main/traits/BoltLockSettingsTraitDataSink.cpp \ $(PROJECT_ROOT)/main/traits/DeviceIdentityTraitDataSource.cpp \ + $(PROJECT_ROOT)/main/traits/SecurityOpenCloseTraitDataSink.cpp \ $(PROJECT_ROOT)/main/schema/BoltLockTrait.cpp \ $(PROJECT_ROOT)/main/schema/BoltLockSettingsTrait.cpp \ $(PROJECT_ROOT)/main/schema/DeviceIdentityTrait.cpp \ + $(PROJECT_ROOT)/main/schema/OpenCloseTrait.cpp \ + $(PROJECT_ROOT)/main/schema/SecurityOpenCloseTrait.cpp \ $(PROJECT_ROOT)/main/support/CXXExceptionStubs.cpp \ $(PROJECT_ROOT)/main/support/nRF5Sbrk.c \ $(PROJECT_ROOT)/main/support/FreeRTOSNewlibLockSupport.c \ diff --git a/main/AppEvent.cpp b/main/AppEvent.cpp new file mode 100644 index 00000000..7a7e05f7 --- /dev/null +++ b/main/AppEvent.cpp @@ -0,0 +1,83 @@ +/* + * + * 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 "AppEvent.h" + +int AppEventDispatcher::AddEventHandler(EventHandler Handler, intptr_t Arg = 0) +{ + int ret = NRF_SUCCESS; + EventHandler_t * eventHandler; + + // Do nothing if the event handler is already registered. + for (eventHandler = mAppEventHandlerList; eventHandler != NULL; eventHandler = eventHandler->Next) + { + if (eventHandler->Handler == Handler && eventHandler->Arg == Arg) + { + return ret; + } + } + + eventHandler = (EventHandler_t *)malloc(sizeof(EventHandler_t)); + if (eventHandler == NULL) + { + ret = NRF_ERROR_NO_MEM; + } + else + { + eventHandler->Next = mAppEventHandlerList; + eventHandler->Handler = Handler; + eventHandler->Arg = Arg; + + mAppEventHandlerList = eventHandler; + } + + return ret; +} + + +void AppEventDispatcher::RemoveEventHandler(EventHandler Handler, intptr_t Arg = 0) +{ + EventHandler_t ** eventHandlerIndirectPtr; + + for (eventHandlerIndirectPtr = &mAppEventHandlerList; *eventHandlerIndirectPtr != NULL; ) + { + EventHandler_t * eventHandler = (*eventHandlerIndirectPtr); + + if (eventHandler->Handler == Handler && eventHandler->Arg == Arg) + { + *eventHandlerIndirectPtr = eventHandler->Next; + free(eventHandler); + } + else + { + eventHandlerIndirectPtr = &eventHandler->Next; + } + } +} + + +void AppEventDispatcher::DispatchEvent(const AppEvent * event) +{ + // Dispatch the event to each of the registered application event handlers. + for (EventHandler_t * eventHandler = mAppEventHandlerList; + eventHandler != NULL; + eventHandler = eventHandler->Next) + { + eventHandler->Handler(event, eventHandler->Arg); + } +} \ No newline at end of file diff --git a/main/AppTask.cpp b/main/AppTask.cpp index 2bb24ebb..ed086487 100644 --- a/main/AppTask.cpp +++ b/main/AppTask.cpp @@ -18,6 +18,8 @@ #include "AppTask.h" #include "AppEvent.h" +#include "ButtonManager.h" +#include "DeviceDiscovery.h" #include "WDMFeature.h" #include "LEDWidget.h" @@ -34,13 +36,11 @@ #include #include -#include using namespace ::nl::Weave::TLV; using namespace ::nl::Weave::DeviceLayer; using namespace ::nl::Weave::Profiles::SoftwareUpdate; -#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 #define APP_TASK_STACK_SIZE (4096) #define APP_TASK_PRIORITY 2 @@ -53,9 +53,24 @@ static SemaphoreHandle_t sWeaveEventLock; static TaskHandle_t sAppTaskHandle; static QueueHandle_t sAppEventQueue; +/* Shows the system status (service provisioned, + * service subscription establishment) + */ static LEDWidget sStatusLED; + +/* Shows the lock/unlock state: + * Solid LED -> Locked + * Off -> Unlocked + * Blinking -> Actuator moving/In progress + */ static LEDWidget sLockLED; -static LEDWidget sUnusedLED; + +/* Shows the state of device subscription: + * Off: Not subscribed to another device + * Solid: Subscription established + */ +static LEDWidget sDeviceSubStatusLED; + static LEDWidget sUnusedLED_1; static bool sIsThreadProvisioned = false; @@ -63,6 +78,7 @@ static bool sIsThreadEnabled = false; static bool sIsThreadAttached = false; static bool sIsPairedToAccount = false; static bool sIsServiceSubscriptionEstablished = false; +static bool sIsDeviceSubscriptionEstablished = false; static bool sHaveBLEConnections = false; static bool sHaveServiceConnectivity = false; @@ -125,43 +141,36 @@ int AppTask::Init() sLockLED.Init(LOCK_STATE_LED); sLockLED.Set(!BoltLockMgr().IsUnlocked()); - sUnusedLED.Init(BSP_LED_2); + sDeviceSubStatusLED.Init(BSP_LED_2); sUnusedLED_1.Init(BSP_LED_3); - // Initialize buttons - static app_button_cfg_t sButtons[] = { - { LOCK_BUTTON, APP_BUTTON_ACTIVE_LOW, BUTTON_PULL, ButtonEventHandler }, - { FUNCTION_BUTTON, APP_BUTTON_ACTIVE_LOW, BUTTON_PULL, ButtonEventHandler }, - }; - - ret = app_button_init(sButtons, ARRAY_SIZE(sButtons), pdMS_TO_TICKS(FUNCTION_BUTTON_DEBOUNCE_PERIOD_MS)); + // Initialize Timer for Function Selection + ret = app_timer_init(); if (ret != NRF_SUCCESS) { - NRF_LOG_INFO("app_button_init() failed"); + NRF_LOG_INFO("app_timer_init() failed"); APP_ERROR_HANDLER(ret); } - ret = app_button_enable(); + ret = app_timer_create(&sFunctionTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEventHandler); if (ret != NRF_SUCCESS) { - NRF_LOG_INFO("app_button_enable() failed"); + NRF_LOG_INFO("app_timer_create() failed"); APP_ERROR_HANDLER(ret); } - // Initialize Timer for Function Selection - ret = app_timer_init(); + ret = ButtonMgr().Init(); if (ret != NRF_SUCCESS) { - NRF_LOG_INFO("app_timer_init() failed"); + NRF_LOG_INFO("ButtonMgr().Init() failed"); APP_ERROR_HANDLER(ret); } - ret = app_timer_create(&sFunctionTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEventHandler); - if (ret != NRF_SUCCESS) - { - NRF_LOG_INFO("app_timer_create() failed"); - APP_ERROR_HANDLER(ret); - } +#if SERVICELESS_DEVICE_DISCOVERY_ENABLED + ButtonMgr().AddButtonEventHandler(ATTENTION_BUTTON, AttentionButtonHandler); +#endif + + ButtonMgr().AddButtonEventHandler(FUNCTION_BUTTON, FunctionButtonHandler); ret = BoltLockMgr().Init(); if (ret != NRF_SUCCESS) @@ -170,7 +179,7 @@ int AppTask::Init() APP_ERROR_HANDLER(ret); } - BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted); + BoltLockMgr().AddEventHandler(LockActionEventHandler, 0); sWeaveEventLock = xSemaphoreCreateMutex(); if (sWeaveEventLock == NULL) @@ -198,6 +207,13 @@ int AppTask::Init() err = ConfigurationMgr().GetFirmwareRevision(currentFirmwareRev, sizeof(currentFirmwareRev), currentFirmwareRevLen); APP_ERROR_CHECK(err); + ret = DeviceDiscoveryFeature().Init(); + if (ret != WEAVE_NO_ERROR) + { + NRF_LOG_INFO("DeviceDiscoveryFeature().Init() failed"); + APP_ERROR_HANDLER(ret); + } + NRF_LOG_INFO("Current Firmware Version: %s", currentFirmwareRev); return ret; @@ -238,6 +254,7 @@ void AppTask::AppTaskMain(void * pvParameter) sIsPairedToAccount = ConfigurationMgr().IsPairedToAccount(); sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity(); sIsServiceSubscriptionEstablished = WdmFeature().AreServiceSubscriptionsEstablished(); + sIsDeviceSubscriptionEstablished = WdmFeature().IsDeviceSubscriptionEstablished(); PlatformMgr().UnlockWeaveStack(); } @@ -257,7 +274,7 @@ void AppTask::AppTaskMain(void * pvParameter) // rate of 100ms. // // Otherwise, blink the LED ON for a very short time. - if (sAppTask.mFunction != kFunction_FactoryReset) + if (!sAppTask.mFactoryResetTimerActive) { if (isFullyConnected) { @@ -275,174 +292,89 @@ void AppTask::AppTaskMain(void * pvParameter) { sStatusLED.Blink(50, 950); } + + // Show the status of device subscription. + sDeviceSubStatusLED.Set(sIsDeviceSubscriptionEstablished); } sStatusLED.Animate(); sLockLED.Animate(); - sUnusedLED.Animate(); + sDeviceSubStatusLED.Animate(); sUnusedLED_1.Animate(); + ButtonMgr().Poll(); } } -void AppTask::LockActionEventHandler(AppEvent * aEvent) +void AppTask::TimerEventHandler(void * p_context) { - bool initiated = false; - BoltLockManager::Action_t action; - int32_t actor; - ret_code_t ret = NRF_SUCCESS; - - if (aEvent->Type == AppEvent::kEventType_Lock) + if (sAppTask.mFactoryResetTimerActive) { - action = static_cast(aEvent->LockEvent.Action); - actor = aEvent->LockEvent.Actor; - } - else if (aEvent->Type == AppEvent::kEventType_Button) - { - if (BoltLockMgr().IsUnlocked()) - { - action = BoltLockManager::LOCK_ACTION; - } - else - { - action = BoltLockManager::UNLOCK_ACTION; - } - - actor = Schema::Weave::Trait::Security::BoltLockTrait::BOLT_LOCK_ACTOR_METHOD_PHYSICAL; - } - else - { - ret = NRF_ERROR_NULL; - } - - if (ret == NRF_SUCCESS) - { - initiated = BoltLockMgr().InitiateAction(actor, action); - - if (!initiated) - { - NRF_LOG_INFO("Action is already in progress or active."); - } + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.TimerEvent.Context = p_context; + event.Handler = FactoryResetTriggerTimerExpired; + sAppTask.PostEvent(&event); } } -void AppTask::ButtonEventHandler(uint8_t pin_no, uint8_t button_action) +void AppTask::FactoryResetTriggerTimerExpired(const AppEvent * aEvent) { - if (pin_no != LOCK_BUTTON && pin_no != FUNCTION_BUTTON) - { - return; - } - - AppEvent button_event; - button_event.Type = AppEvent::kEventType_Button; - button_event.ButtonEvent.PinNo = pin_no; - button_event.ButtonEvent.Action = button_action; + sAppTask.mFactoryResetTimerActive = false; - if (pin_no == LOCK_BUTTON && button_action == APP_BUTTON_PUSH) - { - button_event.Handler = LockActionEventHandler; - } - else if (pin_no == FUNCTION_BUTTON) - { - button_event.Handler = FunctionHandler; - } - - sAppTask.PostEvent(&button_event); + // Actually trigger Factory Reset + nl::Weave::DeviceLayer::ConfigurationMgr().InitiateFactoryReset(); } -void AppTask::TimerEventHandler(void * p_context) +void AppTask::FunctionButtonHandler(const AppEvent * aEvent) { - AppEvent event; - event.Type = AppEvent::kEventType_Timer; - event.TimerEvent.Context = p_context; - event.Handler = FunctionTimerEventHandler; - sAppTask.PostEvent(&event); -} - -void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) -{ - if (aEvent->Type != AppEvent::kEventType_Timer) - return; + // To trigger software update: press the FUNCTION_BUTTON button briefly (< LONG_PRESS_TIMEOUT_MS) + // To initiate factory reset: press the FUNCTION_BUTTON for LONG_PRESS_TIMEOUT_MS + FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + // All LEDs start blinking after LONG_PRESS_TIMEOUT_MS to signal factory reset has been initiated. + // To cancel factory reset: release the FUNCTION_BUTTON once all LEDs start blinking within the + // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT - // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, initiate factory reset - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) + if (aEvent->ButtonEvent.Action == ButtonManager::kButtonAction_Release) + { + if (SoftwareUpdateMgr().IsInProgress()) + { + NRF_LOG_INFO("Canceling In Progress Software Update"); + SoftwareUpdateMgr().Abort(); + } + else + { + NRF_LOG_INFO("Manual Software Update Triggered"); + SoftwareUpdateMgr().CheckNow(); + } + } + else if (aEvent->ButtonEvent.Action == ButtonManager::kButtonAction_LongPress) { - NRF_LOG_INFO("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_TRIGGER_TIMEOUT); + NRF_LOG_INFO("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); sUnusedLED_1.Set(false); - sUnusedLED.Set(false); + sDeviceSubStatusLED.Set(false); sStatusLED.Blink(500); sLockLED.Blink(500); - sUnusedLED.Blink(500); + sDeviceSubStatusLED.Blink(500); sUnusedLED_1.Blink(500); } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - // Actually trigger Factory Reset - nl::Weave::DeviceLayer::ConfigurationMgr().InitiateFactoryReset(); - } -} - -void AppTask::FunctionHandler(AppEvent * aEvent) -{ - if (aEvent->ButtonEvent.PinNo != FUNCTION_BUTTON) - return; - - // To trigger software update: press the FUNCTION_BUTTON button briefly (< FACTORY_RESET_TRIGGER_TIMEOUT) - // To initiate factory reset: press the FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + FACTORY_RESET_CANCEL_WINDOW_TIMEOUT - // All LEDs start blinking after FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. - // To cancel factory reset: release the FUNCTION_BUTTON once all LEDs start blinking within the - // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT - if (aEvent->ButtonEvent.Action == APP_BUTTON_PUSH) - { - if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) - { - sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT); - - sAppTask.mFunction = kFunction_SoftwareUpdate; - } - } - else + else if (aEvent->ButtonEvent.Action == ButtonManager::kButtonAction_LongPress_Release && sAppTask.mFactoryResetTimerActive) { - // If the button was released before factory reset got initiated, trigger a software update. - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) - { - sAppTask.CancelTimer(); - - if (SoftwareUpdateMgr().IsInProgress()) - { - NRF_LOG_INFO("Canceling In Progress Software Update"); - SoftwareUpdateMgr().Abort(); - } - else - { - NRF_LOG_INFO("Manual Software Update Triggered"); - SoftwareUpdateMgr().CheckNow(); - } - } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - sUnusedLED.Set(false); - sUnusedLED_1.Set(false); - - // Set lock status LED back to show state of lock. - sLockLED.Set(!BoltLockMgr().IsUnlocked()); + sDeviceSubStatusLED.Set(false); + sUnusedLED_1.Set(false); - sAppTask.CancelTimer(); + // Set lock status LED back to show state of lock. + sLockLED.Set(!BoltLockMgr().IsUnlocked()); - // Change the function to none selected since factory reset has been canceled. - sAppTask.mFunction = kFunction_NoneSelected; + sAppTask.CancelTimer(); - NRF_LOG_INFO("Factory Reset has been Canceled"); - } + NRF_LOG_INFO("Factory Reset has been Canceled"); } } @@ -457,7 +389,7 @@ void AppTask::CancelTimer() APP_ERROR_HANDLER(ret); } - mFunctionTimerActive = false; + mFactoryResetTimerActive = false; } void AppTask::StartTimer(uint32_t aTimeoutInMs) @@ -467,64 +399,45 @@ void AppTask::StartTimer(uint32_t aTimeoutInMs) ret = app_timer_start(sFunctionTimer, pdMS_TO_TICKS(aTimeoutInMs), this); if (ret != NRF_SUCCESS) { - NRF_LOG_INFO("app_timer_start() failed"); - APP_ERROR_HANDLER(ret); + NRF_LOG_INFO("app_timer_start() failed") +; APP_ERROR_HANDLER(ret); } - mFunctionTimerActive = true; + mFactoryResetTimerActive = true; } -void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor) +void AppTask::LockActionEventHandler(const AppEvent * aEvent, intptr_t Arg) { - // 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) - { - WdmFeature().GetBoltLockTraitDataSource().InitiateLock(aActor); - NRF_LOG_INFO("Lock Action has been initiated") - } - else if (aAction == BoltLockManager::UNLOCK_ACTION) + if (aEvent->Type == AppEvent::kEventType_LockAction_Initiated) { - WdmFeature().GetBoltLockTraitDataSource().InitiateUnlock(aActor); - NRF_LOG_INFO("Unlock Action has been initiated") - } - - 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) - { - NRF_LOG_INFO("Lock Action has been completed") - - WdmFeature().GetBoltLockTraitDataSource().LockingSuccessful(); + if (aEvent->LockAction_Initiated.Action == BoltLockManager::LOCK_ACTION) + { + NRF_LOG_INFO("Lock Action has been initiated") + } + else if (aEvent->LockAction_Initiated.Action == BoltLockManager::UNLOCK_ACTION) + { + NRF_LOG_INFO("Unlock Action has been initiated") + } - sLockLED.Set(true); + sLockLED.Blink(50, 50); } - else if (aAction == BoltLockManager::UNLOCK_ACTION) + else if (aEvent->Type == AppEvent::kEventType_LockAction_Completed) { - NRF_LOG_INFO("Unlock Action has been completed") + if (aEvent->LockAction_Completed.Action == BoltLockManager::LOCK_ACTION) + { + NRF_LOG_INFO("Lock Action has been completed") - WdmFeature().GetBoltLockTraitDataSource().UnlockingSuccessful(); + sLockLED.Set(true); + } + else if (aEvent->LockAction_Completed.Action == BoltLockManager::UNLOCK_ACTION) + { + NRF_LOG_INFO("Unlock Action has been completed") - sLockLED.Set(false); + sLockLED.Set(false); + } } } -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; - PostEvent(&event); -} - void AppTask::PostEvent(const AppEvent * aEvent) { if (sAppEventQueue != NULL) @@ -544,11 +457,19 @@ void AppTask::DispatchEvent(AppEvent * aEvent) } else { - NRF_LOG_INFO("Event received with no handler. Dropping event."); + if (aEvent->Type == AppEvent::kEventType_LocalDeviceDiscovered) + { + NRF_LOG_INFO("Local Device Discovered Event received. Attempting Device Subscription."); + PlatformMgr().ScheduleWork(WdmFeature().InitiateSubscriptionToDevice); + } + else + { + NRF_LOG_INFO("Event received with no handler. Dropping event."); + } } } -void AppTask::InstallEventHandler(AppEvent * aEvent) +void AppTask::InstallEventHandler(const AppEvent * aEvent) { SoftwareUpdateMgr().ImageInstallComplete(WEAVE_NO_ERROR); } @@ -736,3 +657,27 @@ void AppTask::HandleSoftwareUpdateEvent(void *apAppState, break; } } + +#if SERVICELESS_DEVICE_DISCOVERY_ENABLED + +void AppTask::AttentionButtonHandler(const AppEvent * aEvent) +{ + if (aEvent->ButtonEvent.Action == ButtonManager::kButtonAction_Release) + { + ConnectivityMgr().SetUserSelectedMode(true); + } + else if (aEvent->ButtonEvent.Action == ButtonManager::kButtonAction_LongPress) + { + PlatformMgr().ScheduleWork(sAppTask.SeachForLocalSDKOpenCloseSensors); + } +} + +void AppTask::SeachForLocalSDKOpenCloseSensors(intptr_t arg) +{ + /* We are only interested in other devices that have user selected mode enabled and are of the type + * SDK Open Close Example. + */ + DeviceDiscoveryFeature().SearchForUserSelectedModeDevices(WEAVE_DEVICE_CONFIG_DEVICE_VENDOR_ID, 0xFE02); +} + +#endif // SERVICELESS_DEVICE_DISCOVERY_ENABLED \ No newline at end of file diff --git a/main/BoltLockManager.cpp b/main/BoltLockManager.cpp index a1b2f6f0..ccac7334 100644 --- a/main/BoltLockManager.cpp +++ b/main/BoltLockManager.cpp @@ -17,6 +17,7 @@ */ #include "BoltLockManager.h" +#include "WDMFeature.h" #include "app_config.h" #include "app_timer.h" @@ -29,13 +30,21 @@ BoltLockManager BoltLockManager::sLock; -APP_TIMER_DEF(sLockTimer); +APP_TIMER_DEF(sLockActuationTimer); +APP_TIMER_DEF(sAutoRelockTimer); int BoltLockManager::Init() { ret_code_t ret; - ret = app_timer_create(&sLockTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEventHandler); + ret = app_timer_create(&sLockActuationTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEventHandler); + if (ret != NRF_SUCCESS) + { + NRF_LOG_INFO("app_timer_create() failed"); + APP_ERROR_HANDLER(ret); + } + + ret = app_timer_create(&sAutoRelockTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEventHandler); if (ret != NRF_SUCCESS) { NRF_LOG_INFO("app_timer_create() failed"); @@ -44,19 +53,21 @@ int BoltLockManager::Init() mState = kState_LockingCompleted; mAutoLockTimerArmed = false; - mAutoRelock = false; + +#if WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING + mAutoRelockEnabled = AUTO_RELOCK_ENABLED_DEFAULT; + mAutoLockDuration = AUTO_LOCK_DURATION_SECS_DEFAULT; + mDoorCheckEnabled = DOOR_CHECK_ENABLE_DEFAULT; +#else + mAutoRelockEnabled = false; mAutoLockDuration = 0; + mDoorCheckEnabled = false; +#endif + ButtonMgr().AddButtonEventHandler(LOCK_BUTTON, LockActionEventHandler); return ret; } -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) ? true : false; @@ -67,9 +78,14 @@ bool BoltLockManager::IsUnlocked() return (mState == kState_UnlockingCompleted) ? true : false; } -void BoltLockManager::EnableAutoRelock(bool aOn) +void BoltLockManager::EnableAutoRelock(bool aEnable) { - mAutoRelock = aOn; + mAutoRelockEnabled = aEnable; +} + +void BoltLockManager::EnableDoorCheck(bool aEnable) +{ + mDoorCheckEnabled = aEnable; } void BoltLockManager::SetAutoLockDuration(uint32_t aDurationInSecs) @@ -102,29 +118,30 @@ bool BoltLockManager::InitiateAction(int32_t aActor, Action_t aAction) { // If auto lock timer has been armed and someone initiates locking, // cancel the timer and continue as normal. - mAutoLockTimerArmed = false; + CancelTimer(sAutoRelockTimer); - CancelTimer(); + mAutoLockTimerArmed = false; } - StartTimer(ACTUATOR_MOVEMENT_PERIOS_MS); + StartTimer(sLockActuationTimer, 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); - } + AppEvent event; + event.Type = AppEvent::kEventType_LockAction_Initiated; + event.LockAction_Initiated.Action = aAction; + event.LockAction_Initiated.Actor = aActor; + DispatchEvent(&event); } return action_initiated; } -void BoltLockManager::StartTimer(uint32_t aTimeoutMs) +void BoltLockManager::StartTimer(app_timer_id_t aTimer, uint32_t aTimeoutMs) { ret_code_t ret; - ret = app_timer_start(sLockTimer, pdMS_TO_TICKS(aTimeoutMs), this); + ret = app_timer_start(aTimer, pdMS_TO_TICKS(aTimeoutMs), this); if (ret != NRF_SUCCESS) { NRF_LOG_INFO("app_timer_start() failed"); @@ -132,10 +149,10 @@ void BoltLockManager::StartTimer(uint32_t aTimeoutMs) } } -void BoltLockManager::CancelTimer(void) +void BoltLockManager::CancelTimer(app_timer_id_t aTimer) { ret_code_t ret; - ret = app_timer_stop(sLockTimer); + ret = app_timer_stop(aTimer); if (ret != NRF_SUCCESS) { NRF_LOG_INFO("app_timer_stop() failed"); @@ -164,7 +181,7 @@ void BoltLockManager::TimerEventHandler(void * p_context) GetAppTask().PostEvent(&event); } -void BoltLockManager::AutoReLockTimerEventHandler(AppEvent * aEvent) +void BoltLockManager::AutoReLockTimerEventHandler(const AppEvent * aEvent) { BoltLockManager * lock = static_cast(aEvent->TimerEvent.Context); int32_t actor = Schema::Weave::Trait::Security::BoltLockTrait::BOLT_LOCK_ACTOR_METHOD_LOCAL_IMPLICIT; @@ -182,7 +199,7 @@ void BoltLockManager::AutoReLockTimerEventHandler(AppEvent * aEvent) lock->InitiateAction(actor, LOCK_ACTION); } -void BoltLockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) +void BoltLockManager::ActuatorMovementTimerEventHandler(const AppEvent * aEvent) { Action_t actionCompleted = INVALID_ACTION; @@ -201,19 +218,128 @@ void BoltLockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) if (actionCompleted != INVALID_ACTION) { - if (lock->mActionCompleted_CB) + AppEvent event; + event.Type = AppEvent::kEventType_LockAction_Completed; + event.LockAction_Completed.Action = actionCompleted; + lock->DispatchEvent(&event); + + // If we are about to finish unlocking, evaluate the auto lock + // state. + if (actionCompleted == UNLOCK_ACTION) + lock->EvaluateAutoRelockState(NULL); + } +} + +void BoltLockManager::PostLockActionRequest(int32_t aActor, Action_t aAction) +{ + AppEvent event; + + event.Type = AppEvent::kEventType_LockAction_Requested; + event.LockAction_Requested.Actor = aActor; + event.LockAction_Requested.Action = aAction; + event.Handler = LockActionEventHandler; + + GetAppTask().PostEvent(&event); +} + +void BoltLockManager::LockActionEventHandler(const AppEvent * aEvent) +{ + bool initiated = false; + BoltLockManager::Action_t action; + int32_t actor; + ret_code_t ret = NRF_SUCCESS; + + if (aEvent->Type == AppEvent::kEventType_LockAction_Requested) + { + action = static_cast(aEvent->LockAction_Requested.Action); + actor = aEvent->LockAction_Requested.Actor; + } + else if (aEvent->Type == AppEvent::kEventType_Button && aEvent->ButtonEvent.Action == ButtonManager::kButtonAction_Release) + { + if (BoltLockMgr().IsUnlocked()) + { + action = BoltLockManager::LOCK_ACTION; + } + else + { + action = BoltLockManager::UNLOCK_ACTION; + } + + actor = Schema::Weave::Trait::Security::BoltLockTrait::BOLT_LOCK_ACTOR_METHOD_PHYSICAL; + } + else + { + ret = NRF_ERROR_NULL; + } + + if (ret == NRF_SUCCESS) + { + initiated = BoltLockMgr().InitiateAction(actor, action); + + if (!initiated) { - lock->mActionCompleted_CB(actionCompleted); + NRF_LOG_INFO("Action is already in progress or active."); } + } +} - if (lock->mAutoRelock && actionCompleted == UNLOCK_ACTION) +void BoltLockManager::EvaluateAutoRelockState(const AppEvent * aEvent) +{ + BoltLockManager & lock = static_cast(BoltLockMgr()); + + // Check to see if auto relock is enabled and that the door is unlocked + if (lock.mAutoRelockEnabled && lock.mState == kState_UnlockingCompleted) + { + // Check to see that the subscription to device is valid + if (lock.mDoorCheckEnabled && WdmFeature().IsDeviceSubscriptionEstablished()) { - // Start the timer for auto relock - lock->StartTimer(lock->mAutoLockDuration * 1000); + // Check to see if door is closed and that a timer is already not armed + if (!WdmFeature().GetSecurityOpenCloseTraitDataSink().IsOpen()) + { + // Check to see if the timer is already not armed. + if (!lock.mAutoLockTimerArmed) + { + // If device subscription is established and door is closed + // Start the timer for auto relock + lock.StartTimer(sAutoRelockTimer, lock.mAutoLockDuration * 1000); + + lock.mAutoLockTimerArmed = true; + + NRF_LOG_INFO("Associated O/C Sensor State is CLOSED. Auto Re-lock will be triggered " \ + "in %u seconds", lock.mAutoLockDuration); + } + } + else + { + // If the state is open then we cancel the autolock timer. + lock.CancelTimer(sAutoRelockTimer); + lock.mAutoLockTimerArmed = false; + + NRF_LOG_INFO("Associated O/C Sensor State is OPEN. Auto Re-lock suspended."); + } + } + else + { + // If subscription to device has not been established or if door check is disabled, + // start the auto relock timer only if it hadn't already been started. + if (!lock.mAutoLockTimerArmed) + { + // If device subscription is not valid or not established + lock.StartTimer(sAutoRelockTimer, lock.mAutoLockDuration * 1000); - lock->mAutoLockTimerArmed = true; + lock.mAutoLockTimerArmed = true; - NRF_LOG_INFO("Auto Re-lock enabled. Will be triggered in %u seconds", lock->mAutoLockDuration); + NRF_LOG_INFO("Auto Re-lock will be triggered in %u seconds", lock.mAutoLockDuration); + } } } } + +void BoltLockManager::EvaluateChange(void) +{ + AppEvent event; + event.Type = AppEvent::kEventType_AutoReLock_Evaluate; + event.Handler = EvaluateAutoRelockState; + + GetAppTask().PostEvent(&event); +} \ No newline at end of file diff --git a/main/ButtonManager.cpp b/main/ButtonManager.cpp new file mode 100644 index 00000000..2929a952 --- /dev/null +++ b/main/ButtonManager.cpp @@ -0,0 +1,267 @@ +/* + * + * 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 "AppEvent.h" +#include "AppTask.h" +#include "ButtonManager.h" + +#include "app_config.h" +#include "app_timer.h" +#include "app_button.h" +#include "boards.h" + +#include "nrf_log.h" + +#include + +ButtonManager ButtonManager::sButtonManager; + +int ButtonManager::Init(void) +{ + int ret = NRF_SUCCESS; + + mLongPressActivatedFlag = 0; + mLongPressTimeoutMs = LONG_PRESS_TIMEOUT_MS; + mAppEventHandlerList = NULL; + memset(mLongPressButtonTimerExpirationMs, 0, sizeof(mLongPressButtonTimerExpirationMs)); + + // Initialize buttons + static app_button_cfg_t sButtons[] = { + { BUTTON_1, APP_BUTTON_ACTIVE_LOW, BUTTON_PULL, ButtonEventHandler }, + { BUTTON_2, APP_BUTTON_ACTIVE_LOW, BUTTON_PULL, ButtonEventHandler }, + { BUTTON_3, APP_BUTTON_ACTIVE_LOW, BUTTON_PULL, ButtonEventHandler }, + { BUTTON_4, APP_BUTTON_ACTIVE_LOW, BUTTON_PULL, ButtonEventHandler }, + }; + + ret = app_button_init(sButtons, ARRAY_SIZE(sButtons), pdMS_TO_TICKS(FUNCTION_BUTTON_DEBOUNCE_PERIOD_MS)); + if (ret != NRF_SUCCESS) + { + NRF_LOG_INFO("app_button_init() failed"); + VerifyOrExit(ret == NRF_SUCCESS, ); + } + + ret = app_button_enable(); + if (ret != NRF_SUCCESS) + { + NRF_LOG_INFO("app_button_enable() failed"); + VerifyOrExit(ret == NRF_SUCCESS, ); + } + +exit: + return ret; +} + +bool ButtonManager::GetButtonIdFromPinNo(uint8_t aPinNo, uint8_t & aButtonId) +{ + for (uint8_t index = 0; index < ARRAY_SIZE(mButtonIndex); index++) + { + if (mButtonIndex[index] == aPinNo) + { + aButtonId = index; + return true; + } + } + + return false; +} + +void ButtonManager::SetLongPressTimeoutMs(uint16_t aLongPressTimeoutMs) +{ + mLongPressTimeoutMs = aLongPressTimeoutMs; +} + +bool ButtonManager::IsLongPressActive(uint8_t button_id) +{ + return mLongPressActivatedFlag & (1 << button_id); +} + +void ButtonManager::StartLongPressTimer(uint8_t button_id) +{ + uint32_t now = ::nl::Weave::System::Platform::Layer::GetClock_MonotonicMS(); + + mLongPressButtonTimerExpirationMs[button_id] = now + mLongPressTimeoutMs; +} + +void ButtonManager::CancelLongPressTimer(uint8_t button_id) +{ + mLongPressButtonTimerExpirationMs[button_id] = 0; +} + +void ButtonManager::ButtonEventHandler(uint8_t pin_no, uint8_t button_action) +{ + ButtonAction action = kButtonAction_Press; + AppEvent event; + + event.Type = AppEvent::kEventType_Button; + event.ButtonEvent.PinNo = pin_no; + + if (button_action == APP_BUTTON_PUSH) + { + action = kButtonAction_Press; + } + else if (button_action == APP_BUTTON_RELEASE) + { + action = kButtonAction_Release; + } + + event.ButtonEvent.Action = action; + event.Handler = ButtonManagerEventHandler; + + GetAppTask().PostEvent(&event); +} + +void ButtonManager::ButtonManagerEventHandler(const AppEvent * aButtonEvent) +{ + uint8_t button_id; + ButtonManager & _this = static_cast(ButtonMgr()); + + _this.GetButtonIdFromPinNo(aButtonEvent->ButtonEvent.PinNo, button_id); + + AppEvent event; + event.Type = AppEvent::kEventType_Button; + event.ButtonEvent.PinNo = aButtonEvent->ButtonEvent.PinNo; + event.Handler = NULL; + + if (aButtonEvent->ButtonEvent.Action == kButtonAction_Press) + { + // NRF_LOG_INFO("Button[%u] Press Detected.", button_id); + + /* Notify interested parties that the button was just pressed. + * Also start a timer to detect if the user intends to do a long press. + */ + _this.StartLongPressTimer(button_id); + + event.ButtonEvent.Action = kButtonAction_Press; + _this.DispatchButtonEvent(&event); + } + else if (aButtonEvent->ButtonEvent.Action == kButtonAction_Release) + { + if (_this.IsLongPressActive(button_id)) + { + // NRF_LOG_INFO("Button[%u] Release from Long Press Detected.", button_id); + + _this.mLongPressActivatedFlag &= ~(1 << button_id); + + /* Notify interested parties that the button was released after a long press.*/ + event.ButtonEvent.Action = kButtonAction_LongPress_Release; + _this.DispatchButtonEvent(&event); + } + else if (_this.mLongPressButtonTimerExpirationMs[button_id] != 0) + { + // NRF_LOG_INFO("Button[%u] Release Detected.", button_id); + + _this.CancelLongPressTimer(button_id); + + /* Notify interested parties that the button was released after a short press + * since the user released the button before the long press timer expired.*/ + event.ButtonEvent.Action = kButtonAction_Release; + _this.DispatchButtonEvent(&event); + } + } +} + +void ButtonManager::LongPressActivated(uint8_t button_id) +{ + AppEvent event; + event.Type = AppEvent::kEventType_Button; + event.ButtonEvent.PinNo = mButtonIndex[button_id]; + event.ButtonEvent.Action = kButtonAction_LongPress; + event.Handler = NULL; + + // NRF_LOG_INFO("Button[%u] Long Press Detected.", button_id); + + CancelLongPressTimer(button_id); + + mLongPressActivatedFlag |= (1 << button_id); + + DispatchButtonEvent(&event); +} + +void ButtonManager::Poll(void) +{ + uint32_t now = ::nl::Weave::System::Platform::Layer::GetClock_MonotonicMS(); + + for (uint8_t button_idx = 0; button_idx < 4; button_idx++) + { + if (mLongPressButtonTimerExpirationMs[button_idx] != 0 && now >= mLongPressButtonTimerExpirationMs[button_idx]) + { + LongPressActivated(button_idx); + } + } +} + +int ButtonManager::AddButtonEventHandler(uint8_t aButtonPinNo, ButtonEventHandlerFunct handler) +{ + int ret = NRF_SUCCESS; + ButtonEventHandler_t * eventHandler; + + // Do nothing if the event handler is already registered. + for (eventHandler = mAppEventHandlerList; eventHandler != NULL; eventHandler = eventHandler->Next) + { + if (eventHandler->PinNo == aButtonPinNo && eventHandler->Handler == handler) + { + ExitNow(); + } + } + + eventHandler = (ButtonEventHandler_t *)malloc(sizeof(ButtonEventHandler_t)); + VerifyOrExit(eventHandler != NULL, ret = NRF_ERROR_NO_MEM); + + eventHandler->Next = mAppEventHandlerList; + eventHandler->PinNo = aButtonPinNo; + eventHandler->Handler = handler; + + mAppEventHandlerList = eventHandler; + +exit: + return ret; +} + +void ButtonManager::RemoveEventHandler(uint8_t aButtonPinNo, ButtonEventHandlerFunct handler) +{ + ButtonEventHandler_t ** eventHandlerIndirectPtr; + + for (eventHandlerIndirectPtr = &mAppEventHandlerList; *eventHandlerIndirectPtr != NULL; ) + { + ButtonEventHandler_t * eventHandler = (*eventHandlerIndirectPtr); + + if (eventHandler->PinNo == aButtonPinNo && eventHandler->Handler == handler) + { + *eventHandlerIndirectPtr = eventHandler->Next; + free(eventHandler); + } + else + { + eventHandlerIndirectPtr = &eventHandler->Next; + } + } +} + +void ButtonManager::DispatchButtonEvent(const AppEvent * event) +{ + // Dispatch the event to each of the registered application event handlers. + for (ButtonEventHandler_t * eventHandler = mAppEventHandlerList; + eventHandler != NULL; + eventHandler = eventHandler->Next) + { + if (eventHandler->PinNo == event->ButtonEvent.PinNo) + { + eventHandler->Handler(event); + } + } +} \ No newline at end of file diff --git a/main/DeviceDiscovery.cpp b/main/DeviceDiscovery.cpp new file mode 100644 index 00000000..5f3ab751 --- /dev/null +++ b/main/DeviceDiscovery.cpp @@ -0,0 +1,139 @@ +/* + * + * 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 + +#include "AppEvent.h" +#include "AppTask.h" + +#include "nrf_log.h" + +#include + +using namespace ::nl::Weave; +using namespace ::nl::Weave::DeviceLayer; +using namespace ::nl::Weave::Profiles::DeviceDescription; + +static DeviceDescriptionClient sDeviceDescriptionClient; +DeviceDiscovery DeviceDiscovery::sDeviceDiscoveryFeature; + +WEAVE_ERROR DeviceDiscovery::Init(void) +{ + WEAVE_ERROR err = WEAVE_NO_ERROR; + + mDiscoveredNodeId = 0; + +#if SERVICELESS_DEVICE_DISCOVERY_ENABLED + err = sDeviceDescriptionClient.Init(&ExchangeMgr); + SuccessOrExit(err); + + sDeviceDescriptionClient.OnIdentifyResponseReceived = HandleIdentifyResponseReceived; + +exit: +#endif + return err; +} + +bool DeviceDiscovery::IsPeerDeviceDiscovered(void) +{ + return mDiscoveredNodeId != 0 ? true:false; +} + +void DeviceDiscovery::SetDeviceDiscoveredNodeId(uint64_t aNodeId) +{ + mDiscoveredNodeId = aNodeId; +} + +bool DeviceDiscovery::GetDiscoveredDeviceNodeId(uint64_t & aNodeId) +{ + bool device_discovered = false; + + if (IsPeerDeviceDiscovered()) + { + aNodeId = mDiscoveredNodeId; + device_discovered = true; + } + + return device_discovered; +} + +#if SERVICELESS_DEVICE_DISCOVERY_ENABLED +void DeviceDiscovery::EnableUserSelectedMode(bool aEnable, uint16_t aTimeOutMs) +{ + if (aTimeOutMs != 0) + { + ConnectivityMgr().SetUserSelectedModeTimeout(aTimeOutMs); + } + + ConnectivityMgr().SetUserSelectedMode(aEnable); +} + +WEAVE_ERROR DeviceDiscovery::SearchForUserSelectedModeDevices(uint16_t aVendorId, uint16_t aProductId) +{ + WEAVE_ERROR err = WEAVE_NO_ERROR; + IdentifyRequestMessage identifyReqMsg; + nl::Inet::IPAddress ip_addr; + + if (!ConfigurationMgr().IsMemberOfFabric()) + { + NRF_LOG_INFO("DeviceDiscovery err: Device not fabric provisioned"); + err = WEAVE_ERROR_INCORRECT_STATE; + } + SuccessOrExit(err); + + ip_addr = nl::Inet::IPAddress::MakeIPv6WellKnownMulticast(nl::Inet::kIPv6MulticastScope_Link, + nl::Inet::kIPV6MulticastGroup_AllNodes); + + identifyReqMsg.TargetFabricId = ::nl::Weave::DeviceLayer::FabricState.FabricId; + identifyReqMsg.TargetModes = kTargetDeviceMode_UserSelectedMode; + identifyReqMsg.TargetVendorId = aVendorId; + identifyReqMsg.TargetProductId = aProductId; + identifyReqMsg.TargetDeviceId = nl::Weave::kAnyNodeId; + + err = sDeviceDescriptionClient.SendIdentifyRequest(ip_addr, identifyReqMsg); + SuccessOrExit(err); + +exit: + return err; +} + +void DeviceDiscovery::HandleIdentifyResponseReceived(void *apAppState, + uint64_t aNodeId, + const nl::Inet::IPAddress& aNodeAddr, + const IdentifyResponseMessage& aRespMsg) +{ + WeaveDeviceDescriptor deviceDesc = aRespMsg.DeviceDesc; + + NRF_LOG_INFO("IdentifyResponse received from node 0x%016" PRIX64, aNodeId); + NRF_LOG_INFO(" Source Fabric Id: %016" PRIX64, deviceDesc.FabricId); + NRF_LOG_INFO(" Source Vendor Id: %04X", (unsigned)deviceDesc.VendorId); + NRF_LOG_INFO(" Source Product Id: %04X", (unsigned)deviceDesc.ProductId); + NRF_LOG_INFO(" Source Product Revision: %04X", (unsigned)deviceDesc.ProductRevision); + + NRF_LOG_INFO("Device Description Operation Completed"); + + sDeviceDescriptionClient.CancelExchange(); + + sDeviceDiscoveryFeature.mDiscoveredNodeId = aNodeId; + + AppEvent event; + event.Type = AppEvent::kEventType_LocalDeviceDiscovered; + event.Handler = NULL; + GetAppTask().PostEvent(&event); +} +#endif // SERVICELESS_DEVICE_DISCOVERY_ENABLED \ No newline at end of file diff --git a/main/WDMFeature.cpp b/main/WDMFeature.cpp index 1d78642d..e720adab 100644 --- a/main/WDMFeature.cpp +++ b/main/WDMFeature.cpp @@ -17,6 +17,8 @@ */ #include "WDMFeature.h" +#include "DeviceDiscovery.h" +#include "BoltLockManager.h" #include "nrf_log.h" #include "nrf_error.h" @@ -33,51 +35,12 @@ using namespace ::nl::Weave::Profiles::DataManagement_Current; // TODO: Remove this #define kServiceEndpoint_Data_Management 0x18B4300200000003ull ///< Core Weave data management protocol endpoint -/** Defines the timeout for liveness between the service and the device. - * For sleepy end node devices, this timeout will be much larger than the current - * value to preserve battery. - */ -#define SERVICE_LIVENESS_TIMEOUT_SEC 60 * 1 // 1 minute - -/** Defines the timeout for a response to any message initiated by the device to the service. - * This includes notifies, subscribe confirms, cancels and updates. - * This timeout is kept SERVICE_WRM_MAX_RETRANS x SERVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS + some buffer - * to account for latency in the message transmission through multiple hops. - */ -#define SERVICE_MESSAGE_RESPONSE_TIMEOUT_MS 10000 - -/** Defines the timeout for a message to get retransmitted when no wrm ack or - * response has been heard back from the service. This timeout is kept larger - * for now since the message has to travel through multiple hops and service - * layers before actually making it to the actual receiver. - * @note - * WRM has an initial and active retransmission timeouts to interact with - * sleepy destination nodes. For the time being, the pinna WRM config would - * set both these timeouts to be the same based on the assumption that the pinna - * would not be interacting directly with a sleepy peer. - */ -#define SERVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS 2500 - -#define SERVICE_WRM_ACTIVE_RETRANS_TIMEOUT_MS 2500 - -/** Define the maximum number of retransmissions in WRM - */ -#define SERVICE_WRM_MAX_RETRANS 3 - -/** Define the timeout for piggybacking a WRM acknowledgment message - */ -#define SERVICE_WRM_PIGGYBACK_ACK_TIMEOUT_MS 200 - -/** Defines the timeout for expecting a subscribe response after sending a subscribe request. - * This is meant to be a gross timeout - the MESSAGE_RESPONSE_TIMEOUT_MS will usually trip first - * to catch timeouts for each message in the subscribe request exchange. - * SUBSCRIPTION_RESPONSE_TIMEOUT_MS > Average no. of notifies during a subscription * MESSAGE_RESPONSE_TIMEOUT_MS - */ -#define SUBSCRIPTION_RESPONSE_TIMEOUT_MS 40000 - const nl::Weave::WRMPConfig gWRMPConfigService = { SERVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS, SERVICE_WRM_ACTIVE_RETRANS_TIMEOUT_MS, SERVICE_WRM_PIGGYBACK_ACK_TIMEOUT_MS, SERVICE_WRM_MAX_RETRANS }; +const nl::Weave::WRMPConfig gWRMPConfigDevice = { DEVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS, DEVICE_WRM_ACTIVE_RETRANS_TIMEOUT_MS, + DEVICE_WRM_PIGGYBACK_ACK_TIMEOUT_MS, DEVICE_WRM_MAX_RETRANS }; + WDMFeature WDMFeature::sWDMfeature; SubscriptionEngine * SubscriptionEngine::GetInstance() @@ -114,14 +77,19 @@ WEAVE_ERROR PublisherLock::Unlock() WDMFeature::WDMFeature(void) : mServiceSinkTraitCatalog(ResourceIdentifier(ResourceIdentifier::SELF_NODE_ID), mServiceSinkCatalogStore, sizeof(mServiceSinkCatalogStore) / sizeof(mServiceSinkCatalogStore[0])) + , mDeviceSinkTraitCatalog(ResourceIdentifier(ResourceIdentifier::SELF_NODE_ID), mDeviceSinkCatalogStore, + sizeof(mDeviceSinkCatalogStore) / sizeof(mDeviceSinkCatalogStore[0])) , mServiceSourceTraitCatalog(ResourceIdentifier(ResourceIdentifier::SELF_NODE_ID), mServiceSourceCatalogStore, sizeof(mServiceSourceCatalogStore) / sizeof(mServiceSourceCatalogStore[0])) , mServiceSubClient(NULL) + , mDeviceSubClient(NULL) , mServiceCounterSubHandler(NULL) , mServiceSubBinding(NULL) + , mDeviceSubBinding(NULL) , mIsSubToServiceEstablished(false) , mIsServiceCounterSubEstablished(false) , mIsSubToServiceActivated(false) + , mIsDeviceSubEstablished(false) { } @@ -158,6 +126,11 @@ bool WDMFeature::AreServiceSubscriptionsEstablished(void) return (mIsSubToServiceEstablished && mIsServiceCounterSubEstablished); } +bool WDMFeature::IsDeviceSubscriptionEstablished(void) +{ + return mIsDeviceSubEstablished; +} + void WDMFeature::InitiateSubscriptionToService(void) { NRF_LOG_INFO("Initiating Subscription To Service"); @@ -166,7 +139,24 @@ void WDMFeature::InitiateSubscriptionToService(void) mServiceSubClient->InitiateSubscription(); } -void WDMFeature::TearDownSubscriptions(void) +void WDMFeature::InitiateSubscriptionToDevice(intptr_t arg) +{ + NRF_LOG_INFO("Initiating Subscription To Device"); + + WdmFeature().TearDownDeviceSubscription(); + WdmFeature().mDeviceSubClient->EnableResubscribe(NULL); + WdmFeature().mDeviceSubClient->InitiateSubscription(); +} + +void WDMFeature::TearDownDeviceSubscription(void) +{ + if (mDeviceSubClient) + { + mDeviceSubClient->AbortSubscription(); + } +} + +void WDMFeature::TearDownServiceSubscriptions(void) { if (mServiceSubClient) { @@ -179,7 +169,7 @@ void WDMFeature::TearDownSubscriptions(void) } } -void WDMFeature::HandleServiceBindingEvent(void * appState, ::nl::Weave::Binding::EventType eventType, +void WDMFeature::HandleBindingEvent(void * appState, ::nl::Weave::Binding::EventType eventType, const ::nl::Weave::Binding::InEventParam & inParam, ::nl::Weave::Binding::OutEventParam & outParam) { @@ -188,25 +178,53 @@ void WDMFeature::HandleServiceBindingEvent(void * appState, ::nl::Weave::Binding switch (eventType) { case Binding::kEvent_PrepareRequested: - outParam.PrepareRequested.PrepareError = binding->BeginConfiguration() + { + uint64_t target_node = kServiceEndpoint_Data_Management; + + if (binding == sWDMfeature.mDeviceSubBinding) + { + if (!DeviceDiscoveryFeature().GetDiscoveredDeviceNodeId(target_node)) + { + outParam.PrepareRequested.PrepareError = WEAVE_ERROR_INCORRECT_STATE; + break; + } + + outParam.PrepareRequested.PrepareError = binding->BeginConfiguration() + .Target_NodeId(target_node) + .TargetAddress_WeaveFabric(::nl::Weave::kWeaveSubnetId_ThreadMesh) + .Transport_UDP_WRM() + .Transport_DefaultWRMPConfig(gWRMPConfigDevice) + .Exchange_ResponseTimeoutMsec(DEVICE_MESSAGE_RESPONSE_TIMEOUT_MS) + .Security_CASESession() + .PrepareBinding(); + } + else if (binding == sWDMfeature.mServiceSubBinding) + { + outParam.PrepareRequested.PrepareError = binding->BeginConfiguration() .Target_ServiceEndpoint(kServiceEndpoint_Data_Management) .Transport_UDP_WRM() .Transport_DefaultWRMPConfig(gWRMPConfigService) .Exchange_ResponseTimeoutMsec(SERVICE_MESSAGE_RESPONSE_TIMEOUT_MS) .Security_SharedCASESession() .PrepareBinding(); + } break; + } case Binding::kEvent_PrepareFailed: - NRF_LOG_INFO("Failed to prepare service subscription binding: %s", ErrorStr(inParam.PrepareFailed.Reason)); + NRF_LOG_INFO("Failed to prepare %s subscription binding: %s", + (binding == sWDMfeature.mServiceSubBinding) ? "service" : "device", + ErrorStr(inParam.PrepareFailed.Reason)); break; case Binding::kEvent_BindingFailed: - NRF_LOG_INFO("Service subscription binding failed: %s", ErrorStr(inParam.BindingFailed.Reason)); + NRF_LOG_INFO("%s subscription binding failed: %s", + (binding == sWDMfeature.mServiceSubBinding) ? "service" : "device", + ErrorStr(inParam.BindingFailed.Reason)); break; case Binding::kEvent_BindingReady: - NRF_LOG_INFO("Service subscription binding ready"); + NRF_LOG_INFO("%s subscription binding ready",(binding == sWDMfeature.mServiceSubBinding) ? "service" : "device"); break; default: @@ -231,15 +249,16 @@ void WDMFeature::HandleInboundSubscriptionEvent(void * aAppState, SubscriptionHa inParam.mSubscribeRequestParsed.mSubscriptionId, inParam.mSubscribeRequestParsed.mNumTraitInstances); sWDMfeature.mServiceCounterSubHandler = inParam.mSubscribeRequestParsed.mHandler; + + binding->SetDefaultResponseTimeout(SERVICE_MESSAGE_RESPONSE_TIMEOUT_MS); + binding->SetDefaultWRMPConfig(gWRMPConfigService); } else { - break; + NRF_LOG_INFO("Inbound device subscription request received (sub id %016" PRIX64 ", path count %" PRId16 ")", + inParam.mSubscribeRequestParsed.mSubscriptionId, inParam.mSubscribeRequestParsed.mNumTraitInstances); } - binding->SetDefaultResponseTimeout(SERVICE_MESSAGE_RESPONSE_TIMEOUT_MS); - binding->SetDefaultWRMPConfig(gWRMPConfigService); - inParam.mSubscribeRequestParsed.mHandler->AcceptSubscribeRequest(inParam.mSubscribeRequestParsed.mTimeoutSecMin); break; } @@ -252,6 +271,10 @@ void WDMFeature::HandleInboundSubscriptionEvent(void * aAppState, SubscriptionHa sWDMfeature.mIsServiceCounterSubEstablished = true; } + else + { + NRF_LOG_INFO("Inbound device subscription established"); + } break; } @@ -268,6 +291,10 @@ void WDMFeature::HandleInboundSubscriptionEvent(void * aAppState, SubscriptionHa sWDMfeature.mServiceCounterSubHandler = NULL; sWDMfeature.mIsServiceCounterSubEstablished = false; } + else + { + NRF_LOG_INFO("Inbound device subscription terminated: %s", termDesc); + } break; } @@ -285,34 +312,76 @@ void WDMFeature::HandleOutboundServiceSubscriptionEvent(void * appState, Subscri { case SubscriptionClient::kEvent_OnSubscribeRequestPrepareNeeded: { - outParam.mSubscribeRequestPrepareNeeded.mPathList = &(sWDMfeature.mServiceSinkTraitPaths[0]); - outParam.mSubscribeRequestPrepareNeeded.mPathListSize = 1; + if (inParam.mSubscribeRequestPrepareNeeded.mClient == sWDMfeature.mServiceSubClient) + { + outParam.mSubscribeRequestPrepareNeeded.mPathList = &(sWDMfeature.mServiceSinkTraitPaths[0]); + outParam.mSubscribeRequestPrepareNeeded.mPathListSize = kSinkHandle_Service_Max; + outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMin = SERVICE_LIVENESS_TIMEOUT_SEC; + outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMax = SERVICE_LIVENESS_TIMEOUT_SEC; + } + else if (inParam.mSubscribeRequestPrepareNeeded.mClient == sWDMfeature.mDeviceSubClient) + { + outParam.mSubscribeRequestPrepareNeeded.mPathList = &(sWDMfeature.mDeviceSinkTraitPaths[0]); + outParam.mSubscribeRequestPrepareNeeded.mPathListSize = kSinkHandle_Device_Max; + outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMin = DEVICE_LIVENESS_TIMEOUT_SEC; + outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMax = DEVICE_LIVENESS_TIMEOUT_SEC; + } + outParam.mSubscribeRequestPrepareNeeded.mVersionedPathList = NULL; outParam.mSubscribeRequestPrepareNeeded.mNeedAllEvents = false; outParam.mSubscribeRequestPrepareNeeded.mLastObservedEventList = NULL; outParam.mSubscribeRequestPrepareNeeded.mLastObservedEventListSize = 0; - outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMin = SERVICE_LIVENESS_TIMEOUT_SEC; - outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMax = SERVICE_LIVENESS_TIMEOUT_SEC; - NRF_LOG_INFO("Sending outbound service subscribe request (path count 1)"); + NRF_LOG_INFO("Sending outbound service subscribe request"); break; } case SubscriptionClient::kEvent_OnSubscriptionEstablished: - NRF_LOG_INFO("Outbound service subscription established (sub id %016" PRIX64 ")", - inParam.mSubscriptionEstablished.mSubscriptionId); - sWDMfeature.mIsSubToServiceEstablished = true; + if (inParam.mSubscriptionEstablished.mClient == sWDMfeature.mServiceSubClient) + { + NRF_LOG_INFO("Outbound service subscription established (sub id %016" PRIX64 ")", + inParam.mSubscriptionEstablished.mSubscriptionId); + + sWDMfeature.mIsSubToServiceEstablished = true; + } + else if (inParam.mSubscriptionEstablished.mClient == sWDMfeature.mDeviceSubClient) + { + NRF_LOG_INFO("Outbound device subscription established (sub id %016" PRIX64 ")", + inParam.mSubscriptionEstablished.mSubscriptionId); + + sWDMfeature.mIsDeviceSubEstablished = true; + + BoltLockMgr().EvaluateChange(); + } break; case SubscriptionClient::kEvent_OnSubscriptionTerminated: - NRF_LOG_INFO( - "Outbound service subscription terminated: %s", - (inParam.mSubscriptionTerminated.mIsStatusCodeValid) - ? StatusReportStr(inParam.mSubscriptionTerminated.mStatusProfileId, inParam.mSubscriptionTerminated.mStatusCode) - : ErrorStr(inParam.mSubscriptionTerminated.mReason)); + { + if (inParam.mSubscriptionTerminated.mClient == sWDMfeature.mServiceSubClient) + { + NRF_LOG_INFO( + "Outbound service subscription terminated: %s", + (inParam.mSubscriptionTerminated.mIsStatusCodeValid) + ? StatusReportStr(inParam.mSubscriptionTerminated.mStatusProfileId, inParam.mSubscriptionTerminated.mStatusCode) + : ErrorStr(inParam.mSubscriptionTerminated.mReason)); - sWDMfeature.mIsSubToServiceEstablished = false; + + sWDMfeature.mIsSubToServiceEstablished = false; + } + else if (inParam.mSubscriptionTerminated.mClient == sWDMfeature.mDeviceSubClient) + { + NRF_LOG_INFO( + "Outbound device subscription terminated: %s", + (inParam.mSubscriptionTerminated.mIsStatusCodeValid) + ? StatusReportStr(inParam.mSubscriptionTerminated.mStatusProfileId, inParam.mSubscriptionTerminated.mStatusCode) + : ErrorStr(inParam.mSubscriptionTerminated.mReason)); + + sWDMfeature.mIsDeviceSubEstablished = false; + + BoltLockMgr().EvaluateChange(); + } break; + } default: SubscriptionClient::DefaultEventHandler(eventType, inParam, outParam); @@ -343,38 +412,53 @@ WEAVE_ERROR WDMFeature::Init() { WEAVE_ERROR err; int ret; - Binding * binding; ret = mPublisherLock.Init(); VerifyOrExit(ret != NRF_ERROR_NULL, err = WEAVE_ERROR_NO_MEMORY); PlatformMgr().AddEventHandler(PlatformEventHandler); + mBoltLockTraitSource.Init(); + mServiceSourceTraitCatalog.AddAt(0, &mBoltLockTraitSource, kSourceHandle_BoltLockTrait); mServiceSourceTraitCatalog.AddAt(0, &mDeviceIdentityTraitSource, kSourceHandle_DeviceIdentityTrait); - mServiceSinkTraitCatalog.AddAt(0, &mBoltLockSettingsTraitSink, kSinkHandle_BoltLockSettingsTrait); + mServiceSinkTraitCatalog.AddAt(0, &mBoltLockSettingsTraitSink, kSinkHandle_Service_BoltLockSettingsTrait); + + mDeviceSinkTraitCatalog.AddAt(0, &mSecurityOpenCloseTraitSink, kSinkHandle_Device_SecurityOpenCloseTrait); - for (uint8_t handle = 0; handle < kSinkHandle_Max; handle++) + for (uint8_t handle = 0; handle < kSinkHandle_Service_Max; handle++) { mServiceSinkTraitPaths[handle].mTraitDataHandle = handle; mServiceSinkTraitPaths[handle].mPropertyPathHandle = kRootPropertyPathHandle; } + for (uint8_t handle = 0; handle < kSinkHandle_Device_Max; handle++) + { + mDeviceSinkTraitPaths[handle].mTraitDataHandle = handle; + mDeviceSinkTraitPaths[handle].mPropertyPathHandle = kRootPropertyPathHandle; + } + err = mSubscriptionEngine.Init(&ExchangeMgr, this, HandleSubscriptionEngineEvent); SuccessOrExit(err); err = mSubscriptionEngine.EnablePublisher(&mPublisherLock, &mServiceSourceTraitCatalog); SuccessOrExit(err); - binding = ExchangeMgr.NewBinding(HandleServiceBindingEvent, this); - VerifyOrExit(NULL != binding, err = WEAVE_ERROR_NO_MEMORY); + mServiceSubBinding = ExchangeMgr.NewBinding(HandleBindingEvent, this); + VerifyOrExit(NULL != mServiceSubBinding, err = WEAVE_ERROR_NO_MEMORY); + + mDeviceSubBinding = ExchangeMgr.NewBinding(HandleBindingEvent, this); + VerifyOrExit(NULL != mDeviceSubBinding, err = WEAVE_ERROR_NO_MEMORY); - err = mSubscriptionEngine.NewClient(&(mServiceSubClient), binding, this, HandleOutboundServiceSubscriptionEvent, + err = mSubscriptionEngine.NewClient(&(mServiceSubClient), mServiceSubBinding, this, HandleOutboundServiceSubscriptionEvent, &mServiceSinkTraitCatalog, SUBSCRIPTION_RESPONSE_TIMEOUT_MS); SuccessOrExit(err); - mServiceSubBinding = binding; + err = mSubscriptionEngine.NewClient(&(mDeviceSubClient), mDeviceSubBinding, this, HandleOutboundServiceSubscriptionEvent, + &mDeviceSinkTraitCatalog, SUBSCRIPTION_RESPONSE_TIMEOUT_MS); + SuccessOrExit(err); + NRF_LOG_INFO("WDMFeature Init Complete"); diff --git a/main/include/AppEvent.h b/main/include/AppEvent.h index 9e3a288f..fe1b86b7 100644 --- a/main/include/AppEvent.h +++ b/main/include/AppEvent.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2018 Nest Labs, Inc. + * Copyright (c) 2019 Google LLC. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,8 +19,8 @@ #ifndef APP_EVENT_H #define APP_EVENT_H -struct AppEvent; -typedef void (*EventHandler)(AppEvent *); +#include +#include "ButtonManager.h" struct AppEvent { @@ -28,7 +28,11 @@ struct AppEvent { kEventType_Button = 0, kEventType_Timer, - kEventType_Lock, + kEventType_LockAction_Requested, + kEventType_LockAction_Initiated, + kEventType_LockAction_Completed, + kEventType_LocalDeviceDiscovered, + kEventType_AutoReLock_Evaluate, kEventType_Install, }; @@ -39,7 +43,7 @@ struct AppEvent struct { uint8_t PinNo; - uint8_t Action; + ButtonManager::ButtonAction Action; } ButtonEvent; struct { @@ -49,10 +53,40 @@ struct AppEvent { uint8_t Action; int32_t Actor; - } LockEvent; + } LockAction_Requested, LockAction_Initiated; + struct + { + uint8_t Action; + } LockAction_Completed; }; + typedef void (*EventHandler)(const AppEvent *); EventHandler Handler; }; + +class AppEventDispatcher +{ + +public: + typedef void (*EventHandler)(const AppEvent *, intptr_t Arg); + + struct EventHandler_t + { + EventHandler_t * Next; + EventHandler Handler; + intptr_t Arg; + }; + + // API to add or remove a button event handler. + int AddEventHandler(EventHandler Handler, intptr_t arg); + void RemoveEventHandler(EventHandler Handler, intptr_t arg); + +protected: + void DispatchEvent(const AppEvent * aEvent); + + EventHandler_t * mAppEventHandlerList; + +}; + #endif // APP_EVENT_H diff --git a/main/include/AppTask.h b/main/include/AppTask.h index d3d4ce62..c6f4bd43 100644 --- a/main/include/AppTask.h +++ b/main/include/AppTask.h @@ -36,7 +36,6 @@ class AppTask int StartAppTask(); static void AppTaskMain(void * pvParameter); - void PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction); void PostEvent(const AppEvent * event); private: @@ -44,19 +43,20 @@ class AppTask int Init(); - static void ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor); - static void ActionCompleted(BoltLockManager::Action_t aAction); - void CancelTimer(void); void DispatchEvent(AppEvent * event); - static void FunctionTimerEventHandler(AppEvent * aEvent); - static void FunctionHandler(AppEvent * aEvent); - static void LockActionEventHandler(AppEvent * aEvent); - static void InstallEventHandler(AppEvent * aEvent); + static void SeachForLocalSDKOpenCloseSensors(intptr_t arg); + + // Button Event Handlers + static void FunctionButtonHandler(const AppEvent * aEvent); + static void AttentionButtonHandler(const AppEvent * aEvent); - static void ButtonEventHandler(uint8_t pin_no, uint8_t button_action); + // Other Event Handler + static void LockActionEventHandler(const AppEvent * aEvent, intptr_t Arg); + static void FactoryResetTriggerTimerExpired(const AppEvent * aEvent); + static void InstallEventHandler(const AppEvent * aEvent); static void TimerEventHandler(void * p_context); static void HandleSoftwareUpdateEvent(void *apAppState, @@ -64,6 +64,7 @@ class AppTask const SoftwareUpdateManager::InEventParam& aInParam, SoftwareUpdateManager::OutEventParam& aOutParam); + void StartTimer(uint32_t aTimeoutInMs); enum Function_t @@ -75,8 +76,7 @@ class AppTask kFunction_Invalid } Function; - Function_t mFunction; - bool mFunctionTimerActive; + bool mFactoryResetTimerActive; static AppTask sAppTask; }; diff --git a/main/include/BoltLockManager.h b/main/include/BoltLockManager.h index 05886bc2..e390c7e2 100644 --- a/main/include/BoltLockManager.h +++ b/main/include/BoltLockManager.h @@ -22,9 +22,12 @@ #include #include +// nRF SDK includes +#include "app_timer.h" + #include "AppEvent.h" -class BoltLockManager +class BoltLockManager : public AppEventDispatcher { public: enum Action_t @@ -45,32 +48,33 @@ class BoltLockManager int Init(); bool IsUnlocked(); - void EnableAutoRelock(bool aOn); + void EvaluateChange(void); + void EnableAutoRelock(bool aEnable); + void EnableDoorCheck(bool aEnable); 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); + void PostLockActionRequest(int32_t aActor, Action_t aAction); private: friend BoltLockManager & BoltLockMgr(void); State_t mState; - Callback_fn_initiated mActionInitiated_CB; - Callback_fn_completed mActionCompleted_CB; + bool mAutoRelockEnabled; + bool mDoorCheckEnabled; + bool mAutoLockTimerArmed; - bool mAutoRelock; uint32_t mAutoLockDuration; - bool mAutoLockTimerArmed; - void CancelTimer(void); - void StartTimer(uint32_t aTimeoutMs); + void CancelTimer(app_timer_id_t aTimer); + void StartTimer(app_timer_id_t aTimer, uint32_t aTimeoutMs); static void TimerEventHandler(void * p_context); - static void AutoReLockTimerEventHandler(AppEvent * aEvent); - static void ActuatorMovementTimerEventHandler(AppEvent *aEvent); + static void EvaluateAutoRelockState(const AppEvent * aEvent); + static void AutoReLockTimerEventHandler(const AppEvent * aEvent); + static void ActuatorMovementTimerEventHandler(const AppEvent *aEvent); + static void LockActionEventHandler(const AppEvent * aEvent); static BoltLockManager sLock; }; diff --git a/main/include/ButtonManager.h b/main/include/ButtonManager.h new file mode 100644 index 00000000..17e642f1 --- /dev/null +++ b/main/include/ButtonManager.h @@ -0,0 +1,87 @@ +/* + * + * 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. + */ + +#ifndef BUTTON_MANAGER_H +#define BUTTON_MANAGER_H + +#include "boards.h" + +// Forward declaration for AppEvent +struct AppEvent; + +class ButtonManager +{ +public: + typedef void (*ButtonEventHandlerFunct)(const AppEvent * event); + + struct ButtonEventHandler_t + { + uint8_t PinNo; + ButtonEventHandler_t * Next; + ButtonEventHandlerFunct Handler; + }; + + enum ButtonAction + { + kButtonAction_Press = 0, + kButtonAction_Release, + kButtonAction_LongPress, + kButtonAction_LongPress_Release + }; + + int Init(void); + void Poll(void); + + // API to set long press timeout + void SetLongPressTimeoutMs(uint16_t aLongPressTimeoutMs); + + // API to add or remove a button event handler. + int AddButtonEventHandler(uint8_t aButtonPinNo, ButtonEventHandlerFunct Handler); + void RemoveEventHandler(uint8_t aButtonPinNo, ButtonEventHandlerFunct handler); + +private: + + friend ButtonManager & ButtonMgr(void); + + bool IsLongPressActive(uint8_t button_id); + bool GetButtonIdFromPinNo(uint8_t aPinNo, uint8_t & aButtonId); + + void CancelLongPressTimer(uint8_t button_id); + void DispatchButtonEvent(const AppEvent * event); + void LongPressActivated(uint8_t button_id); + void StartLongPressTimer(uint8_t button_id); + + static void ButtonManagerEventHandler(const AppEvent * aButtonEvent); + static void ButtonEventHandler(uint8_t pin_no, uint8_t button_action); + + uint8_t mButtonIndex[BUTTONS_NUMBER] = BUTTONS_LIST; + uint8_t mLongPressActivatedFlag; + uint16_t mLongPressTimeoutMs; + uint64_t mLongPressButtonTimerExpirationMs[BUTTONS_NUMBER]; + ButtonEventHandler_t * mAppEventHandlerList; + + static ButtonManager sButtonManager; + +}; + +inline ButtonManager & ButtonMgr(void) +{ + return ButtonManager::sButtonManager; +} + +#endif // BUTTON_MANAGER_H \ No newline at end of file diff --git a/main/include/DeviceDiscovery.h b/main/include/DeviceDiscovery.h new file mode 100644 index 00000000..e9482e32 --- /dev/null +++ b/main/include/DeviceDiscovery.h @@ -0,0 +1,64 @@ +/* + * + * 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. + */ + +#ifndef DEVICE_DISCOVERY_H +#define DEVICE_DISCOVERY_H + +#include +#include + +#include "nrf_log.h" + +#include +#include + +class DeviceDiscovery +{ +public: + + WEAVE_ERROR Init(void); + + void EnableUserSelectedMode(bool aEnable, uint16_t aTimeOutMs = 0); + void SetDeviceDiscoveredNodeId(uint64_t aNodeId); + + bool IsPeerDeviceDiscovered(void); + bool GetDiscoveredDeviceNodeId(uint64_t & aNodeId); + + WEAVE_ERROR SearchForUserSelectedModeDevices(uint16_t aVendorId, uint16_t aProductId); + +private: + + friend DeviceDiscovery & DeviceDiscoveryFeature(void); + uint64_t mDiscoveredNodeId; + + static void HandleIdentifyResponseReceived(void *apAppState, + uint64_t aNodeId, + const nl::Inet::IPAddress& aNodeAddr, + const nl::Weave::Profiles::DeviceDescription::IdentifyResponseMessage& aRespMsg); + + + static DeviceDiscovery sDeviceDiscoveryFeature; + +}; + +inline DeviceDiscovery & DeviceDiscoveryFeature(void) +{ + return DeviceDiscovery::sDeviceDiscoveryFeature; +} + +#endif // DEVICE_DISCOVERY_H \ No newline at end of file diff --git a/main/include/WDMFeature.h b/main/include/WDMFeature.h index 9a5916b1..a43717a1 100644 --- a/main/include/WDMFeature.h +++ b/main/include/WDMFeature.h @@ -25,6 +25,7 @@ #include "traits/include/BoltLockTraitDataSource.h" #include "traits/include/DeviceIdentityTraitDataSource.h" #include "traits/include/BoltLockSettingsTraitDataSink.h" +#include "traits/include/SecurityOpenCloseTraitDataSink.h" #include "FreeRTOS.h" #include "semphr.h" @@ -60,11 +61,15 @@ class WDMFeature WDMFeature(void); WEAVE_ERROR Init(void); void ProcessTraitChanges(void); - void TearDownSubscriptions(void); + void TearDownDeviceSubscription(void); + void TearDownServiceSubscriptions(void); + static void InitiateSubscriptionToDevice(intptr_t arg); + bool IsDeviceSubscriptionEstablished(void); bool AreServiceSubscriptionsEstablished(void); BoltLockTraitDataSource & GetBoltLockTraitDataSource(void); + SecurityOpenCloseTraitDataSink & GetSecurityOpenCloseTraitDataSink(void); nl::Weave::Profiles::DataManagement::SubscriptionEngine mSubscriptionEngine; @@ -79,28 +84,39 @@ class WDMFeature kSourceHandle_Max }; - enum SinkTraitHandle + enum SinkTrait_Service_Handle { - kSinkHandle_BoltLockSettingsTrait = 0, + kSinkHandle_Service_BoltLockSettingsTrait = 0, - kSinkHandle_Max + kSinkHandle_Service_Max + }; + + enum SinkTrait_Device_Handle + { + kSinkHandle_Device_SecurityOpenCloseTrait = 0, + + kSinkHandle_Device_Max }; // Published Traits BoltLockTraitDataSource mBoltLockTraitSource; DeviceIdentityTraitDataSource mDeviceIdentityTraitSource; - // Subscribed Traits + // Subscribed Traits - Service BoltLockSettingsTraitDataSink mBoltLockSettingsTraitSink; + // Subscribed Traits - Device + SecurityOpenCloseTraitDataSink mSecurityOpenCloseTraitSink; + void InitiateSubscriptionToService(void); + static void AsyncProcessChanges(intptr_t arg); static void PlatformEventHandler(const ::nl::Weave::DeviceLayer::WeaveDeviceEvent * event, intptr_t arg); static void HandleSubscriptionEngineEvent(void * appState, SubscriptionEngine::EventID eventType, const SubscriptionEngine::InEventParam & inParam, SubscriptionEngine::OutEventParam & outParam); - static void HandleServiceBindingEvent(void * appState, ::nl::Weave::Binding::EventType eventType, + static void HandleBindingEvent(void * appState, ::nl::Weave::Binding::EventType eventType, const ::nl::Weave::Binding::InEventParam & inParam, ::nl::Weave::Binding::OutEventParam & outParam); static void HandleOutboundServiceSubscriptionEvent(void * appState, SubscriptionClient::EventID eventType, @@ -110,25 +126,32 @@ class WDMFeature const SubscriptionHandler::InEventParam & inParam, SubscriptionHandler::OutEventParam & outParam); - // Sink Catalog - nl::Weave::Profiles::DataManagement::SingleResourceSinkTraitCatalog::CatalogItem mServiceSinkCatalogStore[kSinkHandle_Max]; + // Sink Catalog - Service + nl::Weave::Profiles::DataManagement::SingleResourceSinkTraitCatalog::CatalogItem mServiceSinkCatalogStore[kSinkHandle_Service_Max]; nl::Weave::Profiles::DataManagement::SingleResourceSinkTraitCatalog mServiceSinkTraitCatalog; + // Sink Catalog - Device + nl::Weave::Profiles::DataManagement::SingleResourceSinkTraitCatalog::CatalogItem mDeviceSinkCatalogStore[kSinkHandle_Device_Max]; + nl::Weave::Profiles::DataManagement::SingleResourceSinkTraitCatalog mDeviceSinkTraitCatalog; + // Source Catalog nl::Weave::Profiles::DataManagement::SingleResourceSourceTraitCatalog mServiceSourceTraitCatalog; nl::Weave::Profiles::DataManagement::SingleResourceSourceTraitCatalog::CatalogItem mServiceSourceCatalogStore[kSourceHandle_Max]; - nl::Weave::Profiles::DataManagement::TraitPath mServiceSinkTraitPaths[kSinkHandle_Max]; + nl::Weave::Profiles::DataManagement::TraitPath mServiceSinkTraitPaths[kSinkHandle_Service_Max]; + nl::Weave::Profiles::DataManagement::TraitPath mDeviceSinkTraitPaths[kSinkHandle_Device_Max]; // Subscription Clients nl::Weave::Profiles::DataManagement::SubscriptionClient * mServiceSubClient; + nl::Weave::Profiles::DataManagement::SubscriptionClient * mDeviceSubClient; // Subscription Handler nl::Weave::Profiles::DataManagement::SubscriptionHandler * mServiceCounterSubHandler; // Binding nl::Weave::Binding * mServiceSubBinding; + nl::Weave::Binding * mDeviceSubBinding; static WDMFeature sWDMfeature; PublisherLock mPublisherLock; @@ -136,6 +159,7 @@ class WDMFeature bool mIsSubToServiceEstablished; bool mIsServiceCounterSubEstablished; bool mIsSubToServiceActivated; + bool mIsDeviceSubEstablished; }; inline WDMFeature & WdmFeature(void) @@ -148,4 +172,8 @@ inline BoltLockTraitDataSource & WDMFeature::GetBoltLockTraitDataSource(void) return mBoltLockTraitSource; } +inline SecurityOpenCloseTraitDataSink & WDMFeature::GetSecurityOpenCloseTraitDataSink(void) +{ + return mSecurityOpenCloseTraitSink; +} #endif // WDM_FEATURE_H diff --git a/main/include/app_config.h b/main/include/app_config.h index cba28c97..851905d3 100644 --- a/main/include/app_config.h +++ b/main/include/app_config.h @@ -109,15 +109,28 @@ #define BUTTON_ENABLED 1 #define GPIOTE_ENABLED 1 -#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 2 +#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 4 -#define APP_TIMER_CONFIG_OP_QUEUE_SIZE 10 +#define APP_TIMER_CONFIG_OP_QUEUE_SIZE 20 // ---- Lock Example App Config ---- +#define SERVICELESS_DEVICE_DISCOVERY_ENABLED 0 +#define WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING SERVICELESS_DEVICE_DISCOVERY_ENABLED + +// If account pairing is disabled, device will not be able to subscribe to service. +// Defaults provided for auto relock enable and auto lock duration +#if WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING +#define AUTO_RELOCK_ENABLED_DEFAULT 1 +#define AUTO_LOCK_DURATION_SECS_DEFAULT 10 +#define DOOR_CHECK_ENABLE_DEFAULT 1 +#endif + +#define ATTENTION_BUTTON BUTTON_3 #define LOCK_BUTTON BUTTON_2 #define FUNCTION_BUTTON BUTTON_1 #define FUNCTION_BUTTON_DEBOUNCE_PERIOD_MS 50 +#define LONG_PRESS_TIMEOUT_MS (3000) #define SYSTEM_STATE_LED BSP_LED_0 #define LOCK_STATE_LED BSP_LED_1 @@ -127,12 +140,83 @@ #define ACTUATOR_MOVEMENT_PERIOS_MS 2000 // ---- Lock Example SWU Config ---- -#define SWU_INTERVAl_WINDOW_MIN_MS (23*60*60*1000) // 23 hours -#define SWU_INTERVAl_WINDOW_MAX_MS (24*60*60*1000) // 24 hours +#define SWU_INTERVAl_WINDOW_MIN_MS (23*60*60*1000) // 23 hours +#define SWU_INTERVAl_WINDOW_MAX_MS (24*60*60*1000) // 24 hours // ---- Thread Polling Config ---- #define THREAD_ACTIVE_POLLING_INTERVAL_MS 100 #define THREAD_INACTIVE_POLLING_INTERVAL_MS 1000 +// ---- Device to Service Subscription Config ---- + +/** Defines the timeout for liveness between the service and the device. + * For sleepy end node devices, this timeout will be much larger than the current + * value to preserve battery. + */ +#define SERVICE_LIVENESS_TIMEOUT_SEC 60 * 1 // 1 minute + +/** Defines the timeout for a response to any message initiated by the device to the service. + * This includes notifies, subscribe confirms, cancels and updates. + * This timeout is kept SERVICE_WRM_MAX_RETRANS x SERVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS + some buffer + * to account for latency in the message transmission through multiple hops. + */ +#define SERVICE_MESSAGE_RESPONSE_TIMEOUT_MS 10000 + +/** Defines the timeout for a message to get retransmitted when no wrm ack or + * response has been heard back from the service. This timeout is kept larger + * for now since the message has to travel through multiple hops and service + * layers before actually making it to the actual receiver. + * @note + * WRM has an initial and active retransmission timeouts to interact with + * sleepy destination nodes. + */ +#define SERVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS 2500 + +#define SERVICE_WRM_ACTIVE_RETRANS_TIMEOUT_MS 2500 + +/** Define the maximum number of retransmissions in WRM + */ +#define SERVICE_WRM_MAX_RETRANS 3 + +/** Define the timeout for piggybacking a WRM acknowledgment message + */ +#define SERVICE_WRM_PIGGYBACK_ACK_TIMEOUT_MS 200 + +/** Defines the timeout for expecting a subscribe response after sending a subscribe request. + * This is meant to be a gross timeout - the MESSAGE_RESPONSE_TIMEOUT_MS will usually trip first + * to catch timeouts for each message in the subscribe request exchange. + * SUBSCRIPTION_RESPONSE_TIMEOUT_MS > Average no. of notifies during a subscription * MESSAGE_RESPONSE_TIMEOUT_MS + */ +#define SUBSCRIPTION_RESPONSE_TIMEOUT_MS 40000 + +// ---- Device to Device Subscription Config ---- + +/** Defines the timeout for liveness between the initiating device and the publishing device. + * For sleepy end node devices, this timeout may be much larger than the current + * value to preserve battery. + */ +#define DEVICE_LIVENESS_TIMEOUT_SEC 10 // 10 seconds + +/** Defines the timeout for a response to any message sent by initiating device to the publishing device. + * This includes notifies, subscribe confirms, cancels and updates. + * This timeout is kept SERVICE_WRM_MAX_RETRANS x SERVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS + some buffer + * to account for latency in the message transmission through multiple hops. + */ +#define DEVICE_MESSAGE_RESPONSE_TIMEOUT_MS 3000 + +/** Defines the timeout for a message to get retransmitted when no wrm ack or + * response has been heard back from the publishing device. + */ +#define DEVICE_WRM_INITIAL_RETRANS_TIMEOUT_MS 800 + +#define DEVICE_WRM_ACTIVE_RETRANS_TIMEOUT_MS 500 + +/** Define the maximum number of retransmissions in WRM + */ +#define DEVICE_WRM_MAX_RETRANS 3 + +/** Define the timeout for piggybacking a WRM acknowledgment message + */ +#define DEVICE_WRM_PIGGYBACK_ACK_TIMEOUT_MS 200 #endif //APP_CONFIG_H diff --git a/main/main.cpp b/main/main.cpp index b323d566..7acd92f1 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -263,8 +263,11 @@ int main(void) // mbedtls_platform_set_calloc_free(calloc, free); - // Configure device to operate as a Thread sleepy end-device. +#if SERVICELESS_DEVICE_DISCOVERY_ENABLED + ret = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_Router); +#else ret = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_SleepyEndDevice); +#endif if (ret != WEAVE_NO_ERROR) { NRF_LOG_INFO("ConnectivityMgr().SetThreadDeviceType() failed"); diff --git a/main/traits/BoltLockTraitDataSource.cpp b/main/traits/BoltLockTraitDataSource.cpp index f6765ce1..4ed78fa0 100644 --- a/main/traits/BoltLockTraitDataSource.cpp +++ b/main/traits/BoltLockTraitDataSource.cpp @@ -46,6 +46,11 @@ BoltLockTraitDataSource::BoltLockTraitDataSource() : TraitDataSource(&BoltLockTr mState = BOLT_STATE_EXTENDED; } +void BoltLockTraitDataSource::Init() +{ + BoltLockMgr().AddEventHandler(LockActionEventHandler, 0); +} + bool BoltLockTraitDataSource::IsLocked() { bool lock_state = false; @@ -57,6 +62,32 @@ bool BoltLockTraitDataSource::IsLocked() return lock_state; } +void BoltLockTraitDataSource::LockActionEventHandler(const AppEvent * aEvent, intptr_t Arg) +{ + if (aEvent->Type == AppEvent::kEventType_LockAction_Initiated) + { + if (aEvent->LockAction_Initiated.Action == BoltLockManager::LOCK_ACTION) + { + WdmFeature().GetBoltLockTraitDataSource().InitiateLock(aEvent->LockAction_Initiated.Actor); + } + else if (aEvent->LockAction_Initiated.Action == BoltLockManager::UNLOCK_ACTION) + { + WdmFeature().GetBoltLockTraitDataSource().InitiateUnlock(aEvent->LockAction_Initiated.Actor); + } + } + else if (aEvent->Type == AppEvent::kEventType_LockAction_Completed) + { + if (aEvent->LockAction_Completed.Action == BoltLockManager::LOCK_ACTION) + { + WdmFeature().GetBoltLockTraitDataSource().LockingSuccessful(); + } + else if (aEvent->LockAction_Completed.Action == BoltLockManager::UNLOCK_ACTION) + { + WdmFeature().GetBoltLockTraitDataSource().UnlockingSuccessful(); + } + } +} + void BoltLockTraitDataSource::InitiateLock(int32_t aLockActor) { Lock(); @@ -317,11 +348,11 @@ void BoltLockTraitDataSource::OnCustomCommand(nl::Weave::Profiles::DataManagemen if (changeRequestParam_State == BOLT_STATE_RETRACTED) { - GetAppTask().PostLockActionRequest(changeRequestParam_Actor, BoltLockManager::UNLOCK_ACTION); + BoltLockMgr().PostLockActionRequest(changeRequestParam_Actor, BoltLockManager::UNLOCK_ACTION); } else if (changeRequestParam_State == BOLT_STATE_EXTENDED) { - GetAppTask().PostLockActionRequest(changeRequestParam_Actor, BoltLockManager::LOCK_ACTION); + BoltLockMgr().PostLockActionRequest(changeRequestParam_Actor, BoltLockManager::LOCK_ACTION); } else { diff --git a/main/traits/SecurityOpenCloseTraitDataSink.cpp b/main/traits/SecurityOpenCloseTraitDataSink.cpp new file mode 100644 index 00000000..cb946a41 --- /dev/null +++ b/main/traits/SecurityOpenCloseTraitDataSink.cpp @@ -0,0 +1,93 @@ +/* + * + * 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 "SecurityOpenCloseTraitDataSink.h" +#include "BoltLockManager.h" + +#include "nrf_log.h" + +#include +#include + +#include +#include + +using namespace nl::Weave; +using namespace nl::Weave::TLV; +using namespace nl::Weave::Profiles::DataManagement; +using namespace Schema::Nest::Trait::Security; +using namespace Schema::Nest::Trait::Detector::OpenCloseTrait; +using namespace Schema::Nest::Trait::Security::SecurityOpenCloseTrait; + +SecurityOpenCloseTraitDataSink::SecurityOpenCloseTraitDataSink() + : TraitDataSink(&SecurityOpenCloseTrait::TraitSchema) +{ +} + +bool SecurityOpenCloseTraitDataSink::IsOpen(void) +{ + return mSecurityOpenCloseState == OPEN_CLOSE_STATE_CLOSED ? false : true; +} + +WEAVE_ERROR SecurityOpenCloseTraitDataSink::SetLeafData(PropertyPathHandle aLeafHandle, TLVReader & aReader) +{ + WEAVE_ERROR err = WEAVE_NO_ERROR; + + switch (aLeafHandle) + { + case SecurityOpenCloseTrait::kPropertyHandle_OpenCloseState: + { + int32_t open_close_state; + + err = aReader.Get(open_close_state); + SuccessOrExit(err); + + mSecurityOpenCloseState = open_close_state; + + BoltLockMgr().EvaluateChange(); + + NRF_LOG_INFO("SecurityOpenCloseTrait -> Open Close State: %d", open_close_state); + break; + } + case SecurityOpenCloseTrait::kPropertyHandle_BypassRequested: + { + /* Bypass is not a supported feature in this example */ + bool bypass_requested; + err = aReader.Get(bypass_requested); + SuccessOrExit(err); + NRF_LOG_INFO("SecurityOpenCloseTrait -> Bypass Requested: %d", bypass_requested); + break; + } + + case SecurityOpenCloseTrait::kPropertyHandle_FirstObservedAtMs: + { + uint64_t currentTime = 0; + err = aReader.Get(currentTime); + NRF_LOG_INFO("SecurityOpenCloseTrait -> First Observed At (ms): %llu", currentTime); + SuccessOrExit(err); + break; + } + + default: + NRF_LOG_INFO("SecurityOpenCloseTrait::Unexpected Leaf"); + break; + } + +exit: + return err; +} \ No newline at end of file diff --git a/main/traits/include/BoltLockTraitDataSource.h b/main/traits/include/BoltLockTraitDataSource.h index b2dbf194..a5db5178 100644 --- a/main/traits/include/BoltLockTraitDataSource.h +++ b/main/traits/include/BoltLockTraitDataSource.h @@ -25,6 +25,7 @@ #ifndef BOLT_LOCK_TRAIT_DATA_SOURCE_H #define BOLT_LOCK_TRAIT_DATA_SOURCE_H +#include "AppEvent.h" #include class BoltLockTraitDataSource : public nl::Weave::Profiles::DataManagement::TraitDataSource @@ -32,6 +33,7 @@ class BoltLockTraitDataSource : public nl::Weave::Profiles::DataManagement::Trai public: BoltLockTraitDataSource(); + void Init(); bool IsLocked(); void InitiateLock(int32_t aLockActor); void InitiateUnlock(int32_t aLockActor); @@ -48,6 +50,7 @@ class BoltLockTraitDataSource : public nl::Weave::Profiles::DataManagement::Trai const int64_t & aExpiryTimeMicroSecond, const bool aIsMustBeVersionValid, const uint64_t & aMustBeVersion, nl::Weave::TLV::TLVReader & aArgumentReader); + static void LockActionEventHandler(const AppEvent * aEvent, intptr_t Arg); int32_t mLockedState; int32_t mLockActor; int32_t mActuatorState; diff --git a/main/traits/include/SecurityOpenCloseTraitDataSink.h b/main/traits/include/SecurityOpenCloseTraitDataSink.h new file mode 100644 index 00000000..4da4380d --- /dev/null +++ b/main/traits/include/SecurityOpenCloseTraitDataSink.h @@ -0,0 +1,38 @@ +/* + * + * 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. + */ + +#ifndef SECURITY_OPEN_CLOSE_TRAIT_DATA_SINK_H +#define SECURITY_OPEN_CLOSE_TRAIT_DATA_SINK_H + +#include +#include + +#include + +class SecurityOpenCloseTraitDataSink : public nl::Weave::Profiles::DataManagement::TraitDataSink +{ +public: + SecurityOpenCloseTraitDataSink(); + bool IsOpen(); +private: + WEAVE_ERROR SetLeafData(nl::Weave::Profiles::DataManagement::PropertyPathHandle aLeafHandle, + nl::Weave::TLV::TLVReader & aReader); + + int32_t mSecurityOpenCloseState; +}; +#endif // SECURITY_OPEN_CLOSE_TRAIT_DATA_SINK_H