[ESP32] Fix build and crash and add DeviceManagementCluster, EVSE mode and DeviceManagementCluster mode. (#31591)

* [ESP32] Fix build failure and Add DeviceEnergyManagement clusters initialization

* Fix crash

* Support EVSE and Device Energy Management Mode

* Sync esp32 with linux app
diff --git a/.github/workflows/examples-esp32.yaml b/.github/workflows/examples-esp32.yaml
index 47908d6..2e5432d 100644
--- a/.github/workflows/examples-esp32.yaml
+++ b/.github/workflows/examples-esp32.yaml
@@ -165,3 +165,6 @@
 
             - name: Build example Lighting App (external platform)
               run: scripts/examples/esp_example.sh lighting-app sdkconfig.ext_plat.defaults
+
+            - name: Build example Energy Management App
+              run: scripts/examples/esp_example.sh energy-management-app sdkconfig.defaults
diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp
index 710b13f..2464388 100644
--- a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp
+++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp
@@ -27,7 +27,12 @@
 {
 
     SafeAttributePersistenceProvider * aProvider = GetSafeAttributePersistenceProvider();
-    EndpointId aEndpointId                       = mDelegate->GetEndpointId();
+    if (aProvider == nullptr)
+    {
+        ChipLogError(AppServer, "GetSafeAttributePersistenceProvider returned NULL");
+        return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
+    }
+    EndpointId aEndpointId = mDelegate->GetEndpointId();
     CHIP_ERROR err;
 
     // Restore ChargingEnabledUntil value
diff --git a/examples/energy-management-app/esp32/main/CMakeLists.txt b/examples/energy-management-app/esp32/main/CMakeLists.txt
index 1c7f2a5..46f97d7 100644
--- a/examples/energy-management-app/esp32/main/CMakeLists.txt
+++ b/examples/energy-management-app/esp32/main/CMakeLists.txt
@@ -50,6 +50,7 @@
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/time-format-localization-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/barrier-control-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/energy-evse-server"
+                      "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/device-energy-management-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/occupancy-sensor-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server"
@@ -63,6 +64,7 @@
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ota-requestor"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/groups-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server"
+                      "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/mode-base-server"
 )
 
 set(PRIV_REQUIRES_LIST chip QRCode bt led_strip app_update openthread driver nvs_flash spi_flash)
diff --git a/examples/energy-management-app/esp32/main/main.cpp b/examples/energy-management-app/esp32/main/main.cpp
index 77899a4..fd79e60 100644
--- a/examples/energy-management-app/esp32/main/main.cpp
+++ b/examples/energy-management-app/esp32/main/main.cpp
@@ -16,9 +16,12 @@
  */
 
 #include "DeviceCallbacks.h"
+#include <DeviceEnergyManagementManager.h>
 #include <EVSEManufacturerImpl.h>
 #include <EnergyEvseManager.h>
 #include <EnergyManagementManager.h>
+#include <device-energy-management-modes.h>
+#include <energy-evse-modes.h>
 
 #include "esp_log.h"
 #include <common/CHIPDeviceManager.h>
@@ -74,9 +77,11 @@
 using namespace ::chip::DeviceManager;
 using namespace ::chip::DeviceLayer;
 
-static EnergyEvseDelegate * gDelegate       = nullptr;
-static EnergyEvseManager * gInstance        = nullptr;
-static EVSEManufacturer * gEvseManufacturer = nullptr;
+static std::unique_ptr<EnergyEvseDelegate> gEvseDelegate;
+static std::unique_ptr<EnergyEvseManager> gEvseInstance;
+static std::unique_ptr<DeviceEnergyManagementDelegate> gDEMDelegate;
+static std::unique_ptr<DeviceEnergyManagementManager> gDEMInstance;
+static std::unique_ptr<EVSEManufacturer> gEvseManufacturer;
 
 #if CONFIG_ENABLE_ESP_INSIGHTS_TRACE
 extern const char insights_auth_key_start[] asm("_binary_insights_auth_key_txt_start");
@@ -116,44 +121,238 @@
 
 } // namespace
 
-void ApplicationInit()
+EVSEManufacturer * EnergyEvse::GetEvseManufacturer()
 {
-    if ((gDelegate == nullptr) && (gInstance == nullptr) && (gEvseManufacturer == nullptr))
+    return gEvseManufacturer.get();
+}
+
+/*
+ *  @brief  Creates a Delegate and Instance for DEM
+ *
+ * The Instance is a container around the Delegate, so
+ * create the Delegate first, then wrap it in the Instance
+ * Then call the Instance->Init() to register the attribute and command handlers
+ */
+CHIP_ERROR DeviceEnergyManagementInit()
+{
+    if (gDEMDelegate || gDEMInstance)
     {
-        gDelegate = new EnergyEvseDelegate();
-        if (gDelegate != nullptr)
-        {
-            gInstance = new EnergyEvseManager(
-                EndpointId(ENERGY_EVSE_ENDPOINT), *gDelegate,
-                BitMask<EnergyEvse::Feature, uint32_t>(EnergyEvse::Feature::kChargingPreferences,
-                                                       EnergyEvse::Feature::kPlugAndCharge, EnergyEvse::Feature::kRfid,
-                                                       EnergyEvse::Feature::kSoCReporting, EnergyEvse::Feature::kV2x),
-                BitMask<OptionalAttributes, uint32_t>(OptionalAttributes::kSupportsUserMaximumChargingCurrent,
-                                                      OptionalAttributes::kSupportsRandomizationWindow,
-                                                      OptionalAttributes::kSupportsApproximateEvEfficiency),
-                BitMask<OptionalCommands, uint32_t>(OptionalCommands::kSupportsStartDiagnostics));
-            gInstance->Init(); /* Register Attribute & Command handlers */
-        }
-    }
-    else
-    {
-        ChipLogError(AppServer, "EVSE Instance or Delegate already exist.")
+        ESP_LOGE(TAG, "DEM Instance or Delegate already exist.");
+        return CHIP_ERROR_INCORRECT_STATE;
     }
 
-    if (gEvseManufacturer == nullptr)
+    gDEMDelegate = std::make_unique<DeviceEnergyManagementDelegate>();
+    if (!gDEMDelegate)
     {
-        gEvseManufacturer = new EVSEManufacturer();
-        gEvseManufacturer->Init(gInstance);
+        ESP_LOGE(TAG, "Failed to allocate memory for DeviceEnergyManagementDelegate");
+        return CHIP_ERROR_NO_MEMORY;
     }
-    else
+
+    /* Manufacturer may optionally not support all features, commands & attributes */
+    gDEMInstance = std::make_unique<DeviceEnergyManagementManager>(
+        EndpointId(ENERGY_EVSE_ENDPOINT), *gDEMDelegate,
+        BitMask<DeviceEnergyManagement::Feature, uint32_t>(
+            DeviceEnergyManagement::Feature::kPowerAdjustment, DeviceEnergyManagement::Feature::kPowerForecastReporting,
+            DeviceEnergyManagement::Feature::kStateForecastReporting, DeviceEnergyManagement::Feature::kStartTimeAdjustment,
+            DeviceEnergyManagement::Feature::kPausable, DeviceEnergyManagement::Feature::kForecastAdjustment,
+            DeviceEnergyManagement::Feature::kConstraintBasedAdjustment));
+
+    if (!gDEMInstance)
     {
-        ChipLogError(AppServer, "EVSEManufacturer already exists.")
+        ESP_LOGE(TAG, "Failed to allocate memory for DeviceEnergyManagementManager");
+        gDEMDelegate.reset();
+        return CHIP_ERROR_NO_MEMORY;
     }
+
+    CHIP_ERROR err = gDEMInstance->Init(); /* Register Attribute & Command handlers */
+    if (err != CHIP_NO_ERROR)
+    {
+        ESP_LOGE(TAG, "Init failed on gDEMInstance, err:%" CHIP_ERROR_FORMAT, err.Format());
+        gDEMInstance.reset();
+        gDEMDelegate.reset();
+        return err;
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR DeviceEnergyManagementShutdown()
+{
+    /* Do this in the order Instance first, then delegate
+     * Ensure we call the Instance->Shutdown to free attribute & command handlers first
+     */
+    if (gDEMInstance)
+    {
+        /* deregister attribute & command handlers */
+        gDEMInstance->Shutdown();
+        gDEMInstance.reset();
+    }
+    if (gDEMDelegate)
+    {
+        gDEMDelegate.reset();
+    }
+    return CHIP_NO_ERROR;
+}
+
+/*
+ *  @brief  Creates a Delegate and Instance for EVSE cluster
+ *
+ * The Instance is a container around the Delegate, so
+ * create the Delegate first, then wrap it in the Instance
+ * Then call the Instance->Init() to register the attribute and command handlers
+ */
+CHIP_ERROR EnergyEvseInit()
+{
+    CHIP_ERROR err;
+
+    if (gEvseDelegate || gEvseInstance)
+    {
+        ESP_LOGE(TAG, "EVSE Instance or Delegate already exist.");
+        return CHIP_ERROR_INCORRECT_STATE;
+    }
+
+    gEvseDelegate = std::make_unique<EnergyEvseDelegate>();
+    if (!gEvseDelegate)
+    {
+        ESP_LOGE(TAG, "Failed to allocate memory for EnergyEvseDelegate");
+        return CHIP_ERROR_NO_MEMORY;
+    }
+
+    /* Manufacturer may optionally not support all features, commands & attributes */
+    gEvseInstance = std::make_unique<EnergyEvseManager>(
+        EndpointId(ENERGY_EVSE_ENDPOINT), *gEvseDelegate,
+        BitMask<EnergyEvse::Feature, uint32_t>(EnergyEvse::Feature::kChargingPreferences, EnergyEvse::Feature::kPlugAndCharge,
+                                               EnergyEvse::Feature::kRfid, EnergyEvse::Feature::kSoCReporting,
+                                               EnergyEvse::Feature::kV2x),
+        BitMask<EnergyEvse::OptionalAttributes, uint32_t>(EnergyEvse::OptionalAttributes::kSupportsUserMaximumChargingCurrent,
+                                                          EnergyEvse::OptionalAttributes::kSupportsRandomizationWindow,
+                                                          EnergyEvse::OptionalAttributes::kSupportsApproximateEvEfficiency),
+        BitMask<EnergyEvse::OptionalCommands, uint32_t>(EnergyEvse::OptionalCommands::kSupportsStartDiagnostics));
+
+    if (!gEvseInstance)
+    {
+        ESP_LOGE(TAG, "Failed to allocate memory for EnergyEvseManager");
+        gEvseDelegate.reset();
+        return CHIP_ERROR_NO_MEMORY;
+    }
+
+    err = gEvseInstance->Init(); /* Register Attribute & Command handlers */
+    if (err != CHIP_NO_ERROR)
+    {
+        ESP_LOGE(TAG, "Init failed on gEvseInstance, err:%" CHIP_ERROR_FORMAT, err.Format());
+        gEvseInstance.reset();
+        gEvseDelegate.reset();
+        return err;
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR EnergyEvseShutdown()
+{
+    /* Do this in the order Instance first, then delegate
+     * Ensure we call the Instance->Shutdown to free attribute & command handlers first
+     */
+    if (gEvseInstance)
+    {
+        /* deregister attribute & command handlers */
+        gEvseInstance->Shutdown();
+        gEvseInstance.reset();
+    }
+
+    if (gEvseDelegate)
+    {
+        gEvseDelegate.reset();
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+/*
+ *  @brief  Creates a EVSEManufacturer class to hold the EVSE & DEM clusters
+ *
+ * The Instance is a container around the Delegate, so
+ * create the Delegate first, then wrap it in the Instance
+ * Then call the Instance->Init() to register the attribute and command handlers
+ */
+CHIP_ERROR EVSEManufacturerInit()
+{
+    CHIP_ERROR err;
+
+    if (gEvseManufacturer)
+    {
+        ESP_LOGE(TAG, "EvseManufacturer already exist.");
+        return CHIP_ERROR_INCORRECT_STATE;
+    }
+
+    /* Now create EVSEManufacturer */
+    gEvseManufacturer = std::make_unique<EVSEManufacturer>(gEvseInstance.get());
+    if (!gEvseManufacturer)
+    {
+        ESP_LOGE(TAG, "Failed to allocate memory for EvseManufacturer");
+        return CHIP_ERROR_NO_MEMORY;
+    }
+
+    /* Call Manufacturer specific init */
+    err = gEvseManufacturer->Init();
+    if (err != CHIP_NO_ERROR)
+    {
+        ESP_LOGE(TAG, "Init failed on gEvseManufacturer, err:%" CHIP_ERROR_FORMAT, err.Format());
+        gEvseManufacturer.reset();
+        return err;
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR EVSEManufacturerShutdown()
+{
+    if (gEvseManufacturer)
+    {
+        /* Shutdown the EVSEManufacturer */
+        gEvseManufacturer->Shutdown();
+        gEvseManufacturer.reset();
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+void ApplicationInit()
+{
+    if (DeviceEnergyManagementInit() != CHIP_NO_ERROR)
+    {
+        return;
+    }
+
+    if (EnergyEvseInit() != CHIP_NO_ERROR)
+    {
+        DeviceEnergyManagementShutdown();
+        return;
+    }
+
+    if (EVSEManufacturerInit() != CHIP_NO_ERROR)
+    {
+        DeviceEnergyManagementShutdown();
+        EnergyEvseShutdown();
+        return;
+    }
+}
+
+void ApplicationShutdown()
+{
+    ESP_LOGD(TAG, "Energy Management App: ApplicationShutdown()");
+
+    /* Shutdown in reverse order that they were created */
+    EVSEManufacturerShutdown();       /* Free the EVSEManufacturer */
+    EnergyEvseShutdown();             /* Free the EnergyEvse */
+    DeviceEnergyManagementShutdown(); /* Free the DEM */
+
+    Clusters::DeviceEnergyManagementMode::Shutdown();
+    Clusters::EnergyEvseMode::Shutdown();
 }
 
 static void InitServer(intptr_t context)
 {
-    ApplicationInit();
     // Print QR Code URL
     PrintOnboardingCodes(chip::RendezvousInformationFlags(CONFIG_RENDEZVOUS_MODE));
 
@@ -175,6 +374,9 @@
     static Tracing::Insights::ESP32Backend backend;
     Tracing::Register(backend);
 #endif
+
+    // Application code should always be initialised after the initialisation of server.
+    ApplicationInit();
 }
 
 extern "C" void app_main()