Feature/energypreferences (#30244)

* Adding the Energy Preferences XML

* Added just the xml and regenerated.  No implementations yet as
requested.

* regen

* put the cluster back into all-clusters after merge.

* Apply suggestions from code review

Added suggestions for ARRAY vs array

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* regenerate

* Finished up impl for energy-preferences

* Added some error checking for features, and constraint checking.

Also turned on the features in the all-clusters-app

* Restyled by whitespace

* Restyled by clang-format

* Fixed copy paste bug

* Removed some unecessary changes and a comment.

* initialize the delegate pointer

* Put the cluster back in the all-clusters app after the merge

* Fixed minor issues from review

* Restyled by whitespace

* Addressing some review concerns

* Some more comments resolved

* Better documentation, a set of braces I missed last time around, and
added an extra header include.

* fix build error after merge

* Restyled by whitespace

* Restyled by clang-format

* Update src/app/clusters/energy-preference-server/energy-preference-server.cpp

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Implement proposed API changes with clearer delineation of storage

* Restyled by clang-format

* Typo

* Regen zap, update code to reflect the updated return from Feature::Get

* ZAP regen, to fix merge errors

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Address suggestion re: optionality conversion

* Fix one more omission

* another small fix

* Fix up copyright headers

* Restyled by clang-format

* Reinitialize storage objects on every iteration

* fix typo

* Restyled by clang-format

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

---------

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Robert Szewczyk <szewczyk@google.com>
diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter
index 62dc0d7..5a7b510 100644
--- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter
+++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter
@@ -4455,6 +4455,40 @@
   timed command ClearTargets(): DefaultSuccess = 7;
 }
 
+/** This cluster provides an interface to specify preferences for how devices should consume energy. */
+provisional cluster EnergyPreference = 155 {
+  revision 1;
+
+  enum EnergyPriorityEnum : enum8 {
+    kComfort = 0;
+    kSpeed = 1;
+    kEfficiency = 2;
+    kWaterConsumption = 3;
+  }
+
+  bitmap Feature : bitmap32 {
+    kEnergyBalance = 0x1;
+    kLowPowerModeSensitivity = 0x2;
+  }
+
+  struct BalanceStruct {
+    percent step = 0;
+    optional char_string<64> label = 1;
+  }
+
+  readonly attribute optional BalanceStruct energyBalances[] = 0;
+  attribute optional int8u currentEnergyBalance = 1;
+  readonly attribute optional EnergyPriorityEnum energyPriorities[] = 2;
+  readonly attribute optional BalanceStruct lowPowerModeSensitivities[] = 3;
+  attribute optional int8u currentLowPowerModeSensitivity = 4;
+  readonly attribute command_id generatedCommandList[] = 65528;
+  readonly attribute command_id acceptedCommandList[] = 65529;
+  readonly attribute event_id eventList[] = 65530;
+  readonly attribute attrib_id attributeList[] = 65531;
+  readonly attribute bitmap32 featureMap = 65532;
+  readonly attribute int16u clusterRevision = 65533;
+}
+
 /** The Power Topology Cluster provides a mechanism for expressing how power is flowing between endpoints. */
 provisional cluster PowerTopology = 156 {
   revision 1;
@@ -8361,6 +8395,20 @@
     handle command ClearTargets;
   }
 
+  server cluster EnergyPreference {
+    callback attribute energyBalances;
+    ram      attribute currentEnergyBalance;
+    callback attribute energyPriorities;
+    callback attribute lowPowerModeSensitivities;
+    ram      attribute currentLowPowerModeSensitivity;
+    callback attribute generatedCommandList;
+    callback attribute acceptedCommandList;
+    callback attribute eventList;
+    callback attribute attributeList;
+    ram      attribute featureMap default = 3;
+    ram      attribute clusterRevision default = 1;
+  }
+
   server cluster PowerTopology {
     callback attribute availableEndpoints;
     callback attribute activeEndpoints;
diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap
index 0a2a3fa..21bcfee 100644
--- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap
+++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap
@@ -14693,6 +14693,192 @@
           ]
         },
         {
+          "name": "Energy Preference",
+          "code": 155,
+          "mfgCode": null,
+          "define": "ENERGY_PREFERENCE_CLUSTER",
+          "side": "server",
+          "enabled": 1,
+          "attributes": [
+            {
+              "name": "EnergyBalances",
+              "code": 0,
+              "mfgCode": null,
+              "side": "server",
+              "type": "array",
+              "included": 1,
+              "storageOption": "External",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": null,
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "CurrentEnergyBalance",
+              "code": 1,
+              "mfgCode": null,
+              "side": "server",
+              "type": "int8u",
+              "included": 1,
+              "storageOption": "RAM",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": "",
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "EnergyPriorities",
+              "code": 2,
+              "mfgCode": null,
+              "side": "server",
+              "type": "array",
+              "included": 1,
+              "storageOption": "External",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": null,
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "LowPowerModeSensitivities",
+              "code": 3,
+              "mfgCode": null,
+              "side": "server",
+              "type": "array",
+              "included": 1,
+              "storageOption": "External",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": null,
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "CurrentLowPowerModeSensitivity",
+              "code": 4,
+              "mfgCode": null,
+              "side": "server",
+              "type": "int8u",
+              "included": 1,
+              "storageOption": "RAM",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": "",
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "GeneratedCommandList",
+              "code": 65528,
+              "mfgCode": null,
+              "side": "server",
+              "type": "array",
+              "included": 1,
+              "storageOption": "External",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": null,
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "AcceptedCommandList",
+              "code": 65529,
+              "mfgCode": null,
+              "side": "server",
+              "type": "array",
+              "included": 1,
+              "storageOption": "External",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": null,
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "EventList",
+              "code": 65530,
+              "mfgCode": null,
+              "side": "server",
+              "type": "array",
+              "included": 1,
+              "storageOption": "External",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": null,
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "AttributeList",
+              "code": 65531,
+              "mfgCode": null,
+              "side": "server",
+              "type": "array",
+              "included": 1,
+              "storageOption": "External",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": null,
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "FeatureMap",
+              "code": 65532,
+              "mfgCode": null,
+              "side": "server",
+              "type": "bitmap32",
+              "included": 1,
+              "storageOption": "RAM",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": "3",
+              "reportable": 1,
+              "minInterval": 1,
+              "maxInterval": 65534,
+              "reportableChange": 0
+            },
+            {
+              "name": "ClusterRevision",
+              "code": 65533,
+              "mfgCode": null,
+              "side": "server",
+              "type": "int16u",
+              "included": 1,
+              "storageOption": "RAM",
+              "singleton": 0,
+              "bounded": 0,
+              "defaultValue": "1",
+              "reportable": 1,
+              "minInterval": 0,
+              "maxInterval": 65344,
+              "reportableChange": 0
+            }
+          ]
+        },
+        {
           "name": "Window Covering",
           "code": 258,
           "mfgCode": null,
diff --git a/examples/all-clusters-app/all-clusters-common/src/energy-preference-delegate.cpp b/examples/all-clusters-app/all-clusters-common/src/energy-preference-delegate.cpp
new file mode 100644
index 0000000..cd9b47d
--- /dev/null
+++ b/examples/all-clusters-app/all-clusters-common/src/energy-preference-delegate.cpp
@@ -0,0 +1,137 @@
+/*
+ *
+ *    Copyright (c) 2024 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.
+ */
+
+#include <app/clusters/energy-preference-server/energy-preference-server.h>
+
+using namespace chip;
+using namespace chip::app::Clusters::EnergyPreference;
+using namespace chip::app::Clusters::EnergyPreference::Structs;
+
+static BalanceStruct::Type gsEnergyBalances[] = {
+    { .step = 0, .label = Optional<chip::CharSpan>("Efficient"_span) },
+    { .step = 50, .label = Optional<chip::CharSpan>() },
+    { .step = 100, .label = Optional<chip::CharSpan>("Comfort"_span) },
+};
+
+static BalanceStruct::Type gsPowerBalances[] = {
+    { .step = 0, .label = Optional<chip::CharSpan>("1 Minute"_span) },
+    { .step = 12, .label = Optional<chip::CharSpan>("5 Minutes"_span) },
+    { .step = 24, .label = Optional<chip::CharSpan>("10 Minutes"_span) },
+    { .step = 36, .label = Optional<chip::CharSpan>("15 Minutes"_span) },
+    { .step = 48, .label = Optional<chip::CharSpan>("20 Minutes"_span) },
+    { .step = 60, .label = Optional<chip::CharSpan>("25 Minutes"_span) },
+    { .step = 70, .label = Optional<chip::CharSpan>("30 Minutes"_span) },
+    { .step = 80, .label = Optional<chip::CharSpan>("60 Minutes"_span) },
+    { .step = 90, .label = Optional<chip::CharSpan>("120 Minutes"_span) },
+    { .step = 100, .label = Optional<chip::CharSpan>("Never"_span) },
+};
+
+// assumes it'll be the only delegate for it's lifetime.
+struct EPrefDelegate : public Delegate
+{
+    EPrefDelegate();
+    virtual ~EPrefDelegate();
+
+    CHIP_ERROR GetEnergyBalanceAtIndex(chip::EndpointId aEndpoint, size_t aIndex, chip::Percent & aOutStep,
+                                       chip::Optional<chip::MutableCharSpan> & aOutLabel) override;
+    CHIP_ERROR GetEnergyPriorityAtIndex(chip::EndpointId aEndpoint, size_t aIndex, EnergyPriorityEnum & priority) override;
+    CHIP_ERROR GetLowPowerModeSensitivityAtIndex(chip::EndpointId aEndpoint, size_t aIndex, chip::Percent & aOutStep,
+                                                 chip::Optional<chip::MutableCharSpan> & aOutLabel) override;
+
+    size_t GetNumEnergyBalances(chip::EndpointId aEndpoint) override;
+    size_t GetNumLowPowerModeSensitivities(chip::EndpointId aEndpoint) override;
+};
+
+EPrefDelegate::EPrefDelegate() : Delegate()
+{
+    VerifyOrDie(GetDelegate() == nullptr);
+    SetDelegate(this);
+}
+
+EPrefDelegate::~EPrefDelegate()
+{
+    VerifyOrDie(GetDelegate() == this);
+    SetDelegate(nullptr);
+}
+
+size_t EPrefDelegate::GetNumEnergyBalances(chip::EndpointId aEndpoint)
+{
+    return (ArraySize(gsEnergyBalances));
+}
+
+size_t EPrefDelegate::GetNumLowPowerModeSensitivities(chip::EndpointId aEndpoint)
+{
+    return (ArraySize(gsEnergyBalances));
+}
+
+CHIP_ERROR
+EPrefDelegate::GetEnergyBalanceAtIndex(chip::EndpointId aEndpoint, size_t aIndex, chip::Percent & aOutStep,
+                                       chip::Optional<chip::MutableCharSpan> & aOutLabel)
+{
+    if (aIndex < GetNumEnergyBalances(aEndpoint))
+    {
+        aOutStep = gsEnergyBalances[aIndex].step;
+        if (gsEnergyBalances[aIndex].label.HasValue())
+        {
+            chip::CopyCharSpanToMutableCharSpan(gsEnergyBalances[aIndex].label.Value(), aOutLabel.Value());
+        }
+        else
+        {
+            aOutLabel.ClearValue();
+        }
+        return CHIP_NO_ERROR;
+    }
+    return CHIP_ERROR_NOT_FOUND;
+}
+
+CHIP_ERROR
+EPrefDelegate::GetEnergyPriorityAtIndex(chip::EndpointId aEndpoint, size_t aIndex, EnergyPriorityEnum & priority)
+{
+    static EnergyPriorityEnum priorities[] = { EnergyPriorityEnum::kEfficiency, EnergyPriorityEnum::kComfort };
+
+    if (aIndex < ArraySize(priorities))
+    {
+        priority = priorities[aIndex];
+        return CHIP_NO_ERROR;
+    }
+
+    return CHIP_ERROR_NOT_FOUND;
+}
+
+CHIP_ERROR
+EPrefDelegate::GetLowPowerModeSensitivityAtIndex(chip::EndpointId aEndpoint, size_t aIndex, chip::Percent & aOutStep,
+                                                 chip::Optional<chip::MutableCharSpan> & aOutLabel)
+{
+    if (aIndex < GetNumLowPowerModeSensitivities(aEndpoint))
+    {
+        aOutStep = gsPowerBalances[aIndex].step;
+        if (gsPowerBalances[aIndex].label.HasValue())
+        {
+            chip::CopyCharSpanToMutableCharSpan(gsPowerBalances[aIndex].label.Value(), aOutLabel.Value());
+        }
+        else
+        {
+            aOutLabel.ClearValue();
+        }
+        return CHIP_NO_ERROR;
+    }
+
+    return CHIP_ERROR_NOT_FOUND;
+}
+
+static EPrefDelegate gsDelegate;
diff --git a/examples/all-clusters-app/ameba/chip_main.cmake b/examples/all-clusters-app/ameba/chip_main.cmake
index 0d8b99c..c457206 100755
--- a/examples/all-clusters-app/ameba/chip_main.cmake
+++ b/examples/all-clusters-app/ameba/chip_main.cmake
@@ -158,6 +158,7 @@
     ${chip_dir}/examples/all-clusters-app/all-clusters-common/src/concentration-measurement-instances.cpp
     ${chip_dir}/examples/all-clusters-app/all-clusters-common/src/device-energy-management-stub.cpp
     ${chip_dir}/examples/all-clusters-app/all-clusters-common/src/energy-evse-stub.cpp
+    ${chip_dir}/examples/all-clusters-app/all-clusters-common/src/energy-preference-delegate.cpp
     ${chip_dir}/examples/all-clusters-app/all-clusters-common/src/fan-stub.cpp
     ${chip_dir}/examples/all-clusters-app/all-clusters-common/src/oven-modes.cpp
     ${chip_dir}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-controls-delegate-impl.cpp
diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt
index 238f0e8..63851b1 100644
--- a/examples/all-clusters-app/esp32/main/CMakeLists.txt
+++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt
@@ -96,8 +96,11 @@
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/time-synchronization-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/valve-configuration-and-control-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/dishwasher-alarm-server"
-                      "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/laundry-washer-controls-server"
+            		      "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/laundry-washer-controls-server"
+		                  "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/laundry-washer-controls-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/laundry-dryer-controls-server"
+                      "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/all-clusters-app/all-clusters-common/src"
+                      "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/energy-preference-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/electrical-energy-measurement-server"
                       "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/electrical-power-measurement-server"
 )
diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn
index 998f98a..3d52ef7 100644
--- a/examples/all-clusters-app/linux/BUILD.gn
+++ b/examples/all-clusters-app/linux/BUILD.gn
@@ -40,6 +40,7 @@
     "${chip_root}/examples/all-clusters-app/all-clusters-common/src/electrical-energy-measurement-stub.cpp",
     "${chip_root}/examples/all-clusters-app/all-clusters-common/src/electrical-power-measurement-stub.cpp",
     "${chip_root}/examples/all-clusters-app/all-clusters-common/src/energy-evse-stub.cpp",
+    "${chip_root}/examples/all-clusters-app/all-clusters-common/src/energy-preference-delegate.cpp",
     "${chip_root}/examples/all-clusters-app/all-clusters-common/src/fan-stub.cpp",
     "${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-dryer-controls-delegate-impl.cpp",
     "${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-controls-delegate-impl.cpp",
diff --git a/src/app/clusters/energy-preference-server/energy-preference-server.cpp b/src/app/clusters/energy-preference-server/energy-preference-server.cpp
new file mode 100644
index 0000000..7cb377a
--- /dev/null
+++ b/src/app/clusters/energy-preference-server/energy-preference-server.cpp
@@ -0,0 +1,235 @@
+/**
+ *
+ *    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 "energy-preference-server.h"
+
+#include <app/util/attribute-storage.h> // Needed for registerAttributeAccessOverride
+
+#include <app-common/zap-generated/attributes/Accessors.h>
+#include <app-common/zap-generated/callback.h>
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app-common/zap-generated/ids/Attributes.h>
+#include <app/AttributeAccessInterface.h>
+#include <app/ConcreteAttributePath.h>
+#include <lib/core/CHIPEncoding.h>
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::EnergyPreference;
+using namespace chip::app::Clusters::EnergyPreference::Structs;
+using namespace chip::app::Clusters::EnergyPreference::Attributes;
+
+using Status = Protocols::InteractionModel::Status;
+
+namespace {
+
+class EnergyPrefAttrAccess : public AttributeAccessInterface
+{
+public:
+    EnergyPrefAttrAccess() : AttributeAccessInterface(Optional<EndpointId>::Missing(), EnergyPreference::Id) {}
+
+    CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
+};
+
+EnergyPrefAttrAccess gEnergyPrefAttrAccess;
+Delegate * gsDelegate = nullptr;
+
+CHIP_ERROR EnergyPrefAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
+{
+    VerifyOrDie(aPath.mClusterId == EnergyPreference::Id);
+    EndpointId endpoint          = aPath.mEndpointId;
+    uint32_t ourFeatureMap       = 0;
+    const bool featureMapIsGood  = FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == Status::Success;
+    const bool balanceSupported  = featureMapIsGood && ((ourFeatureMap & to_underlying(Feature::kEnergyBalance)) != 0);
+    const bool lowPowerSupported = featureMapIsGood && ((ourFeatureMap & to_underlying(Feature::kLowPowerModeSensitivity)) != 0);
+
+    switch (aPath.mAttributeId)
+    {
+    case EnergyBalances::Id:
+        if (!balanceSupported)
+        {
+            return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
+        }
+
+        if (gsDelegate != nullptr)
+        {
+            return aEncoder.EncodeList([endpoint](const auto & encoder) -> CHIP_ERROR {
+                size_t index   = 0;
+                CHIP_ERROR err = CHIP_NO_ERROR;
+                do
+                {
+                    Percent step;
+                    char buffer[64];
+                    Optional<MutableCharSpan> label{ MutableCharSpan(buffer) };
+                    if ((err = gsDelegate->GetEnergyBalanceAtIndex(endpoint, index, step, label)) == CHIP_NO_ERROR)
+                    {
+                        BalanceStruct::Type balance = { step,
+                                                        label.HasValue() ? Optional<CharSpan>(label.Value())
+                                                                         : Optional<CharSpan>() };
+                        ReturnErrorOnFailure(encoder.Encode(balance));
+                        index++;
+                    }
+                } while (err == CHIP_NO_ERROR);
+
+                if (err == CHIP_ERROR_NOT_FOUND)
+                {
+                    return CHIP_NO_ERROR;
+                }
+                return err;
+            });
+        }
+        return CHIP_ERROR_INCORRECT_STATE;
+    case EnergyPriorities::Id:
+        if (balanceSupported == false)
+        {
+            return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
+        }
+
+        if (gsDelegate != nullptr)
+        {
+            return aEncoder.EncodeList([endpoint](const auto & encoder) -> CHIP_ERROR {
+                EnergyPriorityEnum priority;
+                size_t index   = 0;
+                CHIP_ERROR err = CHIP_NO_ERROR;
+                while ((err = gsDelegate->GetEnergyPriorityAtIndex(endpoint, index, priority)) == CHIP_NO_ERROR)
+                {
+                    ReturnErrorOnFailure(encoder.Encode(priority));
+                    index++;
+                }
+                if (err == CHIP_ERROR_NOT_FOUND)
+                {
+                    return CHIP_NO_ERROR;
+                }
+                return err;
+            });
+        }
+        return CHIP_ERROR_INCORRECT_STATE;
+    case LowPowerModeSensitivities::Id:
+        if (lowPowerSupported == false)
+        {
+            return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
+        }
+
+        if (gsDelegate != nullptr)
+        {
+            return aEncoder.EncodeList([endpoint](const auto & encoder) -> CHIP_ERROR {
+                size_t index   = 0;
+                CHIP_ERROR err = CHIP_NO_ERROR;
+                do
+                {
+                    Percent step;
+                    char buffer[64];
+                    Optional<MutableCharSpan> label{ MutableCharSpan(buffer) };
+                    if ((err = gsDelegate->GetLowPowerModeSensitivityAtIndex(endpoint, index, step, label)) == CHIP_NO_ERROR)
+                    {
+                        BalanceStruct::Type balance = { step,
+                                                        label.HasValue() ? Optional<CharSpan>(label.Value())
+                                                                         : Optional<CharSpan>() };
+                        ReturnErrorOnFailure(encoder.Encode(balance));
+                        index++;
+                    }
+                } while (err == CHIP_NO_ERROR);
+                if (err == CHIP_ERROR_NOT_FOUND)
+                {
+                    return CHIP_NO_ERROR;
+                }
+                return err;
+            });
+        }
+        return CHIP_ERROR_INCORRECT_STATE;
+    default: // return CHIP_NO_ERROR and just read from the attribute store in default
+        break;
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+} // anonymous namespace
+
+namespace chip::app::Clusters::EnergyPreference {
+
+void SetDelegate(Delegate * aDelegate)
+{
+    gsDelegate = aDelegate;
+}
+
+Delegate * GetDelegate()
+{
+    return gsDelegate;
+}
+
+} // namespace chip::app::Clusters::EnergyPreference
+
+Status MatterEnergyPreferenceClusterServerPreAttributeChangedCallback(const ConcreteAttributePath & attributePath,
+                                                                      EmberAfAttributeType attributeType, uint16_t size,
+                                                                      uint8_t * value)
+{
+    EndpointId endpoint = attributePath.mEndpointId;
+    Delegate * delegate = GetDelegate();
+    uint32_t ourFeatureMap;
+    const bool featureMapIsGood  = FeatureMap::Get(attributePath.mEndpointId, &ourFeatureMap) == Status::Success;
+    const bool balanceSupported  = featureMapIsGood && ((ourFeatureMap & to_underlying(Feature::kEnergyBalance)) != 0);
+    const bool lowPowerSupported = featureMapIsGood && ((ourFeatureMap & to_underlying(Feature::kLowPowerModeSensitivity)) != 0);
+
+    if (delegate == nullptr)
+    {
+        return Status::UnsupportedWrite;
+    }
+
+    switch (attributePath.mAttributeId)
+    {
+    case CurrentEnergyBalance::Id: {
+        if (balanceSupported == false)
+        {
+            return Status::UnsupportedAttribute;
+        }
+
+        uint8_t index    = Encoding::Get8(value);
+        size_t arraySize = delegate->GetNumEnergyBalances(endpoint);
+        if (index >= arraySize)
+        {
+            return Status::ConstraintError;
+        }
+
+        return Status::Success;
+    }
+
+    case CurrentLowPowerModeSensitivity::Id: {
+        if (lowPowerSupported == false)
+        {
+            return Status::UnsupportedAttribute;
+        }
+
+        uint8_t index    = Encoding::Get8(value);
+        size_t arraySize = delegate->GetNumLowPowerModeSensitivities(endpoint);
+        if (index >= arraySize)
+        {
+            return Status::ConstraintError;
+        }
+
+        return Status::Success;
+    }
+    default:
+        return Status::Success;
+    }
+}
+
+void MatterEnergyPreferencePluginServerInitCallback()
+{
+    registerAttributeAccessOverride(&gEnergyPrefAttrAccess);
+}
diff --git a/src/app/clusters/energy-preference-server/energy-preference-server.h b/src/app/clusters/energy-preference-server/energy-preference-server.h
new file mode 100644
index 0000000..7710578
--- /dev/null
+++ b/src/app/clusters/energy-preference-server/energy-preference-server.h
@@ -0,0 +1,107 @@
+/**
+ *
+ *    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 <stddef.h>
+
+#include <app-common/zap-generated/cluster-enums.h>
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/util/basic-types.h>
+#include <lib/core/CHIPError.h>
+
+namespace chip::app::Clusters::EnergyPreference {
+
+struct Delegate
+{
+    // Note: This delegate does not handle the "Current Active" indexes attributes storage.
+    // eg: Current Energy Balance and Current Low Power Mode Sensitivity.  These can be handled using
+    // ember built in storage, or via the external callbacks as desired by the implementer.
+
+    virtual ~Delegate() {}
+
+    /**
+     * Get an Energy Balance.
+     *
+     * The delegate method is called by the cluster to fill out the
+     * values for the list in EnergyBalances attribute. Storage for
+     * both aOutStep and aOutLabel is provided by the caller.
+     *
+     * @param aEndpoint The endpoint to query.
+     * @param aIndex The index of the balance, with 0 representing the first one.
+     * @param aOutStep The Step value from BalanceStruct
+     *
+     * @param aOutLabel The Label value from BalanceStruct. Storage is
+     * provided by the caller, and is large enough to accomodate the
+     * longest label (64 chars), on successful return the size of the span must be
+     * adjusted to reflect the length of the value, or ClearValue() called on the Optional to indicate there is no label.
+     *
+     * @return CHIP_ERROR_NOT_FOUND if the index is out of range.
+     */
+    virtual CHIP_ERROR GetEnergyBalanceAtIndex(chip::EndpointId aEndpoint, size_t aIndex, chip::Percent & aOutStep,
+                                               chip::Optional<chip::MutableCharSpan> & aOutLabel) = 0;
+
+    /**
+     * Get an Energy Priority.
+     * @param aEndpoint The endpoint to query.
+     * @param aIndex The index of the priority, with 0 representing the first one.
+     * @param aOutPriority The EnergyPriorityEnum to copy the data into.
+     * @return CHIP_ERROR_NOT_FOUND if the index is out of range.
+     */
+    virtual CHIP_ERROR GetEnergyPriorityAtIndex(chip::EndpointId aEndpoint, size_t aIndex,
+                                                chip::app::Clusters::EnergyPreference::EnergyPriorityEnum & aOutPriority) = 0;
+
+    /**
+     * Get a Power Sensitity Balance Struct data at the specified index.
+     *
+     * The delegate method is called by the cluster to fill out the
+     * values for the list in LowPowerSensitivities attribute. Storage for
+     * both aOutStep and aOutLabel is provided by the caller.
+     *
+     * @param aEndpoint The endpoint to query.
+     * @param aIndex The index of the priority, with 0 representing the first one.
+     * @param aOutStep The Step value from BalanceStruct
+     *
+     * @param aOutLabel The Label value from BalanceStruct. Storage is
+     * provided by the caller, and is large enough to accomodate the
+     * longest label (64 chars), on successful return the size of the span must be
+     * adjusted to reflect the length of the value, or ClearValue() called on the Optional to indicate there is no label.
+     *
+     * @return CHIP_ERROR_NOT_FOUND if the index is out of range.
+     */
+    virtual CHIP_ERROR GetLowPowerModeSensitivityAtIndex(chip::EndpointId aEndpoint, size_t aIndex, chip::Percent & aOutStep,
+                                                         chip::Optional<chip::MutableCharSpan> & aOutLabel) = 0;
+
+    /**
+     * Get the number of energy balances this endpoint has.
+     * @param aEndpoint The endpoint to query.
+     * @return the number of balance structs in the list.
+     */
+    virtual size_t GetNumEnergyBalances(chip::EndpointId aEndpoint) = 0;
+
+    /**
+     * Get the number of low power mode sensitivities this endpoint has.
+     * @param aEndpoint The endpoint to query.
+     * @return the number of balance structs in the list.
+     */
+    virtual size_t GetNumLowPowerModeSensitivities(chip::EndpointId aEndpoint) = 0;
+};
+
+void SetDelegate(Delegate * aDelegate);
+Delegate * GetDelegate();
+
+} // namespace chip::app::Clusters::EnergyPreference
diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml
index 2067718..875cdbc 100644
--- a/src/app/common/templates/config-data.yaml
+++ b/src/app/common/templates/config-data.yaml
@@ -84,5 +84,6 @@
     - Mode Select
     - Fan Control
     - Thermostat
+    - Energy Preference
     - Laundry Washer Controls
     - Laundry Dryer Controls
diff --git a/src/app/zap-templates/zcl/data-model/chip/energy-preference-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/energy-preference-cluster.xml
index ceff984..32893bd 100644
--- a/src/app/zap-templates/zcl/data-model/chip/energy-preference-cluster.xml
+++ b/src/app/zap-templates/zcl/data-model/chip/energy-preference-cluster.xml
@@ -22,6 +22,7 @@
     <server init="false" tick="false">true</server>
     <description>This cluster provides an interface to specify preferences for how devices should consume energy.</description>
     <!--Attributes-->
+    <globalAttribute side="either" code="0xFFFD" value="1"/>
 
     <!--Conformance feature BALA - for now optional-->
     <attribute code="0x0000" side="server" type="array" entryType="BalanceStruct" define="ENERGY_PREFERENCE_ENERGY_BALANCES" isNullable="false" min="2" max="10" writable="false" optional="true">EnergyBalances</attribute>
diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json
index 56adf0a..2edfb27 100644
--- a/src/app/zap_cluster_list.json
+++ b/src/app/zap_cluster_list.json
@@ -192,7 +192,7 @@
         "ETHERNET_NETWORK_DIAGNOSTICS_CLUSTER": [
             "ethernet-network-diagnostics-server"
         ],
-        "ENERGY_PREFERENCE_CLUSTER": [""],
+        "ENERGY_PREFERENCE_CLUSTER": ["energy-preference-server"],
         "FAN_CONTROL_CLUSTER": ["fan-control-server"],
         "FAULT_INJECTION_CLUSTER": ["fault-injection-server"],
         "FIXED_LABEL_CLUSTER": ["fixed-label-server"],
diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter
index 8986463..235366f 100644
--- a/src/controller/data_model/controller-clusters.matter
+++ b/src/controller/data_model/controller-clusters.matter
@@ -4978,7 +4978,7 @@
 
 /** This cluster provides an interface to specify preferences for how devices should consume energy. */
 provisional cluster EnergyPreference = 155 {
-  revision 1; // NOTE: Default/not specifically set
+  revision 1;
 
   enum EnergyPriorityEnum : enum8 {
     kComfort = 0;