diff --git a/examples/lit-icd-app/esp32/main/IcdUatButton.cpp b/examples/lit-icd-app/esp32/main/IcdUatButton.cpp
new file mode 100644
index 00000000000000..b45b17c634b877
--- /dev/null
+++ b/examples/lit-icd-app/esp32/main/IcdUatButton.cpp
@@ -0,0 +1,148 @@
+/*
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "IcdUatButton.h"
+
+#include "driver/gpio.h"
+#include "esp_attr.h"
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+#include "freertos/task.h"
+#include "hal/gpio_types.h"
+#include <cstdint>
+
+#define ESP_INTR_FLAG_DEFAULT 0
+
+static const char TAG[]              = "Button";
+QueueHandle_t UatButton::sEventQueue = nullptr;
+TaskHandle_t UatButton::sTaskHandle  = nullptr;
+
+static void IRAM_ATTR gpio_isr_handler(void * arg)
+{
+    if (UatButton::sEventQueue)
+    {
+        UatButton * button = (UatButton *) arg;
+        button->GpioIntrEnable(false);
+        xQueueSendFromISR(UatButton::sEventQueue, &button, NULL);
+    }
+}
+
+void UatButton::RunEventLoop(void * arg)
+{
+    bool eventDone     = true;
+    UatButton * button = nullptr;
+
+    for (;;)
+    {
+        if (xQueueReceive(sEventQueue, &button, portMAX_DELAY) == pdTRUE && button)
+        {
+            button->GpioIntrEnable(false);
+            eventDone = false;
+        }
+        while (!eventDone)
+        {
+            // GPIO Pull up is enabled so the button is pressed when this value is false.
+            bool value = gpio_get_level(button->mGpioNum);
+            switch (button->mState)
+            {
+            case ButtonState::kIdle:
+                button->mState = value == false ? ButtonState::kPressed : ButtonState::kIdle;
+                break;
+            case ButtonState::kPressed:
+                button->mState = value == false ? ButtonState::kPressed : ButtonState::kReleased;
+                break;
+            case ButtonState::kReleased:
+                button->mState = ButtonState::kIdle;
+                if (button->mUatButtonPressCallback)
+                {
+                    button->mUatButtonPressCallback(button);
+                }
+                break;
+            default:
+                break;
+            }
+            if (button->mState == ButtonState::kIdle)
+            {
+                button->GpioIntrEnable(true);
+                eventDone = true;
+                break;
+            }
+            vTaskDelay(10 / portTICK_PERIOD_MS);
+        }
+    }
+}
+
+void UatButton::GpioIntrEnable(bool enable)
+{
+    if (enable)
+    {
+        gpio_intr_enable(mGpioNum);
+    }
+    else
+    {
+        gpio_intr_disable(mGpioNum);
+    }
+}
+
+void UatButton::Init(gpio_num_t gpioNum, esp_sleep_ext1_wakeup_mode_t wakeupMode)
+{
+    mGpioNum              = gpioNum;
+    mState                = ButtonState::kIdle;
+    gpio_config_t io_conf = {};
+    io_conf.intr_type     = GPIO_INTR_LOW_LEVEL;
+    io_conf.pin_bit_mask  = (1ULL << static_cast<uint8_t>(mGpioNum));
+    io_conf.mode          = GPIO_MODE_INPUT;
+    io_conf.pull_down_en  = GPIO_PULLDOWN_DISABLE;
+    io_conf.pull_up_en    = GPIO_PULLUP_ENABLE;
+    // configure GPIO with the given settings
+    gpio_config(&io_conf);
+    if (!sEventQueue)
+    {
+        // create a queue to handle gpio event from isr
+        sEventQueue = xQueueCreate(10, sizeof(UatButton *));
+        if (!sEventQueue)
+        {
+            ESP_LOGE(TAG, "Failed to create GPIO EventQueue");
+            return;
+        }
+    }
+    if (!sTaskHandle)
+    {
+        // start gpio task
+        xTaskCreate(RunEventLoop, "UatButton", 4096, nullptr, 10, &sTaskHandle);
+        if (!sTaskHandle)
+        {
+            ESP_LOGE(TAG, "Failed to create GPIO Task");
+            return;
+        }
+    }
+    // install gpio isr service
+    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
+    // hook isr handler for specific gpio pin
+    gpio_isr_handler_add(mGpioNum, gpio_isr_handler, (void *) this);
+    ESP_LOGI(TAG, "UAT Button initialized..");
+    // Configure RTC IO wake up
+    esp_sleep_enable_ext1_wakeup(1ULL << static_cast<uint8_t>(mGpioNum), wakeupMode);
+#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
+    rtc_gpio_pulldown_dis(mGpioNum);
+    rtc_gpio_pullup_en(mGpioNum);
+#else
+    gpio_pulldown_dis(mGpioNum);
+    gpio_pullup_en(mGpioNum);
+#endif
+}
diff --git a/examples/lit-icd-app/esp32/main/include/IcdUatButton.h b/examples/lit-icd-app/esp32/main/include/IcdUatButton.h
new file mode 100644
index 00000000000000..2104d1a7c4079d
--- /dev/null
+++ b/examples/lit-icd-app/esp32/main/include/IcdUatButton.h
@@ -0,0 +1,50 @@
+/*
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#pragma once
+
+#include <driver/gpio.h>
+#include <esp_sleep.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/queue.h>
+#include <freertos/task.h>
+
+class UatButton
+{
+public:
+    using UatButtonPressCallback = void (*)(UatButton *);
+
+    enum class ButtonState : uint8_t
+    {
+        kIdle = 0,
+        kPressed,
+        kReleased,
+    };
+
+    void Init(gpio_num_t gpioNum, esp_sleep_ext1_wakeup_mode_t wakeMode);
+    void SetUatButtonPressCallback(UatButtonPressCallback buttonCallback) { mUatButtonPressCallback = buttonCallback; }
+    void GpioIntrEnable(bool enable);
+
+    static void RunEventLoop(void * arg);
+    static TaskHandle_t sTaskHandle;
+    static QueueHandle_t sEventQueue;
+
+private:
+    gpio_num_t mGpioNum;
+    ButtonState mState;
+    UatButtonPressCallback mUatButtonPressCallback;
+};
diff --git a/examples/lit-icd-app/esp32/main/main.cpp b/examples/lit-icd-app/esp32/main/main.cpp
index 340ae124d816c3..4bb736b219eabd 100644
--- a/examples/lit-icd-app/esp32/main/main.cpp
+++ b/examples/lit-icd-app/esp32/main/main.cpp
@@ -15,6 +15,7 @@
  *    limitations under the License.
  */
 
+#include "app/icd/server/ICDNotifier.h"
 #include "esp_log.h"
 #include "esp_netif.h"
 #include "esp_system.h"
@@ -24,6 +25,8 @@
 #include "nvs_flash.h"
 
 #include <DeviceCallbacks.h>
+#include <IcdUatButton.h>
+#include <app/icd/server/ICDManager.h>
 #include <app/server/Server.h>
 #include <common/CHIPDeviceManager.h>
 #include <common/Esp32AppServer.h>
@@ -31,6 +34,7 @@
 #include <credentials/DeviceAttestationCredsProvider.h>
 #include <credentials/examples/DeviceAttestationCredsExample.h>
 #include <platform/ESP32/ESP32Utils.h>
+#include <platform/PlatformManager.h>
 
 #if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
 #include <platform/ESP32/ESP32FactoryDataProvider.h>
@@ -58,6 +62,18 @@ chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider;
 #error "Currently this example only support Thread chips"
 #endif
 
+#ifdef CONFIG_IDF_TARGET_ESP32H2
+// GPIO9-GPIO14 could be used to wake up ESP32-H2.
+// For ESP32-H2 DevKitM, the boot button is GPIO9.
+#define UAT_GPIO GPIO_NUM_9
+#elif defined(CONFIG_IDF_TARGET_ESP32C6)
+// GPIO0-GPIO7 could be used to wake up ESP32-C6.
+// For ESP32-C6 DevKitC, the boot button is GPIO9, we cannot use it to wake up the chip.
+#define UAT_GPIO GPIO_NUM_7
+#else
+#error "Unsupport IDF target"
+#endif
+
 using namespace ::chip;
 using namespace ::chip::DeviceManager;
 using namespace ::chip::Credentials;
@@ -66,6 +82,11 @@ extern const char TAG[] = "lit-icd-app";
 
 static AppDeviceCallbacks EchoCallbacks;
 
+static void UatButtonHandler(UatButton * button)
+{
+    DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t) { app::ICDNotifier::GetInstance().NotifyNetworkActivityNotification(); });
+}
+
 static void InitServer(intptr_t context)
 {
     Esp32AppServer::Init(); // Init ZCL Data Model and CHIP App Server AND Initialize device attestation config
@@ -110,6 +131,9 @@ extern "C" void app_main()
     SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider());
 #endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
     ESPOpenThreadInit();
+    static UatButton sButton;
+    sButton.Init(UAT_GPIO, ESP_EXT1_WAKEUP_ANY_LOW);
+    sButton.SetUatButtonPressCallback(UatButtonHandler);
 
     chip::DeviceLayer::PlatformMgr().ScheduleWork(InitServer, reinterpret_cast<intptr_t>(nullptr));
 }
diff --git a/examples/platform/esp32/common/Esp32AppServer.cpp b/examples/platform/esp32/common/Esp32AppServer.cpp
index 53957784b8c5f5..873115c25ab1af 100644
--- a/examples/platform/esp32/common/Esp32AppServer.cpp
+++ b/examples/platform/esp32/common/Esp32AppServer.cpp
@@ -61,14 +61,14 @@ static app::Clusters::NetworkCommissioning::Instance
     sEthernetNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::ESPEthernetDriver::GetInstance()));
 #endif
 
-#if CONFIG_TEST_EVENT_TRIGGER_ENABLED && CONFIG_ENABLE_OTA_REQUESTOR
+#if CONFIG_TEST_EVENT_TRIGGER_ENABLED
 static uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
                                                                                           0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
                                                                                           0xcc, 0xdd, 0xee, 0xff };
 #endif
 } // namespace
 
-#if CONFIG_TEST_EVENT_TRIGGER_ENABLED && CONFIG_ENABLE_OTA_REQUESTOR
+#if CONFIG_TEST_EVENT_TRIGGER_ENABLED
 static int hex_digit_to_int(char hex)
 {
     if ('A' <= hex && hex <= 'F')
@@ -107,7 +107,7 @@ static size_t hex_string_to_binary(const char * hex_string, uint8_t * buf, size_
 
     return buf_size;
 }
-#endif // CONFIG_TEST_EVENT_TRIGGER_ENABLED && CONFIG_ENABLE_OTA_REQUESTOR
+#endif // CONFIG_TEST_EVENT_TRIGGER_ENABLED
 
 void Esp32AppServer::DeInitBLEIfCommissioned(void)
 {
@@ -158,7 +158,7 @@ void Esp32AppServer::Init(AppDelegate * sAppDelegate)
 {
     // Init ZCL Data Model and CHIP App Server
     static chip::CommonCaseDeviceServerInitParams initParams;
-#if CONFIG_TEST_EVENT_TRIGGER_ENABLED && CONFIG_ENABLE_OTA_REQUESTOR
+#if CONFIG_TEST_EVENT_TRIGGER_ENABLED
     if (hex_string_to_binary(CONFIG_TEST_EVENT_TRIGGER_ENABLE_KEY, sTestEventTriggerEnableKey,
                              sizeof(sTestEventTriggerEnableKey)) == 0)
     {
@@ -166,9 +166,11 @@ void Esp32AppServer::Init(AppDelegate * sAppDelegate)
         memset(sTestEventTriggerEnableKey, 0, sizeof(sTestEventTriggerEnableKey));
     }
     static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{};
-    static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{};
     VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR);
+#if CONFIG_ENABLE_OTA_REQUESTOR
+    static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{};
     VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR);
+#endif
     initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate;
 #endif // CONFIG_TEST_EVENT_TRIGGER_ENABLED && CONFIG_ENABLE_OTA_REQUESTOR
     (void) initParams.InitializeStaticResourcesBeforeServerInit();