[HVAC] Sync atomic write error order with spec (#34936)

* Add support for Presets attributes and commands to the Thermostat cluster

Clean up the Thermostat cluster and remove the TemperatureSetpointHoldPolicy attribute
and SetTemperatureSetpointHoldPolicy command

* Restyled by whitespace

* Restyled by clang-format

* Restyled by gn.

* Fix build error for Linux configure build of all-clusters-app

* Fix Darwin CI issues

Editorial fixes

* Restyled by clang-format

* More fixes

* Restyled by clang-format

* BUILD.gn fixes for CI

* Apply suggestions from code review

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

* Address review comments.

* Restyled by clang-format

* Regenerate Thermostat XML from spec

* Move atomic enum to global-enums.xml, actually

# Conflicts:
#	src/app/zap-templates/zcl/data-model/chip/global-structs.xml

* Regenerate XML and convert thermostat-server to atomic writes

* Pull in ACCapacityFormat typo un-fix

* Update Test_TC_TSTAT_1_1 to know about AtomicResponse command.

* Restyled patch

* Fix weird merge with upstream

* Fix emberAfIsTypeSigned not understanding temperature type

* Merge fixes from atomic write branch

* Relocate thermostat-manager sample code to all-clusters-common

* Fix g++ build error on linux

* Fix C formatter for long int, cast whole expression

* Sync cast fix with master

* Add thermostat-common dependency to thermostat app under linux

* Remove MatterPostAttributeChangeCallback from thermostat-manager, as it conflicts with other implementations

* Convert Atomic enums and structs to global

* Restyled patch

* Apply suggestions from code review

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

* Regen with alchemy 0.6.1

* Updates based on comments

* Add TC_MCORE_FS_1_3.py test implementation (#34650)

* Fix most TC-SWTCH-2.4 remaining issues (#34677)

- Move 2.4 in a better place in the file
- Add test steps properly
- Allow default button press position override

Issue #34656

Testing done:

- Test still passes on DUT with automation

* Initial test script for Fabric Sync TC_MCORE_FS_1_2 (#34675)

* Initial test script for Fabric Sync TC_MCORE_FS_1_2

* Apply suggestions from code review

Co-authored-by: C Freeman <cecille@google.com>

* Address Review Comments

* Address review comments

* Fix default timeout after other timeouts changed

* Restyled by autopep8

* Fix linter error

---------

Co-authored-by: C Freeman <cecille@google.com>
Co-authored-by: Restyled.io <commits@restyled.io>

* Test automation for FabricSync ICD BridgedDeviceBasicInfoCluster (#34628)

* WIP Bridged ICD, commissioning to both fabrics

* wip testing sending KeepActive

* wip most steps implemented

* using SIGSTOP and SIGCONT to control ICD server pausing

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: Terence Hampson <thampson@google.com>

* comments addressed

* more comments addressed

* lint pass

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: C Freeman <cecille@google.com>

* comments addressed, incl TH_SERVER configurable

* added setupQRCode and setupManualCode as options for DUT commissioning

* Restyled by autopep8

* Restyled by isort

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: Terence Hampson <thampson@google.com>

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: Terence Hampson <thampson@google.com>

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: Terence Hampson <thampson@google.com>

* comments addressed

* Restyled by autopep8

---------

Co-authored-by: Terence Hampson <thampson@google.com>
Co-authored-by: C Freeman <cecille@google.com>
Co-authored-by: Restyled.io <commits@restyled.io>

* ServiceArea test scripts (#34548)

* initial commit

* fix bugs

* fix issues reported by the linter

* fix bug in checking for unique areaDesc

* add TC 1.5

* Update src/python_testing/TC_SEAR_1_2.py

Co-authored-by: William <hicklin@users.noreply.github.com>

* Update src/python_testing/TC_SEAR_1_2.py

Co-authored-by: William <hicklin@users.noreply.github.com>

* address code review comments

* fix issue introduced by the previous commit

* address code review feedback

* Update src/python_testing/TC_SEAR_1_2.py

Co-authored-by: Kiel Oleson <kielo@apple.com>

* address code review feedback

* remove PICS checked by the TC_SEAR_1.6

* more code review updates

* Restyled by autopep8

---------

Co-authored-by: William <hicklin@users.noreply.github.com>
Co-authored-by: Kiel Oleson <kielo@apple.com>
Co-authored-by: Restyled.io <commits@restyled.io>

* Remove manual tests for Thermostat presets (#34679)

* Dump details about leaked ExchangeContexts before aborting (#34617)

* Dump details about leaked ExchangeContexts before aborting

This is implemented via a VerifyOrDieWithObject() variant of the existing
VerifyOrDie() macro that calls a DumpToLog() method on the provided object if
it exists (otherwise this is simply a no-op).

If CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE is not enabled, VerifyOrDieWithObject()
simply behaves like a plain VerifyOrDie(). DumpToLog() implementations can use
ChipLogFormatRtti to log type information about an object (usually a delegate);
if RTTI is disabled this simply outputs whether the object was null or not.

* Address review comments

* Make gcc happy and improve documentation

* Remove unused include

* Fix compile error without CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE

* Avoid unused parameter warning

* [TI] CC13x4_26x4 build fixes (#34682)

* lwip pbuf, map file, and hex creation when OTA is disabled

* added cc13x4 family define around the non OTA hex creation

* whitespace fix

* reversed custom factoy data flash with cc13x4 check

* more whitespace fixes

* [ICD] Add missing polling function to NoWifi connectivity manager (#34684)

* Add missing polling function to NoWifi connectivity manager

* Update GenericConnectivityManagerImpl_NoWiFi.h

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

---------

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

* [OPSTATE] Add Q test script for CountdownTime (#34632)

* Add Q test

* Added test to test set

* Remove unused var

* Restyled by autopep8

* Restyled by isort

* Fix name

* Use pics over other method

* Removed unused stuff

* Added pipe commands

* Fix reset

* Get example to report appropriate changes.

* WiP

* Added some comments

* Changes to make things work

* Removed dev msgs

* Missed some

* Removed dev msgs

* Straggler

* Restyled by clang-format

* Restyled by autopep8

* Restyled by isort

* Commented unused var

* Update examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp

* Fix bug

---------

Co-authored-by: Restyled.io <commits@restyled.io>

* YAML update to BRBINFO, ProductId (#34513)

* Bridged Device Information Cluster, Attribute ProductID test reflects marking as O, not X

* Update src/app/tests/suites/certification/Test_TC_BRBINFO_2_1.yaml

Co-authored-by: Terence Hampson <thampson@google.com>

* corrected pics

* corrected pics

* WIP Bridged ICD, commissioning to both fabrics

* wip testing sending KeepActive

* update to bridged-device-basic-information.xml and zap generated files

* removed unrelated file

---------

Co-authored-by: Terence Hampson <thampson@google.com>
Co-authored-by: Andrei Litvin <andy314@gmail.com>

* Fix simplified Linux tv-casting-app gn build error. (#34692)

* adding parallel execution to restyle-diff (#34663)

* adding parallel execution to restyle-diff

* using xargs to call restyle-paths

* fixing Copyright year

* restyle the restyler

* Add some bits to exercise global structs/enums to Unit Testing cluster. (#34540)

* Adds things to the Unit Testing cluster XML.
* This requires those things to be enabled in all-clusters-app,
  all-clusters-minimal-app, and one of the chef contact sensors to pass CI.
* That requires an implementation in test-cluster-server
* At which point might as well add a YAML test to exercise it all.

* [Silabs] Port platform specific Multi-Chip OTA work  (#34440)

* Pull request #1836: Cherry multi ota

Merge in WMN_TOOLS/matter from cherry-multi-ota to silabs_slc_1.3

Squashed commit of the following:

commit 4320bb46571658bc44fb82345348265def394991
Author: Michael Rupp <michael.rupp@silabs.com>
Date:   Fri May 10 14:26:07 2024 -0400

    remove some unwanted diffs in provision files

commit be160931dc600de7e7ead378b70d6a43c3945e46
Author: Michael Rupp <michael.rupp@silabs.com>
Date:   Fri May 10 14:24:25 2024 -0400

    revert changes to generator.project.mak

commit 14b6605887166e6d5284a61feb2bf407d850bdcf
Author: Michael Rupp <michael.rupp@silabs.com>
Date:   Fri May 10 13:06:12 2024 -0400

    revert NVM key changes and script changes

... and 8 more commits

* Restyled by whitespace

* Restyled by clang-format

* Restyled by gn

* Restyled by autopep8

* remove unused libs caught by linter

* update doctree with new readmes

* rerun CI, cirque failing for unknown reasons

* fix include guards in provision examples

* Restyled by clang-format

---------

Co-authored-by: Restyled.io <commits@restyled.io>

* Add python tests for Thermostat presets feature (#34693)

* Add python tests for Thermostat presets feature

* Restyled by autopep8

* Restyled by isort

* Update the PICS code for presets attribute

---------

Co-authored-by: Restyled.io <commits@restyled.io>

* removing unneccessary git fetch (#34698)

* Restyle patch

* Regen to fix ordering of global structs

* Apply suggestions from code review

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

* Return correct AtomicResponse when committing or rolling back

* Patch tests for atomic write of presets

* Fix tests to work with the new setup.

Specific changes:

* Enable SetActivePresetRequest command in all-clusters-app.
* Fix assignment of a PresetStructWithOwnedMembers to another
  PresetStructWithOwnedMembers to actually work correctly.
* Move constraint checks that happen on write from commit to write.
* Fix sending of atomic responses to not have use-stack-after-return.
* Fix PICS for the tests involved.

* Fix PICS values for atomic requests

* Remove PresetsSchedulesEditable and QueuedPreset from various places

* Restyled patch

* Restyled patch, again

* Remove PICS value for PresetsSchedulesEditable

* clang-tidy fixes

* clang-tidy fixes

* Clear associated atomic writes when fabric is removed

* Add tests for fabric removal and lockout of clients outside of atomic write

* Python linter

* Restyled patch

* Clear timer when fabric is removed

* Check for open atomic write before resetting

* Revert auto delegate declaration on lines where there's no collision

* Allow Thermostat delegate to provide timeout for atomic requests

* Relocate thermostat example code to thermostat-common

* Remove thermostat-manager code, replace with thermostat delegate

* Sync atomic write error order with spec

* Restyle patch

* Drop memset of atomic write sessions

* Add PreCommit stage to allow rollback of multiple attributes when only one fails

* Separate OnTimerExpired method, vs ResetWrite

* Method documentation

* Apply suggestions from code review

Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com>

* Remove unused InWrite check

* Drop imcode alias

* Switch AtomicWriteState to enum class

* DRY up atomic write manager

* Apply suggestions from code review

Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com>

* Drop duplicate doc comments

* Rename GetAtomicWriteScopedNodeId to GetAtomicWriteOriginatorScopedNodeId

* Updates based on comments

* Add MatterReportingAttributeChangeCallback calls for updated attributes

* Relocate thermostat example code to thermostat-common, and remove thermostat-manager

* Merge atomic write code back into thermostat-server

* Apply suggestions from code review

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

* Fix build after suggestions

* Actually track attribute IDs associated with atomic write

* Only commit presets if all attribute precommits were successful

* Fix scope on err

* Add documentation to methods

* Remove duplicate preset check.

* Move various functions into anonymous namespaces, or Thermostat namespace

* Drop impossible non-atomic attribute status after rollback

* Namespace workaround for compilers on other platforms

* Apply suggestions from code review

---------

Co-authored-by: Nivedita Sarkar <nivedita_sarkar@apple.com>
Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
Co-authored-by: Terence Hampson <thampson@google.com>
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
Co-authored-by: Chris Letnick <cletnick@google.com>
Co-authored-by: C Freeman <cecille@google.com>
Co-authored-by: Douglas Rocha Ferraz <rochaferraz@google.com>
Co-authored-by: Petru Lauric <81822411+plauric@users.noreply.github.com>
Co-authored-by: William <hicklin@users.noreply.github.com>
Co-authored-by: Kiel Oleson <kielo@apple.com>
Co-authored-by: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com>
Co-authored-by: Anu Biradar <104591549+abiradarti@users.noreply.github.com>
Co-authored-by: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com>
Co-authored-by: Rob Bultman <rob.Bultman@gmail.com>
Co-authored-by: Andrei Litvin <andy314@gmail.com>
Co-authored-by: Shao Ling Tan <161761051+shaoltan-amazon@users.noreply.github.com>
Co-authored-by: Amine Alami <43780877+Alami-Amine@users.noreply.github.com>
Co-authored-by: Michael Rupp <95718139+mykrupp@users.noreply.github.com>
diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn
index ed228b5..baac520 100644
--- a/examples/all-clusters-app/linux/BUILD.gn
+++ b/examples/all-clusters-app/linux/BUILD.gn
@@ -75,7 +75,7 @@
     "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/src/EnergyEvseTargetsStore.cpp",
     "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/src/energy-evse-mode.cpp",
     "${chip_root}/examples/energy-management-app/energy-management-common/energy-reporting/src/ElectricalPowerMeasurementDelegate.cpp",
-    "${chip_root}/examples/thermostat/linux/thermostat-delegate-impl.cpp",
+    "${chip_root}/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp",
     "AllClustersCommandDelegate.cpp",
     "AllClustersCommandDelegate.h",
     "AppOptions.cpp",
@@ -102,7 +102,7 @@
     "${chip_root}/examples/energy-management-app/energy-management-common/device-energy-management/include",
     "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/include",
     "${chip_root}/examples/energy-management-app/energy-management-common/energy-reporting/include",
-    "${chip_root}/examples/thermostat/linux/include",
+    "${chip_root}/examples/thermostat/thermostat-common/include",
   ]
 
   if (chip_enable_pw_rpc) {
diff --git a/examples/thermostat/linux/BUILD.gn b/examples/thermostat/linux/BUILD.gn
index 71c0ecc..0683b39 100644
--- a/examples/thermostat/linux/BUILD.gn
+++ b/examples/thermostat/linux/BUILD.gn
@@ -17,11 +17,10 @@
 
 executable("thermostat-app") {
   sources = [
+    "${chip_root}/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp",
     "include/low-power/LowPowerManager.cpp",
     "include/low-power/LowPowerManager.h",
     "main.cpp",
-    "thermostat-delegate-impl.cpp",
-    "thermostat-manager.cpp",
   ]
 
   deps = [
@@ -30,7 +29,10 @@
     "${chip_root}/src/lib",
   ]
 
-  include_dirs = [ "include" ]
+  include_dirs = [
+    "include",
+    "${chip_root}/examples/thermostat/thermostat-common/include",
+  ]
 
   cflags = [ "-Wconversion" ]
 
diff --git a/examples/thermostat/linux/include/thermostat-manager.h b/examples/thermostat/linux/include/thermostat-manager.h
deleted file mode 100644
index 274f66c..0000000
--- a/examples/thermostat/linux/include/thermostat-manager.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- *
- *    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.
- */
-
-#pragma once
-
-#include <app-common/zap-generated/attributes/Accessors.h>
-
-class ThermostatManager
-{
-public:
-    CHIP_ERROR Init();
-
-    /// @brief Callback called when any attribute changed on the device
-    void AttributeChangeHandler(chip::EndpointId endpointId, chip::ClusterId clusterId, chip::AttributeId attributeId,
-                                uint8_t * value, uint16_t size);
-
-    chip::app::Clusters::Thermostat::SystemModeEnum GetSystemMode();
-    chip::app::Clusters::Thermostat::ThermostatRunningModeEnum GetRunningMode();
-    int16_t GetCurrentTemperature();
-    int16_t GetCurrentHeatingSetPoint();
-    int16_t GetCurrentCoolingSetPoint();
-    uint8_t GetNumberOfPresets();
-    CHIP_ERROR SetSystemMode(chip::app::Clusters::Thermostat::SystemModeEnum systemMode);
-    CHIP_ERROR SetRunningMode(chip::app::Clusters::Thermostat::ThermostatRunningModeEnum runningMode);
-    CHIP_ERROR SetCurrentTemperature(int16_t temperature);
-    CHIP_ERROR SetCurrentHeatingSetPoint(int16_t heatingSetpoint);
-    CHIP_ERROR SetCurrentCoolingSetPoint(int16_t coolingSetpoint);
-
-private:
-    friend ThermostatManager & ThermostatMgr();
-
-    chip::app::Clusters::Thermostat::SystemModeEnum mSystemMode;
-    chip::app::Clusters::Thermostat::ThermostatRunningModeEnum mRunningMode;
-    int16_t mLocalTemperature;
-    int16_t mOccupiedCoolingSetpoint;
-    int16_t mOccupiedHeatingSetpoint;
-    uint8_t mOccupiedSetback;
-
-    static ThermostatManager sThermostatMgr;
-
-    /// @brief attribute handler for the thermostat endpoint
-    void ThermostatEndpointAttributeChangeHandler(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t * value,
-                                                  uint16_t size);
-    void ThermostatClusterAttributeChangeHandler(chip::AttributeId attributeId, uint8_t * value, uint16_t size);
-    void LocalTemperatureMeasurementEndpointAttributeChangeHandler(chip::ClusterId clusterId, chip::AttributeId attributeId,
-                                                                   uint8_t * value, uint16_t size);
-    void LocalTemperatureMeasurementClusterAttributeChangeHandler(chip::AttributeId attributeId, uint8_t * value, uint16_t size);
-
-    /// @brief  Main method that evaluates the current thermostat state and updates attributes
-    void EvalThermostatState();
-    void UpdateRunningModeForHeating();
-    void UpdateRunningModeForCooling();
-};
-
-inline ThermostatManager & ThermostatMgr()
-{
-    return ThermostatManager::sThermostatMgr;
-}
diff --git a/examples/thermostat/linux/main.cpp b/examples/thermostat/linux/main.cpp
index 2279f02..b9f8269 100644
--- a/examples/thermostat/linux/main.cpp
+++ b/examples/thermostat/linux/main.cpp
@@ -22,8 +22,6 @@
 #include <app/CommandHandler.h>
 #include <app/clusters/identify-server/identify-server.h>
 
-#include "thermostat-manager.h"
-
 using namespace chip;
 using namespace chip::app;
 // using namespace chip::app::Clusters;
@@ -76,19 +74,7 @@
 
 int main(int argc, char * argv[])
 {
-    if (ChipLinuxAppInit(argc, argv) != 0)
-    {
-        return -1;
-    }
-    ChipLogProgress(Zcl, "Starting Thermostat Manager");
-    CHIP_ERROR err = ThermostatManager().Init();
-
-    if (err != CHIP_NO_ERROR)
-    {
-        ChipLogError(AppServer, "Failed to initialize thermostat manager: %" CHIP_ERROR_FORMAT, err.Format());
-        chip::DeviceLayer::PlatformMgr().Shutdown();
-        return -1;
-    }
+    VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0);
     ChipLinuxAppMainLoop();
     return 0;
 }
diff --git a/examples/thermostat/linux/thermostat-manager.cpp b/examples/thermostat/linux/thermostat-manager.cpp
deleted file mode 100644
index ea1f437..0000000
--- a/examples/thermostat/linux/thermostat-manager.cpp
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- *
- *    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.
- */
-
-/**********************************************************
- * Includes
- *********************************************************/
-
-#include <thermostat-delegate-impl.h>
-#include <thermostat-manager.h>
-
-#include <app/clusters/bindings/BindingManager.h>
-#include <app/clusters/thermostat-server/thermostat-server.h>
-#include <controller/ReadInteraction.h>
-#include <platform/PlatformManager.h>
-
-/**********************************************************
- * Defines and Constants
- *********************************************************/
-
-using namespace chip;
-using namespace chip::app;
-using namespace chip::app::DataModel;
-using namespace chip::Controller;
-using namespace chip::app::Clusters;
-using namespace chip::app::Clusters::Thermostat;
-using namespace chip::app::Clusters::Thermostat::Structs;
-using namespace chip::app::Clusters::Thermostat::Attributes;
-using namespace chip::app::Clusters::TemperatureMeasurement;
-using namespace chip::app::Clusters::TemperatureMeasurement::Attributes;
-using namespace Protocols::InteractionModel;
-
-using namespace chip::DeviceLayer;
-
-static constexpr EndpointId kThermostatEndpoint = 1;
-
-static constexpr uint16_t kMaxIntervalCeilingSeconds = 3600;
-
-static const char * SystemModeString(SystemModeEnum systemMode);
-static const char * RunningModeString(ThermostatRunningModeEnum runningMode);
-
-/**********************************************************
- * Variable declarations
- *********************************************************/
-
-ThermostatManager ThermostatManager::sThermostatMgr;
-
-namespace {
-
-template <typename DecodableAttributeType>
-static void OnAttributeChangeReported(const ConcreteDataAttributePath & path, const DecodableAttributeType & value);
-
-template <>
-void OnAttributeChangeReported<MeasuredValue::TypeInfo::DecodableType>(const ConcreteDataAttributePath & path,
-                                                                       const MeasuredValue::TypeInfo::DecodableType & value)
-{
-    ClusterId clusterId = path.mClusterId;
-    if (clusterId != TemperatureMeasurement::Id)
-    {
-        ChipLogError(AppServer,
-                     "Attribute change reported for TemperatureMeasurement cluster on incorrect cluster id " ChipLogFormatMEI,
-                     ChipLogValueMEI(clusterId));
-        return;
-    }
-
-    AttributeId attributeId = path.mAttributeId;
-    if (attributeId != MeasuredValue::Id)
-    {
-        ChipLogError(AppServer,
-                     "Attribute change reported for TemperatureMeasurement cluster for incorrect attribute" ChipLogFormatMEI,
-                     ChipLogValueMEI(attributeId));
-        return;
-    }
-
-    if (!value.IsNull())
-    {
-        ChipLogDetail(AppServer, "Attribute change reported for TemperatureMeasurement cluster - MeasuredValue is %d",
-                      value.Value());
-    }
-}
-
-static void OnError(const ConcreteDataAttributePath * path, ChipError err)
-{
-    ChipLogError(AppServer,
-                 "Subscribing to cluster Id " ChipLogFormatMEI " and attribute Id " ChipLogFormatMEI
-                 " failed with error %" CHIP_ERROR_FORMAT,
-                 ChipLogValueMEI(path->mClusterId), ChipLogValueMEI(path->mAttributeId), err.Format());
-}
-
-static void OnSubscriptionEstablished(const ReadClient & client, unsigned int value)
-{
-    ChipLogDetail(AppServer, "OnSubscriptionEstablished with subscription Id: %d", value);
-}
-
-template <typename DecodableAttributeType>
-void SubscribeToAttribute(ClusterId clusterId, AttributeId attributeId, const EmberBindingTableEntry & binding,
-                          OperationalDeviceProxy * peer_device)
-{
-    VerifyOrReturn(peer_device->GetSecureSession().HasValue(),
-                   ChipLogError(AppServer, "SubscribeToAttribute failed. Secure session is null"));
-
-    SubscribeAttribute<DecodableAttributeType>(
-        peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), binding.remote, clusterId, attributeId,
-        &OnAttributeChangeReported<DecodableAttributeType>, &OnError, 0, kMaxIntervalCeilingSeconds, &OnSubscriptionEstablished,
-        nullptr, true /* fabricFiltered */, false /* keepExistingSubscription */);
-}
-
-static void ThermostatBoundDeviceChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device,
-                                                void * context)
-{
-    VerifyOrReturn(binding.clusterId.has_value(), ChipLogError(AppServer, "Cluster Id is null"));
-    ClusterId clusterId = binding.clusterId.value();
-
-    switch (clusterId)
-    {
-    case TemperatureMeasurement::Id:
-
-        // Subscribe to the MeasuredValue attribute
-        SubscribeToAttribute<MeasuredValue::TypeInfo::DecodableType>(clusterId, MeasuredValue::Id, binding, peer_device);
-        break;
-    default:
-        ChipLogError(AppServer, "Unsupported Cluster Id");
-        break;
-    }
-}
-
-void NotifyBoundClusterChangedForAllClusters()
-{
-    BindingManager::GetInstance().NotifyBoundClusterChanged(kThermostatEndpoint, TemperatureMeasurement::Id, nullptr);
-}
-
-static void OnPlatformChipDeviceEvent(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg)
-{
-    if (event->Type == DeviceLayer::DeviceEventType::kBindingsChangedViaCluster)
-    {
-        NotifyBoundClusterChangedForAllClusters();
-    }
-}
-
-void InitBindingManager(intptr_t context)
-{
-    auto & server    = Server::GetInstance();
-    CHIP_ERROR error = BindingManager::GetInstance().Init(
-        { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() });
-
-    if (error != CHIP_NO_ERROR)
-    {
-        ChipLogError(AppServer, "Failed to init binding manager");
-    }
-
-    BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(ThermostatBoundDeviceChangedHandler);
-    NotifyBoundClusterChangedForAllClusters();
-}
-
-} // anonymous namespace
-
-CHIP_ERROR ThermostatManager::Init()
-{
-    // Init binding manager
-
-    DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformChipDeviceEvent, reinterpret_cast<intptr_t>(this));
-    DeviceLayer::PlatformMgr().ScheduleWork(InitBindingManager);
-
-    mLocalTemperature        = GetCurrentTemperature();
-    mSystemMode              = GetSystemMode();
-    mRunningMode             = GetRunningMode();
-    mOccupiedCoolingSetpoint = GetCurrentCoolingSetPoint();
-    mOccupiedHeatingSetpoint = GetCurrentHeatingSetPoint();
-    // TODO: Gotta expose this properly on attribute
-    mOccupiedSetback = 5; // 0.5 C
-
-    ChipLogError(AppServer,
-                 "Initialized a thermostat with \n "
-                 "mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n "
-                 "mOccupiedCoolingSetpoint: %d"
-                 "NumberOfPresets: %d",
-                 to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode),
-                 RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint,
-                 GetNumberOfPresets());
-
-    // TODO: Should this be called later?
-    EvalThermostatState();
-
-    return CHIP_NO_ERROR;
-}
-
-void ThermostatManager::AttributeChangeHandler(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, uint8_t * value,
-                                               uint16_t size)
-{
-    switch (endpointId)
-    {
-    case kThermostatEndpoint:
-        ThermostatEndpointAttributeChangeHandler(clusterId, attributeId, value, size);
-        break;
-
-    default:
-        ChipLogError(AppServer, "Attribute change reported for Thermostat on incorrect endpoint. Ignoring.");
-        break;
-    }
-}
-
-void ThermostatManager::ThermostatEndpointAttributeChangeHandler(ClusterId clusterId, AttributeId attributeId, uint8_t * value,
-                                                                 uint16_t size)
-{
-    switch (clusterId)
-    {
-    case Thermostat::Id:
-        ThermostatClusterAttributeChangeHandler(attributeId, value, size);
-        break;
-
-    default:
-        ChipLogError(AppServer,
-                     "Attribute change reported for Thermostat on incorrect cluster for the thermostat endpoint. Ignoring.");
-        break;
-    }
-}
-
-void ThermostatManager::ThermostatClusterAttributeChangeHandler(AttributeId attributeId, uint8_t * value, uint16_t size)
-{
-    switch (attributeId)
-    {
-    case LocalTemperature::Id: {
-        memcpy(&mLocalTemperature, value, size);
-        ChipLogError(AppServer, "Local temperature changed to %d", mLocalTemperature);
-        EvalThermostatState();
-    }
-    break;
-
-    case OccupiedCoolingSetpoint::Id: {
-        memcpy(&mOccupiedCoolingSetpoint, value, size);
-        ChipLogError(AppServer, "Cooling temperature changed to %d", mOccupiedCoolingSetpoint);
-        EvalThermostatState();
-    }
-    break;
-
-    case OccupiedHeatingSetpoint::Id: {
-        memcpy(&mOccupiedHeatingSetpoint, value, size);
-        ChipLogError(AppServer, "Heating temperature changed to %d", mOccupiedHeatingSetpoint);
-        EvalThermostatState();
-    }
-    break;
-
-    case SystemMode::Id: {
-        mSystemMode = static_cast<SystemModeEnum>(*value);
-        ChipLogError(AppServer, "System mode changed to %u (%s)", *value, SystemModeString(mSystemMode));
-        EvalThermostatState();
-    }
-    break;
-
-    case ThermostatRunningMode::Id: {
-        mRunningMode = static_cast<ThermostatRunningModeEnum>(*value);
-        ChipLogError(AppServer, "Running mode changed to %u (%s)", *value, RunningModeString(mRunningMode));
-    }
-    break;
-
-    default: {
-        ChipLogError(AppServer, "Unhandled thermostat attribute %u", static_cast<uint>(attributeId));
-        return;
-    }
-    break;
-    }
-}
-
-SystemModeEnum ThermostatManager::GetSystemMode()
-{
-    SystemModeEnum systemMode;
-    SystemMode::Get(kThermostatEndpoint, &systemMode);
-    return systemMode;
-}
-
-ThermostatRunningModeEnum ThermostatManager::GetRunningMode()
-{
-    ThermostatRunningModeEnum runningMode;
-    ThermostatRunningMode::Get(kThermostatEndpoint, &runningMode);
-    return runningMode;
-}
-
-int16_t ThermostatManager::GetCurrentTemperature()
-{
-    DataModel::Nullable<int16_t> currentTemperature;
-    currentTemperature.SetNull();
-    LocalTemperature::Get(kThermostatEndpoint, currentTemperature);
-    return currentTemperature.ValueOr(0);
-}
-
-int16_t ThermostatManager::GetCurrentHeatingSetPoint()
-{
-    int16_t heatingSetpoint;
-    OccupiedHeatingSetpoint::Get(kThermostatEndpoint, &heatingSetpoint);
-    return heatingSetpoint;
-}
-
-int16_t ThermostatManager::GetCurrentCoolingSetPoint()
-{
-    int16_t coolingSetpoint;
-    OccupiedCoolingSetpoint::Get(kThermostatEndpoint, &coolingSetpoint);
-    return coolingSetpoint;
-}
-
-uint8_t ThermostatManager::GetNumberOfPresets()
-{
-    return ThermostatDelegate::GetInstance().GetNumberOfPresets();
-}
-
-CHIP_ERROR ThermostatManager::SetSystemMode(SystemModeEnum systemMode)
-{
-    uint8_t systemModeValue = to_underlying(systemMode);
-    if (mSystemMode == systemMode)
-    {
-        ChipLogDetail(AppServer, "Already in system mode: %u (%s)", systemModeValue, SystemModeString(systemMode));
-        return CHIP_NO_ERROR;
-    }
-
-    ChipLogError(AppServer, "Setting system mode: %u (%s)", systemModeValue, SystemModeString(systemMode));
-    return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(SystemMode::Set(kThermostatEndpoint, systemMode));
-}
-
-CHIP_ERROR ThermostatManager::SetRunningMode(ThermostatRunningModeEnum runningMode)
-{
-    uint8_t runningModeValue = to_underlying(runningMode);
-    if (mRunningMode == runningMode)
-    {
-        ChipLogDetail(AppServer, "Already in running mode: %u (%s)", runningModeValue, RunningModeString(runningMode));
-        return CHIP_NO_ERROR;
-    }
-
-    ChipLogError(AppServer, "Setting running mode: %u (%s)", runningModeValue, RunningModeString(runningMode));
-    return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(ThermostatRunningMode::Set(kThermostatEndpoint, runningMode));
-}
-
-CHIP_ERROR ThermostatManager::SetCurrentTemperature(int16_t temperature)
-{
-    return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(LocalTemperature::Set(kThermostatEndpoint, temperature));
-}
-
-CHIP_ERROR ThermostatManager::SetCurrentHeatingSetPoint(int16_t heatingSetpoint)
-{
-    return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(OccupiedHeatingSetpoint::Set(kThermostatEndpoint, heatingSetpoint));
-}
-
-CHIP_ERROR ThermostatManager::SetCurrentCoolingSetPoint(int16_t coolingSetpoint)
-{
-    return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(OccupiedCoolingSetpoint::Set(kThermostatEndpoint, coolingSetpoint));
-}
-
-void ThermostatManager::EvalThermostatState()
-{
-    ChipLogError(AppServer,
-                 "Eval Thermostat Running Mode \n "
-                 "mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n "
-                 "mOccupiedCoolingSetpoint: %d",
-                 to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode),
-                 RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint);
-
-    switch (mSystemMode)
-    {
-    case SystemModeEnum::kOff: {
-        SetRunningMode(ThermostatRunningModeEnum::kOff);
-        break;
-    }
-    case SystemModeEnum::kHeat: {
-        UpdateRunningModeForHeating();
-        break;
-    }
-    case SystemModeEnum::kCool: {
-        UpdateRunningModeForCooling();
-        break;
-    }
-    case SystemModeEnum::kAuto: {
-        UpdateRunningModeForHeating();
-        UpdateRunningModeForCooling();
-        break;
-    }
-    default:
-        break;
-    }
-}
-
-void ThermostatManager::UpdateRunningModeForHeating()
-{
-    const int16_t heatingOnThreshold  = mOccupiedHeatingSetpoint - static_cast<int16_t>(mOccupiedSetback * 10);
-    const int16_t heatingOffThreshold = mOccupiedHeatingSetpoint + static_cast<int16_t>(mOccupiedSetback * 10);
-
-    if (mRunningMode == ThermostatRunningModeEnum::kHeat)
-    {
-        if (mLocalTemperature >= heatingOffThreshold)
-        {
-            ChipLogDetail(AppServer, "Eval Heat - Turning off");
-            SetRunningMode(ThermostatRunningModeEnum::kOff);
-        }
-        else
-        {
-            ChipLogDetail(AppServer, "Eval Heat - Keep Heating");
-        }
-    }
-    else
-    {
-        if (mLocalTemperature <= heatingOnThreshold)
-        {
-            ChipLogDetail(AppServer, "Eval Heat - Turn on");
-            SetRunningMode(ThermostatRunningModeEnum::kHeat);
-        }
-        else
-        {
-            ChipLogDetail(AppServer, "Eval Heat - Nothing to do");
-        }
-    }
-}
-
-void ThermostatManager::UpdateRunningModeForCooling()
-{
-    const int16_t coolingOffThreshold = mOccupiedCoolingSetpoint - static_cast<int16_t>(mOccupiedSetback * 10);
-    const int16_t coolingOnThreshold  = mOccupiedCoolingSetpoint + static_cast<int16_t>(mOccupiedSetback * 10);
-
-    if (mRunningMode == ThermostatRunningModeEnum::kCool)
-    {
-        if (mLocalTemperature <= coolingOffThreshold)
-        {
-            ChipLogDetail(AppServer, "Eval Cool - Turning off");
-            SetRunningMode(ThermostatRunningModeEnum::kOff);
-        }
-        else
-        {
-            ChipLogDetail(AppServer, "Eval Cool - Keep Cooling");
-        }
-    }
-    else
-    {
-        if (mLocalTemperature >= coolingOnThreshold)
-        {
-            ChipLogDetail(AppServer, "Eval Cool - Turn on");
-            SetRunningMode(ThermostatRunningModeEnum::kCool);
-        }
-        else
-        {
-            ChipLogDetail(AppServer, "Eval Cool - Nothing to do");
-        }
-    }
-}
-
-static const char * SystemModeString(SystemModeEnum systemMode)
-{
-    switch (systemMode)
-    {
-    case SystemModeEnum::kOff:
-        return "Off";
-    case SystemModeEnum::kAuto:
-        return "Auto";
-    case SystemModeEnum::kCool:
-        return "Cool";
-    case SystemModeEnum::kHeat:
-        return "Heat";
-    default:
-        return "Unknown";
-    }
-}
-
-static const char * RunningModeString(ThermostatRunningModeEnum runningMode)
-{
-    switch (runningMode)
-    {
-    case ThermostatRunningModeEnum::kOff:
-        return "Off";
-    case ThermostatRunningModeEnum::kCool:
-        return "Cool";
-    case ThermostatRunningModeEnum::kHeat:
-        return "Heat";
-    default:
-        return "Unknown";
-    }
-}
-
-void emberAfThermostatClusterInitCallback(EndpointId endpoint)
-{
-    ChipLogProgress(Zcl, "Starting Thermostat Manager");
-    ThermostatManager().Init();
-
-    // Register the delegate for the Thermostat
-    auto & delegate = ThermostatDelegate::GetInstance();
-    // Set the default delegate for endpoint kThermostatEndpoint.
-    VerifyOrDie(endpoint == kThermostatEndpoint);
-    SetDefaultDelegate(endpoint, &delegate);
-}
diff --git a/examples/thermostat/thermostat-common/BUILD.gn b/examples/thermostat/thermostat-common/BUILD.gn
index 93a0c75..1f8f839 100644
--- a/examples/thermostat/thermostat-common/BUILD.gn
+++ b/examples/thermostat/thermostat-common/BUILD.gn
@@ -16,6 +16,10 @@
 
 import("${chip_root}/src/app/chip_data_model.gni")
 
+config("config") {
+  include_dirs = [ "include" ]
+}
+
 chip_data_model("thermostat-common") {
   zap_file = "thermostat.zap"
   is_server = true
diff --git a/examples/thermostat/linux/include/thermostat-delegate-impl.h b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h
similarity index 92%
rename from examples/thermostat/linux/include/thermostat-delegate-impl.h
rename to examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h
index 6bf9d02..9edf13f 100644
--- a/examples/thermostat/linux/include/thermostat-delegate-impl.h
+++ b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h
@@ -44,9 +44,7 @@
 public:
     static inline ThermostatDelegate & GetInstance() { return sInstance; }
 
-    std::optional<System::Clock::Milliseconds16>
-    GetAtomicWriteTimeout(DataModel::DecodableList<chip::AttributeId> attributeRequests,
-                          System::Clock::Milliseconds16 timeoutRequest) override;
+    std::optional<System::Clock::Milliseconds16> GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) override;
 
     CHIP_ERROR GetPresetTypeAtIndex(size_t index, Structs::PresetTypeStruct::Type & presetType) override;
 
@@ -64,7 +62,7 @@
 
     CHIP_ERROR GetPendingPresetAtIndex(size_t index, PresetStructWithOwnedMembers & preset) override;
 
-    CHIP_ERROR ApplyPendingPresets() override;
+    CHIP_ERROR CommitPendingPresets() override;
 
     void ClearPendingPresetList() override;
 
diff --git a/examples/thermostat/linux/thermostat-delegate-impl.cpp b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp
similarity index 75%
rename from examples/thermostat/linux/thermostat-delegate-impl.cpp
rename to examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp
index b931db2..8c411cd 100644
--- a/examples/thermostat/linux/thermostat-delegate-impl.cpp
+++ b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp
@@ -17,7 +17,6 @@
  */
 
 #include <thermostat-delegate-impl.h>
-#include <thermostat-manager.h>
 
 #include <app-common/zap-generated/attributes/Accessors.h>
 #include <lib/support/Span.h>
@@ -36,34 +35,12 @@
     mNextFreeIndexInPresetsList        = 0;
     mNextFreeIndexInPendingPresetsList = 0;
 
-    InitializePresetTypes();
     InitializePresets();
 
     memset(mActivePresetHandleData, 0, sizeof(mActivePresetHandleData));
     mActivePresetHandleDataSize = 0;
 }
 
-void ThermostatDelegate::InitializePresetTypes()
-{
-    PresetScenarioEnum presetScenarioEnumArray[kMaxNumberOfPresetTypes] = {
-        PresetScenarioEnum::kOccupied, PresetScenarioEnum::kUnoccupied, PresetScenarioEnum::kSleep,
-        PresetScenarioEnum::kWake,     PresetScenarioEnum::kVacation,   PresetScenarioEnum::kGoingToSleep
-    };
-    static_assert(ArraySize(presetScenarioEnumArray) <= ArraySize(mPresetTypes));
-
-    uint8_t index = 0;
-    for (PresetScenarioEnum presetScenario : presetScenarioEnumArray)
-    {
-        mPresetTypes[index].presetScenario  = presetScenario;
-        mPresetTypes[index].numberOfPresets = kMaxNumberOfPresetsOfEachType;
-        mPresetTypes[index].presetTypeFeatures =
-            (presetScenario == PresetScenarioEnum::kOccupied || presetScenario == PresetScenarioEnum::kUnoccupied)
-            ? PresetTypeFeaturesBitmap::kAutomatic
-            : PresetTypeFeaturesBitmap::kSupportsNames;
-        index++;
-    }
-}
-
 void ThermostatDelegate::InitializePresets()
 {
     // Initialize the presets with 2 built in presets - occupied and unoccupied.
@@ -94,9 +71,26 @@
 
 CHIP_ERROR ThermostatDelegate::GetPresetTypeAtIndex(size_t index, PresetTypeStruct::Type & presetType)
 {
-    if (index < ArraySize(mPresetTypes))
+    static PresetTypeStruct::Type presetTypes[] = {
+        { .presetScenario     = PresetScenarioEnum::kOccupied,
+          .numberOfPresets    = kMaxNumberOfPresetsOfEachType,
+          .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) },
+        { .presetScenario     = PresetScenarioEnum::kUnoccupied,
+          .numberOfPresets    = kMaxNumberOfPresetsOfEachType,
+          .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) },
+        { .presetScenario     = PresetScenarioEnum::kSleep,
+          .numberOfPresets    = kMaxNumberOfPresetsOfEachType,
+          .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) },
+        { .presetScenario     = PresetScenarioEnum::kWake,
+          .numberOfPresets    = kMaxNumberOfPresetsOfEachType,
+          .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) },
+        { .presetScenario     = PresetScenarioEnum::kVacation,
+          .numberOfPresets    = kMaxNumberOfPresetsOfEachType,
+          .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) },
+    };
+    if (index < ArraySize(presetTypes))
     {
-        presetType = mPresetTypes[index];
+        presetType = presetTypes[index];
         return CHIP_NO_ERROR;
     }
     return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED;
@@ -158,45 +152,19 @@
     return CHIP_NO_ERROR;
 }
 
-std::optional<System::Clock::Milliseconds16>
-ThermostatDelegate::GetAtomicWriteTimeout(DataModel::DecodableList<AttributeId> attributeRequests,
-                                          System::Clock::Milliseconds16 timeoutRequest)
+std::optional<System::Clock::Milliseconds16> ThermostatDelegate::GetMaxAtomicWriteTimeout(chip::AttributeId attributeId)
 {
-    auto attributeIdsIter = attributeRequests.begin();
-    bool requestedPresets = false, requestedSchedules = false;
-    while (attributeIdsIter.Next())
+    switch (attributeId)
     {
-        auto & attributeId = attributeIdsIter.GetValue();
-
-        switch (attributeId)
-        {
-        case Attributes::Presets::Id:
-            requestedPresets = true;
-            break;
-        case Attributes::Schedules::Id:
-            requestedSchedules = true;
-            break;
-        default:
-            return System::Clock::Milliseconds16(0);
-        }
-    }
-    if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR)
-    {
-        return System::Clock::Milliseconds16(0);
-    }
-    auto timeout = System::Clock::Milliseconds16(0);
-    if (requestedPresets)
-    {
+    case Attributes::Presets::Id:
         // If the client expects to edit the presets, then we'll give it 3 seconds to do so
-        timeout += std::chrono::milliseconds(3000);
-    }
-    if (requestedSchedules)
-    {
+        return std::chrono::milliseconds(3000);
+    case Attributes::Schedules::Id:
         // If the client expects to edit the schedules, then we'll give it 9 seconds to do so
-        timeout += std::chrono::milliseconds(9000);
+        return std::chrono::milliseconds(9000);
+    default:
+        return std::nullopt;
     }
-    // If the client requested an even smaller timeout, then use that one
-    return std::min(timeoutRequest, timeout);
 }
 
 void ThermostatDelegate::InitializePendingPresets()
@@ -238,7 +206,7 @@
     return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED;
 }
 
-CHIP_ERROR ThermostatDelegate::ApplyPendingPresets()
+CHIP_ERROR ThermostatDelegate::CommitPendingPresets()
 {
     mNextFreeIndexInPresetsList = 0;
     for (uint8_t indexInPendingPresets = 0; indexInPendingPresets < mNextFreeIndexInPendingPresetsList; indexInPendingPresets++)
diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni
index 3e4448a..01d47a4 100644
--- a/src/app/chip_data_model.gni
+++ b/src/app/chip_data_model.gni
@@ -428,6 +428,8 @@
         ]
       } else if (cluster == "thermostat-server") {
         sources += [
+          "${_app_root}/clusters/${cluster}/${cluster}-atomic.cpp",
+          "${_app_root}/clusters/${cluster}/${cluster}-presets.cpp",
           "${_app_root}/clusters/${cluster}/${cluster}.cpp",
           "${_app_root}/clusters/${cluster}/${cluster}.h",
           "${_app_root}/clusters/${cluster}/PresetStructWithOwnedMembers.cpp",
diff --git a/src/app/clusters/thermostat-server/thermostat-delegate.h b/src/app/clusters/thermostat-server/thermostat-delegate.h
index 0f89f69..ccb690a 100644
--- a/src/app/clusters/thermostat-server/thermostat-delegate.h
+++ b/src/app/clusters/thermostat-server/thermostat-delegate.h
@@ -39,15 +39,12 @@
     virtual ~Delegate() = default;
 
     /**
-     * @brief Get the maximum timeout for atomically writing to a set of attributes
+     * @brief Get the maximum timeout for atomically writing to an attribute
      *
-     * @param[in] attributeRequests The list of attributes to write to.
-     * @param[out] timeoutRequest The timeout proposed by the client.
-     * @return The maximum allowed timeout; zero if the request is invalid.
+     * @param[in] attributeId The attribute to write to.
+     * @return The maximum allowed timeout; nullopt if the request is invalid.
      */
-    virtual std::optional<System::Clock::Milliseconds16>
-    GetAtomicWriteTimeout(DataModel::DecodableList<AttributeId> attributeRequests,
-                          System::Clock::Milliseconds16 timeoutRequest) = 0;
+    virtual std::optional<System::Clock::Milliseconds16> GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) = 0;
 
     /**
      * @brief Get the preset type at a given index in the PresetTypes attribute
@@ -129,7 +126,7 @@
      * @return CHIP_ERROR if the updates to the presets attribute failed to commit for some reason.
      *
      */
-    virtual CHIP_ERROR ApplyPendingPresets() = 0;
+    virtual CHIP_ERROR CommitPendingPresets() = 0;
 
     /**
      * @brief Clears the pending presets list.
diff --git a/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp
new file mode 100644
index 0000000..2a6e52e
--- /dev/null
+++ b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp
@@ -0,0 +1,644 @@
+/**
+ *
+ *    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 "thermostat-server.h"
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::Thermostat;
+using namespace chip::app::Clusters::Thermostat::Attributes;
+using namespace chip::app::Clusters::Thermostat::Structs;
+using namespace chip::app::Clusters::Globals::Structs;
+using namespace chip::Protocols::InteractionModel;
+
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace Thermostat {
+
+extern ThermostatAttrAccess gThermostatAttrAccess;
+
+/**
+ * @brief Callback that is called when the timeout for editing the presets expires.
+ *
+ * @param[in] systemLayer The system layer.
+ * @param[in] callbackContext The context passed to the timer callback.
+ */
+void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext)
+{
+    EndpointId endpoint = static_cast<EndpointId>(reinterpret_cast<uintptr_t>(callbackContext));
+    gThermostatAttrAccess.ResetAtomicWrite(endpoint);
+}
+
+/**
+ * @brief Schedules a timer for the given timeout in milliseconds.
+ *
+ * @param[in] endpoint The endpoint to use.
+ * @param[in] timeoutMilliseconds The timeout in milliseconds.
+ */
+void ScheduleTimer(EndpointId endpoint, System::Clock::Milliseconds16 timeout)
+{
+    DeviceLayer::SystemLayer().StartTimer(timeout, TimerExpiredCallback,
+                                          reinterpret_cast<void *>(static_cast<uintptr_t>(endpoint)));
+}
+
+/**
+ * @brief Clears the currently scheduled timer.
+ *
+ * @param[in] endpoint The endpoint to use.
+ */
+void ClearTimer(EndpointId endpoint)
+{
+    DeviceLayer::SystemLayer().CancelTimer(TimerExpiredCallback, reinterpret_cast<void *>(static_cast<uintptr_t>(endpoint)));
+}
+
+/**
+ * @brief Get the source scoped node id.
+ *
+ * @param[in] commandObj The command handler object.
+ *
+ * @return The scoped node id of the source node. If the scoped node id is not retreived, return ScopedNodeId().
+ */
+ScopedNodeId GetSourceScopedNodeId(CommandHandler * commandObj)
+{
+    ScopedNodeId sourceNodeId = ScopedNodeId();
+    auto sessionHandle        = commandObj->GetExchangeContext()->GetSessionHandle();
+
+    if (sessionHandle->IsSecureSession())
+    {
+        sourceNodeId = sessionHandle->AsSecureSession()->GetPeer();
+    }
+    else if (sessionHandle->IsGroupSession())
+    {
+        sourceNodeId = sessionHandle->AsIncomingGroupSession()->GetPeer();
+    }
+    return sourceNodeId;
+}
+
+/**
+ * @brief Counts the number of attribute requests
+ *
+ * @param attributeRequests The decodable list of attribute IDs
+ * @param attributeRequestCount The total number of attribute requests
+ * @param requestedPresets Whether the Presets attribute was requested
+ * @param requestedSchedules Whether the Schedules attribute was requested
+ * @return true if the attribute list was counted
+ * @return false if there was an error reading the list
+ */
+bool CountAttributeRequests(const DataModel::DecodableList<chip::AttributeId> attributeRequests, size_t & attributeRequestCount,
+                            bool & requestedPresets, bool & requestedSchedules)
+{
+    attributeRequestCount = 0;
+    requestedPresets      = false;
+    requestedSchedules    = false;
+    auto attributeIdsIter = attributeRequests.begin();
+    while (attributeIdsIter.Next())
+    {
+        auto & attributeId = attributeIdsIter.GetValue();
+        switch (attributeId)
+        {
+        case Presets::Id:
+            requestedPresets = true;
+            break;
+        case Schedules::Id:
+            requestedSchedules = true;
+            break;
+        default:
+            break;
+        }
+        attributeRequestCount++;
+    }
+    return attributeIdsIter.GetStatus() == CHIP_NO_ERROR;
+}
+
+/// @brief Builds the list of attribute statuses to return from an AtomicRequest invocation
+/// @param endpoint The associated endpoint for the AtomicRequest invocation
+/// @param attributeRequests The list of requested attributes
+/// @param attributeStatusCount The number of attribute statuses in attributeStatuses
+/// @param attributeStatuses The status of each requested attribute, plus additional attributes if needed
+/// @return Status::Success if the request is valid, an error status if it is not
+Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::DecodableList<chip::AttributeId> attributeRequests,
+                              Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses)
+{
+
+    bool requestedPresets = false, requestedSchedules = false;
+    size_t attributeStatusCount = 0;
+    if (!CountAttributeRequests(attributeRequests, attributeStatusCount, requestedPresets, requestedSchedules))
+    {
+        // We errored reading the list
+        return Status::InvalidCommand;
+    }
+    if (attributeStatusCount == 0)
+    {
+        // List can't be empty
+        return Status::InvalidCommand;
+    }
+    attributeStatuses.Alloc(attributeStatusCount);
+    for (size_t i = 0; i < attributeStatusCount; ++i)
+    {
+        attributeStatuses[i].attributeID = kInvalidAttributeId;
+        attributeStatuses[i].statusCode  = 0;
+    }
+    auto attributeIdsIter = attributeRequests.begin();
+    size_t index          = 0;
+    while (attributeIdsIter.Next())
+    {
+        auto & attributeId = attributeIdsIter.GetValue();
+
+        for (size_t i = 0; i < index; ++i)
+        {
+            auto & attributeStatus = attributeStatuses[i];
+            if (attributeStatus.attributeID == attributeId)
+            {
+                // Double-requesting an attribute is invalid
+                return Status::InvalidCommand;
+            }
+        }
+        attributeStatuses[index].attributeID = attributeId;
+        attributeStatuses[index].statusCode  = to_underlying(Status::Success);
+        index++;
+    }
+    if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR)
+    {
+        return Status::InvalidCommand;
+    }
+    for (size_t i = 0; i < index; ++i)
+    {
+        auto & attributeStatus = attributeStatuses[i];
+        const EmberAfAttributeMetadata * metadata =
+            emberAfLocateAttributeMetadata(endpoint, Thermostat::Id, attributeStatus.attributeID);
+
+        if (metadata == nullptr)
+        {
+            // This is not a valid attribute on the Thermostat cluster on the supplied endpoint
+            return Status::InvalidCommand;
+        }
+    }
+    return Status::Success;
+}
+
+bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, Optional<AttributeId> attributeId)
+{
+
+    uint16_t ep =
+        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
+
+    if (ep >= ArraySize(mAtomicWriteSessions))
+    {
+        return false;
+    }
+    auto & atomicWriteSession = mAtomicWriteSessions[ep];
+    if (atomicWriteSession.state != AtomicWriteState::Open)
+    {
+        return false;
+    }
+    if (!attributeId.HasValue())
+    {
+        return true;
+    }
+    for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i)
+    {
+        if (atomicWriteSession.attributeIds[i] == attributeId.Value())
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, const Access::SubjectDescriptor & subjectDescriptor,
+                                         Optional<AttributeId> attributeId)
+{
+    if (!InAtomicWrite(endpoint, attributeId))
+    {
+        return false;
+    }
+    return subjectDescriptor.authMode == Access::AuthMode::kCase &&
+        GetAtomicWriteOriginatorScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex);
+}
+
+bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, Optional<AttributeId> attributeId)
+{
+    if (!InAtomicWrite(endpoint, attributeId))
+    {
+        return false;
+    }
+    ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj);
+    return GetAtomicWriteOriginatorScopedNodeId(endpoint) == sourceNodeId;
+}
+
+bool ThermostatAttrAccess::InAtomicWrite(
+    EndpointId endpoint, CommandHandler * commandObj,
+    Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses)
+{
+    uint16_t ep =
+        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
+
+    if (ep >= ArraySize(mAtomicWriteSessions))
+    {
+        return false;
+    }
+    auto & atomicWriteSession = mAtomicWriteSessions[ep];
+    if (atomicWriteSession.state != AtomicWriteState::Open)
+    {
+        return false;
+    }
+    if (atomicWriteSession.attributeIds.AllocatedSize() == 0 ||
+        atomicWriteSession.attributeIds.AllocatedSize() != attributeStatuses.AllocatedSize())
+    {
+        return false;
+    }
+    for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i)
+    {
+        bool hasAttribute = false;
+        auto attributeId  = atomicWriteSession.attributeIds[i];
+        for (size_t j = 0; j < attributeStatuses.AllocatedSize(); ++j)
+        {
+            auto & attributeStatus = attributeStatuses[j];
+            if (attributeStatus.attributeID == attributeId)
+            {
+                hasAttribute = true;
+                break;
+            }
+        }
+        if (!hasAttribute)
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool ThermostatAttrAccess::SetAtomicWrite(
+    EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state,
+    Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses)
+{
+    uint16_t ep =
+        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
+
+    if (ep >= ArraySize(mAtomicWriteSessions))
+    {
+        return false;
+    }
+
+    auto & atomicWriteSession     = mAtomicWriteSessions[ep];
+    atomicWriteSession.endpointId = endpoint;
+    if (!atomicWriteSession.attributeIds.Alloc(attributeStatuses.AllocatedSize()))
+    {
+        atomicWriteSession.state  = AtomicWriteState::Closed;
+        atomicWriteSession.nodeId = ScopedNodeId();
+        return false;
+    }
+
+    atomicWriteSession.state  = state;
+    atomicWriteSession.nodeId = originatorNodeId;
+
+    for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
+    {
+        atomicWriteSession.attributeIds[i] = attributeStatuses[i].attributeID;
+    }
+    return true;
+}
+
+void ThermostatAttrAccess::ResetAtomicWrite(EndpointId endpoint)
+{
+    auto delegate = GetDelegate(endpoint);
+    if (delegate != nullptr)
+    {
+        delegate->ClearPendingPresetList();
+    }
+    ClearTimer(endpoint);
+    uint16_t ep =
+        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
+
+    if (ep >= ArraySize(mAtomicWriteSessions))
+    {
+        return;
+    }
+    auto & atomicWriteSession     = mAtomicWriteSessions[ep];
+    atomicWriteSession.state      = AtomicWriteState::Closed;
+    atomicWriteSession.endpointId = endpoint;
+    atomicWriteSession.nodeId     = ScopedNodeId();
+    atomicWriteSession.attributeIds.Free();
+}
+
+ScopedNodeId ThermostatAttrAccess::GetAtomicWriteOriginatorScopedNodeId(const EndpointId endpoint)
+{
+    ScopedNodeId originatorNodeId = ScopedNodeId();
+    uint16_t ep =
+        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
+
+    if (ep < ArraySize(mAtomicWriteSessions))
+    {
+        originatorNodeId = mAtomicWriteSessions[ep].nodeId;
+    }
+    return originatorNodeId;
+}
+
+void SendAtomicResponse(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, Status status,
+                        const Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses,
+                        Optional<uint16_t> timeout = NullOptional)
+{
+    Commands::AtomicResponse::Type response;
+    response.statusCode = to_underlying(status);
+    response.attributeStatus =
+        DataModel::List<const AtomicAttributeStatusStruct::Type>(attributeStatuses.Get(), attributeStatuses.AllocatedSize());
+    response.timeout = timeout;
+    commandObj->AddResponse(commandPath, response);
+}
+
+void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                            const Commands::AtomicRequest::DecodableType & commandData)
+{
+    EndpointId endpoint = commandPath.mEndpointId;
+
+    auto delegate = GetDelegate(endpoint);
+
+    if (delegate == nullptr)
+    {
+        ChipLogError(Zcl, "Delegate is null");
+        commandObj->AddStatus(commandPath, Status::InvalidInState);
+        return;
+    }
+
+    Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> attributeStatuses;
+    auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses);
+    if (status != Status::Success)
+    {
+        commandObj->AddStatus(commandPath, status);
+        return;
+    }
+
+    if (InAtomicWrite(endpoint, commandObj))
+    {
+        // This client already has an open atomic write
+        commandObj->AddStatus(commandPath, Status::InvalidInState);
+        return;
+    }
+
+    if (!commandData.timeout.HasValue())
+    {
+        commandObj->AddStatus(commandPath, Status::InvalidCommand);
+        return;
+    }
+
+    auto maximumTimeout   = System::Clock::Milliseconds16(0);
+    auto attributeIdsIter = commandData.attributeRequests.begin();
+    while (attributeIdsIter.Next())
+    {
+        auto & attributeId = attributeIdsIter.GetValue();
+        switch (attributeId)
+        {
+        case Presets::Id:
+        case Schedules::Id:
+            auto attributeTimeout = delegate->GetMaxAtomicWriteTimeout(attributeId);
+
+            if (attributeTimeout.has_value())
+            {
+                // Add to the maximum timeout
+                maximumTimeout += attributeTimeout.value();
+            }
+            break;
+        }
+    }
+
+    status = Status::Success;
+    for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
+    {
+        auto & attributeStatus = attributeStatuses[i];
+        auto statusCode        = Status::Success;
+        switch (attributeStatus.attributeID)
+        {
+        case Presets::Id:
+        case Schedules::Id:
+            statusCode = InAtomicWrite(endpoint, MakeOptional(attributeStatus.attributeID)) ? Status::Busy : Status::Success;
+            break;
+        default:
+            statusCode = Status::InvalidCommand;
+            break;
+        }
+        if (statusCode != Status::Success)
+        {
+            status = Status::Failure;
+        }
+        attributeStatus.statusCode = to_underlying(statusCode);
+    }
+
+    auto timeout = std::min(System::Clock::Milliseconds16(commandData.timeout.Value()), maximumTimeout);
+    if (timeout.count() == 0)
+    {
+        commandObj->AddStatus(commandPath, Status::InvalidInState);
+        return;
+    }
+
+    if (status == Status::Success)
+    {
+        if (!SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), AtomicWriteState::Open, attributeStatuses))
+        {
+            for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
+            {
+                attributeStatuses[i].statusCode = to_underlying(Status::ResourceExhausted);
+            }
+            status = Status::Failure;
+        }
+        else
+        {
+            // This is a valid request to open an atomic write. Tell the delegate it
+            // needs to keep track of a pending preset list now.
+            delegate->InitializePendingPresets();
+            ScheduleTimer(endpoint, timeout);
+        }
+    }
+
+    SendAtomicResponse(commandObj, commandPath, status, attributeStatuses, MakeOptional(timeout.count()));
+}
+
+void ThermostatAttrAccess::CommitAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                             const Commands::AtomicRequest::DecodableType & commandData)
+{
+    EndpointId endpoint = commandPath.mEndpointId;
+    auto delegate       = GetDelegate(endpoint);
+
+    if (delegate == nullptr)
+    {
+        ChipLogError(Zcl, "Delegate is null");
+        commandObj->AddStatus(commandPath, Status::InvalidInState);
+        return;
+    }
+
+    Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> attributeStatuses;
+    auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses);
+    if (status != Status::Success)
+    {
+        commandObj->AddStatus(commandPath, status);
+        return;
+    }
+
+    if (!InAtomicWrite(endpoint, commandObj, attributeStatuses))
+    {
+        commandObj->AddStatus(commandPath, Status::InvalidInState);
+        return;
+    }
+
+    status = Status::Success;
+    for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
+    {
+        auto & attributeStatus = attributeStatuses[i];
+        auto statusCode        = Status::Success;
+        switch (attributeStatus.attributeID)
+        {
+        case Presets::Id:
+            statusCode = PrecommitPresets(endpoint);
+            break;
+        case Schedules::Id:
+            statusCode = Status::Success;
+            break;
+        default:
+            commandObj->AddStatus(commandPath, Status::InvalidInState);
+            return;
+        }
+        attributeStatus.statusCode = to_underlying(statusCode);
+        if (statusCode != Status::Success)
+        {
+            status = Status::Failure;
+        }
+    }
+
+    if (status == Status::Success)
+    {
+        for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
+        {
+            auto & attributeStatus = attributeStatuses[i];
+            auto statusCode        = Status::Success;
+            CHIP_ERROR err;
+            switch (attributeStatus.attributeID)
+            {
+            case Presets::Id:
+                err = delegate->CommitPendingPresets();
+                if (err != CHIP_NO_ERROR)
+                {
+                    statusCode = Status::InvalidInState;
+                }
+                break;
+            case Schedules::Id:
+                break;
+            default:
+                // Not reachable, since we returned in this situation above.
+                break;
+            }
+            attributeStatus.statusCode = to_underlying(statusCode);
+            if (statusCode != Status::Success)
+            {
+                status = Status::Failure;
+            }
+        }
+    }
+
+    ResetAtomicWrite(endpoint);
+    SendAtomicResponse(commandObj, commandPath, status, attributeStatuses);
+}
+
+void ThermostatAttrAccess::RollbackAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                               const Commands::AtomicRequest::DecodableType & commandData)
+{
+
+    EndpointId endpoint = commandPath.mEndpointId;
+    auto delegate       = GetDelegate(endpoint);
+
+    if (delegate == nullptr)
+    {
+        ChipLogError(Zcl, "Delegate is null");
+        commandObj->AddStatus(commandPath, Status::InvalidInState);
+        return;
+    }
+
+    Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> attributeStatuses;
+    auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses);
+    if (status != Status::Success)
+    {
+        commandObj->AddStatus(commandPath, status);
+        return;
+    }
+
+    if (!InAtomicWrite(endpoint, commandObj, attributeStatuses))
+    {
+        // There's no open atomic write
+        commandObj->AddStatus(commandPath, Status::InvalidInState);
+        return;
+    }
+
+    ResetAtomicWrite(endpoint);
+
+    for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
+    {
+        attributeStatuses[i].statusCode = to_underlying(Status::Success);
+    }
+
+    SendAtomicResponse(commandObj, commandPath, status, attributeStatuses);
+}
+
+void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint)
+{
+    ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint);
+    gThermostatAttrAccess.ResetAtomicWrite(endpoint);
+}
+
+bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                                   const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData)
+{
+    auto & requestType = commandData.requestType;
+
+    // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the
+    // privilege necessary to write to the atomic attributes, so no need to check
+
+    switch (requestType)
+    {
+    case Globals::AtomicRequestTypeEnum::kBeginWrite:
+        gThermostatAttrAccess.BeginAtomicWrite(commandObj, commandPath, commandData);
+        return true;
+    case Globals::AtomicRequestTypeEnum::kCommitWrite:
+        gThermostatAttrAccess.CommitAtomicWrite(commandObj, commandPath, commandData);
+        return true;
+    case Globals::AtomicRequestTypeEnum::kRollbackWrite:
+        gThermostatAttrAccess.RollbackAtomicWrite(commandObj, commandPath, commandData);
+        return true;
+    case Globals::AtomicRequestTypeEnum::kUnknownEnumValue:
+        commandObj->AddStatus(commandPath, Status::InvalidCommand);
+        return true;
+    }
+
+    return false;
+}
+
+} // namespace Thermostat
+} // namespace Clusters
+} // namespace app
+} // namespace chip
+
+bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                                   const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData)
+{
+    return Thermostat::emberAfThermostatClusterAtomicRequestCallback(commandObj, commandPath, commandData);
+}
+
+void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint)
+{
+    Thermostat::MatterThermostatClusterServerShutdownCallback(endpoint);
+}
diff --git a/src/app/clusters/thermostat-server/thermostat-server-presets.cpp b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp
new file mode 100644
index 0000000..e57c2f9
--- /dev/null
+++ b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp
@@ -0,0 +1,546 @@
+/**
+ *
+ *    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 "thermostat-server.h"
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::Thermostat;
+using namespace chip::app::Clusters::Thermostat::Attributes;
+using namespace chip::app::Clusters::Thermostat::Structs;
+using namespace chip::app::Clusters::Globals::Structs;
+using namespace chip::Protocols::InteractionModel;
+
+namespace {
+
+/**
+ * @brief Check if a preset is valid.
+ *
+ * @param[in] preset The preset to check.
+ *
+ * @return true If the preset is valid i.e the PresetHandle (if not null) fits within size constraints and the presetScenario enum
+ *         value is valid. Otherwise, return false.
+ */
+bool IsValidPresetEntry(const PresetStruct::Type & preset)
+{
+    // Check that the preset handle is not too long.
+    if (!preset.presetHandle.IsNull() && preset.presetHandle.Value().size() > kPresetHandleSize)
+    {
+        return false;
+    }
+
+    // Ensure we have a valid PresetScenario.
+    return (preset.presetScenario != PresetScenarioEnum::kUnknownEnumValue);
+}
+
+/**
+ * @brief Checks if the preset is built-in
+ *
+ * @param[in] preset The preset to check.
+ *
+ * @return true If the preset is built-in, false otherwise.
+ */
+bool IsBuiltIn(const PresetStructWithOwnedMembers & preset)
+{
+    return preset.GetBuiltIn().ValueOr(false);
+}
+
+/**
+ * @brief Checks if the presets are matching i.e the presetHandles are the same.
+ *
+ * @param[in] preset The preset to check.
+ * @param[in] presetToMatch The preset to match with.
+ *
+ * @return true If the presets match, false otherwise. If both preset handles are null, returns false
+ */
+bool PresetHandlesExistAndMatch(const PresetStructWithOwnedMembers & preset, const PresetStructWithOwnedMembers & presetToMatch)
+{
+    return !preset.GetPresetHandle().IsNull() && !presetToMatch.GetPresetHandle().IsNull() &&
+        preset.GetPresetHandle().Value().data_equal(presetToMatch.GetPresetHandle().Value());
+}
+
+/**
+ * @brief Finds an entry in the pending presets list that matches a preset.
+ *        The presetHandle of the two presets must match.
+ *
+ * @param[in] delegate The delegate to use.
+ * @param[in] presetToMatch The preset to match with.
+ *
+ * @return true if a matching entry was found in the pending presets list, false otherwise.
+ */
+bool MatchingPendingPresetExists(Delegate * delegate, const PresetStructWithOwnedMembers & presetToMatch)
+{
+    VerifyOrReturnValue(delegate != nullptr, false);
+
+    for (uint8_t i = 0; true; i++)
+    {
+        PresetStructWithOwnedMembers preset;
+        CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, preset);
+
+        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
+        {
+            break;
+        }
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(Zcl, "MatchingPendingPresetExists: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT,
+                         err.Format());
+            return false;
+        }
+
+        if (PresetHandlesExistAndMatch(preset, presetToMatch))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * @brief Finds and returns an entry in the Presets attribute list that matches
+ *        a preset, if such an entry exists. The presetToMatch must have a preset handle.
+ *
+ * @param[in] delegate The delegate to use.
+ * @param[in] presetToMatch The preset to match with.
+ * @param[out] matchingPreset The preset in the Presets attribute list that has the same PresetHandle as the presetToMatch.
+ *
+ * @return true if a matching entry was found in the  presets attribute list, false otherwise.
+ */
+bool GetMatchingPresetInPresets(Delegate * delegate, const PresetStruct::Type & presetToMatch,
+                                PresetStructWithOwnedMembers & matchingPreset)
+{
+    VerifyOrReturnValue(delegate != nullptr, false);
+
+    for (uint8_t i = 0; true; i++)
+    {
+        CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset);
+
+        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
+        {
+            break;
+        }
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(Zcl, "GetMatchingPresetInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, err.Format());
+            return false;
+        }
+
+        // Note: presets coming from our delegate always have a handle.
+        if (presetToMatch.presetHandle.Value().data_equal(matchingPreset.GetPresetHandle().Value()))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * @brief Returns the length of the list of presets if the pending presets were to be applied. The size of the pending presets list
+ *        calculated, after all the constraint checks are done, is the new size of the updated Presets attribute since the pending
+ *        preset list is expected to have all existing presets with or without edits plus new presets.
+ *        This is called before changes are actually applied.
+ *
+ * @param[in] delegate The delegate to use.
+ *
+ * @return count of the updated Presets attribute if the pending presets were applied to it. Return 0 for error cases.
+ */
+uint8_t CountNumberOfPendingPresets(Delegate * delegate)
+{
+    uint8_t numberOfPendingPresets = 0;
+
+    VerifyOrReturnValue(delegate != nullptr, 0);
+
+    for (uint8_t i = 0; true; i++)
+    {
+        PresetStructWithOwnedMembers pendingPreset;
+        CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, pendingPreset);
+
+        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
+        {
+            break;
+        }
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(Zcl, "CountNumberOfPendingPresets: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT,
+                         err.Format());
+            return 0;
+        }
+        numberOfPendingPresets++;
+    }
+
+    return numberOfPendingPresets;
+}
+
+/**
+ * @brief Checks if the presetScenario is present in the PresetTypes attribute.
+ *
+ * @param[in] delegate The delegate to use.
+ * @param[in] presetScenario The presetScenario to match with.
+ *
+ * @return true if the presetScenario is found, false otherwise.
+ */
+bool PresetScenarioExistsInPresetTypes(Delegate * delegate, PresetScenarioEnum presetScenario)
+{
+    VerifyOrReturnValue(delegate != nullptr, false);
+
+    for (uint8_t i = 0; true; i++)
+    {
+        PresetTypeStruct::Type presetType;
+        auto err = delegate->GetPresetTypeAtIndex(i, presetType);
+        if (err != CHIP_NO_ERROR)
+        {
+            return false;
+        }
+
+        if (presetType.presetScenario == presetScenario)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * @brief Returns the count of preset entries in the pending presets list that have the matching presetHandle.
+ * @param[in] delegate The delegate to use.
+ * @param[in] presetHandleToMatch The preset handle to match.
+ *
+ * @return count of the number of presets found with the matching presetHandle. Returns 0 if no matching presets were found.
+ */
+uint8_t CountPresetsInPendingListWithPresetHandle(Delegate * delegate, const ByteSpan & presetHandleToMatch)
+{
+    uint8_t count = 0;
+    VerifyOrReturnValue(delegate != nullptr, count);
+
+    for (uint8_t i = 0; true; i++)
+    {
+        PresetStructWithOwnedMembers preset;
+        auto err = delegate->GetPendingPresetAtIndex(i, preset);
+        if (err != CHIP_NO_ERROR)
+        {
+            return count;
+        }
+
+        DataModel::Nullable<ByteSpan> presetHandle = preset.GetPresetHandle();
+        if (!presetHandle.IsNull() && presetHandle.Value().data_equal(presetHandleToMatch))
+        {
+            count++;
+        }
+    }
+    return count;
+}
+
+/**
+ * @brief Checks if the presetType for the given preset scenario supports name in the presetTypeFeatures bitmap.
+ *
+ * @param[in] delegate The delegate to use.
+ * @param[in] presetScenario The presetScenario to match with.
+ *
+ * @return true if the presetType for the given preset scenario supports name, false otherwise.
+ */
+bool PresetTypeSupportsNames(Delegate * delegate, PresetScenarioEnum scenario)
+{
+    VerifyOrReturnValue(delegate != nullptr, false);
+
+    for (uint8_t i = 0; true; i++)
+    {
+        PresetTypeStruct::Type presetType;
+        auto err = delegate->GetPresetTypeAtIndex(i, presetType);
+        if (err != CHIP_NO_ERROR)
+        {
+            return false;
+        }
+
+        if (presetType.presetScenario == scenario)
+        {
+            return (presetType.presetTypeFeatures.Has(PresetTypeFeaturesBitmap::kSupportsNames));
+        }
+    }
+    return false;
+}
+
+/**
+ * @brief Checks if the given preset handle is present in the  presets attribute
+ * @param[in] delegate The delegate to use.
+ * @param[in] presetHandleToMatch The preset handle to match with.
+ *
+ * @return true if the given preset handle is present in the  presets attribute list, false otherwise.
+ */
+bool IsPresetHandlePresentInPresets(Delegate * delegate, const ByteSpan & presetHandleToMatch)
+{
+    VerifyOrReturnValue(delegate != nullptr, false);
+
+    PresetStructWithOwnedMembers matchingPreset;
+    for (uint8_t i = 0; true; i++)
+    {
+        CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset);
+
+        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
+        {
+            return false;
+        }
+
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(Zcl, "IsPresetHandlePresentInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT,
+                         err.Format());
+            return false;
+        }
+
+        if (!matchingPreset.GetPresetHandle().IsNull() && matchingPreset.GetPresetHandle().Value().data_equal(presetHandleToMatch))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+} // namespace
+
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace Thermostat {
+
+extern ThermostatAttrAccess gThermostatAttrAccess;
+extern int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoint);
+extern int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoint);
+
+Status ThermostatAttrAccess::SetActivePreset(EndpointId endpoint, DataModel::Nullable<ByteSpan> presetHandle)
+{
+
+    auto delegate = GetDelegate(endpoint);
+
+    if (delegate == nullptr)
+    {
+        ChipLogError(Zcl, "Delegate is null");
+        return Status::InvalidInState;
+    }
+
+    // If the preset handle passed in the command is not present in the Presets attribute, return INVALID_COMMAND.
+    if (!presetHandle.IsNull() && !IsPresetHandlePresentInPresets(delegate, presetHandle.Value()))
+    {
+        return Status::InvalidCommand;
+    }
+
+    CHIP_ERROR err = delegate->SetActivePresetHandle(presetHandle);
+
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(Zcl, "Failed to set ActivePresetHandle with error %" CHIP_ERROR_FORMAT, err.Format());
+        return StatusIB(err).mStatus;
+    }
+
+    return Status::Success;
+}
+
+CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset)
+{
+    if (!IsValidPresetEntry(preset))
+    {
+        return CHIP_IM_GLOBAL_STATUS(ConstraintError);
+    }
+
+    if (preset.presetHandle.IsNull())
+    {
+        if (IsBuiltIn(preset))
+        {
+            return CHIP_IM_GLOBAL_STATUS(ConstraintError);
+        }
+    }
+    else
+    {
+        auto & presetHandle = preset.presetHandle.Value();
+
+        // Per spec we need to check that:
+        // (a) There is an existing non-pending preset with this handle.
+        PresetStructWithOwnedMembers matchingPreset;
+        if (!GetMatchingPresetInPresets(delegate, preset, matchingPreset))
+        {
+            return CHIP_IM_GLOBAL_STATUS(NotFound);
+        }
+
+        // (b) There is no existing pending preset with this handle.
+        if (CountPresetsInPendingListWithPresetHandle(delegate, presetHandle) > 0)
+        {
+            return CHIP_IM_GLOBAL_STATUS(ConstraintError);
+        }
+
+        // (c)/(d) The built-in fields do not have a mismatch.
+        // TODO: What's the story with nullability on the BuiltIn field?
+        if (!preset.builtIn.IsNull() && !matchingPreset.GetBuiltIn().IsNull() &&
+            preset.builtIn.Value() != matchingPreset.GetBuiltIn().Value())
+        {
+            return CHIP_IM_GLOBAL_STATUS(ConstraintError);
+        }
+    }
+
+    if (!PresetScenarioExistsInPresetTypes(delegate, preset.presetScenario))
+    {
+        return CHIP_IM_GLOBAL_STATUS(ConstraintError);
+    }
+
+    if (preset.name.HasValue() && !PresetTypeSupportsNames(delegate, preset.presetScenario))
+    {
+        return CHIP_IM_GLOBAL_STATUS(ConstraintError);
+    }
+
+    return delegate->AppendToPendingPresetList(preset);
+}
+
+Status ThermostatAttrAccess::PrecommitPresets(EndpointId endpoint)
+{
+    auto delegate = GetDelegate(endpoint);
+
+    if (delegate == nullptr)
+    {
+        ChipLogError(Zcl, "Delegate is null");
+        return Status::InvalidInState;
+    }
+
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    // For each preset in the presets attribute, check that the matching preset in the pending presets list does not
+    // violate any spec constraints.
+    for (uint8_t i = 0; true; i++)
+    {
+        PresetStructWithOwnedMembers preset;
+        err = delegate->GetPresetAtIndex(i, preset);
+
+        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
+        {
+            break;
+        }
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(Zcl,
+                         "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPresetAtIndex failed with error "
+                         "%" CHIP_ERROR_FORMAT,
+                         err.Format());
+            return Status::InvalidInState;
+        }
+
+        bool found = MatchingPendingPresetExists(delegate, preset);
+
+        // If a built in preset in the Presets attribute list is removed and not found in the pending presets list, return
+        // CONSTRAINT_ERROR.
+        if (IsBuiltIn(preset) && !found)
+        {
+            return Status::ConstraintError;
+        }
+    }
+
+    // If there is an ActivePresetHandle set, find the preset in the pending presets list that matches the ActivePresetHandle
+    // attribute. If a preset is not found with the same presetHandle, return INVALID_IN_STATE. If there is no ActivePresetHandle
+    // attribute set, continue with other checks.
+    uint8_t buffer[kPresetHandleSize];
+    MutableByteSpan activePresetHandleSpan(buffer);
+    auto activePresetHandle = DataModel::MakeNullable(activePresetHandleSpan);
+
+    err = delegate->GetActivePresetHandle(activePresetHandle);
+
+    if (err != CHIP_NO_ERROR)
+    {
+        return Status::InvalidInState;
+    }
+
+    if (!activePresetHandle.IsNull())
+    {
+        uint8_t count = CountPresetsInPendingListWithPresetHandle(delegate, activePresetHandle.Value());
+        if (count == 0)
+        {
+            return Status::InvalidInState;
+        }
+    }
+
+    // For each preset in the pending presets list, check that the preset does not violate any spec constraints.
+    for (uint8_t i = 0; true; i++)
+    {
+        PresetStructWithOwnedMembers pendingPreset;
+        err = delegate->GetPendingPresetAtIndex(i, pendingPreset);
+
+        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
+        {
+            break;
+        }
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(Zcl,
+                         "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPendingPresetAtIndex failed with error "
+                         "%" CHIP_ERROR_FORMAT,
+                         err.Format());
+            return Status::InvalidInState;
+        }
+
+        // Enforce the Setpoint Limits for both the cooling and heating setpoints in the pending preset.
+        // TODO: This code does not work, because it's modifying our temporary copy.
+        Optional<int16_t> coolingSetpointValue = pendingPreset.GetCoolingSetpoint();
+        if (coolingSetpointValue.HasValue())
+        {
+            pendingPreset.SetCoolingSetpoint(MakeOptional(EnforceCoolingSetpointLimits(coolingSetpointValue.Value(), endpoint)));
+        }
+
+        Optional<int16_t> heatingSetpointValue = pendingPreset.GetHeatingSetpoint();
+        if (heatingSetpointValue.HasValue())
+        {
+            pendingPreset.SetHeatingSetpoint(MakeOptional(EnforceHeatingSetpointLimits(heatingSetpointValue.Value(), endpoint)));
+        }
+    }
+
+    uint8_t totalCount = CountNumberOfPendingPresets(delegate);
+
+    uint8_t numberOfPresetsSupported = delegate->GetNumberOfPresets();
+
+    if (numberOfPresetsSupported == 0)
+    {
+        ChipLogError(Zcl, "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: Failed to get NumberOfPresets");
+        return Status::InvalidInState;
+    }
+
+    // If the expected length of the presets attribute with the applied changes exceeds the total number of presets supported,
+    // return RESOURCE_EXHAUSTED. Note that the changes are not yet applied.
+    if (numberOfPresetsSupported > 0 && totalCount > numberOfPresetsSupported)
+    {
+        return Status::ResourceExhausted;
+    }
+
+    // TODO: Check if the number of presets for each presetScenario exceeds the max number of presets supported for that
+    // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation.
+    return Status::Success;
+}
+
+bool emberAfThermostatClusterSetActivePresetRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                                            const Commands::SetActivePresetRequest::DecodableType & commandData)
+{
+    auto status = gThermostatAttrAccess.SetActivePreset(commandPath.mEndpointId, commandData.presetHandle);
+    commandObj->AddStatus(commandPath, status);
+    return true;
+}
+
+} // namespace Thermostat
+} // namespace Clusters
+} // namespace app
+} // namespace chip
+
+bool emberAfThermostatClusterSetActivePresetRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                                            const Commands::SetActivePresetRequest::DecodableType & commandData)
+{
+    return Thermostat::emberAfThermostatClusterSetActivePresetRequestCallback(commandObj, commandPath, commandData);
+}
diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp
index 8fe7021..fe8ccf8 100644
--- a/src/app/clusters/thermostat-server/thermostat-server.cpp
+++ b/src/app/clusters/thermostat-server/thermostat-server.cpp
@@ -30,7 +30,6 @@
 #include <app/server/Server.h>
 #include <app/util/endpoint-config-api.h>
 #include <lib/core/CHIPEncoding.h>
-#include <platform/internal/CHIPDeviceLayerInternal.h>
 
 using namespace chip;
 using namespace chip::app;
@@ -38,8 +37,7 @@
 using namespace chip::app::Clusters::Thermostat;
 using namespace chip::app::Clusters::Thermostat::Structs;
 using namespace chip::app::Clusters::Thermostat::Attributes;
-
-using imcode = Protocols::InteractionModel::Status;
+using namespace Protocols::InteractionModel;
 
 constexpr int16_t kDefaultAbsMinHeatSetpointLimit = 700;  // 7C (44.5 F) is the default
 constexpr int16_t kDefaultAbsMaxHeatSetpointLimit = 3000; // 30C (86 F) is the default
@@ -69,381 +67,16 @@
 
 #define FEATURE_MAP_DEFAULT FEATURE_MAP_HEAT | FEATURE_MAP_COOL | FEATURE_MAP_AUTO
 
-namespace {
-
-ThermostatAttrAccess gThermostatAttrAccess;
-
 static_assert(kThermostatEndpointCount <= kEmberInvalidEndpointIndex, "Thermostat Delegate table size error");
 
 Delegate * gDelegateTable[kThermostatEndpointCount] = { nullptr };
 
-Delegate * GetDelegate(EndpointId endpoint)
-{
-    uint16_t ep =
-        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
-    return (ep >= ArraySize(gDelegateTable) ? nullptr : gDelegateTable[ep]);
-}
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace Thermostat {
 
-/**
- * @brief Check if a preset is valid.
- *
- * @param[in] preset The preset to check.
- *
- * @return true If the preset is valid i.e the PresetHandle (if not null) fits within size constraints and the presetScenario enum
- *         value is valid. Otherwise, return false.
- */
-bool IsValidPresetEntry(const PresetStruct::Type & preset)
-{
-    // Check that the preset handle is not too long.
-    if (!preset.presetHandle.IsNull() && preset.presetHandle.Value().size() > kPresetHandleSize)
-    {
-        return false;
-    }
-
-    // Ensure we have a valid PresetScenario.
-    return (preset.presetScenario != PresetScenarioEnum::kUnknownEnumValue);
-}
-
-/**
- * @brief Callback that is called when the timeout for editing the presets expires.
- *
- * @param[in] systemLayer The system layer.
- * @param[in] callbackContext The context passed to the timer callback.
- */
-void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext)
-{
-    EndpointId endpoint = static_cast<EndpointId>(reinterpret_cast<uintptr_t>(callbackContext));
-
-    Delegate * delegate = GetDelegate(endpoint);
-    VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is null. Unable to handle timer expired"));
-
-    delegate->ClearPendingPresetList();
-    gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed);
-}
-
-/**
- * @brief Schedules a timer for the given timeout in milliseconds.
- *
- * @param[in] endpoint The endpoint to use.
- * @param[in] timeoutMilliseconds The timeout in milliseconds.
- */
-void ScheduleTimer(EndpointId endpoint, System::Clock::Milliseconds16 timeout)
-{
-    DeviceLayer::SystemLayer().StartTimer(timeout, TimerExpiredCallback,
-                                          reinterpret_cast<void *>(static_cast<uintptr_t>(endpoint)));
-}
-
-/**
- * @brief Clears the currently scheduled timer.
- *
- * @param[in] endpoint The endpoint to use.
- */
-void ClearTimer(EndpointId endpoint)
-{
-    DeviceLayer::SystemLayer().CancelTimer(TimerExpiredCallback, reinterpret_cast<void *>(static_cast<uintptr_t>(endpoint)));
-}
-
-/**
- * @brief Checks if the preset is built-in
- *
- * @param[in] preset The preset to check.
- *
- * @return true If the preset is built-in, false otherwise.
- */
-bool IsBuiltIn(const PresetStructWithOwnedMembers & preset)
-{
-    return preset.GetBuiltIn().ValueOr(false);
-}
-
-/**
- * @brief Checks if the presets are matching i.e the presetHandles are the same.
- *
- * @param[in] preset The preset to check.
- * @param[in] presetToMatch The preset to match with.
- *
- * @return true If the presets match, false otherwise. If both preset handles are null, returns false
- */
-bool PresetHandlesExistAndMatch(const PresetStructWithOwnedMembers & preset, const PresetStructWithOwnedMembers & presetToMatch)
-{
-    return !preset.GetPresetHandle().IsNull() && !presetToMatch.GetPresetHandle().IsNull() &&
-        preset.GetPresetHandle().Value().data_equal(presetToMatch.GetPresetHandle().Value());
-}
-
-/**
- * @brief Get the source scoped node id.
- *
- * @param[in] commandObj The command handler object.
- *
- * @return The scoped node id of the source node. If the scoped node id is not retreived, return ScopedNodeId().
- */
-ScopedNodeId GetSourceScopedNodeId(CommandHandler * commandObj)
-{
-    ScopedNodeId sourceNodeId = ScopedNodeId();
-    auto sessionHandle        = commandObj->GetExchangeContext()->GetSessionHandle();
-
-    if (sessionHandle->IsSecureSession())
-    {
-        sourceNodeId = sessionHandle->AsSecureSession()->GetPeer();
-    }
-    else if (sessionHandle->IsGroupSession())
-    {
-        sourceNodeId = sessionHandle->AsIncomingGroupSession()->GetPeer();
-    }
-    return sourceNodeId;
-}
-
-/**
- * @brief Discards pending atomic writes and atomic state.
- *
- * @param[in] delegate The delegate to use.
- * @param[in] endpoint The endpoint to use.
- *
- */
-void resetAtomicWrite(Delegate * delegate, EndpointId endpoint)
-{
-    if (delegate != nullptr)
-    {
-        delegate->ClearPendingPresetList();
-    }
-    ClearTimer(endpoint);
-    gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed);
-}
-
-/**
- * @brief Finds an entry in the pending presets list that matches a preset.
- *        The presetHandle of the two presets must match.
- *
- * @param[in] delegate The delegate to use.
- * @param[in] presetToMatch The preset to match with.
- *
- * @return true if a matching entry was found in the pending presets list, false otherwise.
- */
-bool MatchingPendingPresetExists(Delegate * delegate, const PresetStructWithOwnedMembers & presetToMatch)
-{
-    VerifyOrReturnValue(delegate != nullptr, false);
-
-    for (uint8_t i = 0; true; i++)
-    {
-        PresetStructWithOwnedMembers preset;
-        CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, preset);
-
-        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
-        {
-            break;
-        }
-        if (err != CHIP_NO_ERROR)
-        {
-            ChipLogError(Zcl, "MatchingPendingPresetExists: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT,
-                         err.Format());
-            return false;
-        }
-
-        if (PresetHandlesExistAndMatch(preset, presetToMatch))
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Finds and returns an entry in the Presets attribute list that matches
- *        a preset, if such an entry exists. The presetToMatch must have a preset handle.
- *
- * @param[in] delegate The delegate to use.
- * @param[in] presetToMatch The preset to match with.
- * @param[out] matchingPreset The preset in the Presets attribute list that has the same PresetHandle as the presetToMatch.
- *
- * @return true if a matching entry was found in the  presets attribute list, false otherwise.
- */
-bool GetMatchingPresetInPresets(Delegate * delegate, const PresetStruct::Type & presetToMatch,
-                                PresetStructWithOwnedMembers & matchingPreset)
-{
-    VerifyOrReturnValue(delegate != nullptr, false);
-
-    for (uint8_t i = 0; true; i++)
-    {
-        CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset);
-
-        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
-        {
-            break;
-        }
-        if (err != CHIP_NO_ERROR)
-        {
-            ChipLogError(Zcl, "GetMatchingPresetInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, err.Format());
-            return false;
-        }
-
-        // Note: presets coming from our delegate always have a handle.
-        if (presetToMatch.presetHandle.Value().data_equal(matchingPreset.GetPresetHandle().Value()))
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Checks if the given preset handle is present in the  presets attribute
- * @param[in] delegate The delegate to use.
- * @param[in] presetHandleToMatch The preset handle to match with.
- *
- * @return true if the given preset handle is present in the  presets attribute list, false otherwise.
- */
-bool IsPresetHandlePresentInPresets(Delegate * delegate, const ByteSpan & presetHandleToMatch)
-{
-    VerifyOrReturnValue(delegate != nullptr, false);
-
-    PresetStructWithOwnedMembers matchingPreset;
-    for (uint8_t i = 0; true; i++)
-    {
-        CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset);
-
-        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
-        {
-            return false;
-        }
-
-        if (err != CHIP_NO_ERROR)
-        {
-            ChipLogError(Zcl, "IsPresetHandlePresentInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT,
-                         err.Format());
-            return false;
-        }
-
-        if (!matchingPreset.GetPresetHandle().IsNull() && matchingPreset.GetPresetHandle().Value().data_equal(presetHandleToMatch))
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Returns the length of the list of presets if the pending presets were to be applied. The size of the pending presets list
- *        calculated, after all the constraint checks are done, is the new size of the updated Presets attribute since the pending
- *        preset list is expected to have all existing presets with or without edits plus new presets.
- *        This is called before changes are actually applied.
- *
- * @param[in] delegate The delegate to use.
- *
- * @return count of the updated Presets attribute if the pending presets were applied to it. Return 0 for error cases.
- */
-uint8_t CountNumberOfPendingPresets(Delegate * delegate)
-{
-    uint8_t numberOfPendingPresets = 0;
-
-    VerifyOrReturnValue(delegate != nullptr, 0);
-
-    for (uint8_t i = 0; true; i++)
-    {
-        PresetStructWithOwnedMembers pendingPreset;
-        CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, pendingPreset);
-
-        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
-        {
-            break;
-        }
-        if (err != CHIP_NO_ERROR)
-        {
-            ChipLogError(Zcl, "CountNumberOfPendingPresets: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT,
-                         err.Format());
-            return 0;
-        }
-        numberOfPendingPresets++;
-    }
-
-    return numberOfPendingPresets;
-}
-
-/**
- * @brief Checks if the presetScenario is present in the PresetTypes attribute.
- *
- * @param[in] delegate The delegate to use.
- * @param[in] presetScenario The presetScenario to match with.
- *
- * @return true if the presetScenario is found, false otherwise.
- */
-bool PresetScenarioExistsInPresetTypes(Delegate * delegate, PresetScenarioEnum presetScenario)
-{
-    VerifyOrReturnValue(delegate != nullptr, false);
-
-    for (uint8_t i = 0; true; i++)
-    {
-        PresetTypeStruct::Type presetType;
-        auto err = delegate->GetPresetTypeAtIndex(i, presetType);
-        if (err != CHIP_NO_ERROR)
-        {
-            return false;
-        }
-
-        if (presetType.presetScenario == presetScenario)
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Returns the count of preset entries in the pending presets list that have the matching presetHandle.
- * @param[in] delegate The delegate to use.
- * @param[in] presetHandleToMatch The preset handle to match.
- *
- * @return count of the number of presets found with the matching presetHandle. Returns 0 if no matching presets were found.
- */
-uint8_t CountPresetsInPendingListWithPresetHandle(Delegate * delegate, const ByteSpan & presetHandleToMatch)
-{
-    uint8_t count = 0;
-    VerifyOrReturnValue(delegate != nullptr, count);
-
-    for (uint8_t i = 0; true; i++)
-    {
-        PresetStructWithOwnedMembers preset;
-        auto err = delegate->GetPendingPresetAtIndex(i, preset);
-        if (err != CHIP_NO_ERROR)
-        {
-            return count;
-        }
-
-        DataModel::Nullable<ByteSpan> presetHandle = preset.GetPresetHandle();
-        if (!presetHandle.IsNull() && presetHandle.Value().data_equal(presetHandleToMatch))
-        {
-            count++;
-        }
-    }
-    return count;
-}
-
-/**
- * @brief Checks if the presetType for the given preset scenario supports name in the presetTypeFeatures bitmap.
- *
- * @param[in] delegate The delegate to use.
- * @param[in] presetScenario The presetScenario to match with.
- *
- * @return true if the presetType for the given preset scenario supports name, false otherwise.
- */
-bool PresetTypeSupportsNames(Delegate * delegate, PresetScenarioEnum scenario)
-{
-    VerifyOrReturnValue(delegate != nullptr, false);
-
-    for (uint8_t i = 0; true; i++)
-    {
-        PresetTypeStruct::Type presetType;
-        auto err = delegate->GetPresetTypeAtIndex(i, presetType);
-        if (err != CHIP_NO_ERROR)
-        {
-            return false;
-        }
-
-        if (presetType.presetScenario == scenario)
-        {
-            return (presetType.presetTypeFeatures.Has(PresetTypeFeaturesBitmap::kSupportsNames));
-        }
-    }
-    return false;
-}
+ThermostatAttrAccess gThermostatAttrAccess;
 
 int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoint)
 {
@@ -461,7 +94,7 @@
 
     // Note that the limits are initialized above per the spec limits
     // if they are not present Get() will not update the value so the defaults are used
-    imcode status;
+    Status status;
 
     // https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/3724
     // behavior is not specified when Abs * values are not present and user values are present
@@ -471,24 +104,24 @@
     // if a attribute is not present then it's default shall be used.
 
     status = AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         ChipLogError(Zcl, "Warning: AbsMinHeatSetpointLimit missing using default");
     }
 
     status = AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         ChipLogError(Zcl, "Warning: AbsMaxHeatSetpointLimit missing using default");
     }
     status = MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         MinHeatSetpointLimit = AbsMinHeatSetpointLimit;
     }
 
     status = MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         MaxHeatSetpointLimit = AbsMaxHeatSetpointLimit;
     }
@@ -532,7 +165,7 @@
 
     // Note that the limits are initialized above per the spec limits
     // if they are not present Get() will not update the value so the defaults are used
-    imcode status;
+    Status status;
 
     // https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/3724
     // behavior is not specified when Abs * values are not present and user values are present
@@ -542,25 +175,25 @@
     // if a attribute is not present then it's default shall be used.
 
     status = AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         ChipLogError(Zcl, "Warning: AbsMinCoolSetpointLimit missing using default");
     }
 
     status = AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         ChipLogError(Zcl, "Warning: AbsMaxCoolSetpointLimit missing using default");
     }
 
     status = MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         MinCoolSetpointLimit = AbsMinCoolSetpointLimit;
     }
 
     status = MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit);
-    if (status != imcode::Success)
+    if (status != Status::Success)
     {
         MaxCoolSetpointLimit = AbsMaxCoolSetpointLimit;
     }
@@ -587,12 +220,12 @@
     return CoolingSetpoint;
 }
 
-} // anonymous namespace
-
-namespace chip {
-namespace app {
-namespace Clusters {
-namespace Thermostat {
+Delegate * GetDelegate(EndpointId endpoint)
+{
+    uint16_t ep =
+        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
+    return (ep >= ArraySize(gDelegateTable) ? nullptr : gDelegateTable[ep]);
+}
 
 void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate)
 {
@@ -605,71 +238,12 @@
     }
 }
 
-void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state)
-{
-    uint16_t ep =
-        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
-
-    if (ep < ArraySize(mAtomicWriteSessions))
-    {
-        mAtomicWriteSessions[ep].state      = state;
-        mAtomicWriteSessions[ep].endpointId = endpoint;
-        mAtomicWriteSessions[ep].nodeId     = originatorNodeId;
-    }
-}
-
-bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint)
-{
-    bool inAtomicWrite = false;
-    uint16_t ep =
-        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
-
-    if (ep < ArraySize(mAtomicWriteSessions))
-    {
-        inAtomicWrite = (mAtomicWriteSessions[ep].state == kAtomicWriteState_Open);
-    }
-    return inAtomicWrite;
-}
-
-bool ThermostatAttrAccess::InAtomicWrite(const Access::SubjectDescriptor & subjectDescriptor, EndpointId endpoint)
-{
-    if (!InAtomicWrite(endpoint))
-    {
-        return false;
-    }
-    return subjectDescriptor.authMode == Access::AuthMode::kCase &&
-        GetAtomicWriteScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex);
-}
-
-bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint)
-{
-    if (!InAtomicWrite(endpoint))
-    {
-        return false;
-    }
-    ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj);
-    return GetAtomicWriteScopedNodeId(endpoint) == sourceNodeId;
-}
-
-ScopedNodeId ThermostatAttrAccess::GetAtomicWriteScopedNodeId(EndpointId endpoint)
-{
-    ScopedNodeId originatorNodeId = ScopedNodeId();
-    uint16_t ep =
-        emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
-
-    if (ep < ArraySize(mAtomicWriteSessions))
-    {
-        originatorNodeId = mAtomicWriteSessions[ep].nodeId;
-    }
-    return originatorNodeId;
-}
-
 CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
 {
     VerifyOrDie(aPath.mClusterId == Thermostat::Id);
 
     uint32_t ourFeatureMap;
-    bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == imcode::Success) &&
+    bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == Status::Success) &&
         ((ourFeatureMap & to_underlying(Feature::kLocalTemperatureNotExposed)) != 0);
 
     switch (aPath.mAttributeId)
@@ -684,8 +258,8 @@
         if (localTemperatureNotExposedSupported)
         {
             BitMask<RemoteSensingBitmap> valueRemoteSensing;
-            imcode status = RemoteSensing::Get(aPath.mEndpointId, &valueRemoteSensing);
-            if (status != imcode::Success)
+            Status status = RemoteSensing::Get(aPath.mEndpointId, &valueRemoteSensing);
+            if (status != Status::Success)
             {
                 StatusIB statusIB(status);
                 return statusIB.ToChipError();
@@ -725,7 +299,7 @@
         VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));
 
         auto & subjectDescriptor = aEncoder.GetSubjectDescriptor();
-        if (InAtomicWrite(subjectDescriptor, aPath.mEndpointId))
+        if (InAtomicWrite(aPath.mEndpointId, subjectDescriptor, MakeOptional(aPath.mAttributeId)))
         {
             return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR {
                 for (uint8_t i = 0; true; i++)
@@ -801,12 +375,12 @@
         VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));
 
         // Presets are not editable, return INVALID_IN_STATE.
-        VerifyOrReturnError(InAtomicWrite(endpoint), CHIP_IM_GLOBAL_STATUS(InvalidInState),
+        VerifyOrReturnError(InAtomicWrite(endpoint, MakeOptional(aPath.mAttributeId)), CHIP_IM_GLOBAL_STATUS(InvalidInState),
                             ChipLogError(Zcl, "Presets are not editable"));
 
         // OK, we're in an atomic write, make sure the requesting node is the same one that started the atomic write,
         // otherwise return BUSY.
-        if (!InAtomicWrite(subjectDescriptor, endpoint))
+        if (!InAtomicWrite(endpoint, subjectDescriptor, MakeOptional(aPath.mAttributeId)))
         {
             ChipLogError(Zcl, "Another node is editing presets. Server is busy. Try again later");
             return CHIP_IM_GLOBAL_STATUS(Busy);
@@ -848,14 +422,14 @@
     }
 
     // This is not an atomic attribute, so check to make sure we don't have an atomic write going for this client
-    if (InAtomicWrite(subjectDescriptor, endpoint))
+    if (InAtomicWrite(endpoint, subjectDescriptor))
     {
         ChipLogError(Zcl, "Can not write to non-atomic attributes during atomic write");
         return CHIP_IM_GLOBAL_STATUS(InvalidInState);
     }
 
     uint32_t ourFeatureMap;
-    bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == imcode::Success) &&
+    bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == Status::Success) &&
         ((ourFeatureMap & to_underlying(Feature::kLocalTemperatureNotExposed)) != 0);
 
     switch (aPath.mAttributeId)
@@ -869,7 +443,7 @@
             {
                 return CHIP_IM_GLOBAL_STATUS(ConstraintError);
             }
-            imcode status = RemoteSensing::Set(aPath.mEndpointId, valueRemoteSensing);
+            Status status = RemoteSensing::Set(aPath.mEndpointId, valueRemoteSensing);
             StatusIB statusIB(status);
             return statusIB.ToChipError();
         }
@@ -882,73 +456,14 @@
     return CHIP_NO_ERROR;
 }
 
-CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset)
-{
-    if (!IsValidPresetEntry(preset))
-    {
-        return CHIP_IM_GLOBAL_STATUS(ConstraintError);
-    }
-
-    if (preset.presetHandle.IsNull())
-    {
-        if (IsBuiltIn(preset))
-        {
-            return CHIP_IM_GLOBAL_STATUS(ConstraintError);
-        }
-    }
-    else
-    {
-        auto & presetHandle = preset.presetHandle.Value();
-
-        // Per spec we need to check that:
-        // (a) There is an existing non-pending preset with this handle.
-        PresetStructWithOwnedMembers matchingPreset;
-        if (!GetMatchingPresetInPresets(delegate, preset, matchingPreset))
-        {
-            return CHIP_IM_GLOBAL_STATUS(NotFound);
-        }
-
-        // (b) There is no existing pending preset with this handle.
-        if (CountPresetsInPendingListWithPresetHandle(delegate, presetHandle) > 0)
-        {
-            return CHIP_IM_GLOBAL_STATUS(ConstraintError);
-        }
-
-        // (c)/(d) The built-in fields do not have a mismatch.
-        // TODO: What's the story with nullability on the BuiltIn field?
-        if (!preset.builtIn.IsNull() && !matchingPreset.GetBuiltIn().IsNull() &&
-            preset.builtIn.Value() != matchingPreset.GetBuiltIn().Value())
-        {
-            return CHIP_IM_GLOBAL_STATUS(ConstraintError);
-        }
-    }
-
-    if (!PresetScenarioExistsInPresetTypes(delegate, preset.presetScenario))
-    {
-        return CHIP_IM_GLOBAL_STATUS(ConstraintError);
-    }
-
-    if (preset.name.HasValue() && !PresetTypeSupportsNames(delegate, preset.presetScenario))
-    {
-        return CHIP_IM_GLOBAL_STATUS(ConstraintError);
-    }
-
-    return delegate->AppendToPendingPresetList(preset);
-}
-
 void ThermostatAttrAccess::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex)
 {
     for (size_t i = 0; i < ArraySize(mAtomicWriteSessions); ++i)
     {
-        auto atomicWriteState = mAtomicWriteSessions[i];
-        if (atomicWriteState.state == kAtomicWriteState_Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex)
+        auto & atomicWriteState = mAtomicWriteSessions[i];
+        if (atomicWriteState.state == AtomicWriteState::Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex)
         {
-            auto delegate = GetDelegate(atomicWriteState.endpointId);
-            if (delegate == nullptr)
-            {
-                continue;
-            }
-            resetAtomicWrite(delegate, atomicWriteState.endpointId);
+            ResetAtomicWrite(atomicWriteState.endpointId);
         }
     }
 }
@@ -1003,7 +518,7 @@
     bool CoolSupported      = false;
     bool OccupancySupported = false;
 
-    if (FeatureMap::Get(endpoint, &OurFeatureMap) != imcode::Success)
+    if (FeatureMap::Get(endpoint, &OurFeatureMap) != Status::Success)
         OurFeatureMap = FEATURE_MAP_DEFAULT;
 
     if (OurFeatureMap & 1 << 5) // Bit 5 is Auto Mode supported
@@ -1020,63 +535,63 @@
 
     if (AutoSupported)
     {
-        if (MinSetpointDeadBand::Get(endpoint, &DeadBand) != imcode::Success)
+        if (MinSetpointDeadBand::Get(endpoint, &DeadBand) != Status::Success)
         {
             DeadBand = kDefaultDeadBand;
         }
         DeadBandTemp = static_cast<int16_t>(DeadBand * 10);
     }
 
-    if (AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit) != imcode::Success)
+    if (AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit) != Status::Success)
         AbsMinCoolSetpointLimit = kDefaultAbsMinCoolSetpointLimit;
 
-    if (AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit) != imcode::Success)
+    if (AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit) != Status::Success)
         AbsMaxCoolSetpointLimit = kDefaultAbsMaxCoolSetpointLimit;
 
-    if (MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit) != imcode::Success)
+    if (MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit) != Status::Success)
         MinCoolSetpointLimit = AbsMinCoolSetpointLimit;
 
-    if (MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit) != imcode::Success)
+    if (MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit) != Status::Success)
         MaxCoolSetpointLimit = AbsMaxCoolSetpointLimit;
 
-    if (AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit) != imcode::Success)
+    if (AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit) != Status::Success)
         AbsMinHeatSetpointLimit = kDefaultAbsMinHeatSetpointLimit;
 
-    if (AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit) != imcode::Success)
+    if (AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit) != Status::Success)
         AbsMaxHeatSetpointLimit = kDefaultAbsMaxHeatSetpointLimit;
 
-    if (MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit) != imcode::Success)
+    if (MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit) != Status::Success)
         MinHeatSetpointLimit = AbsMinHeatSetpointLimit;
 
-    if (MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit) != imcode::Success)
+    if (MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit) != Status::Success)
         MaxHeatSetpointLimit = AbsMaxHeatSetpointLimit;
 
     if (CoolSupported)
-        if (OccupiedCoolingSetpoint::Get(endpoint, &OccupiedCoolingSetpoint) != imcode::Success)
+        if (OccupiedCoolingSetpoint::Get(endpoint, &OccupiedCoolingSetpoint) != Status::Success)
         {
             ChipLogError(Zcl, "Error: Can not read Occupied Cooling Setpoint");
-            return imcode::Failure;
+            return Status::Failure;
         }
 
     if (HeatSupported)
-        if (OccupiedHeatingSetpoint::Get(endpoint, &OccupiedHeatingSetpoint) != imcode::Success)
+        if (OccupiedHeatingSetpoint::Get(endpoint, &OccupiedHeatingSetpoint) != Status::Success)
         {
             ChipLogError(Zcl, "Error: Can not read Occupied Heating Setpoint");
-            return imcode::Failure;
+            return Status::Failure;
         }
 
     if (CoolSupported && OccupancySupported)
-        if (UnoccupiedCoolingSetpoint::Get(endpoint, &UnoccupiedCoolingSetpoint) != imcode::Success)
+        if (UnoccupiedCoolingSetpoint::Get(endpoint, &UnoccupiedCoolingSetpoint) != Status::Success)
         {
             ChipLogError(Zcl, "Error: Can not read Unoccupied Cooling Setpoint");
-            return imcode::Failure;
+            return Status::Failure;
         }
 
     if (HeatSupported && OccupancySupported)
-        if (UnoccupiedHeatingSetpoint::Get(endpoint, &UnoccupiedHeatingSetpoint) != imcode::Success)
+        if (UnoccupiedHeatingSetpoint::Get(endpoint, &UnoccupiedHeatingSetpoint) != Status::Success)
         {
             ChipLogError(Zcl, "Error: Can not read Unoccupied Heating Setpoint");
-            return imcode::Failure;
+            return Status::Failure;
         }
 
     switch (attributePath.mAttributeId)
@@ -1084,143 +599,143 @@
     case OccupiedHeatingSetpoint::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!HeatSupported)
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit ||
             requested > MaxHeatSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested > OccupiedCoolingSetpoint - DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
 
     case OccupiedCoolingSetpoint::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!CoolSupported)
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit ||
             requested > MaxCoolSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested < OccupiedHeatingSetpoint + DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
 
     case UnoccupiedHeatingSetpoint::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!(HeatSupported && OccupancySupported))
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit ||
             requested > MaxHeatSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested > UnoccupiedCoolingSetpoint - DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
     case UnoccupiedCoolingSetpoint::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!(CoolSupported && OccupancySupported))
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit ||
             requested > MaxCoolSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested < UnoccupiedHeatingSetpoint + DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
 
     case MinHeatSetpointLimit::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!HeatSupported)
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinHeatSetpointLimit || requested > MaxHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested > MinCoolSetpointLimit - DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
     case MaxHeatSetpointLimit::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!HeatSupported)
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested > MaxCoolSetpointLimit - DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
     case MinCoolSetpointLimit::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!CoolSupported)
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinCoolSetpointLimit || requested > MaxCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested < MinHeatSetpointLimit + DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
     case MaxCoolSetpointLimit::Id: {
         requested = static_cast<int16_t>(chip::Encoding::LittleEndian::Get16(value));
         if (!CoolSupported)
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit)
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         if (AutoSupported)
         {
             if (requested < MaxHeatSetpointLimit + DeadBandTemp)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
         }
-        return imcode::Success;
+        return Status::Success;
     }
     case MinSetpointDeadBand::Id: {
         requested = *value;
         if (!AutoSupported)
-            return imcode::UnsupportedAttribute;
+            return Status::UnsupportedAttribute;
         if (requested < 0 || requested > 25)
-            return imcode::InvalidValue;
-        return imcode::Success;
+            return Status::InvalidValue;
+        return Status::Success;
     }
 
     case ControlSequenceOfOperation::Id: {
         uint8_t requestedCSO;
         requestedCSO = *value;
         if (requestedCSO > to_underlying(ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat))
-            return imcode::InvalidValue;
-        return imcode::Success;
+            return Status::InvalidValue;
+        return Status::Success;
     }
 
     case SystemMode::Id: {
         ControlSequenceOfOperationEnum ControlSequenceOfOperation;
-        imcode status = ControlSequenceOfOperation::Get(endpoint, &ControlSequenceOfOperation);
-        if (status != imcode::Success)
+        Status status = ControlSequenceOfOperation::Get(endpoint, &ControlSequenceOfOperation);
+        if (status != Status::Success)
         {
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         }
         auto RequestedSystemMode = static_cast<SystemModeEnum>(*value);
         if (ControlSequenceOfOperation > ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat ||
             RequestedSystemMode > SystemModeEnum::kFanOnly)
         {
-            return imcode::InvalidValue;
+            return Status::InvalidValue;
         }
 
         switch (ControlSequenceOfOperation)
@@ -1228,22 +743,22 @@
         case ControlSequenceOfOperationEnum::kCoolingOnly:
         case ControlSequenceOfOperationEnum::kCoolingWithReheat:
             if (RequestedSystemMode == SystemModeEnum::kHeat || RequestedSystemMode == SystemModeEnum::kEmergencyHeat)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
             else
-                return imcode::Success;
+                return Status::Success;
 
         case ControlSequenceOfOperationEnum::kHeatingOnly:
         case ControlSequenceOfOperationEnum::kHeatingWithReheat:
             if (RequestedSystemMode == SystemModeEnum::kCool || RequestedSystemMode == SystemModeEnum::kPrecooling)
-                return imcode::InvalidValue;
+                return Status::InvalidValue;
             else
-                return imcode::Success;
+                return Status::Success;
         default:
-            return imcode::Success;
+            return Status::Success;
         }
     }
     default:
-        return imcode::Success;
+        return Status::Success;
     }
 }
 
@@ -1279,363 +794,6 @@
     return false;
 }
 
-bool emberAfThermostatClusterSetActivePresetRequestCallback(
-    CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
-    const Clusters::Thermostat::Commands::SetActivePresetRequest::DecodableType & commandData)
-{
-    EndpointId endpoint = commandPath.mEndpointId;
-    Delegate * delegate = GetDelegate(endpoint);
-
-    if (delegate == nullptr)
-    {
-        ChipLogError(Zcl, "Delegate is null");
-        commandObj->AddStatus(commandPath, imcode::InvalidInState);
-        return true;
-    }
-
-    DataModel::Nullable<ByteSpan> newPresetHandle = commandData.presetHandle;
-
-    // If the preset handle passed in the command is not present in the Presets attribute, return INVALID_COMMAND.
-    if (!newPresetHandle.IsNull() && !IsPresetHandlePresentInPresets(delegate, newPresetHandle.Value()))
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidCommand);
-        return true;
-    }
-
-    CHIP_ERROR err = delegate->SetActivePresetHandle(newPresetHandle);
-
-    if (err != CHIP_NO_ERROR)
-    {
-        ChipLogError(Zcl, "Failed to set ActivePresetHandle with error %" CHIP_ERROR_FORMAT, err.Format());
-        commandObj->AddStatus(commandPath, StatusIB(err).mStatus);
-        return true;
-    }
-
-    commandObj->AddStatus(commandPath, imcode::Success);
-    return true;
-}
-
-bool validAtomicAttributes(const Commands::AtomicRequest::DecodableType & commandData, bool requireBoth)
-{
-    auto attributeIdsIter = commandData.attributeRequests.begin();
-    bool requestedPresets = false, requestedSchedules = false;
-    while (attributeIdsIter.Next())
-    {
-        auto & attributeId = attributeIdsIter.GetValue();
-
-        switch (attributeId)
-        {
-        case Presets::Id:
-            if (requestedPresets) // Double-requesting an attribute is invalid
-            {
-                return false;
-            }
-            requestedPresets = true;
-            break;
-        case Schedules::Id:
-            if (requestedSchedules) // Double-requesting an attribute is invalid
-            {
-                return false;
-            }
-            requestedSchedules = true;
-            break;
-        default:
-            return false;
-        }
-    }
-    if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR)
-    {
-        return false;
-    }
-    if (requireBoth)
-    {
-        return (requestedPresets && requestedSchedules);
-    }
-    // If the atomic request doesn't contain at least one of these attributes, it's invalid
-    return (requestedPresets || requestedSchedules);
-}
-
-void sendAtomicResponse(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, imcode status, imcode presetsStatus,
-                        imcode schedulesStatus, Optional<uint16_t> timeout = NullOptional)
-{
-    Commands::AtomicResponse::Type response;
-    Globals::Structs::AtomicAttributeStatusStruct::Type attributeStatus[] = {
-        { .attributeID = Presets::Id, .statusCode = to_underlying(presetsStatus) },
-        { .attributeID = Schedules::Id, .statusCode = to_underlying(schedulesStatus) }
-    };
-    response.statusCode      = to_underlying(status);
-    response.attributeStatus = attributeStatus;
-    response.timeout         = timeout;
-    commandObj->AddResponse(commandPath, response);
-}
-
-void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
-                       const Commands::AtomicRequest::DecodableType & commandData)
-{
-    EndpointId endpoint = commandPath.mEndpointId;
-
-    Delegate * delegate = GetDelegate(endpoint);
-
-    if (delegate == nullptr)
-    {
-        ChipLogError(Zcl, "Delegate is null");
-        commandObj->AddStatus(commandPath, imcode::InvalidInState);
-        return;
-    }
-
-    if (gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint))
-    {
-        // This client already has an open atomic write
-        commandObj->AddStatus(commandPath, imcode::InvalidInState);
-        return;
-    }
-
-    if (!commandData.timeout.HasValue())
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidCommand);
-        return;
-    }
-
-    if (!validAtomicAttributes(commandData, false))
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidCommand);
-        return;
-    }
-
-    if (gThermostatAttrAccess.InAtomicWrite(endpoint))
-    {
-        sendAtomicResponse(commandObj, commandPath, imcode::Failure, imcode::Busy, imcode::Busy);
-        return;
-    }
-
-    // This is a valid request to open an atomic write. Tell the delegate it
-    // needs to keep track of a pending preset list now.
-    delegate->InitializePendingPresets();
-
-    auto timeout =
-        delegate->GetAtomicWriteTimeout(commandData.attributeRequests, System::Clock::Milliseconds16(commandData.timeout.Value()));
-
-    if (!timeout.has_value())
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidCommand);
-        return;
-    }
-    ScheduleTimer(endpoint, timeout.value());
-    gThermostatAttrAccess.SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), kAtomicWriteState_Open);
-    sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success,
-                       MakeOptional(timeout.value().count()));
-}
-
-imcode commitPresets(Delegate * delegate, EndpointId endpoint)
-{
-    CHIP_ERROR err = CHIP_NO_ERROR;
-
-    // For each preset in the presets attribute, check that the matching preset in the pending presets list does not
-    // violate any spec constraints.
-    for (uint8_t i = 0; true; i++)
-    {
-        PresetStructWithOwnedMembers preset;
-        err = delegate->GetPresetAtIndex(i, preset);
-
-        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
-        {
-            break;
-        }
-        if (err != CHIP_NO_ERROR)
-        {
-            ChipLogError(Zcl,
-                         "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPresetAtIndex failed with error "
-                         "%" CHIP_ERROR_FORMAT,
-                         err.Format());
-            return imcode::InvalidInState;
-        }
-
-        bool found = MatchingPendingPresetExists(delegate, preset);
-
-        // If a built in preset in the Presets attribute list is removed and not found in the pending presets list, return
-        // CONSTRAINT_ERROR.
-        if (IsBuiltIn(preset) && !found)
-        {
-            return imcode::ConstraintError;
-        }
-    }
-
-    // If there is an ActivePresetHandle set, find the preset in the pending presets list that matches the ActivePresetHandle
-    // attribute. If a preset is not found with the same presetHandle, return INVALID_IN_STATE. If there is no ActivePresetHandle
-    // attribute set, continue with other checks.
-    uint8_t buffer[kPresetHandleSize];
-    MutableByteSpan activePresetHandleSpan(buffer);
-    auto activePresetHandle = DataModel::MakeNullable(activePresetHandleSpan);
-
-    err = delegate->GetActivePresetHandle(activePresetHandle);
-
-    if (err != CHIP_NO_ERROR)
-    {
-        return imcode::InvalidInState;
-    }
-
-    if (!activePresetHandle.IsNull())
-    {
-        uint8_t count = CountPresetsInPendingListWithPresetHandle(delegate, activePresetHandle.Value());
-        if (count == 0)
-        {
-            return imcode::InvalidInState;
-        }
-    }
-
-    // For each preset in the pending presets list, check that the preset does not violate any spec constraints.
-    for (uint8_t i = 0; true; i++)
-    {
-        PresetStructWithOwnedMembers pendingPreset;
-        err = delegate->GetPendingPresetAtIndex(i, pendingPreset);
-
-        if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED)
-        {
-            break;
-        }
-        if (err != CHIP_NO_ERROR)
-        {
-            ChipLogError(Zcl,
-                         "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPendingPresetAtIndex failed with error "
-                         "%" CHIP_ERROR_FORMAT,
-                         err.Format());
-            return imcode::InvalidInState;
-        }
-
-        // Enforce the Setpoint Limits for both the cooling and heating setpoints in the pending preset.
-        // TODO: This code does not work, because it's modifying our temporary copy.
-        Optional<int16_t> coolingSetpointValue = pendingPreset.GetCoolingSetpoint();
-        if (coolingSetpointValue.HasValue())
-        {
-            pendingPreset.SetCoolingSetpoint(MakeOptional(EnforceCoolingSetpointLimits(coolingSetpointValue.Value(), endpoint)));
-        }
-
-        Optional<int16_t> heatingSetpointValue = pendingPreset.GetHeatingSetpoint();
-        if (heatingSetpointValue.HasValue())
-        {
-            pendingPreset.SetHeatingSetpoint(MakeOptional(EnforceHeatingSetpointLimits(heatingSetpointValue.Value(), endpoint)));
-        }
-    }
-
-    uint8_t totalCount = CountNumberOfPendingPresets(delegate);
-
-    uint8_t numberOfPresetsSupported = delegate->GetNumberOfPresets();
-
-    if (numberOfPresetsSupported == 0)
-    {
-        ChipLogError(Zcl, "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: Failed to get NumberOfPresets");
-        return imcode::InvalidInState;
-    }
-
-    // If the expected length of the presets attribute with the applied changes exceeds the total number of presets supported,
-    // return RESOURCE_EXHAUSTED. Note that the changes are not yet applied.
-    if (numberOfPresetsSupported > 0 && totalCount > numberOfPresetsSupported)
-    {
-        return imcode::ResourceExhausted;
-    }
-
-    // TODO: Check if the number of presets for each presetScenario exceeds the max number of presets supported for that
-    // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation.
-
-    // Call the delegate API to apply the pending presets to the presets attribute and update it.
-    err = delegate->ApplyPendingPresets();
-
-    if (err != CHIP_NO_ERROR)
-    {
-        return imcode::InvalidInState;
-    }
-
-    return imcode::Success;
-}
-
-void handleAtomicCommit(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
-                        const Commands::AtomicRequest::DecodableType & commandData)
-{
-    if (!validAtomicAttributes(commandData, true))
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidCommand);
-        return;
-    }
-    EndpointId endpoint = commandPath.mEndpointId;
-    bool inAtomicWrite  = gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint);
-    if (!inAtomicWrite)
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidInState);
-        return;
-    }
-
-    Delegate * delegate = GetDelegate(endpoint);
-
-    if (delegate == nullptr)
-    {
-        ChipLogError(Zcl, "Delegate is null");
-        commandObj->AddStatus(commandPath, imcode::InvalidInState);
-        return;
-    }
-
-    auto presetsStatus = commitPresets(delegate, endpoint);
-    // TODO: copy over schedules code
-    auto schedulesStatus = imcode::Success;
-    resetAtomicWrite(delegate, endpoint);
-    imcode status = (presetsStatus == imcode::Success && schedulesStatus == imcode::Success) ? imcode::Success : imcode::Failure;
-    sendAtomicResponse(commandObj, commandPath, status, presetsStatus, schedulesStatus);
-}
-
-void handleAtomicRollback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
-                          const Commands::AtomicRequest::DecodableType & commandData)
-{
-    if (!validAtomicAttributes(commandData, true))
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidCommand);
-        return;
-    }
-    EndpointId endpoint = commandPath.mEndpointId;
-    bool inAtomicWrite  = gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint);
-    if (!inAtomicWrite)
-    {
-        commandObj->AddStatus(commandPath, imcode::InvalidInState);
-        return;
-    }
-
-    Delegate * delegate = GetDelegate(endpoint);
-
-    if (delegate == nullptr)
-    {
-        ChipLogError(Zcl, "Delegate is null");
-        commandObj->AddStatus(commandPath, imcode::InvalidInState);
-        return;
-    }
-    resetAtomicWrite(delegate, endpoint);
-    sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success);
-}
-
-bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
-                                                   const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData)
-{
-    auto & requestType = commandData.requestType;
-
-    // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the
-    // privilege necessary to write to the atomic attributes, so no need to check
-
-    switch (requestType)
-    {
-    case Globals::AtomicRequestTypeEnum::kBeginWrite:
-        handleAtomicBegin(commandObj, commandPath, commandData);
-        return true;
-    case Globals::AtomicRequestTypeEnum::kCommitWrite:
-        handleAtomicCommit(commandObj, commandPath, commandData);
-        return true;
-    case Globals::AtomicRequestTypeEnum::kRollbackWrite:
-        handleAtomicRollback(commandObj, commandPath, commandData);
-        return true;
-    case Globals::AtomicRequestTypeEnum::kUnknownEnumValue:
-        commandObj->AddStatus(commandPath, imcode::InvalidCommand);
-        return true;
-    }
-
-    return false;
-}
-
 bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * commandObj,
                                                         const app::ConcreteCommandPath & commandPath,
                                                         const Commands::SetpointRaiseLower::DecodableType & commandData)
@@ -1646,9 +804,9 @@
     EndpointId aEndpointId = commandPath.mEndpointId;
 
     int16_t HeatingSetpoint = kDefaultHeatingSetpoint, CoolingSetpoint = kDefaultCoolingSetpoint; // Set to defaults to be safe
-    imcode status                     = imcode::Failure;
-    imcode WriteCoolingSetpointStatus = imcode::Failure;
-    imcode WriteHeatingSetpointStatus = imcode::Failure;
+    Status status                     = Status::Failure;
+    Status WriteCoolingSetpointStatus = Status::Failure;
+    Status WriteHeatingSetpointStatus = Status::Failure;
     int16_t DeadBandTemp              = 0;
     int8_t DeadBand                   = 0;
     uint32_t OurFeatureMap;
@@ -1656,7 +814,7 @@
     bool HeatSupported = false;
     bool CoolSupported = false;
 
-    if (FeatureMap::Get(aEndpointId, &OurFeatureMap) != imcode::Success)
+    if (FeatureMap::Get(aEndpointId, &OurFeatureMap) != Status::Success)
         OurFeatureMap = FEATURE_MAP_DEFAULT;
 
     if (OurFeatureMap & 1 << 5) // Bit 5 is Auto Mode supported
@@ -1670,7 +828,7 @@
 
     if (AutoSupported)
     {
-        if (MinSetpointDeadBand::Get(aEndpointId, &DeadBand) != imcode::Success)
+        if (MinSetpointDeadBand::Get(aEndpointId, &DeadBand) != Status::Success)
             DeadBand = kDefaultDeadBand;
         DeadBandTemp = static_cast<int16_t>(DeadBand * 10);
     }
@@ -1681,13 +839,13 @@
         if (HeatSupported && CoolSupported)
         {
             int16_t DesiredCoolingSetpoint, CoolLimit, DesiredHeatingSetpoint, HeatLimit;
-            if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success)
+            if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success)
             {
                 DesiredCoolingSetpoint = static_cast<int16_t>(CoolingSetpoint + amount * 10);
                 CoolLimit              = static_cast<int16_t>(DesiredCoolingSetpoint -
                                                  EnforceCoolingSetpointLimits(DesiredCoolingSetpoint, aEndpointId));
                 {
-                    if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success)
+                    if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success)
                     {
                         DesiredHeatingSetpoint = static_cast<int16_t>(HeatingSetpoint + amount * 10);
                         HeatLimit              = static_cast<int16_t>(DesiredHeatingSetpoint -
@@ -1709,12 +867,12 @@
                                 }
                             }
                             WriteCoolingSetpointStatus = OccupiedCoolingSetpoint::Set(aEndpointId, DesiredCoolingSetpoint);
-                            if (WriteCoolingSetpointStatus != imcode::Success)
+                            if (WriteCoolingSetpointStatus != Status::Success)
                             {
                                 ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!");
                             }
                             WriteHeatingSetpointStatus = OccupiedHeatingSetpoint::Set(aEndpointId, DesiredHeatingSetpoint);
-                            if (WriteHeatingSetpointStatus != imcode::Success)
+                            if (WriteHeatingSetpointStatus != Status::Success)
                             {
                                 ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!");
                             }
@@ -1726,12 +884,12 @@
 
         if (CoolSupported && !HeatSupported)
         {
-            if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success)
+            if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success)
             {
                 CoolingSetpoint            = static_cast<int16_t>(CoolingSetpoint + amount * 10);
                 CoolingSetpoint            = EnforceCoolingSetpointLimits(CoolingSetpoint, aEndpointId);
                 WriteCoolingSetpointStatus = OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint);
-                if (WriteCoolingSetpointStatus != imcode::Success)
+                if (WriteCoolingSetpointStatus != Status::Success)
                 {
                     ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!");
                 }
@@ -1740,34 +898,34 @@
 
         if (HeatSupported && !CoolSupported)
         {
-            if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success)
+            if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success)
             {
                 HeatingSetpoint            = static_cast<int16_t>(HeatingSetpoint + amount * 10);
                 HeatingSetpoint            = EnforceHeatingSetpointLimits(HeatingSetpoint, aEndpointId);
                 WriteHeatingSetpointStatus = OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint);
-                if (WriteHeatingSetpointStatus != imcode::Success)
+                if (WriteHeatingSetpointStatus != Status::Success)
                 {
                     ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!");
                 }
             }
         }
 
-        if ((!HeatSupported || WriteHeatingSetpointStatus == imcode::Success) &&
-            (!CoolSupported || WriteCoolingSetpointStatus == imcode::Success))
-            status = imcode::Success;
+        if ((!HeatSupported || WriteHeatingSetpointStatus == Status::Success) &&
+            (!CoolSupported || WriteCoolingSetpointStatus == Status::Success))
+            status = Status::Success;
         break;
 
     case SetpointRaiseLowerModeEnum::kCool:
         if (CoolSupported)
         {
-            if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success)
+            if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success)
             {
                 CoolingSetpoint = static_cast<int16_t>(CoolingSetpoint + amount * 10);
                 CoolingSetpoint = EnforceCoolingSetpointLimits(CoolingSetpoint, aEndpointId);
                 if (AutoSupported)
                 {
                     // Need to check if we can move the cooling setpoint while maintaining the dead band
-                    if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success)
+                    if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success)
                     {
                         if (CoolingSetpoint - HeatingSetpoint < DeadBandTemp)
                         {
@@ -1778,10 +936,10 @@
                             {
                                 // Desired cooling setpoint is enforcable
                                 // Set the new cooling and heating setpoints
-                                if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == imcode::Success)
+                                if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == Status::Success)
                                 {
-                                    if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == imcode::Success)
-                                        status = imcode::Success;
+                                    if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == Status::Success)
+                                        status = Status::Success;
                                 }
                                 else
                                     ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!");
@@ -1789,7 +947,7 @@
                             else
                             {
                                 ChipLogError(Zcl, "Error: Could Not adjust heating setpoint to maintain dead band!");
-                                status = imcode::InvalidCommand;
+                                status = Status::InvalidCommand;
                             }
                         }
                         else
@@ -1807,20 +965,20 @@
                 ChipLogError(Zcl, "Error: GetOccupiedCoolingSetpoint failed!");
         }
         else
-            status = imcode::InvalidCommand;
+            status = Status::InvalidCommand;
         break;
 
     case SetpointRaiseLowerModeEnum::kHeat:
         if (HeatSupported)
         {
-            if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success)
+            if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success)
             {
                 HeatingSetpoint = static_cast<int16_t>(HeatingSetpoint + amount * 10);
                 HeatingSetpoint = EnforceHeatingSetpointLimits(HeatingSetpoint, aEndpointId);
                 if (AutoSupported)
                 {
                     // Need to check if we can move the cooling setpoint while maintaining the dead band
-                    if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success)
+                    if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success)
                     {
                         if (CoolingSetpoint - HeatingSetpoint < DeadBandTemp)
                         {
@@ -1831,10 +989,10 @@
                             {
                                 // Desired cooling setpoint is enforcable
                                 // Set the new cooling and heating setpoints
-                                if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == imcode::Success)
+                                if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == Status::Success)
                                 {
-                                    if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == imcode::Success)
-                                        status = imcode::Success;
+                                    if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == Status::Success)
+                                        status = Status::Success;
                                 }
                                 else
                                     ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!");
@@ -1842,7 +1000,7 @@
                             else
                             {
                                 ChipLogError(Zcl, "Error: Could Not adjust cooling setpoint to maintain dead band!");
-                                status = imcode::InvalidCommand;
+                                status = Status::InvalidCommand;
                             }
                         }
                         else
@@ -1860,11 +1018,11 @@
                 ChipLogError(Zcl, "Error: GetOccupiedHeatingSetpoint failed!");
         }
         else
-            status = imcode::InvalidCommand;
+            status = Status::InvalidCommand;
         break;
 
     default:
-        status = imcode::InvalidCommand;
+        status = Status::InvalidCommand;
         break;
     }
 
@@ -1877,14 +1035,3 @@
     Server::GetInstance().GetFabricTable().AddFabricDelegate(&gThermostatAttrAccess);
     AttributeAccessInterfaceRegistry::Instance().Register(&gThermostatAttrAccess);
 }
-
-void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint)
-{
-    ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint);
-    Delegate * delegate = GetDelegate(endpoint);
-
-    if (delegate != nullptr)
-    {
-        resetAtomicWrite(delegate, endpoint);
-    }
-}
diff --git a/src/app/clusters/thermostat-server/thermostat-server.h b/src/app/clusters/thermostat-server/thermostat-server.h
index ddede8a..cc941cf 100644
--- a/src/app/clusters/thermostat-server/thermostat-server.h
+++ b/src/app/clusters/thermostat-server/thermostat-server.h
@@ -26,6 +26,7 @@
 
 #include "thermostat-delegate.h"
 
+#include <app-common/zap-generated/callback.h>
 #include <app/AttributeAccessInterfaceRegistry.h>
 #include <app/CommandHandler.h>
 
@@ -34,25 +35,62 @@
 namespace Clusters {
 namespace Thermostat {
 
+enum class AtomicWriteState
+{
+    Closed = 0,
+    Open,
+};
+
 static constexpr size_t kThermostatEndpointCount =
     MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
 
-enum AtomicWriteState
-{
-    kAtomicWriteState_Closed = 0,
-    kAtomicWriteState_Open,
-};
 /**
  * @brief  Thermostat Attribute Access Interface.
  */
 class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public chip::FabricTable::Delegate
 {
+
 public:
     ThermostatAttrAccess() : AttributeAccessInterface(Optional<chip::EndpointId>::Missing(), Thermostat::Id) {}
 
     CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
     CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override;
 
+private:
+    /**
+     * @brief Set the Active Preset to a given preset handle, or null
+     *
+     * @param endpoint The endpoint
+     * @param presetHandle The handle of the preset to set active, or null to clear the active preset
+     * @return Success if the active preset was updated, an error code if not
+     */
+    Protocols::InteractionModel::Status SetActivePreset(EndpointId endpoint, DataModel::Nullable<ByteSpan> presetHandle);
+
+    /**
+     * @brief Apply a preset to the pending lists of presets during an atomic write
+     *
+     * @param delegate The current ThermostatDelegate
+     * @param preset The preset to append
+     * @return CHIP_NO_ERROR if successful, an error code if not
+     */
+    CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset);
+
+    /**
+     * @brief Verifies if the pending presets for a given endpoint are valid
+     *
+     * @param endpoint The endpoint
+     * @return Success if the list of pending presets is valid, an error code if not
+     */
+    Protocols::InteractionModel::Status PrecommitPresets(EndpointId endpoint);
+
+    /**
+     * @brief Callback for when the server is removed from a given fabric; all associated atomic writes are reset
+     *
+     * @param fabricTable The fabric table
+     * @param fabricIndex The fabric index
+     */
+    void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override;
+
     /**
      * @brief Gets the scoped node id of the originator that sent the last successful
      *        AtomicRequest of type BeginWrite for the given endpoint.
@@ -61,7 +99,7 @@
      *
      * @return the scoped node id for the given endpoint if set. Otherwise returns ScopedNodeId().
      */
-    ScopedNodeId GetAtomicWriteScopedNodeId(EndpointId endpoint);
+    ScopedNodeId GetAtomicWriteOriginatorScopedNodeId(EndpointId endpoint);
 
     /**
      * @brief Sets the atomic write state for the given endpoint and originatorNodeId
@@ -69,46 +107,119 @@
      * @param[in] endpoint The endpoint.
      * @param[in] originatorNodeId The originator scoped node id.
      * @param[in] state Whether or not an atomic write is open or closed.
+     * @param attributeStatuses The set of attribute status structs the atomic write should be associated with
+     * @return true if it was able to update the atomic write state
+     * @return false if it was unable to update the atomic write state
      */
-    void SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state);
+    bool
+    SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state,
+                   Platform::ScopedMemoryBufferWithSize<Globals::Structs::AtomicAttributeStatusStruct::Type> & attributeStatuses);
 
     /**
-     * @brief Gets whether an atomic write is in progress for the given endpoint
+     * @brief Sets the atomic write state for the given endpoint and originatorNodeId
      *
-     * @param[in] endpoint The endpoint.
-     *
-     * @return Whether an atomic write is in progress for the given endpoint
      */
-    bool InAtomicWrite(EndpointId endpoint);
+    /**
+     * @brief Resets the atomic write for a given endpoint
+     *
+     * @param endpoint The endpoint
+     */
+    void ResetAtomicWrite(EndpointId endpoint);
 
     /**
-     * @brief Gets whether an atomic write is in progress for the given endpoint
+     * @brief Checks if a given endpoint has an atomic write open, optionally filtered by an attribute ID
      *
-     * @param[in] subjectDescriptor The subject descriptor.
-     * @param[in] endpoint The endpoint.
-     *
-     * @return Whether an atomic write is in progress for the given endpoint
+     * @param endpoint The endpoint
+     * @param attributeId The optional attribute ID to filter on
+     * @return true if the endpoint has an open atomic write
+     * @return false if the endpoint does not have an open atomic write
      */
-    bool InAtomicWrite(const Access::SubjectDescriptor & subjectDescriptor, EndpointId endpoint);
+    bool InAtomicWrite(EndpointId endpoint, Optional<AttributeId> attributeId = NullOptional);
 
     /**
-     * @brief Gets whether an atomic write is in progress for the given endpoint
+     * @brief Checks if a given endpoint has an atomic write open for a given subject descriptor, optionally filtered by an
+     * attribute ID
      *
-     * @param[in] commandObj The command handler.
-     * @param[in] endpoint The endpoint.
-     *
-     * @return Whether an atomic write is in progress for the given endpoint
+     * @param endpoint The endpoint
+     * @param subjectDescriptor The subject descriptor for the client making a read or write request
+     * @param attributeId The optional attribute ID to filter on
+     * @return true if the endpoint has an open atomic write
+     * @return false if the endpoint does not have an open atomic write
      */
-    bool InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint);
+    bool InAtomicWrite(EndpointId endpoint, const Access::SubjectDescriptor & subjectDescriptor,
+                       Optional<AttributeId> attributeId = NullOptional);
 
-private:
-    CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset);
+    /**
+     * @brief Checks if a given endpoint has an atomic write open for a given command invocation, optionally filtered by an
+     * attribute ID
+     *
+     * @param endpoint The endpoint
+     * @param commandObj The CommandHandler for the invoked command
+     * @param attributeId The optional attribute ID to filter on
+     * @return true if the endpoint has an open atomic write
+     * @return false if the endpoint does not have an open atomic write
+     */
+    bool InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, Optional<AttributeId> attributeId = NullOptional);
 
-    void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override;
+    /**
+     * @brief Checks if a given endpoint has an atomic write open for a given command invocation and a list of attributes
+     *
+     * @param endpoint The endpoint
+     * @param commandObj The CommandHandler for the invoked command
+     * @param attributeStatuses The list of attribute statuses whose attributeIds must match the open atomic write
+     * @return true if the endpoint has an open atomic write
+     * @return false if the endpoint does not have an open atomic write
+     */
+    bool
+    InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj,
+                  Platform::ScopedMemoryBufferWithSize<Globals::Structs::AtomicAttributeStatusStruct::Type> & attributeStatuses);
+
+    /**
+     * @brief Handles an AtomicRequest of type BeginWrite
+     *
+     * @param commandObj The AtomicRequest command handler
+     * @param commandPath The path for the Atomic Request command
+     * @param commandData The payload data for the Atomic Request
+     */
+    void BeginAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                          const Commands::AtomicRequest::DecodableType & commandData);
+
+    /**
+     * @brief Handles an AtomicRequest of type CommitWrite
+     *
+     * @param commandObj The AtomicRequest command handler
+     * @param commandPath The path for the Atomic Request command
+     * @param commandData The payload data for the Atomic Request
+     */
+    void CommitAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                           const Commands::AtomicRequest::DecodableType & commandData);
+
+    /**
+     * @brief Handles an AtomicRequest of type RollbackWrite
+     *
+     * @param commandObj The AtomicRequest command handler
+     * @param commandPath The path for the Atomic Request command
+     * @param commandData The payload data for the Atomic Request
+     */
+    void RollbackAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                             const Commands::AtomicRequest::DecodableType & commandData);
+
+    friend void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext);
+
+    friend void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint);
+
+    friend bool emberAfThermostatClusterSetActivePresetRequestCallback(
+        CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+        const Clusters::Thermostat::Commands::SetActivePresetRequest::DecodableType & commandData);
+
+    friend bool
+    emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
+                                                  const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData);
 
     struct AtomicWriteSession
     {
-        AtomicWriteState state = kAtomicWriteState_Closed;
+        AtomicWriteState state = AtomicWriteState::Closed;
+        Platform::ScopedMemoryBufferWithSize<AttributeId> attributeIds;
         ScopedNodeId nodeId;
         EndpointId endpointId = kInvalidEndpointId;
     };
@@ -124,6 +235,8 @@
  */
 void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate);
 
+Delegate * GetDelegate(EndpointId endpoint);
+
 } // namespace Thermostat
 } // namespace Clusters
 } // namespace app
diff --git a/src/python_testing/TC_TSTAT_4_2.py b/src/python_testing/TC_TSTAT_4_2.py
index 5c289ce..563d6f3 100644
--- a/src/python_testing/TC_TSTAT_4_2.py
+++ b/src/python_testing/TC_TSTAT_4_2.py
@@ -61,15 +61,30 @@
 
     def check_atomic_response(self, response: object, expected_status: Status = Status.Success,
                               expected_overall_status: Status = Status.Success,
-                              expected_preset_status: Status = Status.Success):
+                              expected_preset_status: Status = Status.Success,
+                              expected_schedules_status: Status = None,
+                              expected_timeout: int = None):
         asserts.assert_equal(expected_status, Status.Success, "We expected we had a valid response")
         asserts.assert_equal(response.statusCode, expected_overall_status, "Response should have the right overall status")
         found_preset_status = False
+        found_schedules_status = False
         for attrStatus in response.attributeStatus:
             if attrStatus.attributeID == cluster.Attributes.Presets.attribute_id:
                 asserts.assert_equal(attrStatus.statusCode, expected_preset_status,
                                      "Preset attribute should have the right status")
                 found_preset_status = True
+            if attrStatus.attributeID == cluster.Attributes.Schedules.attribute_id:
+                asserts.assert_equal(attrStatus.statusCode, expected_schedules_status,
+                                     "Schedules attribute should have the right status")
+                found_schedules_status = True
+        if expected_timeout is not None:
+            asserts.assert_equal(response.timeout, expected_timeout,
+                                 "Timeout should have the right value")
+        asserts.assert_true(found_preset_status, "Preset attribute should have a status")
+        if expected_schedules_status is not None:
+            asserts.assert_true(found_schedules_status, "Schedules attribute should have a status")
+            asserts.assert_equal(attrStatus.statusCode, expected_schedules_status,
+                                 "Schedules attribute should have the right status")
         asserts.assert_true(found_preset_status, "Preset attribute should have a status")
 
     async def write_presets(self,
@@ -87,17 +102,21 @@
     async def send_atomic_request_begin_command(self,
                                                 dev_ctrl: ChipDeviceCtrl = None,
                                                 endpoint: int = None,
+                                                timeout: int = 1800,
                                                 expected_status: Status = Status.Success,
                                                 expected_overall_status: Status = Status.Success,
-                                                expected_preset_status: Status = Status.Success):
+                                                expected_preset_status: Status = Status.Success,
+                                                expected_schedules_status: Status = None,
+                                                expected_timeout: int = None):
         try:
             response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kBeginWrite,
                                                                                      attributeRequests=[
                                                                                          cluster.Attributes.Presets.attribute_id],
-                                                                                     timeout=1800),
+                                                                                     timeout=timeout),
                                                   dev_ctrl=dev_ctrl,
                                                   endpoint=endpoint)
-            self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status)
+            self.check_atomic_response(response, expected_status, expected_overall_status,
+                                       expected_preset_status, expected_schedules_status, expected_timeout)
 
         except InteractionModelError as e:
             asserts.assert_equal(e.status, expected_status, "Unexpected error returned")
@@ -107,13 +126,15 @@
                                                  endpoint: int = None,
                                                  expected_status: Status = Status.Success,
                                                  expected_overall_status: Status = Status.Success,
-                                                 expected_preset_status: Status = Status.Success):
+                                                 expected_preset_status: Status = Status.Success,
+                                                 expected_schedules_status: Status = None):
         try:
             response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kCommitWrite,
-                                                                                     attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]),
+                                                                                     attributeRequests=[cluster.Attributes.Presets.attribute_id]),
                                                   dev_ctrl=dev_ctrl,
                                                   endpoint=endpoint)
-            self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status)
+            self.check_atomic_response(response, expected_status, expected_overall_status,
+                                       expected_preset_status, expected_schedules_status)
         except InteractionModelError as e:
             asserts.assert_equal(e.status, expected_status, "Unexpected error returned")
 
@@ -122,13 +143,16 @@
                                                    endpoint: int = None,
                                                    expected_status: Status = Status.Success,
                                                    expected_overall_status: Status = Status.Success,
-                                                   expected_preset_status: Status = Status.Success):
+                                                   expected_preset_status: Status = Status.Success,
+                                                   expected_schedules_status: Status = None):
         try:
             response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kRollbackWrite,
-                                                                                     attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]),
+                                                                                     attributeRequests=[cluster.Attributes.Presets.attribute_id]),
                                                   dev_ctrl=dev_ctrl,
                                                   endpoint=endpoint)
-            self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status)
+            self.check_atomic_response(response, expected_status, expected_overall_status,
+                                       expected_preset_status, expected_schedules_status)
+
         except InteractionModelError as e:
             asserts.assert_equal(e.status, expected_status, "Unexpected error returned")
 
@@ -219,7 +243,6 @@
             await self.write_presets(endpoint=endpoint, presets=new_presets, expected_status=Status.InvalidInState)
 
         self.step("3")
-
         if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")):
             await self.send_atomic_request_begin_command()
 
@@ -260,7 +283,7 @@
         if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")):
 
             # Send the AtomicRequest begin command
-            await self.send_atomic_request_begin_command()
+            await self.send_atomic_request_begin_command(timeout=5000, expected_timeout=3000)
 
             # Write to the presets attribute after removing a built in preset from the list. Remove the first entry.
             test_presets = new_presets_with_handle.copy()
@@ -406,10 +429,7 @@
 
         self.step("14")
         if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")):
-
-            # Send the AtomicRequest begin command
             await self.send_atomic_request_begin_command()
-
             # Send the AtomicRequest begin command from separate controller, which should receive busy
             status = await self.send_atomic_request_begin_command(dev_ctrl=secondary_controller, expected_overall_status=Status.Failure, expected_preset_status=Status.Busy)