diff --git a/examples/light-switch-app/telink/include/AppTask.h b/examples/light-switch-app/telink/include/AppTask.h
index 70abb978d2cdc1..b682884b30155d 100755
--- a/examples/light-switch-app/telink/include/AppTask.h
+++ b/examples/light-switch-app/telink/include/AppTask.h
@@ -56,11 +56,14 @@ class AppTask
 
     void DispatchEvent(AppEvent * event);
 
+    static void UpdateStatusLED();
     static void SwitchActionButtonEventHandler(void);
     static void FactoryResetButtonEventHandler(void);
     static void StartThreadButtonEventHandler(void);
     static void StartBleAdvButtonEventHandler(void);
 
+    static void ChipEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg);
+
     static void FactoryResetHandler(AppEvent * aEvent);
     static void StartThreadHandler(AppEvent * aEvent);
     static void SwitchActionEventHandler(AppEvent * aEvent);
diff --git a/examples/light-switch-app/telink/src/AppTask.cpp b/examples/light-switch-app/telink/src/AppTask.cpp
index 8078ac9fff1b6f..8b8bac4ed2d878 100755
--- a/examples/light-switch-app/telink/src/AppTask.cpp
+++ b/examples/light-switch-app/telink/src/AppTask.cpp
@@ -86,6 +86,8 @@ CHIP_ERROR AppTask::Init()
     LEDWidget::InitGpio(SYSTEM_STATE_LED_PORT);
     sStatusLED.Init(SYSTEM_STATE_LED_PIN);
 
+    UpdateStatusLED();
+
     InitButtons();
 
     // Init ZCL Data Model and start server
@@ -108,6 +110,11 @@ CHIP_ERROR AppTask::Init()
 
     PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE));
 
+    // Add CHIP event handler and start CHIP thread.
+    // Note that all the initialization code should happen prior to this point to avoid data races
+    // between the main and the CHIP threads.
+    PlatformMgr().AddEventHandler(ChipEventHandler, 0);
+
     ret = ConnectivityMgr().SetBLEDeviceName("TelinkSwitch");
     if (ret != CHIP_NO_ERROR)
     {
@@ -140,37 +147,6 @@ CHIP_ERROR AppTask::StartApp()
             ret = k_msgq_get(&sAppEventQueue, &event, K_NO_WAIT);
         }
 
-        // Collect connectivity and configuration state from the CHIP stack.  Because the
-        // CHIP event loop is being run in a separate task, the stack must be locked
-        // while these values are queried.  However we use a non-blocking lock request
-        // (TryLockChipStack()) to avoid blocking other UI activities when the CHIP
-        // task is busy (e.g. with a long crypto operation).
-
-        if (PlatformMgr().TryLockChipStack())
-        {
-            sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
-            sIsThreadEnabled     = ConnectivityMgr().IsThreadEnabled();
-            sIsThreadAttached    = ConnectivityMgr().IsThreadAttached();
-            sHaveBLEConnections  = (ConnectivityMgr().NumBLEConnections() != 0);
-            PlatformMgr().UnlockChipStack();
-        }
-
-        if (sIsThreadProvisioned && sIsThreadEnabled)
-        {
-            if (sIsThreadAttached)
-            {
-                sStatusLED.Blink(950, 50);
-            }
-            else
-            {
-                sStatusLED.Blink(100, 100);
-            }
-        }
-        else
-        {
-            sStatusLED.Blink(50, 950);
-        }
-
         sStatusLED.Animate();
     }
 }
@@ -273,6 +249,52 @@ void AppTask::StartBleAdvHandler(AppEvent * aEvent)
     }
 }
 
+void AppTask::UpdateStatusLED()
+{
+    if (sIsThreadProvisioned && sIsThreadEnabled)
+    {
+        if (sIsThreadAttached)
+        {
+            sStatusLED.Blink(950, 50);
+        }
+        else
+        {
+            sStatusLED.Blink(100, 100);
+        }
+    }
+    else
+    {
+        sStatusLED.Blink(50, 950);
+    }
+}
+
+void AppTask::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* arg */)
+{
+    switch (event->Type)
+    {
+    case DeviceEventType::kCHIPoBLEAdvertisingChange:
+        sHaveBLEConnections = ConnectivityMgr().NumBLEConnections() != 0;
+        UpdateStatusLED();
+        break;
+    case DeviceEventType::kThreadStateChange:
+        sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
+        sIsThreadEnabled     = ConnectivityMgr().IsThreadEnabled();
+        sIsThreadAttached    = ConnectivityMgr().IsThreadAttached();
+        UpdateStatusLED();
+        break;
+    case DeviceEventType::kThreadConnectivityChange:
+#if CONFIG_CHIP_OTA_REQUESTOR
+        if (event->ThreadConnectivityChange.Result == kConnectivity_Established)
+        {
+            InitBasicOTARequestor();
+        }
+#endif
+        break;
+    default:
+        break;
+    }
+}
+
 void AppTask::ActionInitiated(AppTask::Action_t aAction, int32_t aActor) {}
 
 void AppTask::ActionCompleted(AppTask::Action_t aAction, int32_t aActor)
diff --git a/examples/lighting-app/telink/include/AppTask.h b/examples/lighting-app/telink/include/AppTask.h
index d660ea20fbe0c2..d926c5286f41f3 100644
--- a/examples/lighting-app/telink/include/AppTask.h
+++ b/examples/lighting-app/telink/include/AppTask.h
@@ -46,11 +46,14 @@ class AppTask
 
     void DispatchEvent(AppEvent * event);
 
+    static void UpdateStatusLED();
     static void LightingActionButtonEventHandler(void);
     static void FactoryResetButtonEventHandler(void);
     static void StartThreadButtonEventHandler(void);
     static void StartBleAdvButtonEventHandler(void);
 
+    static void ChipEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg);
+
     static void FactoryResetHandler(AppEvent * aEvent);
     static void StartThreadHandler(AppEvent * aEvent);
     static void LightingActionEventHandler(AppEvent * aEvent);
diff --git a/examples/lighting-app/telink/src/AppTask.cpp b/examples/lighting-app/telink/src/AppTask.cpp
index f2e370faae8f17..18a99610cdbab8 100644
--- a/examples/lighting-app/telink/src/AppTask.cpp
+++ b/examples/lighting-app/telink/src/AppTask.cpp
@@ -86,6 +86,8 @@ CHIP_ERROR AppTask::Init()
     LEDWidget::InitGpio(SYSTEM_STATE_LED_PORT);
     sStatusLED.Init(SYSTEM_STATE_LED_PIN);
 
+    UpdateStatusLED();
+
     InitButtons();
 
     // Init lighting manager
@@ -109,6 +111,11 @@ CHIP_ERROR AppTask::Init()
     ConfigurationMgr().LogDeviceConfig();
     PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE));
 
+    // Add CHIP event handler and start CHIP thread.
+    // Note that all the initialization code should happen prior to this point to avoid data races
+    // between the main and the CHIP threads.
+    PlatformMgr().AddEventHandler(ChipEventHandler, 0);
+
     ret = ConnectivityMgr().SetBLEDeviceName("TelinkLight");
     if (ret != CHIP_NO_ERROR)
     {
@@ -141,37 +148,6 @@ CHIP_ERROR AppTask::StartApp()
             ret = k_msgq_get(&sAppEventQueue, &event, K_NO_WAIT);
         }
 
-        // Collect connectivity and configuration state from the CHIP stack.  Because the
-        // CHIP event loop is being run in a separate task, the stack must be locked
-        // while these values are queried.  However we use a non-blocking lock request
-        // (TryLockChipStack()) to avoid blocking other UI activities when the CHIP
-        // task is busy (e.g. with a long crypto operation).
-
-        if (PlatformMgr().TryLockChipStack())
-        {
-            sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
-            sIsThreadEnabled     = ConnectivityMgr().IsThreadEnabled();
-            sIsThreadAttached    = ConnectivityMgr().IsThreadAttached();
-            sHaveBLEConnections  = (ConnectivityMgr().NumBLEConnections() != 0);
-            PlatformMgr().UnlockChipStack();
-        }
-
-        if (sIsThreadProvisioned && sIsThreadEnabled)
-        {
-            if (sIsThreadAttached)
-            {
-                sStatusLED.Blink(950, 50);
-            }
-            else
-            {
-                sStatusLED.Blink(100, 100);
-            }
-        }
-        else
-        {
-            sStatusLED.Blink(50, 950);
-        }
-
         sStatusLED.Animate();
     }
 }
@@ -282,6 +258,52 @@ void AppTask::StartBleAdvHandler(AppEvent * aEvent)
     }
 }
 
+void AppTask::UpdateStatusLED()
+{
+    if (sIsThreadProvisioned && sIsThreadEnabled)
+    {
+        if (sIsThreadAttached)
+        {
+            sStatusLED.Blink(950, 50);
+        }
+        else
+        {
+            sStatusLED.Blink(100, 100);
+        }
+    }
+    else
+    {
+        sStatusLED.Blink(50, 950);
+    }
+}
+
+void AppTask::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* arg */)
+{
+    switch (event->Type)
+    {
+    case DeviceEventType::kCHIPoBLEAdvertisingChange:
+        sHaveBLEConnections = ConnectivityMgr().NumBLEConnections() != 0;
+        UpdateStatusLED();
+        break;
+    case DeviceEventType::kThreadStateChange:
+        sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
+        sIsThreadEnabled     = ConnectivityMgr().IsThreadEnabled();
+        sIsThreadAttached    = ConnectivityMgr().IsThreadAttached();
+        UpdateStatusLED();
+        break;
+    case DeviceEventType::kThreadConnectivityChange:
+#if CONFIG_CHIP_OTA_REQUESTOR
+        if (event->ThreadConnectivityChange.Result == kConnectivity_Established)
+        {
+            InitBasicOTARequestor();
+        }
+#endif
+        break;
+    default:
+        break;
+    }
+}
+
 void AppTask::ActionInitiated(LightingManager::Action_t aAction, int32_t aActor)
 {
     if (aAction == LightingManager::ON_ACTION)