【Feature】operational state cluster sdk implementation and example cluster server application (#26971)

* Add operational state cluster implement

* Enable operational state cluster server in all-clusters-app.zap

* Restyled by clang-format

* Restyled by gn

* Enable CommandHandlerInterfaceOnlyClusters feature in operational state cluster

* Fix the data type no match the operational state cluster xml

* Add Uncopyable feature to operational state server class

* Fix no define function

* Add operational state cluster in other platform

* Restyled by clang-format

* Restyled by gn

* Function / Data Struct in headfile are documented.

* Document why operational states to be in storage

* document the function note

* Add prefix members with m for class OperationalStateServer

* Fix Bug: do const_cast no needed

* Optimize to returning a const reference

* Remove the api no needed to be implemented

* use to_underlying to instead static_cast

* use reduce_size api to optimize the MutableByteSpan

* Fix: memory leak in exceptional situation

* Rename the structs for pretty confusing

* comment the member

* remove the documentation return in functions

* Add delete keyword in Uncopyable class

* modify the document of functions

* modify the document of functions

* Fix spelling error in function notes

* Fix: memory leak in exceptional situation

* Fix: read operational state list or phase list fail in some exception situations

* use MakeOptional to optimize the function call

* modify the document of functions

* Fix: ErrorStateStruct's ErrorStateDetails need to be null/missing

* Add the document for function

* Optimize ErrorStateStruct's field -- ErrorStateLabel and ErrorStateDetails

* document the method signature

* document the class

* remove temporary variable

* Add test cases in TestOperationalStateDataProvider

* Remove implementing EnumerateAcceptedCommands in Operational State Cluster

* Enable all commands in operational state cluster for all-clusters-app

* Zap regen all

* Revert file same as master branch

* Optimize operational state server's implement

* Add operational state delegate implement

* Add operational state cluster delegates

* Zap regen

* Modify path of head file included

* add file included in BUILD.gn

* Restyled by whitespace

* Restyled by clang-format

* Restyled by gn

* add operational state cluster impl in other platforms

* Restyled by gn

* Remove log in operational-state-delegate-impl.cpp

* remove unuseful file

* Update src/app/clusters/operational-state-server/operational-state-delegate.h

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

* Optimize struct GenericOperationalState

* drop the extra chip:: in operational-state api

* Optimize struct GenericOperationalError

* Optimize to put a struct instance on the state, then call the delegate to fill it in

* Use  GetOperationalStateAtIndex api to instead GetOperationalStateList api

* Optimize struct GenericOperationalPhase

* Optimize struct GenericOperationalPhase

* Optimize HandleXXStateCallback api param

* Remove unuseful code and document

* Use constexpr val to instead number

* Fix use of totalOperationalTime

* enable operational state cluster event in Operatinal State cluster

* Add unit test of operational state delegate

* Add the rest unit test of operational state delegate

* Optimize class OperationalStateDelegate , add two private member

* move the constexpr val

* fix the err in TestOperationalStateDelegate

* Optimize api GenericOperationalError

* Add OperationalStateDelegateImpl unit test

* add document

* change Log event api in operational state cluster

* Add document

* sync code to examples/placeholder

* Restyled by whitespace

* Restyled by clang-format

* Restyled by gn

* replace GetOperationalState with GetCurrentOperationalState

* update document

* update document

* update document

* replace GetOperationalError with GenericOperationalError

* update document

* document the api for GetOperationalStateDelegate in head file

* optimize the use of operator [] for Span class

* fix the spelling error

* provide a way to  construct a GenericOperationalState without providing a state

* optimize the code of if-else

* Optimize code

* update document

* Optimize class OperationalStateServer:
put all the public bits together instead of interleaving them with the private bits.

* modify document

* Add CurrentPhase and CountdownTime attribute into AttributeAccessInterface-only

* zap regen

* remove unuseful member in class Delegate

* remove useful code

* document the api

* Add set/get current phase api

* Add set/get countdownTime api

* delegate could be null, and that should be handled without crashing, via returning an error

* command callbacks need to respond with an error

* remove something that is not in the spec

* optimize the name of class member function

* Optimize struct GenericOperationCompletion

* update struct GenericOperationCompletion test case

* Optimize struct GenericOperationCompletion api

* add coutdownTime attribute in operational state cluter

* change api of initializing OperationalStateServer instances

* remove the code using operational state in other platform

* Add operational state server init in linux all-cluster-app

* Restyled by whitespace

* Restyled by clang-format

* Restyled by prettier-json

* Add TestOperationalState.yaml

* rm TestOperationalStateDelegateImpl.cpp

* Restyled by whitespace

* Restyled by gn

* Restyled by prettier-yaml

* remove the api,  filing a followup issue to emit the right events

* fix CI build error

* fix CI build err

* Restyled by clang-format

* fix build error

* Restyled by clang-format

* update document

* use NullOptional to instead Missing

* return CHIP_ERROR_INCORRECT_STATE when delegate is nullptr

* optimize reading the operational state list

* move the position of testcase

* fix ci build err

* fix ci build err

* Restyled by clang-format

* fix readability-else-after-return error

* fix Unknown key in CI build

* add note in ciTest.json for darwin-framework-tool

* update document for api

* use Zcl instead NotSpecified in log

* fix TestOperationalState error

* zap_regen_all

* optimize lambda function use

* Restyled by clang-format

* fix ci error: readability-else-after-return

---------

Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Justin Wood <woody@apple.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni
index 90b38f8..4d734a7 100644
--- a/src/app/chip_data_model.gni
+++ b/src/app/chip_data_model.gni
@@ -250,6 +250,12 @@
           "${_app_root}/clusters/scenes-server/ExtensionFieldSetsImpl.cpp",
           "${_app_root}/clusters/scenes-server/SceneTableImpl.cpp",
         ]
+      } else if (cluster == "operational-state-server") {
+        sources += [
+          "${_app_root}/clusters/${cluster}/${cluster}.cpp",
+          "${_app_root}/clusters/${cluster}/${cluster}.h",
+          "${_app_root}/clusters/${cluster}/operational-state-delegate.h",
+        ]
       } else {
         sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ]
       }
diff --git a/src/app/clusters/operational-state-server/operational-state-delegate.h b/src/app/clusters/operational-state-server/operational-state-delegate.h
new file mode 100644
index 0000000..217a99c
--- /dev/null
+++ b/src/app/clusters/operational-state-server/operational-state-delegate.h
@@ -0,0 +1,322 @@
+/*
+ *
+ *    Copyright (c) 2023 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.
+ */
+
+#pragma once
+
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/util/af-enums.h>
+#include <lib/support/CommonIterator.h>
+
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace OperationalState {
+
+constexpr size_t kOperationalStateLabelMaxSize   = 64u;
+constexpr size_t kOperationalErrorLabelMaxSize   = 64u;
+constexpr size_t kOperationalErrorDetailsMaxSize = 64u;
+constexpr size_t kOperationalPhaseNameMaxSize    = 64u;
+
+/**
+ * A class which represents the operational state of an Operational State cluster derivation instance.
+ */
+struct GenericOperationalState : public app::Clusters::detail::Structs::OperationalStateStruct::Type
+{
+    GenericOperationalState(uint8_t state = to_underlying(OperationalStateEnum::kStopped), Optional<CharSpan> label = NullOptional)
+    {
+        Set(state, label);
+    }
+
+    GenericOperationalState(const GenericOperationalState & op) { *this = op; }
+
+    GenericOperationalState & operator=(const GenericOperationalState & op)
+    {
+        Set(op.operationalStateID, op.operationalStateLabel);
+        return *this;
+    }
+
+    void Set(uint8_t state, Optional<CharSpan> label = NullOptional)
+    {
+        operationalStateID = state;
+        if (label.HasValue())
+        {
+            memset(mOperationalStateLabelBuffer, 0, sizeof(mOperationalStateLabelBuffer));
+            if (label.Value().size() > sizeof(mOperationalStateLabelBuffer))
+            {
+                memcpy(mOperationalStateLabelBuffer, label.Value().data(), sizeof(mOperationalStateLabelBuffer));
+                operationalStateLabel.SetValue(CharSpan(mOperationalStateLabelBuffer, sizeof(mOperationalStateLabelBuffer)));
+            }
+            else
+            {
+                memcpy(mOperationalStateLabelBuffer, label.Value().data(), label.Value().size());
+                operationalStateLabel.SetValue(CharSpan(mOperationalStateLabelBuffer, label.Value().size()));
+            }
+        }
+        else
+        {
+            operationalStateLabel = NullOptional;
+        }
+    }
+
+private:
+    char mOperationalStateLabelBuffer[kOperationalStateLabelMaxSize];
+};
+
+/**
+ * A class which represents the operational error of an Operational State cluster derivation instance.
+ */
+struct GenericOperationalError : public app::Clusters::detail::Structs::ErrorStateStruct::Type
+{
+    GenericOperationalError(uint8_t state, Optional<chip::CharSpan> label = NullOptional,
+                            Optional<chip::CharSpan> details = NullOptional)
+    {
+        Set(state, label, details);
+    }
+
+    GenericOperationalError(const GenericOperationalError & error) { *this = error; }
+
+    GenericOperationalError & operator=(const GenericOperationalError & error)
+    {
+        Set(error.errorStateID, error.errorStateLabel, error.errorStateDetails);
+        return *this;
+    }
+
+    void Set(uint8_t state, Optional<chip::CharSpan> label = NullOptional, Optional<chip::CharSpan> details = NullOptional)
+    {
+        errorStateID = state;
+        if (label.HasValue())
+        {
+            memset(mErrorStateLabelBuffer, 0, sizeof(mErrorStateLabelBuffer));
+            if (label.Value().size() > sizeof(mErrorStateLabelBuffer))
+            {
+                memcpy(mErrorStateLabelBuffer, label.Value().data(), sizeof(mErrorStateLabelBuffer));
+                errorStateLabel.SetValue(CharSpan(mErrorStateLabelBuffer, sizeof(mErrorStateLabelBuffer)));
+            }
+            else
+            {
+                memcpy(mErrorStateLabelBuffer, label.Value().data(), label.Value().size());
+                errorStateLabel.SetValue(CharSpan(mErrorStateLabelBuffer, label.Value().size()));
+            }
+        }
+        else
+        {
+            errorStateLabel = NullOptional;
+        }
+
+        if (details.HasValue())
+        {
+            memset(mErrorStateDetailsBuffer, 0, sizeof(mErrorStateDetailsBuffer));
+            if (details.Value().size() > sizeof(mErrorStateDetailsBuffer))
+            {
+                memcpy(mErrorStateDetailsBuffer, details.Value().data(), sizeof(mErrorStateDetailsBuffer));
+                errorStateDetails.SetValue(CharSpan(mErrorStateDetailsBuffer, sizeof(mErrorStateDetailsBuffer)));
+            }
+            else
+            {
+                memcpy(mErrorStateDetailsBuffer, details.Value().data(), details.Value().size());
+                errorStateDetails.SetValue(CharSpan(mErrorStateDetailsBuffer, details.Value().size()));
+            }
+        }
+        else
+        {
+            errorStateDetails = NullOptional;
+        }
+    }
+
+private:
+    char mErrorStateLabelBuffer[kOperationalErrorLabelMaxSize];
+    char mErrorStateDetailsBuffer[kOperationalErrorDetailsMaxSize];
+};
+
+/**
+ * A class which represents the operational phase of an Operational State cluster derivation instance.
+ */
+struct GenericOperationalPhase
+{
+    GenericOperationalPhase(app::DataModel::Nullable<CharSpan> name) { Set(name); }
+
+    GenericOperationalPhase(const GenericOperationalPhase & ph) { *this = ph; }
+
+    GenericOperationalPhase & operator=(const GenericOperationalPhase & ph)
+    {
+        Set(ph.mPhaseName);
+        return *this;
+    }
+
+    bool IsMissing() const { return mPhaseName.IsNull(); }
+    app::DataModel::Nullable<CharSpan> mPhaseName;
+
+private:
+    void Set(app::DataModel::Nullable<CharSpan> name)
+    {
+        if (name.IsNull())
+        {
+            mPhaseName.SetNull();
+        }
+        else
+        {
+            memset(mPhaseNameBuffer, 0, sizeof(mPhaseNameBuffer));
+            if (name.Value().size() > sizeof(mPhaseNameBuffer))
+            {
+                memcpy(mPhaseNameBuffer, name.Value().data(), sizeof(mPhaseNameBuffer));
+                mPhaseName = app::DataModel::Nullable<CharSpan>(CharSpan(mPhaseNameBuffer, sizeof(mPhaseNameBuffer)));
+            }
+            else
+            {
+                memcpy(mPhaseNameBuffer, name.Value().data(), name.Value().size());
+                mPhaseName = app::DataModel::Nullable<CharSpan>(CharSpan(mPhaseNameBuffer, name.Value().size()));
+            }
+        }
+    }
+
+    char mPhaseNameBuffer[kOperationalPhaseNameMaxSize];
+};
+
+/**
+ * A class which represents the operational completion of an Operational State cluster derivation instance.
+ */
+struct GenericOperationCompletion : public app::Clusters::OperationalState::Events::OperationCompletion::Type
+{
+    GenericOperationCompletion(uint8_t aCompletionErrorCode,
+                               const Optional<DataModel::Nullable<uint32_t>> & aTotalOperationalTime = NullOptional,
+                               const Optional<DataModel::Nullable<uint32_t>> & aPausedTime           = NullOptional)
+    {
+        completionErrorCode  = aCompletionErrorCode;
+        totalOperationalTime = aTotalOperationalTime;
+        pausedTime           = aPausedTime;
+    }
+};
+
+/**
+ * A delegate to handle application logic of the Operational State aliased Cluster.
+ * The delegate API assumes there will be separate delegate objects for each cluster instance.
+ * (i.e. each separate operational state cluster derivation, on each separate endpoint),
+ * since the delegate methods are not handed the cluster id or endpoint.
+ */
+class Delegate
+{
+public:
+    /**
+     * Get the current operational state.
+     * @param op The GenericOperationalState to fill with the current operational state value.
+     * @return void.
+     */
+    virtual void GetCurrentOperationalState(GenericOperationalState & op) = 0;
+
+    /**
+     * Get the list of supported operational states.
+     * Fills in the provided GenericOperationalState with the state at index `index` if there is one,
+     * or returns CHIP_ERROR_NOT_FOUND if the index is out of range for the list of states.
+     * @param index The index of the state, with 0 representing the first state.
+     * @param operationalState  The GenericOperationalState is filled.
+     */
+    virtual CHIP_ERROR GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) = 0;
+
+    /**
+     * Get the list of supported operational phases.
+     * Fills in the provided GenericOperationalPhase with the phase at index `index` if there is one,
+     * or returns CHIP_ERROR_NOT_FOUND if the index is out of range for the list of phases.
+     * @param index The index of the phase, with 0 representing the first phase.
+     * @param operationalPhase  The GenericOperationalPhase is filled.
+     */
+    virtual CHIP_ERROR GetOperationalPhaseAtIndex(size_t index, GenericOperationalPhase & operationalPhase) = 0;
+
+    /**
+     * Get current operational error.
+     * @param error The GenericOperationalError to fill with the current operational error value
+     */
+    virtual void GetCurrentOperationalError(GenericOperationalError & error) = 0;
+
+    /**
+     * Get current phase
+     * @param phase The app::DataModel::Nullable<uint8_t> to fill with the current phase value
+     */
+    virtual void GetCurrentPhase(app::DataModel::Nullable<uint8_t> & phase) = 0;
+
+    /**
+     * Get countdown time
+     * @param time The app::DataModel::Nullable<uint32_t> to fill with the coutdown time value
+     */
+    virtual void GetCountdownTime(app::DataModel::Nullable<uint32_t> & time) = 0;
+
+    /**
+     * Set current operational state.
+     * @param opState The operational state that should now be the current one.
+     */
+    virtual CHIP_ERROR SetOperationalState(const GenericOperationalState & opState) = 0;
+
+    /**
+     * Set operational error.
+     * @param opErrState The new operational error.
+     */
+    virtual CHIP_ERROR SetOperationalError(const GenericOperationalError & opErrState) = 0;
+
+    /**
+     * Set operational phase.
+     * @param phase The operational phase that should now be the current one.
+     */
+    virtual CHIP_ERROR SetPhase(const app::DataModel::Nullable<uint8_t> & phase) = 0;
+
+    /**
+     * Set coutdown time.
+     * @param time The coutdown time that should now be the current one.
+     */
+    virtual CHIP_ERROR SetCountdownTime(const app::DataModel::Nullable<uint32_t> & time) = 0;
+
+    // command callback
+    /**
+     * Handle Command Callback in application: Pause
+     * @param[out] get operational error after callback.
+     */
+    virtual void HandlePauseStateCallback(GenericOperationalError & err) = 0;
+
+    /**
+     * Handle Command Callback in application: Resume
+     * @param[out] get operational error after callback.
+     */
+    virtual void HandleResumeStateCallback(GenericOperationalError & err) = 0;
+
+    /**
+     * Handle Command Callback in application: Start
+     * @param[out] get operational error after callback.
+     */
+    virtual void HandleStartStateCallback(GenericOperationalError & err) = 0;
+
+    /**
+     * Handle Command Callback in application: Stop
+     * @param[out] get operational error after callback.
+     */
+    virtual void HandleStopStateCallback(GenericOperationalError & err) = 0;
+
+    Delegate() = default;
+
+    virtual ~Delegate() = default;
+};
+
+// @brief Instance getter for the delegate for the given operational state alias cluster on the given endpoint.
+// The delegate API assumes there will be separate delegate objects for each cluster instance.
+// (i.e. each separate operational state cluster derivation, on each separate endpoint)
+// @note This API should always be called prior to using the delegate and the return pointer should never be cached.
+//   This should be implemented by the application.
+// @return Default global delegate instance.
+Delegate * GetOperationalStateDelegate(EndpointId endpointId, ClusterId clusterId);
+
+} // namespace OperationalState
+} // namespace Clusters
+} // namespace app
+} // namespace chip
diff --git a/src/app/clusters/operational-state-server/operational-state-server.cpp b/src/app/clusters/operational-state-server/operational-state-server.cpp
new file mode 100644
index 0000000..79bb3d1
--- /dev/null
+++ b/src/app/clusters/operational-state-server/operational-state-server.cpp
@@ -0,0 +1,305 @@
+/*
+ *
+ *    Copyright (c) 2023 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.
+ */
+
+/****************************************************************************
+ * @file
+ * @brief Implementation for the Operational State Server Cluster
+ ***************************************************************************/
+#include "operational-state-server.h"
+#include "operational-state-delegate.h"
+#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/enums.h>
+#include <app-common/zap-generated/ids/Attributes.h>
+#include <app/CommandHandler.h>
+#include <app/ConcreteAttributePath.h>
+#include <app/ConcreteCommandPath.h>
+#include <app/InteractionModelEngine.h>
+#include <app/util/af.h>
+#include <app/util/attribute-storage.h>
+#include <app/util/error-mapping.h>
+#include <lib/core/CHIPEncoding.h>
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::OperationalState;
+using namespace chip::app::Clusters::OperationalState::Attributes;
+
+using Status = Protocols::InteractionModel::Status;
+
+CHIP_ERROR OperationalStateServer::Init()
+{
+    // Check if the cluster has been selected in zap
+    if (!emberAfContainsServer(mEndpointId, mClusterId))
+    {
+        ChipLogError(Zcl, "Operational State: The cluster with ID %lu was not enabled in zap.", long(mClusterId));
+        return CHIP_ERROR_INVALID_ARGUMENT;
+    }
+
+    ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->RegisterCommandHandler(this));
+
+    VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE);
+
+    return CHIP_NO_ERROR;
+}
+
+void OperationalStateServer::Shutdown()
+{
+    InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this);
+}
+
+// This function is called by the interaction model engine when a command destined for this instance is received.
+void OperationalStateServer::InvokeCommand(HandlerContext & handlerContext)
+{
+    ChipLogDetail(Zcl, "OperationalState: InvokeCommand");
+    switch (handlerContext.mRequestPath.mCommandId)
+    {
+    case Commands::Pause::Id:
+        ChipLogDetail(Zcl, "OperationalState: Entering handling Pause state");
+
+        HandleCommand<Commands::Pause::DecodableType>(
+            handlerContext, [this](HandlerContext & ctx, const auto & req) { HandlePauseState(ctx, req); });
+        break;
+
+    case Commands::Resume::Id:
+        ChipLogDetail(Zcl, "OperationalState: Entering handling Resume state");
+
+        HandleCommand<Commands::Resume::DecodableType>(
+            handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleResumeState(ctx, req); });
+        break;
+
+    case Commands::Start::Id:
+        ChipLogDetail(Zcl, "OperationalState: Entering handling Start state");
+
+        HandleCommand<Commands::Start::DecodableType>(
+            handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleStartState(ctx, req); });
+        break;
+
+    case Commands::Stop::Id:
+        ChipLogDetail(Zcl, "OperationalState: Entering handling Stop state");
+
+        HandleCommand<Commands::Stop::DecodableType>(handlerContext,
+                                                     [this](HandlerContext & ctx, const auto & req) { HandleStopState(ctx, req); });
+        break;
+    }
+}
+
+void OperationalStateServer::HandlePauseState(HandlerContext & ctx, const Commands::Pause::DecodableType & req)
+{
+    ChipLogDetail(Zcl, "OperationalState: HandlePauseState");
+    Commands::OperationalCommandResponse::Type response;
+    Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+    GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError));
+    GenericOperationalState opState;
+
+    VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure));
+    delegate->GetCurrentOperationalState(opState);
+
+    if (opState.operationalStateID != to_underlying(OperationalStateEnum::kPaused))
+    {
+        delegate->HandlePauseStateCallback(err);
+    }
+    response.commandResponseState = err;
+
+    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
+}
+
+void OperationalStateServer::HandleResumeState(HandlerContext & ctx, const Commands::Resume::DecodableType & req)
+{
+    ChipLogDetail(Zcl, "OperationalState: HandleResumeState");
+    Commands::OperationalCommandResponse::Type response;
+    Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+    GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError));
+    GenericOperationalState opState;
+
+    VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure));
+
+    delegate->GetCurrentOperationalState(opState);
+
+    if (opState.operationalStateID != to_underlying(OperationalStateEnum::kPaused) &&
+        opState.operationalStateID != to_underlying(OperationalStateEnum::kRunning))
+    {
+        err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState));
+    }
+    else if (opState.operationalStateID == to_underlying(OperationalStateEnum::kPaused))
+    {
+        delegate->HandleResumeStateCallback(err);
+    }
+    response.commandResponseState = err;
+
+    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
+}
+
+void OperationalStateServer::HandleStartState(HandlerContext & ctx, const Commands::Start::DecodableType & req)
+{
+    ChipLogDetail(Zcl, "OperationalState: HandleStartState");
+    Commands::OperationalCommandResponse::Type response;
+    Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+    GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError));
+    GenericOperationalState opState;
+
+    VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure));
+
+    delegate->GetCurrentOperationalState(opState);
+
+    if (opState.operationalStateID != to_underlying(OperationalStateEnum::kRunning))
+    {
+        delegate->HandleStartStateCallback(err);
+    }
+    response.commandResponseState = err;
+
+    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
+}
+
+void OperationalStateServer::HandleStopState(HandlerContext & ctx, const Commands::Stop::DecodableType & req)
+{
+    ChipLogDetail(Zcl, "OperationalState: HandleStopState");
+    Commands::OperationalCommandResponse::Type response;
+    Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+    GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError));
+    GenericOperationalState opState;
+
+    VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure));
+
+    delegate->GetCurrentOperationalState(opState);
+
+    if (opState.operationalStateID != to_underlying(OperationalStateEnum::kStopped))
+    {
+        delegate->HandleStopStateCallback(err);
+    }
+    response.commandResponseState = err;
+
+    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
+}
+
+template <typename RequestT, typename FuncT>
+void OperationalStateServer::HandleCommand(HandlerContext & handlerContext, FuncT func)
+{
+    if (!handlerContext.mCommandHandled && (handlerContext.mRequestPath.mCommandId == RequestT::GetCommandId()))
+    {
+        RequestT requestPayload;
+
+        //
+        // If the command matches what the caller is looking for, let's mark this as being handled
+        // even if errors happen after this. This ensures that we don't execute any fall-back strategies
+        // to handle this command since at this point, the caller is taking responsibility for handling
+        // the command in its entirety, warts and all.
+        //
+        handlerContext.SetCommandHandled();
+
+        if (DataModel::Decode(handlerContext.mPayload, requestPayload) != CHIP_NO_ERROR)
+        {
+            handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath,
+                                                     Protocols::InteractionModel::Status::InvalidCommand);
+            return;
+        }
+
+        func(handlerContext, requestPayload);
+    }
+}
+
+CHIP_ERROR OperationalStateServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
+{
+    switch (aPath.mAttributeId)
+    {
+    case OperationalState::Attributes::OperationalStateList::Id: {
+        Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+        VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr"));
+
+        return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR {
+            GenericOperationalState opState;
+            size_t index   = 0;
+            CHIP_ERROR err = CHIP_NO_ERROR;
+            while ((err = delegate->GetOperationalStateAtIndex(index, opState)) == CHIP_NO_ERROR)
+            {
+                ReturnErrorOnFailure(encoder.Encode(opState));
+                index++;
+            }
+            if (err == CHIP_ERROR_NOT_FOUND)
+            {
+                return CHIP_NO_ERROR;
+            }
+            return err;
+        });
+    }
+    break;
+
+    case OperationalState::Attributes::OperationalState::Id: {
+
+        Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+        GenericOperationalState opState;
+        VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr"));
+        delegate->GetCurrentOperationalState(opState);
+        return aEncoder.Encode(opState);
+    }
+    break;
+
+    case OperationalState::Attributes::OperationalError::Id: {
+        Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+        GenericOperationalError opErr(to_underlying(ErrorStateEnum::kNoError));
+        VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr"));
+        delegate->GetCurrentOperationalError(opErr);
+        return aEncoder.Encode(opErr);
+    }
+    break;
+
+    case OperationalState::Attributes::PhaseList::Id: {
+        Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+
+        GenericOperationalPhase phase = GenericOperationalPhase(DataModel::Nullable<CharSpan>());
+        size_t index                  = 0;
+        VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr"));
+
+        if (delegate->GetOperationalPhaseAtIndex(index, phase) == CHIP_ERROR_NOT_FOUND || phase.IsMissing())
+        {
+            return aEncoder.EncodeNull();
+        }
+        return aEncoder.EncodeList([&](const auto & encoder) -> CHIP_ERROR {
+            while (delegate->GetOperationalPhaseAtIndex(index, phase) != CHIP_ERROR_NOT_FOUND)
+            {
+                ReturnErrorOnFailure(encoder.Encode(phase.mPhaseName));
+                index++;
+            }
+            return CHIP_NO_ERROR;
+        });
+    }
+    break;
+
+    case OperationalState::Attributes::CurrentPhase::Id: {
+        DataModel::Nullable<uint8_t> currentPhase;
+        Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+
+        VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr"));
+        delegate->GetCurrentPhase(currentPhase);
+        return aEncoder.Encode(currentPhase);
+    }
+    break;
+
+    case OperationalState::Attributes::CountdownTime::Id: {
+        DataModel::Nullable<uint32_t> countdownTime;
+        Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId);
+
+        VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr"));
+        delegate->GetCountdownTime(countdownTime);
+        return aEncoder.Encode(countdownTime);
+    }
+    break;
+    }
+    return CHIP_NO_ERROR;
+}
diff --git a/src/app/clusters/operational-state-server/operational-state-server.h b/src/app/clusters/operational-state-server/operational-state-server.h
new file mode 100644
index 0000000..247ef37
--- /dev/null
+++ b/src/app/clusters/operational-state-server/operational-state-server.h
@@ -0,0 +1,120 @@
+/*
+ *
+ *    Copyright (c) 2023 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.
+ */
+
+#pragma once
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/AttributeAccessInterface.h>
+#include <app/CommandHandlerInterface.h>
+#include <app/util/af.h>
+
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace OperationalState {
+
+class Uncopyable
+{
+protected:
+    Uncopyable() {}
+    ~Uncopyable() {}
+
+private:
+    Uncopyable(const Uncopyable &) = delete;
+    Uncopyable & operator=(const Uncopyable &) = delete;
+};
+
+/**
+ * OperationalStateServer is a class that represents an instance of a derivation of the operational state cluster.
+ * It implements CommandHandlerInterface so it can generically handle commands for any derivation cluster id.
+ */
+class OperationalStateServer : public CommandHandlerInterface, public AttributeAccessInterface, public Uncopyable
+{
+public:
+    /**
+     * Init the operational state server.
+     * This function must be called after defining a OperationalStateServer class object.
+     * @param void
+     * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
+     */
+    CHIP_ERROR Init();
+
+    /**
+     * Shut down the operational state server.
+     * This function must be called before destroying a OperationalStateServer class object.
+     * @param void
+     */
+    void Shutdown();
+
+    /**
+     * Creates an operational state cluster instance. The Init() function needs to be called for this instance to be registered and
+     * called by the interaction model at the appropriate times.
+     * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration.
+     * @param aClusterId The ID of the ModeSelect aliased cluster to be instantiated.
+     */
+    OperationalStateServer(EndpointId aEndpointId, ClusterId aClusterId) :
+        CommandHandlerInterface(MakeOptional(aEndpointId), aClusterId),
+        AttributeAccessInterface(MakeOptional(aEndpointId), aClusterId)
+    {
+
+        mEndpointId = aEndpointId;
+        mClusterId  = aClusterId;
+    }
+
+    ~OperationalStateServer() override {}
+
+private:
+    // Inherited from CommandHandlerInterface
+    template <typename RequestT, typename FuncT>
+    void HandleCommand(HandlerContext & handlerContext, FuncT func);
+
+    // Inherited from CommandHandlerInterface
+    void InvokeCommand(HandlerContext & ctx) override;
+
+    /// IM-level implementation of read
+    ///
+    /// Returns appropriately mapped CHIP_ERROR if applicable (may return CHIP_IM_GLOBAL_STATUS errors)
+    CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
+
+    /**
+     * Handle Command: Pause.
+     */
+    void HandlePauseState(HandlerContext & ctx, const Commands::Pause::DecodableType & req);
+
+    /**
+     * Handle Command: Resume.
+     */
+    void HandleResumeState(HandlerContext & ctx, const Commands::Resume::DecodableType & req);
+
+    /**
+     * Handle Command: Start.
+     */
+    void HandleStartState(HandlerContext & ctx, const Commands::Start::DecodableType & req);
+
+    /**
+     * Handle Command: Stop.
+     */
+    void HandleStopState(HandlerContext & ctx, const Commands::Stop::DecodableType & req);
+
+    EndpointId mEndpointId;
+    ClusterId mClusterId;
+};
+
+} // namespace OperationalState
+} // namespace Clusters
+} // namespace app
+} // namespace chip
diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml
index dada0c4..5f28213 100644
--- a/src/app/common/templates/config-data.yaml
+++ b/src/app/common/templates/config-data.yaml
@@ -24,6 +24,7 @@
     # This uses asUpperCamelCase versions of the cluster name.
     - NetworkCommissioning
     - Scenes
+    - OperationalState
 
 # We need a more configurable way of deciding which clusters have which init functions....
 # See https://github.com/project-chip/connectedhomeip/issues/4369
diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn
index 22f10f9..c716f44 100644
--- a/src/app/tests/BUILD.gn
+++ b/src/app/tests/BUILD.gn
@@ -106,6 +106,15 @@
   ]
 }
 
+source_set("operational-state-test-srcs") {
+  sources = [ "${chip_root}/src/app/clusters/operational-state-server/operational-state-delegate.h" ]
+
+  public_deps = [
+    "${chip_root}/src/app/common:cluster-objects",
+    "${chip_root}/src/lib/core",
+  ]
+}
+
 chip_test_suite("tests") {
   output_name = "libAppTests"
 
@@ -132,6 +141,7 @@
     "TestInteractionModelEngine.cpp",
     "TestMessageDef.cpp",
     "TestNumericAttributeTraits.cpp",
+    "TestOperationalStateDelegate.cpp",
     "TestPendingNotificationMap.cpp",
     "TestReadInteraction.cpp",
     "TestReportingEngine.cpp",
@@ -172,6 +182,7 @@
   public_deps = [
     ":binding-test-srcs",
     ":icd-management-test-srcs",
+    ":operational-state-test-srcs",
     ":ota-requestor-test-srcs",
     ":scenes-table-test-srcs",
     ":time-sync-data-provider-test-srcs",
diff --git a/src/app/tests/TestOperationalStateDelegate.cpp b/src/app/tests/TestOperationalStateDelegate.cpp
new file mode 100644
index 0000000..a4d5dce
--- /dev/null
+++ b/src/app/tests/TestOperationalStateDelegate.cpp
@@ -0,0 +1,628 @@
+/*
+ *
+ *    Copyright (c) 2023 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 <app/clusters/operational-state-server/operational-state-delegate.h>
+#include <lib/core/CHIPPersistentStorageDelegate.h>
+#include <lib/support/UnitTestRegistration.h>
+
+#include <nlunit-test.h>
+
+using namespace chip;
+using namespace chip::DeviceLayer;
+using namespace chip::app::Clusters::OperationalState;
+
+namespace {
+
+void TestStructGenericOperationalStateConstructorWithOnlyStateID(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+    // General state: Stopped
+    GenericOperationalState operationalStateStopped(to_underlying(OperationalStateEnum::kStopped));
+    NL_TEST_ASSERT(inSuite, operationalStateStopped.operationalStateID == to_underlying(OperationalStateEnum::kStopped));
+    NL_TEST_ASSERT(inSuite, operationalStateStopped.operationalStateLabel.HasValue() == false);
+
+    // General state: Running
+    GenericOperationalState operationalStateRunning(to_underlying(OperationalStateEnum::kRunning));
+    NL_TEST_ASSERT(inSuite, operationalStateRunning.operationalStateID == to_underlying(OperationalStateEnum::kRunning));
+    NL_TEST_ASSERT(inSuite, operationalStateRunning.operationalStateLabel.HasValue() == false);
+
+    // General state: Paused
+    GenericOperationalState operationalStatePaused(to_underlying(OperationalStateEnum::kPaused));
+    NL_TEST_ASSERT(inSuite, operationalStatePaused.operationalStateID == to_underlying(OperationalStateEnum::kPaused));
+    NL_TEST_ASSERT(inSuite, operationalStatePaused.operationalStateLabel.HasValue() == false);
+
+    // General state: Error
+    GenericOperationalState operationalStateError(to_underlying(OperationalStateEnum::kError));
+    NL_TEST_ASSERT(inSuite, operationalStateError.operationalStateID == to_underlying(OperationalStateEnum::kError));
+    NL_TEST_ASSERT(inSuite, operationalStateError.operationalStateLabel.HasValue() == false);
+}
+
+void TestStructGenericOperationalStateConstructorWithStateIDAndStateLabel(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalStateEnum : uint8_t
+    {
+        kRebooting = 0x81,
+    };
+
+    char buffer[kOperationalStateLabelMaxSize] = "rebooting";
+
+    // ManufacturerStates state, label len = 9:
+    GenericOperationalState operationalState(to_underlying(ManufactureOperationalStateEnum::kRebooting),
+                                             Optional<CharSpan>(CharSpan::fromCharString(buffer)));
+
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateID == to_underlying(ManufactureOperationalStateEnum::kRebooting));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.Value().size() == strlen(buffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalState.operationalStateLabel.Value().data()), buffer, strlen(buffer)) == 0);
+}
+
+void TestStructGenericOperationalStateCopyConstructor(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalStateEnum : uint8_t
+    {
+        kRebooting = 0x81,
+    };
+
+    char buffer[kOperationalStateLabelMaxSize] = "rebooting";
+
+    GenericOperationalState srcOperationalState(to_underlying(ManufactureOperationalStateEnum::kRebooting),
+                                                Optional<CharSpan>(CharSpan::fromCharString(buffer)));
+
+    GenericOperationalState desOperationalState(srcOperationalState);
+
+    NL_TEST_ASSERT(inSuite, desOperationalState.operationalStateID == srcOperationalState.operationalStateID);
+    NL_TEST_ASSERT(inSuite, desOperationalState.operationalStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite,
+                   desOperationalState.operationalStateLabel.Value().size() ==
+                       srcOperationalState.operationalStateLabel.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(desOperationalState.operationalStateLabel.Value().data()),
+                          const_cast<char *>(srcOperationalState.operationalStateLabel.Value().data()),
+                          desOperationalState.operationalStateLabel.Value().size()) == 0);
+}
+
+void TestStructGenericOperationalStateCopyAssignment(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalStateEnum : uint8_t
+    {
+        kRebooting = 0x81,
+    };
+
+    char buffer[kOperationalStateLabelMaxSize] = "rebooting";
+
+    GenericOperationalState srcOperationalState(to_underlying(ManufactureOperationalStateEnum::kRebooting),
+                                                Optional<CharSpan>(CharSpan::fromCharString(buffer)));
+
+    GenericOperationalState desOperationalState = srcOperationalState;
+
+    NL_TEST_ASSERT(inSuite, desOperationalState.operationalStateID == srcOperationalState.operationalStateID);
+    NL_TEST_ASSERT(inSuite, desOperationalState.operationalStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite,
+                   desOperationalState.operationalStateLabel.Value().size() ==
+                       srcOperationalState.operationalStateLabel.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(desOperationalState.operationalStateLabel.Value().data()),
+                          const_cast<char *>(srcOperationalState.operationalStateLabel.Value().data()),
+                          desOperationalState.operationalStateLabel.Value().size()) == 0);
+}
+
+void TestStructGenericOperationalStateFuncSet(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalStateEnum : uint8_t
+    {
+        kRebooting = 0x81,
+    };
+
+    char buffer[kOperationalStateLabelMaxSize] = "rebooting";
+
+    // init state
+    GenericOperationalState operationalState(to_underlying(ManufactureOperationalStateEnum::kRebooting),
+                                             Optional<CharSpan>(CharSpan::fromCharString(buffer)));
+
+    // change state without label
+    operationalState.Set(to_underlying(OperationalStateEnum::kStopped));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateID == to_underlying(OperationalStateEnum::kStopped));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.HasValue() == false);
+
+    // change state with label
+    operationalState.Set(to_underlying(ManufactureOperationalStateEnum::kRebooting),
+                         Optional<CharSpan>(CharSpan::fromCharString(buffer)));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateID == to_underlying(ManufactureOperationalStateEnum::kRebooting));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.Value().size() == strlen(buffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalState.operationalStateLabel.Value().data()), buffer, strlen(buffer)) == 0);
+
+    // change state with label, label len = kOperationalStateLabelMaxSize
+    for (size_t i = 0; i < sizeof(buffer); i++)
+    {
+        buffer[i] = 1;
+    }
+    operationalState.Set(to_underlying(ManufactureOperationalStateEnum::kRebooting),
+                         Optional<CharSpan>(CharSpan(buffer, sizeof(buffer))));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateID == to_underlying(ManufactureOperationalStateEnum::kRebooting));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.Value().size() == sizeof(buffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalState.operationalStateLabel.Value().data()), buffer, sizeof(buffer)) == 0);
+
+    // change state with label, label len larger than kOperationalStateLabelMaxSize
+    char buffer2[kOperationalStateLabelMaxSize + 1];
+
+    for (size_t i = 0; i < sizeof(buffer2); i++)
+    {
+        buffer2[i] = 1;
+    }
+    operationalState.Set(to_underlying(ManufactureOperationalStateEnum::kRebooting),
+                         Optional<CharSpan>(CharSpan(buffer2, sizeof(buffer2))));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateID == to_underlying(ManufactureOperationalStateEnum::kRebooting));
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalState.operationalStateLabel.Value().size() == kOperationalStateLabelMaxSize);
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalState.operationalStateLabel.Value().data()), buffer2,
+                          kOperationalStateLabelMaxSize) == 0);
+}
+
+void TestStructGenericOperationalErrorConstructorWithOnlyStateID(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+    // General errors: NoError
+    GenericOperationalError operationalErrorNoErr(to_underlying(ErrorStateEnum::kNoError));
+
+    NL_TEST_ASSERT(inSuite, operationalErrorNoErr.errorStateID == to_underlying(ErrorStateEnum::kNoError));
+    NL_TEST_ASSERT(inSuite, operationalErrorNoErr.errorStateLabel.HasValue() == false);
+    NL_TEST_ASSERT(inSuite, operationalErrorNoErr.errorStateDetails.HasValue() == false);
+
+    // General errors: UnableToStartOrResume
+    GenericOperationalError operationalErrorUnableToStartOrResume(to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+
+    NL_TEST_ASSERT(inSuite,
+                   operationalErrorUnableToStartOrResume.errorStateID == to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+    NL_TEST_ASSERT(inSuite, operationalErrorUnableToStartOrResume.errorStateLabel.HasValue() == false);
+    NL_TEST_ASSERT(inSuite, operationalErrorUnableToStartOrResume.errorStateDetails.HasValue() == false);
+
+    // General errors: UnableToCompleteOperation
+    GenericOperationalError operationalErrorkUnableToCompleteOperation(to_underlying(ErrorStateEnum::kUnableToCompleteOperation));
+
+    NL_TEST_ASSERT(inSuite,
+                   operationalErrorkUnableToCompleteOperation.errorStateID ==
+                       to_underlying(ErrorStateEnum::kUnableToCompleteOperation));
+    NL_TEST_ASSERT(inSuite, operationalErrorkUnableToCompleteOperation.errorStateLabel.HasValue() == false);
+    NL_TEST_ASSERT(inSuite, operationalErrorkUnableToCompleteOperation.errorStateDetails.HasValue() == false);
+
+    // General errors: CommandInvalidInState
+    GenericOperationalError operationalErrorCommandInvalidInState(to_underlying(ErrorStateEnum::kCommandInvalidInState));
+
+    NL_TEST_ASSERT(inSuite,
+                   operationalErrorCommandInvalidInState.errorStateID == to_underlying(ErrorStateEnum::kCommandInvalidInState));
+    NL_TEST_ASSERT(inSuite, operationalErrorCommandInvalidInState.errorStateLabel.HasValue() == false);
+    NL_TEST_ASSERT(inSuite, operationalErrorCommandInvalidInState.errorStateDetails.HasValue() == false);
+}
+
+void TestStructGenericOperationalErrorConstructorWithStateIDAndStateLabel(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalErrorEnum : uint8_t
+    {
+        kLowBattery = 0x81,
+    };
+
+    char labelBuffer[kOperationalErrorLabelMaxSize] = "low battery";
+
+    // ManufacturerStates error with label, label len = 11:
+    GenericOperationalError operationalError(to_underlying(ManufactureOperationalErrorEnum::kLowBattery),
+                                             Optional<CharSpan>(CharSpan::fromCharString(labelBuffer)));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ManufactureOperationalErrorEnum::kLowBattery));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.Value().size() == strlen(labelBuffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateLabel.Value().data()), labelBuffer, strlen(labelBuffer)) ==
+                       0);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == false);
+}
+
+void TestStructGenericOperationalErrorConstructorWithFullParam(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalErrorEnum : uint8_t
+    {
+        kLowBattery = 0x81,
+    };
+
+    // ManufacturerStates error with label(label len = 11) and detail (len = 25):
+    char labelBuffer[kOperationalErrorLabelMaxSize]    = "low battery";
+    char detailBuffer[kOperationalErrorDetailsMaxSize] = "Please plug in for charge";
+
+    GenericOperationalError operationalError(to_underlying(ManufactureOperationalErrorEnum::kLowBattery),
+                                             Optional<CharSpan>(CharSpan::fromCharString(labelBuffer)),
+                                             Optional<CharSpan>(CharSpan::fromCharString(detailBuffer)));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ManufactureOperationalErrorEnum::kLowBattery));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.Value().size() == strlen(labelBuffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateLabel.Value().data()), labelBuffer, strlen(labelBuffer)) ==
+                       0);
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.Value().size() == strlen(detailBuffer));
+    NL_TEST_ASSERT(
+        inSuite,
+        memcmp(const_cast<char *>(operationalError.errorStateDetails.Value().data()), detailBuffer, strlen(detailBuffer)) == 0);
+}
+
+void TestStructGenericOperationalErrorCopyConstructor(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalErrorEnum : uint8_t
+    {
+        kLowBattery = 0x81,
+    };
+
+    // ManufacturerStates error with label(label len = 11) and detail (len = 25):
+    char labelBuffer[kOperationalErrorLabelMaxSize]    = "low battery";
+    char detailBuffer[kOperationalErrorDetailsMaxSize] = "Please plug in for charge";
+
+    GenericOperationalError srcOperationalError(to_underlying(ManufactureOperationalErrorEnum::kLowBattery),
+                                                Optional<CharSpan>(CharSpan::fromCharString(labelBuffer)),
+                                                Optional<CharSpan>(CharSpan::fromCharString(detailBuffer)));
+
+    // call copy constructor
+    GenericOperationalError desOperationalError(srcOperationalError);
+    NL_TEST_ASSERT(inSuite, desOperationalError.errorStateID == srcOperationalError.errorStateID);
+    NL_TEST_ASSERT(inSuite, desOperationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite,
+                   desOperationalError.errorStateLabel.Value().size() == srcOperationalError.errorStateLabel.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(desOperationalError.errorStateLabel.Value().data()),
+                          const_cast<char *>(srcOperationalError.errorStateLabel.Value().data()),
+                          desOperationalError.errorStateLabel.Value().size()) == 0);
+
+    NL_TEST_ASSERT(inSuite, desOperationalError.errorStateDetails.HasValue() == true);
+    NL_TEST_ASSERT(inSuite,
+                   desOperationalError.errorStateDetails.Value().size() == srcOperationalError.errorStateDetails.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(desOperationalError.errorStateDetails.Value().data()),
+                          const_cast<char *>(srcOperationalError.errorStateDetails.Value().data()),
+                          desOperationalError.errorStateDetails.Value().size()) == 0);
+}
+
+void TestStructGenericOperationalErrorCopyAssignment(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+
+    enum class ManufactureOperationalErrorEnum : uint8_t
+    {
+        kLowBattery = 0x81,
+    };
+
+    // ManufacturerStates error with label(label len = 11) and detail (len = 25):
+    char labelBuffer[kOperationalErrorLabelMaxSize]    = "low battery";
+    char detailBuffer[kOperationalErrorDetailsMaxSize] = "Please plug in for charge";
+
+    GenericOperationalError srcOperationalError(to_underlying(ManufactureOperationalErrorEnum::kLowBattery),
+                                                Optional<CharSpan>(CharSpan::fromCharString(labelBuffer)),
+                                                Optional<CharSpan>(CharSpan::fromCharString(detailBuffer)));
+
+    // call copy assignment
+    GenericOperationalError desOperationalError = srcOperationalError;
+    NL_TEST_ASSERT(inSuite, desOperationalError.errorStateID == srcOperationalError.errorStateID);
+    NL_TEST_ASSERT(inSuite, desOperationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite,
+                   desOperationalError.errorStateLabel.Value().size() == srcOperationalError.errorStateLabel.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(desOperationalError.errorStateLabel.Value().data()),
+                          const_cast<char *>(srcOperationalError.errorStateLabel.Value().data()),
+                          desOperationalError.errorStateLabel.Value().size()) == 0);
+
+    NL_TEST_ASSERT(inSuite, desOperationalError.errorStateDetails.HasValue() == true);
+    NL_TEST_ASSERT(inSuite,
+                   desOperationalError.errorStateDetails.Value().size() == srcOperationalError.errorStateDetails.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(desOperationalError.errorStateDetails.Value().data()),
+                          const_cast<char *>(srcOperationalError.errorStateDetails.Value().data()),
+                          desOperationalError.errorStateDetails.Value().size()) == 0);
+}
+
+void TestStructGenericOperationalErrorFuncSet(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app::Clusters::OperationalState;
+    enum class ManufactureOperationalErrorEnum : uint8_t
+    {
+        kLowBattery = 0x81,
+    };
+
+    // ManufacturerStates error with label(label len = 11) and detail (len = 25):
+    char labelBuffer[kOperationalErrorLabelMaxSize]    = "low battery";
+    char detailBuffer[kOperationalErrorDetailsMaxSize] = "Please plug in for charge";
+
+    // General errors: NoError
+    GenericOperationalError operationalError(to_underlying(ErrorStateEnum::kNoError));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ErrorStateEnum::kNoError));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == false);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == false);
+
+    // call Set with stateId
+    operationalError.Set(to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == false);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == false);
+
+    // call Set with stateId and StateLabel
+    operationalError.Set(to_underlying(ErrorStateEnum::kUnableToStartOrResume),
+                         Optional<CharSpan>(CharSpan::fromCharString(labelBuffer)));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.Value().size() == strlen(labelBuffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateLabel.Value().data()), labelBuffer, strlen(labelBuffer)) ==
+                       0);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == false);
+
+    // call Set with stateId, StateLabel and StateDetails
+    operationalError.Set(to_underlying(ErrorStateEnum::kUnableToStartOrResume),
+                         Optional<CharSpan>(CharSpan::fromCharString(labelBuffer)),
+                         Optional<CharSpan>(CharSpan::fromCharString(detailBuffer)));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.Value().size() == strlen(labelBuffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateLabel.Value().data()), labelBuffer, strlen(labelBuffer)) ==
+                       0);
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.Value().size() == strlen(detailBuffer));
+    NL_TEST_ASSERT(
+        inSuite,
+        memcmp(const_cast<char *>(operationalError.errorStateDetails.Value().data()), detailBuffer, strlen(detailBuffer)) == 0);
+
+    // change state with label, label len = kOperationalStateLabelMaxSize
+    for (size_t i = 0; i < sizeof(labelBuffer); i++)
+    {
+        labelBuffer[i] = 1;
+    }
+    operationalError.Set(to_underlying(ErrorStateEnum::kUnableToStartOrResume),
+                         Optional<CharSpan>(CharSpan(labelBuffer, sizeof(labelBuffer))));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.Value().size() == sizeof(labelBuffer));
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateLabel.Value().data()), labelBuffer, sizeof(labelBuffer)) ==
+                       0);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == false);
+
+    // change state with label, label len = kOperationalStateLabelMaxSize + 1
+    char labelBuffer2[kOperationalErrorLabelMaxSize + 1];
+    for (size_t i = 0; i < sizeof(labelBuffer2); i++)
+    {
+        labelBuffer2[i] = 2;
+    }
+    operationalError.Set(to_underlying(ErrorStateEnum::kUnableToStartOrResume),
+                         Optional<CharSpan>(CharSpan(labelBuffer2, sizeof(labelBuffer2))));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.Value().size() == kOperationalErrorLabelMaxSize);
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateLabel.Value().data()), labelBuffer2,
+                          kOperationalErrorLabelMaxSize) == 0);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == false);
+
+    // change state with label and details, details len = kOperationalErrorDetailsMaxSize + 1
+    char detailBuffer2[kOperationalErrorDetailsMaxSize + 1];
+    for (size_t i = 0; i < sizeof(detailBuffer2); i++)
+    {
+        detailBuffer2[i] = 3;
+    }
+    operationalError.Set(to_underlying(ErrorStateEnum::kUnableToStartOrResume),
+                         Optional<CharSpan>(CharSpan(labelBuffer2, sizeof(labelBuffer2))),
+                         Optional<CharSpan>(CharSpan(detailBuffer2, sizeof(detailBuffer2))));
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateID == to_underlying(ErrorStateEnum::kUnableToStartOrResume));
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateLabel.Value().size() == kOperationalErrorLabelMaxSize);
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateLabel.Value().data()), labelBuffer2,
+                          kOperationalErrorLabelMaxSize) == 0);
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.HasValue() == true);
+
+    NL_TEST_ASSERT(inSuite, operationalError.errorStateDetails.Value().size() == kOperationalErrorDetailsMaxSize);
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(operationalError.errorStateDetails.Value().data()), detailBuffer2,
+                          kOperationalErrorDetailsMaxSize) == 0);
+}
+
+void TestStructGenericOperationalPhaseConstructor(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app;
+    using namespace chip::app::Clusters::OperationalState;
+
+    GenericOperationalPhase phase = GenericOperationalPhase(DataModel::Nullable<CharSpan>());
+    NL_TEST_ASSERT(inSuite, phase.IsMissing() == true);
+
+    char phaseBuffer[kOperationalPhaseNameMaxSize] = "start";
+    GenericOperationalPhase phase2(DataModel::Nullable<CharSpan>(CharSpan::fromCharString(phaseBuffer)));
+    NL_TEST_ASSERT(inSuite, phase2.IsMissing() == false);
+    NL_TEST_ASSERT(inSuite, phase2.mPhaseName.Value().size() == strlen(phaseBuffer));
+    NL_TEST_ASSERT(inSuite, memcmp(const_cast<char *>(phase2.mPhaseName.Value().data()), phaseBuffer, strlen(phaseBuffer)) == 0);
+}
+
+void TestStructGenericOperationalPhaseCopyConstructor(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app;
+    using namespace chip::app::Clusters::OperationalState;
+
+    char phaseBuffer[kOperationalPhaseNameMaxSize] = "start";
+    GenericOperationalPhase phase(DataModel::Nullable<CharSpan>(CharSpan::fromCharString(phaseBuffer)));
+
+    GenericOperationalPhase phase2(phase);
+
+    NL_TEST_ASSERT(inSuite, phase2.IsMissing() == false);
+    NL_TEST_ASSERT(inSuite, phase2.mPhaseName.Value().size() == phase.mPhaseName.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(phase2.mPhaseName.Value().data()), const_cast<char *>(phase.mPhaseName.Value().data()),
+                          phase.mPhaseName.Value().size()) == 0);
+}
+
+void TestStructGenericOperationalPhaseCopyAssignment(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app;
+    using namespace chip::app::Clusters::OperationalState;
+
+    // copy assignment with null-name
+    GenericOperationalPhase phase = GenericOperationalPhase(DataModel::Nullable<CharSpan>());
+    NL_TEST_ASSERT(inSuite, phase.IsMissing() == true);
+
+    // copy assignment with name
+    char phaseBuffer[kOperationalPhaseNameMaxSize] = "start";
+    GenericOperationalPhase phase2(DataModel::Nullable<CharSpan>(CharSpan::fromCharString(phaseBuffer)));
+    phase = phase2;
+
+    NL_TEST_ASSERT(inSuite, phase.IsMissing() == false);
+    NL_TEST_ASSERT(inSuite, phase.mPhaseName.Value().size() == phase2.mPhaseName.Value().size());
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(phase.mPhaseName.Value().data()), const_cast<char *>(phase2.mPhaseName.Value().data()),
+                          phase.mPhaseName.Value().size()) == 0);
+
+    // copy assignment with name, name's len = kOperationalPhaseNameMaxSize
+    for (size_t i = 0; i < sizeof(phaseBuffer); i++)
+    {
+        phaseBuffer[i] = 1;
+    }
+    phase = GenericOperationalPhase(DataModel::Nullable<CharSpan>(CharSpan(phaseBuffer, sizeof(phaseBuffer))));
+
+    NL_TEST_ASSERT(inSuite, phase.IsMissing() == false);
+    NL_TEST_ASSERT(inSuite, phase.mPhaseName.Value().size() == sizeof(phaseBuffer));
+    NL_TEST_ASSERT(inSuite, memcmp(const_cast<char *>(phase.mPhaseName.Value().data()), phaseBuffer, sizeof(phaseBuffer)) == 0);
+
+    // copy assignment with name, name's len = kOperationalPhaseNameMaxSize + 1
+    char phaseBuffer2[kOperationalPhaseNameMaxSize + 1];
+    for (size_t i = 0; i < sizeof(phaseBuffer2); i++)
+    {
+        phaseBuffer2[i] = 2;
+    }
+    phase = GenericOperationalPhase(DataModel::Nullable<CharSpan>(CharSpan(phaseBuffer2, sizeof(phaseBuffer2))));
+
+    NL_TEST_ASSERT(inSuite, phase.IsMissing() == false);
+    NL_TEST_ASSERT(inSuite, phase.mPhaseName.Value().size() == kOperationalPhaseNameMaxSize);
+    NL_TEST_ASSERT(inSuite,
+                   memcmp(const_cast<char *>(phase.mPhaseName.Value().data()), phaseBuffer2, kOperationalPhaseNameMaxSize) == 0);
+}
+
+void TestStructGenericOperationalCompletionConstructor(nlTestSuite * inSuite, void * inContext)
+{
+    using namespace chip::app;
+    using namespace chip::app::Clusters::OperationalState;
+
+    // completion with only CompletionErrorCode
+    GenericOperationCompletion genericOperationCompletion(to_underlying(OperationalStateEnum::kError));
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion.completionErrorCode == to_underlying(OperationalStateEnum::kError));
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion.totalOperationalTime.HasValue() == false);
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion.pausedTime.HasValue() == false);
+
+    // completion with errorCode and TotalOperationalTime
+    uint32_t kTotalOperationalTime = 500;
+    GenericOperationCompletion genericOperationCompletion2(
+        to_underlying(OperationalStateEnum::kError),
+        Optional<DataModel::Nullable<uint32_t>>(DataModel::Nullable<uint32_t>(kTotalOperationalTime)));
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion2.completionErrorCode == to_underlying(OperationalStateEnum::kError));
+
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion2.totalOperationalTime.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion2.totalOperationalTime.Value().Value() == kTotalOperationalTime);
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion2.pausedTime.HasValue() == false);
+
+    // completion with errorCode, TotalOperationalTime and PausedTime
+    uint32_t kPausedTime = 2000;
+    GenericOperationCompletion genericOperationCompletion3(
+        to_underlying(OperationalStateEnum::kError),
+        Optional<DataModel::Nullable<uint32_t>>(DataModel::Nullable<uint32_t>(kTotalOperationalTime)),
+        Optional<DataModel::Nullable<uint32_t>>(DataModel::Nullable<uint32_t>(kPausedTime)));
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion3.completionErrorCode == to_underlying(OperationalStateEnum::kError));
+
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion3.totalOperationalTime.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion3.totalOperationalTime.Value().Value() == kTotalOperationalTime);
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion3.pausedTime.HasValue() == true);
+    NL_TEST_ASSERT(inSuite, genericOperationCompletion3.pausedTime.Value().Value() == kPausedTime);
+}
+
+const nlTest sTests[] = {
+    NL_TEST_DEF("Test struct GenericOperationalState: constructor with only StateID",
+                TestStructGenericOperationalStateConstructorWithOnlyStateID),
+    NL_TEST_DEF("Test struct GenericOperationalState: constructor with StateID and StateLabel",
+                TestStructGenericOperationalStateConstructorWithStateIDAndStateLabel),
+    NL_TEST_DEF("Test struct GenericOperationalState: copy constructor", TestStructGenericOperationalStateCopyConstructor),
+    NL_TEST_DEF("Test struct GenericOperationalState: copy assignment", TestStructGenericOperationalStateCopyAssignment),
+    NL_TEST_DEF("Test struct GenericOperationalState: member function 'Set'", TestStructGenericOperationalStateFuncSet),
+    NL_TEST_DEF("Test struct GenericOperationalError: constructor with only StateID",
+                TestStructGenericOperationalErrorConstructorWithOnlyStateID),
+    NL_TEST_DEF("Test struct GenericOperationalError: constructor with StateID and StateLabel",
+                TestStructGenericOperationalErrorConstructorWithStateIDAndStateLabel),
+    NL_TEST_DEF("Test struct GenericOperationalError: constructor with StateID, StateLabel and StateDetail",
+                TestStructGenericOperationalErrorConstructorWithFullParam),
+    NL_TEST_DEF("Test struct GenericOperationalError: copy constructor", TestStructGenericOperationalErrorCopyConstructor),
+    NL_TEST_DEF("Test struct GenericOperationalError: copy assignment", TestStructGenericOperationalErrorCopyAssignment),
+    NL_TEST_DEF("Test struct GenericOperationalError: member function 'Set'", TestStructGenericOperationalErrorFuncSet),
+    NL_TEST_DEF("Test struct GenericOperationalPhase: constructor", TestStructGenericOperationalPhaseConstructor),
+    NL_TEST_DEF("Test struct GenericOperationalPhase: copy constructor", TestStructGenericOperationalPhaseCopyConstructor),
+    NL_TEST_DEF("Test struct GenericOperationalPhase: copy assignment", TestStructGenericOperationalPhaseCopyAssignment),
+    NL_TEST_DEF("Test struct GenericOperationalCompletion: constructor", TestStructGenericOperationalCompletionConstructor),
+    NL_TEST_SENTINEL()
+};
+
+int TestSetup(void * inContext)
+{
+    VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE);
+    return SUCCESS;
+}
+
+int TestTearDown(void * inContext)
+{
+    chip::Platform::MemoryShutdown();
+    return SUCCESS;
+}
+
+} // namespace
+
+int TestOperationalStateDelegate()
+{
+    nlTestSuite theSuite = { "Test Operational State delegate tests", &sTests[0], TestSetup, TestTearDown };
+
+    // Run test suit againt one context.
+    nlTestRunner(&theSuite, nullptr);
+    return nlTestRunnerStats(&theSuite);
+}
+
+CHIP_REGISTER_TEST_SUITE(TestOperationalStateDelegate)
diff --git a/src/app/tests/suites/TestOperationalState.yaml b/src/app/tests/suites/TestOperationalState.yaml
new file mode 100644
index 0000000..62ad14d
--- /dev/null
+++ b/src/app/tests/suites/TestOperationalState.yaml
@@ -0,0 +1,123 @@
+# Copyright (c) 2023 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.
+
+name: Operational State Tests
+
+config:
+    nodeId: 0x12344321
+    cluster: "Operational State"
+    endpoint: 1
+
+tests:
+    - label: "Wait for the commissioned device to be retrieved"
+      cluster: "DelayCommands"
+      command: "WaitForCommissionee"
+      arguments:
+          values:
+              - name: "nodeId"
+                value: nodeId
+
+    - label: "Read Phase List"
+      command: "readAttribute"
+      attribute: "PhaseList"
+      response:
+          value: null
+
+    - label: "Read current Phase"
+      command: "readAttribute"
+      attribute: "CurrentPhase"
+      response:
+          value: null
+
+    - label: "Read Countdown Time"
+      command: "readAttribute"
+      attribute: "CountdownTime"
+      response:
+          value: null
+
+    - label: "Read Operational State List"
+      command: "readAttribute"
+      attribute: "OperationalStateList"
+      response:
+          value:
+              [
+                  { OperationalStateID: 0 },
+                  { OperationalStateID: 1 },
+                  { OperationalStateID: 2 },
+                  { OperationalStateID: 3 },
+              ]
+
+    - label: "Read current Operational Error"
+      command: "readAttribute"
+      attribute: "OperationalError"
+      response:
+          value: { ErrorStateID: 0 }
+
+    - label: "Read current Operational State"
+      command: "readAttribute"
+      attribute: "OperationalState"
+      response:
+          value: { OperationalStateID: 0 }
+
+    - label: "Start Command"
+      command: "Start"
+      response:
+          values:
+              - name: "CommandResponseState"
+                value: { ErrorStateID: 0 }
+
+    - label: "Read current Operational State"
+      command: "readAttribute"
+      attribute: "OperationalState"
+      response:
+          value: { OperationalStateID: 1 }
+
+    - label: "Pause Command"
+      command: "Pause"
+      response:
+          values:
+              - name: "CommandResponseState"
+                value: { ErrorStateID: 0 }
+
+    - label: "Read current Operational State"
+      command: "readAttribute"
+      attribute: "OperationalState"
+      response:
+          value: { OperationalStateID: 2 }
+
+    - label: "Resume Command"
+      command: "Resume"
+      response:
+          values:
+              - name: "CommandResponseState"
+                value: { ErrorStateID: 0 }
+
+    - label: "Read current Operational State"
+      command: "readAttribute"
+      attribute: "OperationalState"
+      response:
+          value: { OperationalStateID: 1 }
+
+    - label: "Stop Command"
+      command: "Stop"
+      response:
+          values:
+              - name: "CommandResponseState"
+                value: { ErrorStateID: 0 }
+
+    - label: "Read current Operational State"
+      command: "readAttribute"
+      attribute: "OperationalState"
+      response:
+          value: { OperationalStateID: 0 }
diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json
index 3018879..71a6406 100644
--- a/src/app/tests/suites/ciTests.json
+++ b/src/app/tests/suites/ciTests.json
@@ -257,7 +257,8 @@
         "TestLevelControlWithOnOffDependency",
         "TestCommissioningWindow",
         "TestCommissionerNodeId",
-        "TestTimeSynchronization"
+        "TestTimeSynchronization",
+        "TestOperationalState"
     ],
     "MultiAdmin": ["TestMultiAdmin"],
     "SoftwareDiagnostics": ["Test_TC_DGSW_1_1"],
diff --git a/src/app/util/util.cpp b/src/app/util/util.cpp
index ed27dea..7ae0d17 100644
--- a/src/app/util/util.cpp
+++ b/src/app/util/util.cpp
@@ -159,6 +159,7 @@
 void MatterPm25ConcentrationMeasurementPluginServerInitCallback() {}
 void MatterRadonConcentrationMeasurementPluginServerInitCallback() {}
 void MatterTotalVolatileOrganicCompoundsConcentrationMeasurementPluginServerInitCallback() {}
+void MatterOperationalStatePluginServerInitCallback() {}
 // ****************************************
 // Print out information about each cluster
 // ****************************************
diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json
index 83c069e..667bcd8 100644
--- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json
+++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json
@@ -306,8 +306,18 @@
             "DSTOffsetListMaxSize"
         ],
         "Temperature Control": ["SupportedTemperatureLevels"],
-        "Operational State": ["OperationalState", "OperationalError"],
-        "RVC Operational State": ["OperationalState", "OperationalError"]
+        "Operational State": [
+            "OperationalState",
+            "OperationalError",
+            "CurrentPhase",
+            "CountdownTime"
+        ],
+        "RVC Operational State": [
+            "OperationalState",
+            "OperationalError",
+            "CurrentPhase",
+            "CountdownTime"
+        ]
     },
     "defaultReportingPolicy": "mandatory",
     "ZCLDataTypes": ["ARRAY", "BITMAP", "ENUM", "NUMBER", "STRING", "STRUCT"],
diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json
index b6554bf..8ece026 100644
--- a/src/app/zap-templates/zcl/zcl.json
+++ b/src/app/zap-templates/zcl/zcl.json
@@ -304,8 +304,18 @@
             "DSTOffsetListMaxSize"
         ],
         "Temperature Control": ["SupportedTemperatureLevels"],
-        "Operational State": ["OperationalState", "OperationalError"],
-        "RVC Operational State": ["OperationalState", "OperationalError"]
+        "Operational State": [
+            "OperationalState",
+            "OperationalError",
+            "CurrentPhase",
+            "CountdownTime"
+        ],
+        "RVC Operational State": [
+            "OperationalState",
+            "OperationalError",
+            "CurrentPhase",
+            "CountdownTime"
+        ]
     },
     "defaultReportingPolicy": "mandatory",
     "ZCLDataTypes": ["ARRAY", "BITMAP", "ENUM", "NUMBER", "STRING", "STRUCT"],
diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json
index 6063b94..ca9592b 100644
--- a/src/app/zap_cluster_list.json
+++ b/src/app/zap_cluster_list.json
@@ -240,6 +240,7 @@
         "ON_OFF_CLUSTER": ["on-off-server"],
         "ON_OFF_SWITCH_CONFIGURATION_CLUSTER": [],
         "OPERATIONAL_CREDENTIALS_CLUSTER": ["operational-credentials-server"],
+        "OPERATIONAL_STATE_CLUSTER": ["operational-state-server"],
         "OTA_BOOTLOAD_CLUSTER": [],
         "OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER": ["ota-provider"],
         "OTA_SOFTWARE_UPDATE_REQUESTOR_CLUSTER": ["ota-requestor"],