/* * * Copyright (c) 2021 Project CHIP Authors * 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. */ #if !defined(GP_APP_DIVERSITY_POWERCYCLECOUNTING) #error This application requires powercycle counting. #endif #include "powercycle_counting.h" #include "qvIO.h" #include "AppConfig.h" #include "AppEvent.h" #include "AppTask.h" #include "ota.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::chip; using namespace ::chip::app; using namespace ::chip::TLV; using namespace ::chip::Credentials; using namespace ::chip::DeviceLayer; #include #define FACTORY_RESET_TRIGGER_TIMEOUT 3000 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 #define OTA_START_TRIGGER_TIMEOUT 1500 #define APP_TASK_STACK_SIZE (3 * 1024) #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 #define QPG_LOCK_ENDPOINT_ID (1) #define SECONDS_IN_HOUR (3600) // we better keep this 3600 #define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * SECONDS_IN_HOUR) // increment every hour #define NMBR_OF_RESETS_BLE_ADVERTISING (3) namespace { TaskHandle_t sAppTaskHandle; QueueHandle_t sAppEventQueue; bool sIsThreadProvisioned = false; bool sIsThreadEnabled = false; bool sHaveBLEConnections = false; bool sIsBLEAdvertisingEnabled = false; // NOTE! This key is for test/certification only and should not be available in production devices! uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; StaticQueue_t sAppEventQueueStruct; StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; StaticTask_t appTaskStruct; chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider; } // namespace AppTask AppTask::sAppTask; namespace { constexpr int extDiscTimeoutSecs = 20; } Clusters::Identify::EffectIdentifierEnum sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; /********************************************************** * Identify Callbacks *********************************************************/ namespace { void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * appState) { sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; } } // namespace void OnTriggerIdentifyEffect(Identify * identify) { sIdentifyEffect = identify->mCurrentEffectIdentifier; if (identify->mEffectVariant != Clusters::Identify::EffectVariantEnum::kDefault) { ChipLogDetail(AppServer, "Identify Effect Variant unsupported. Using default"); } switch (sIdentifyEffect) { case Clusters::Identify::EffectIdentifierEnum::kBlink: ChipLogProgress(Zcl, "kBlink"); qvIO_LedBlink(LOCK_STATE_LED, 100, 100); break; case Clusters::Identify::EffectIdentifierEnum::kBreathe: ChipLogProgress(Zcl, "kBreathe"); qvIO_LedBlink(LOCK_STATE_LED, 500, 500); break; case Clusters::Identify::EffectIdentifierEnum::kOkay: ChipLogProgress(Zcl, "kOkay"); qvIO_LedBlink(LOCK_STATE_LED, 1000, 1000); break; case Clusters::Identify::EffectIdentifierEnum::kChannelChange: ChipLogProgress(Zcl, "kChannelChange"); SystemLayer().ScheduleLambda([identify] { (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerIdentifyEffectCompleted, identify); }); break; case Clusters::Identify::EffectIdentifierEnum::kFinishEffect: ChipLogProgress(Zcl, "kFinishEffect"); SystemLayer().ScheduleLambda([identify] { (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerIdentifyEffectCompleted, identify); }); break; case Clusters::Identify::EffectIdentifierEnum::kStopEffect: ChipLogProgress(Zcl, "kStopEffect"); SystemLayer().ScheduleLambda( [identify] { (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); }); sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; qvIO_LedSet(LOCK_STATE_LED, false); break; default: ChipLogProgress(Zcl, "No identifier effect"); } } Identify gIdentify = { chip::EndpointId{ 1 }, [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); }, [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); }, Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, OnTriggerIdentifyEffect, }; void LockOpenThreadTask(void) { chip::DeviceLayer::ThreadStackMgr().LockThreadStack(); } void UnlockOpenThreadTask(void) { chip::DeviceLayer::ThreadStackMgr().UnlockThreadStack(); } CHIP_ERROR AppTask::StartAppTask() { sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct); if (sAppEventQueue == nullptr) { ChipLogError(NotSpecified, "Failed to allocate app event queue"); return CHIP_ERROR_NO_MEMORY; } // Start App task. sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), nullptr, 1, appStack, &appTaskStruct); if (sAppTaskHandle == nullptr) { return CHIP_ERROR_NO_MEMORY; } return CHIP_NO_ERROR; } void AppTask::InitServer(intptr_t arg) { static chip::CommonCaseDeviceServerInitParams initParams; (void) initParams.InitializeStaticResourcesBeforeServerInit(); gExampleDeviceInfoProvider.SetStorageDelegate(initParams.persistentStorageDelegate); chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; nativeParams.lockCb = LockOpenThreadTask; nativeParams.unlockCb = UnlockOpenThreadTask; nativeParams.openThreadInstancePtr = chip::DeviceLayer::ThreadStackMgrImpl().OTInstance(); initParams.endpointNativeParams = static_cast(&nativeParams); // Use GenericFaultTestEventTriggerHandler to inject faults static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; static GenericFaultTestEventTriggerHandler sFaultTestEventTriggerHandler{}; VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sFaultTestEventTriggerHandler) == CHIP_NO_ERROR); (void) initParams.InitializeStaticResourcesBeforeServerInit(); initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; chip::Server::GetInstance().Init(initParams); #if CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY chip::app::DnssdServer::Instance().SetExtendedDiscoveryTimeoutSecs(extDiscTimeoutSecs); #endif } void AppTask::OpenCommissioning(intptr_t arg) { // Enable BLE advertisements SystemLayer().ScheduleLambda([] { CHIP_ERROR err; err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(); if (err == CHIP_NO_ERROR) { ChipLogProgress(NotSpecified, "BLE advertising started. Waiting for Pairing."); } }); } CHIP_ERROR AppTask::Init() { CHIP_ERROR err = CHIP_NO_ERROR; PlatformMgr().AddEventHandler(MatterEventHandler, 0); ChipLogProgress(NotSpecified, "Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); // Init ZCL Data Model and start server PlatformMgr().ScheduleWork(InitServer, 0); ReturnErrorOnFailure(mFactoryDataProvider.Init()); SetDeviceInstanceInfoProvider(&mFactoryDataProvider); SetCommissionableDataProvider(&mFactoryDataProvider); SetDeviceAttestationCredentialsProvider(&mFactoryDataProvider); // Setup Bolt err = BoltLockMgr().Init(); if (err != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "BoltLockMgr().Init() failed"); return err; } BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted); // Setup button handler qvIO_SetBtnCallback(ButtonEventHandler); qvIO_LedSet(LOCK_STATE_LED, !BoltLockMgr().IsUnlocked()); UpdateClusterState(); ConfigurationMgr().LogDeviceConfig(); PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)); sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned(); sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled(); sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); sIsBLEAdvertisingEnabled = ConnectivityMgr().IsBLEAdvertisingEnabled(); UpdateLEDs(); err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), TotalHoursTimerHandler, this); if (err != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); } return err; } void AppTask::AppTaskMain(void * pvParameter) { AppEvent event; while (true) { BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, portMAX_DELAY); while (eventReceived == pdTRUE) { sAppTask.DispatchEvent(&event); eventReceived = xQueueReceive(sAppEventQueue, &event, 0); } } } void AppTask::JammedLockEventHandler(AppEvent * aEvent) { SystemLayer().ScheduleLambda([] { bool retVal; retVal = DoorLockServer::Instance().SendLockAlarmEvent(QPG_LOCK_ENDPOINT_ID, AlarmCodeEnum::kLockJammed); if (!retVal) { ChipLogProgress(NotSpecified, "[BTN] Lock jammed event send failed"); } else { ChipLogProgress(NotSpecified, "[BTN] Lock jammed event sent"); } }); } void AppTask::LockActionEventHandler(AppEvent * aEvent) { bool initiated = false; BoltLockManager::Action_t action; int32_t actor; CHIP_ERROR err = CHIP_NO_ERROR; if (aEvent->Type == AppEvent::kEventType_Lock) { 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 = AppEvent::kEventType_Button; } else { err = CHIP_ERROR_INTERNAL; } if (err == CHIP_NO_ERROR) { initiated = BoltLockMgr().InitiateAction(actor, action); if (!initiated) { ChipLogProgress(NotSpecified, "Action is already in progress or active."); } } } void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed) { if (btnIdx != APP_LOCK_BUTTON && btnIdx != APP_FUNCTION_BUTTON && btnIdx != APP_LOCK_JAMMED_BUTTON) { return; } AppEvent button_event = {}; button_event.Type = AppEvent::kEventType_Button; button_event.ButtonEvent.ButtonIdx = btnIdx; button_event.ButtonEvent.Action = btnPressed; if (btnIdx == APP_LOCK_BUTTON && btnPressed == true) { button_event.Handler = LockActionEventHandler; } else if (btnIdx == APP_LOCK_JAMMED_BUTTON && btnPressed == true) { button_event.Handler = JammedLockEventHandler; } else if (btnIdx == APP_FUNCTION_BUTTON) { // Hand off to Functionality handler - depends on duration of press button_event.Handler = FunctionHandler; } else { return; } sAppTask.PostEvent(&button_event); } void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState) { AppEvent event; event.Type = AppEvent::kEventType_Timer; event.TimerEvent.Context = aAppState; event.Handler = FunctionTimerEventHandler; sAppTask.PostEvent(&event); } void AppTask::TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState) { ChipLogProgress(NotSpecified, "HourlyTimer"); CHIP_ERROR err; uint32_t totalOperationalHours = 0; err = ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours); if (err == CHIP_NO_ERROR) { ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); } else if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) { totalOperationalHours = 0; // set this explicitly to 0 for safety ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / SECONDS_IN_HOUR)); } else { ChipLogError(DeviceLayer, "Failed to get total operational hours of the Node"); } err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), TotalHoursTimerHandler, nullptr); if (err != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); } } void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) { if (aEvent->Type != AppEvent::kEventType_Timer) { return; } // If we reached here, the button was held past OTA_START_TRIGGER_TIMEOUT, // initiate OTA update if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) { ChipLogProgress(NotSpecified, "[BTN] Release button now to start Software Updater"); ChipLogProgress(NotSpecified, "[BTN] Hold to trigger Factory Reset"); sAppTask.mFunction = kFunction_SoftwareUpdate; sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT - OTA_START_TRIGGER_TIMEOUT); } else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) { ChipLogProgress(NotSpecified, "[BTN] Factory Reset selected. Release within %us to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT / 1000); // 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. qvIO_LedSet(SYSTEM_STATE_LED, false); qvIO_LedSet(LOCK_STATE_LED, false); qvIO_LedBlink(SYSTEM_STATE_LED, 500, 500); qvIO_LedBlink(LOCK_STATE_LED, 500, 500); } else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) { // Actually trigger Factory Reset sAppTask.mFunction = kFunction_NoneSelected; chip::Server::GetInstance().ScheduleFactoryReset(); } } void AppTask::FunctionHandler(AppEvent * aEvent) { if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON) { return; } // To trigger BLE advertising: press the APP_FUNCTION_BUTTON button briefly (< // OTA_START_TRIGGER_TIMEOUT). To trigger software update: press the button // between 1.5sec and 3sec. To initiate factory reset: press the // APP_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 APP_FUNCTION_BUTTON once all LEDs // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT if (aEvent->ButtonEvent.Action == true) { if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) { ChipLogProgress(NotSpecified, "[BTN] Hold to select function:"); ChipLogProgress(NotSpecified, "[BTN] - Trigger BLE adv (0-1.5s)"); ChipLogProgress(NotSpecified, "[BTN] - Trigger OTA (1.5-3s)"); ChipLogProgress(NotSpecified, "[BTN] - Factory Reset (>6s)"); sAppTask.StartTimer(OTA_START_TRIGGER_TIMEOUT); sAppTask.mFunction = kFunction_StartBleAdv; } } else { // If the button was released before 1.5sec, trigger BLE advertising. if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) { sAppTask.CancelTimer(); sAppTask.mFunction = kFunction_NoneSelected; if (ConnectivityMgr().IsBLEAdvertisingEnabled()) { ChipLogProgress(NotSpecified, "BLE advertising already in progress."); } else { if (!ConnectivityMgr().IsThreadProvisioned()) { // Enable BLE advertisements and pairing window SystemLayer().ScheduleLambda([] { CHIP_ERROR err; if ((err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow()) == CHIP_NO_ERROR) { ChipLogProgress(NotSpecified, "BLE advertising started. Waiting for Pairing."); } }); } else { ChipLogError(NotSpecified, "Network is already provisioned, BLE advertisement not enabled"); } } } else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) { sAppTask.CancelTimer(); sAppTask.mFunction = kFunction_NoneSelected; ChipLogProgress(NotSpecified, "[BTN] Triggering OTA Query"); TriggerOTAQuery(); } else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) { // Set lock status LED back to show state of lock. qvIO_LedSet(LOCK_STATE_LED, !BoltLockMgr().IsUnlocked()); sAppTask.CancelTimer(); // Change the function to none selected since factory reset has been // canceled. sAppTask.mFunction = kFunction_NoneSelected; ChipLogProgress(NotSpecified, "[BTN] Factory Reset has been Canceled"); } } } void AppTask::CancelTimer() { SystemLayer().ScheduleLambda([this] { chip::DeviceLayer::SystemLayer().CancelTimer(TimerEventHandler, this); this->mFunctionTimerActive = false; }); } void AppTask::StartTimer(uint32_t aTimeoutInMs) { SystemLayer().ScheduleLambda([aTimeoutInMs, this] { CHIP_ERROR err; chip::DeviceLayer::SystemLayer().CancelTimer(TimerEventHandler, this); err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(aTimeoutInMs), TimerEventHandler, this); SuccessOrExit(err); this->mFunctionTimerActive = true; exit: if (err != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); } }); } void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor) { // If the action has been initiated by the lock, update the bolt lock trait // and start flashing the LEDs rapidly to indicate action initiation. if (aAction == BoltLockManager::LOCK_ACTION) { ChipLogProgress(NotSpecified, "Lock Action has been initiated"); } else if (aAction == BoltLockManager::UNLOCK_ACTION) { ChipLogProgress(NotSpecified, "Unlock Action has been initiated"); } if (aActor == AppEvent::kEventType_Button) { sAppTask.mSyncClusterToButtonAction = true; } if (aActor == AppEvent::kEventType_Lock) { sAppTask.mNotifyState = true; } qvIO_LedBlink(LOCK_STATE_LED, 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) { ChipLogProgress(NotSpecified, "Lock Action has been completed"); qvIO_LedSet(LOCK_STATE_LED, true); } else if (aAction == BoltLockManager::UNLOCK_ACTION) { ChipLogProgress(NotSpecified, "Unlock Action has been completed"); qvIO_LedSet(LOCK_STATE_LED, false); } if (sAppTask.mSyncClusterToButtonAction || sAppTask.mNotifyState) { sAppTask.UpdateClusterState(); sAppTask.mSyncClusterToButtonAction = false; sAppTask.mNotifyState = 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 != nullptr) { if (!xQueueSend(sAppEventQueue, aEvent, 1)) { ChipLogError(NotSpecified, "Failed to post event to app task event queue"); } } else { ChipLogError(NotSpecified, "Event Queue is nullptr should never happen"); } } void AppTask::DispatchEvent(AppEvent * aEvent) { if (aEvent->Handler) { aEvent->Handler(aEvent); } else { ChipLogError(NotSpecified, "Event received with no handler. Dropping event."); } } /** * Update cluster status after application level changes */ void AppTask::UpdateClusterState(void) { using namespace chip::app::Clusters; auto newValue = BoltLockMgr().IsUnlocked() ? DoorLock::DlLockState::kUnlocked : DoorLock::DlLockState::kLocked; SystemLayer().ScheduleLambda([newValue] { bool retVal = true; chip::app::DataModel::Nullable currentLockState; chip::app::Clusters::DoorLock::Attributes::LockState::Get(QPG_LOCK_ENDPOINT_ID, currentLockState); if (currentLockState.IsNull()) { Protocols::InteractionModel::Status status = DoorLock::Attributes::LockState::Set(QPG_LOCK_ENDPOINT_ID, newValue); if (status != Protocols::InteractionModel::Status::Success) { ChipLogError(NotSpecified, "ERR: updating DoorLock %x", to_underlying(status)); } } else { ChipLogProgress(NotSpecified, "Updating LockState attribute"); if (sAppTask.mSyncClusterToButtonAction) { retVal = DoorLockServer::Instance().SetLockState(QPG_LOCK_ENDPOINT_ID, newValue, OperationSourceEnum::kManual); } if (retVal && sAppTask.mNotifyState) { retVal = DoorLockServer::Instance().SetLockState(QPG_LOCK_ENDPOINT_ID, newValue, OperationSourceEnum::kRemote); } if (!retVal) { ChipLogError(NotSpecified, "ERR: updating DoorLock"); } } }); } void AppTask::UpdateLEDs(void) { // If system has "full connectivity", keep the LED On constantly. // // If thread and service provisioned, but not attached to the thread network // yet OR no connectivity to the service OR subscriptions are not fully // established THEN blink the LED Off for a short period of time. // // If the system has ble connection(s) uptill the stage above, THEN blink // the LEDs at an even rate of 100ms. // // Otherwise, turn the LED OFF. if (sIsThreadProvisioned && sIsThreadEnabled) { qvIO_LedSet(SYSTEM_STATE_LED, true); } else if (sIsThreadProvisioned && !sIsThreadEnabled) { qvIO_LedBlink(SYSTEM_STATE_LED, 950, 50); } else if (sHaveBLEConnections) { qvIO_LedBlink(SYSTEM_STATE_LED, 100, 100); } else if (sIsBLEAdvertisingEnabled) { qvIO_LedBlink(SYSTEM_STATE_LED, 50, 50); } else { // not commisioned yet qvIO_LedSet(SYSTEM_STATE_LED, false); } } void AppTask::MatterEventHandler(const ChipDeviceEvent * event, intptr_t) { switch (event->Type) { case DeviceEventType::kServiceProvisioningChange: { sIsThreadProvisioned = event->ServiceProvisioningChange.IsServiceProvisioned; UpdateLEDs(); break; } case DeviceEventType::kThreadConnectivityChange: { sIsThreadEnabled = (event->ThreadConnectivityChange.Result == kConnectivity_Established); UpdateLEDs(); break; } case DeviceEventType::kCHIPoBLEConnectionEstablished: { sHaveBLEConnections = true; UpdateLEDs(); break; } case DeviceEventType::kCHIPoBLEConnectionClosed: { sHaveBLEConnections = false; UpdateLEDs(); break; } case DeviceEventType::kCHIPoBLEAdvertisingChange: { sIsBLEAdvertisingEnabled = (event->CHIPoBLEAdvertisingChange.Result == kActivity_Started); UpdateLEDs(); break; } default: break; } } extern "C" { void gpAppFramework_Reset_cbTriggerResetCountCompleted(void) { uint8_t resetCount = gpAppFramework_Reset_GetResetCount(); ChipLogProgress(NotSpecified, "%d resets so far", resetCount); if (resetCount >= NMBR_OF_RESETS_BLE_ADVERTISING) { // Open commissioning if no fabric was available if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0) { ChipLogProgress(NotSpecified, "No fabrics, starting commissioning."); AppTask::OpenCommissioning((intptr_t) 0); } } } }