Skip to content

Commit a213a4c

Browse files
hastynivi-applerestyled-commitsbzbarsky-appletehampson
authored andcommitted
[HVAC] Allow Thermostat delegate to supply atomic write timeout (project-chip#34823)
* 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 (project-chip#34650) * Fix most TC-SWTCH-2.4 remaining issues (project-chip#34677) - Move 2.4 in a better place in the file - Add test steps properly - Allow default button press position override Issue project-chip#34656 Testing done: - Test still passes on DUT with automation * Initial test script for Fabric Sync TC_MCORE_FS_1_2 (project-chip#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 (project-chip#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 (project-chip#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 (project-chip#34679) * Dump details about leaked ExchangeContexts before aborting (project-chip#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 (project-chip#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 (project-chip#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 (project-chip#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 (project-chip#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. (project-chip#34692) * adding parallel execution to restyle-diff (project-chip#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. (project-chip#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 (project-chip#34440) * Pull request project-chip#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 (project-chip#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 (project-chip#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 * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Document GetAtomicWriteTimeout * Restyled patch * Switch to enum for atomic write state * Use std::optional<timeout> instead of magic zero value --------- 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>
1 parent 4d1de40 commit a213a4c

File tree

8 files changed

+300
-158
lines changed

8 files changed

+300
-158
lines changed

examples/thermostat/linux/include/thermostat-delegate-impl.h

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ class ThermostatDelegate : public Delegate
4444
public:
4545
static inline ThermostatDelegate & GetInstance() { return sInstance; }
4646

47+
std::optional<System::Clock::Milliseconds16>
48+
GetAtomicWriteTimeout(DataModel::DecodableList<chip::AttributeId> attributeRequests,
49+
System::Clock::Milliseconds16 timeoutRequest) override;
50+
4751
CHIP_ERROR GetPresetTypeAtIndex(size_t index, Structs::PresetTypeStruct::Type & presetType) override;
4852

4953
uint8_t GetNumberOfPresets() override;

examples/thermostat/linux/thermostat-delegate-impl.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,47 @@ CHIP_ERROR ThermostatDelegate::SetActivePresetHandle(const DataModel::Nullable<B
148148
return CHIP_NO_ERROR;
149149
}
150150

151+
std::optional<System::Clock::Milliseconds16>
152+
ThermostatDelegate::GetAtomicWriteTimeout(DataModel::DecodableList<AttributeId> attributeRequests,
153+
System::Clock::Milliseconds16 timeoutRequest)
154+
{
155+
auto attributeIdsIter = attributeRequests.begin();
156+
bool requestedPresets = false, requestedSchedules = false;
157+
while (attributeIdsIter.Next())
158+
{
159+
auto & attributeId = attributeIdsIter.GetValue();
160+
161+
switch (attributeId)
162+
{
163+
case Attributes::Presets::Id:
164+
requestedPresets = true;
165+
break;
166+
case Attributes::Schedules::Id:
167+
requestedSchedules = true;
168+
break;
169+
default:
170+
return System::Clock::Milliseconds16(0);
171+
}
172+
}
173+
if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR)
174+
{
175+
return System::Clock::Milliseconds16(0);
176+
}
177+
auto timeout = System::Clock::Milliseconds16(0);
178+
if (requestedPresets)
179+
{
180+
// If the client expects to edit the presets, then we'll give it 3 seconds to do so
181+
timeout += std::chrono::milliseconds(3000);
182+
}
183+
if (requestedSchedules)
184+
{
185+
// If the client expects to edit the schedules, then we'll give it 9 seconds to do so
186+
timeout += std::chrono::milliseconds(9000);
187+
}
188+
// If the client requested an even smaller timeout, then use that one
189+
return std::min(timeoutRequest, timeout);
190+
}
191+
151192
void ThermostatDelegate::InitializePendingPresets()
152193
{
153194
mNextFreeIndexInPendingPresetsList = 0;

scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -2217,6 +2217,7 @@
22172217
}; \
22182218
const EmberAfGenericClusterFunction chipFuncArrayThermostatServer[] = { \
22192219
(EmberAfGenericClusterFunction) emberAfThermostatClusterServerInitCallback, \
2220+
(EmberAfGenericClusterFunction) MatterThermostatClusterServerShutdownCallback, \
22202221
(EmberAfGenericClusterFunction) MatterThermostatClusterServerPreAttributeChangedCallback, \
22212222
}; \
22222223
const EmberAfGenericClusterFunction chipFuncArrayFanControlServer[] = { \
@@ -3755,7 +3756,7 @@
37553756
.attributes = ZAP_ATTRIBUTE_INDEX(616), \
37563757
.attributeCount = 26, \
37573758
.clusterSize = 72, \
3758-
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \
3759+
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \
37593760
.functions = chipFuncArrayThermostatServer, \
37603761
.acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 241 ), \
37613762
.generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 246 ), \

src/app/clusters/thermostat-server/thermostat-delegate.h

+11
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ class Delegate
3838

3939
virtual ~Delegate() = default;
4040

41+
/**
42+
* @brief Get the maximum timeout for atomically writing to a set of attributes
43+
*
44+
* @param[in] attributeRequests The list of attributes to write to.
45+
* @param[out] timeoutRequest The timeout proposed by the client.
46+
* @return The maximum allowed timeout; zero if the request is invalid.
47+
*/
48+
virtual std::optional<System::Clock::Milliseconds16>
49+
GetAtomicWriteTimeout(DataModel::DecodableList<AttributeId> attributeRequests,
50+
System::Clock::Milliseconds16 timeoutRequest) = 0;
51+
4152
/**
4253
* @brief Get the preset type at a given index in the PresetTypes attribute
4354
*

src/app/clusters/thermostat-server/thermostat-server.cpp

+59-36
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <app/CommandHandler.h>
2828
#include <app/ConcreteAttributePath.h>
2929
#include <app/ConcreteCommandPath.h>
30+
#include <app/server/Server.h>
31+
#include <app/util/endpoint-config-api.h>
3032
#include <lib/core/CHIPEncoding.h>
3133
#include <platform/internal/CHIPDeviceLayerInternal.h>
3234

@@ -116,8 +118,7 @@ void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext)
116118
VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is null. Unable to handle timer expired"));
117119

118120
delegate->ClearPendingPresetList();
119-
gThermostatAttrAccess.SetAtomicWrite(endpoint, false);
120-
gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId());
121+
gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed);
121122
}
122123

123124
/**
@@ -205,8 +206,7 @@ void resetAtomicWrite(Delegate * delegate, EndpointId endpoint)
205206
delegate->ClearPendingPresetList();
206207
}
207208
ClearTimer(endpoint);
208-
gThermostatAttrAccess.SetAtomicWrite(endpoint, false);
209-
gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId());
209+
gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed);
210210
}
211211

212212
/**
@@ -605,14 +605,16 @@ void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate)
605605
}
606606
}
607607

608-
void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, bool inProgress)
608+
void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state)
609609
{
610610
uint16_t ep =
611611
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
612612

613-
if (ep < ArraySize(mAtomicWriteState))
613+
if (ep < ArraySize(mAtomicWriteSessions))
614614
{
615-
mAtomicWriteState[ep] = inProgress;
615+
mAtomicWriteSessions[ep].state = state;
616+
mAtomicWriteSessions[ep].endpointId = endpoint;
617+
mAtomicWriteSessions[ep].nodeId = originatorNodeId;
616618
}
617619
}
618620

@@ -622,9 +624,9 @@ bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint)
622624
uint16_t ep =
623625
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
624626

625-
if (ep < ArraySize(mAtomicWriteState))
627+
if (ep < ArraySize(mAtomicWriteSessions))
626628
{
627-
inAtomicWrite = mAtomicWriteState[ep];
629+
inAtomicWrite = (mAtomicWriteSessions[ep].state == kAtomicWriteState_Open);
628630
}
629631
return inAtomicWrite;
630632
}
@@ -649,26 +651,15 @@ bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId
649651
return GetAtomicWriteScopedNodeId(endpoint) == sourceNodeId;
650652
}
651653

652-
void ThermostatAttrAccess::SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId)
653-
{
654-
uint16_t ep =
655-
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
656-
657-
if (ep < ArraySize(mAtomicWriteNodeIds))
658-
{
659-
mAtomicWriteNodeIds[ep] = originatorNodeId;
660-
}
661-
}
662-
663654
ScopedNodeId ThermostatAttrAccess::GetAtomicWriteScopedNodeId(EndpointId endpoint)
664655
{
665656
ScopedNodeId originatorNodeId = ScopedNodeId();
666657
uint16_t ep =
667658
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
668659

669-
if (ep < ArraySize(mAtomicWriteNodeIds))
660+
if (ep < ArraySize(mAtomicWriteSessions))
670661
{
671-
originatorNodeId = mAtomicWriteNodeIds[ep];
662+
originatorNodeId = mAtomicWriteSessions[ep].nodeId;
672663
}
673664
return originatorNodeId;
674665
}
@@ -704,7 +695,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A
704695
}
705696
break;
706697
case PresetTypes::Id: {
707-
Delegate * delegate = GetDelegate(aPath.mEndpointId);
698+
auto delegate = GetDelegate(aPath.mEndpointId);
708699
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));
709700

710701
return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR {
@@ -723,14 +714,14 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A
723714
}
724715
break;
725716
case NumberOfPresets::Id: {
726-
Delegate * delegate = GetDelegate(aPath.mEndpointId);
717+
auto delegate = GetDelegate(aPath.mEndpointId);
727718
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));
728719

729720
ReturnErrorOnFailure(aEncoder.Encode(delegate->GetNumberOfPresets()));
730721
}
731722
break;
732723
case Presets::Id: {
733-
Delegate * delegate = GetDelegate(aPath.mEndpointId);
724+
auto delegate = GetDelegate(aPath.mEndpointId);
734725
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));
735726

736727
auto & subjectDescriptor = aEncoder.GetSubjectDescriptor();
@@ -766,7 +757,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A
766757
}
767758
break;
768759
case ActivePresetHandle::Id: {
769-
Delegate * delegate = GetDelegate(aPath.mEndpointId);
760+
auto delegate = GetDelegate(aPath.mEndpointId);
770761
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));
771762

772763
uint8_t buffer[kPresetHandleSize];
@@ -812,7 +803,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath,
812803
{
813804
case Presets::Id: {
814805

815-
Delegate * delegate = GetDelegate(endpoint);
806+
auto delegate = GetDelegate(endpoint);
816807
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));
817808

818809
// Presets are not editable, return INVALID_IN_STATE.
@@ -897,7 +888,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath,
897888
return CHIP_NO_ERROR;
898889
}
899890

900-
CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const PresetStruct::Type & preset)
891+
CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset)
901892
{
902893
if (!IsValidPresetEntry(preset))
903894
{
@@ -951,6 +942,23 @@ CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const
951942
return delegate->AppendToPendingPresetList(preset);
952943
}
953944

945+
void ThermostatAttrAccess::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex)
946+
{
947+
for (size_t i = 0; i < ArraySize(mAtomicWriteSessions); ++i)
948+
{
949+
auto atomicWriteState = mAtomicWriteSessions[i];
950+
if (atomicWriteState.state == kAtomicWriteState_Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex)
951+
{
952+
auto delegate = GetDelegate(atomicWriteState.endpointId);
953+
if (delegate == nullptr)
954+
{
955+
continue;
956+
}
957+
resetAtomicWrite(delegate, atomicWriteState.endpointId);
958+
}
959+
}
960+
}
961+
954962
} // namespace Thermostat
955963
} // namespace Clusters
956964
} // namespace app
@@ -1394,8 +1402,6 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath &
13941402
return;
13951403
}
13961404

1397-
auto timeout = commandData.timeout.Value();
1398-
13991405
if (!validAtomicAttributes(commandData, false))
14001406
{
14011407
commandObj->AddStatus(commandPath, imcode::InvalidCommand);
@@ -1412,13 +1418,18 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath &
14121418
// needs to keep track of a pending preset list now.
14131419
delegate->InitializePendingPresets();
14141420

1415-
uint16_t maxTimeout = 5000;
1416-
timeout = std::min(timeout, maxTimeout);
1421+
auto timeout =
1422+
delegate->GetAtomicWriteTimeout(commandData.attributeRequests, System::Clock::Milliseconds16(commandData.timeout.Value()));
14171423

1418-
ScheduleTimer(endpoint, System::Clock::Milliseconds16(timeout));
1419-
gThermostatAttrAccess.SetAtomicWrite(endpoint, true);
1420-
gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, GetSourceScopedNodeId(commandObj));
1421-
sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, MakeOptional(timeout));
1424+
if (!timeout.has_value())
1425+
{
1426+
commandObj->AddStatus(commandPath, imcode::InvalidCommand);
1427+
return;
1428+
}
1429+
ScheduleTimer(endpoint, timeout.value());
1430+
gThermostatAttrAccess.SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), kAtomicWriteState_Open);
1431+
sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success,
1432+
MakeOptional(timeout.value().count()));
14221433
}
14231434

14241435
imcode commitPresets(Delegate * delegate, EndpointId endpoint)
@@ -1868,5 +1879,17 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co
18681879

18691880
void MatterThermostatPluginServerInitCallback()
18701881
{
1882+
Server::GetInstance().GetFabricTable().AddFabricDelegate(&gThermostatAttrAccess);
18711883
AttributeAccessInterfaceRegistry::Instance().Register(&gThermostatAttrAccess);
18721884
}
1885+
1886+
void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint)
1887+
{
1888+
ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint);
1889+
Delegate * delegate = GetDelegate(endpoint);
1890+
1891+
if (delegate != nullptr)
1892+
{
1893+
resetAtomicWrite(delegate, endpoint);
1894+
}
1895+
}

src/app/clusters/thermostat-server/thermostat-server.h

+21-16
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,22 @@ namespace Thermostat {
3737
static constexpr size_t kThermostatEndpointCount =
3838
MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
3939

40+
enum AtomicWriteState
41+
{
42+
kAtomicWriteState_Closed = 0,
43+
kAtomicWriteState_Open,
44+
};
4045
/**
4146
* @brief Thermostat Attribute Access Interface.
4247
*/
43-
class ThermostatAttrAccess : public chip::app::AttributeAccessInterface
48+
class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public chip::FabricTable::Delegate
4449
{
4550
public:
4651
ThermostatAttrAccess() : AttributeAccessInterface(Optional<chip::EndpointId>::Missing(), Thermostat::Id) {}
4752

4853
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
4954
CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override;
5055

51-
/**
52-
* @brief Sets the scoped node id of the originator that sent the last successful
53-
* AtomicRequest of type BeginWrite for the given endpoint.
54-
*
55-
* @param[in] endpoint The endpoint.
56-
* @param[in] originatorNodeId The originator scoped node id.
57-
*/
58-
void SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId);
59-
6056
/**
6157
* @brief Gets the scoped node id of the originator that sent the last successful
6258
* AtomicRequest of type BeginWrite for the given endpoint.
@@ -68,12 +64,13 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface
6864
ScopedNodeId GetAtomicWriteScopedNodeId(EndpointId endpoint);
6965

7066
/**
71-
* @brief Sets whether an atomic write is in progress for the given endpoint
67+
* @brief Sets the atomic write state for the given endpoint and originatorNodeId
7268
*
7369
* @param[in] endpoint The endpoint.
74-
* @param[in] inProgress Whether or not an atomic write is in progress.
70+
* @param[in] originatorNodeId The originator scoped node id.
71+
* @param[in] state Whether or not an atomic write is open or closed.
7572
*/
76-
void SetAtomicWrite(EndpointId endpoint, bool inProgress);
73+
void SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state);
7774

7875
/**
7976
* @brief Gets whether an atomic write is in progress for the given endpoint
@@ -105,10 +102,18 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface
105102
bool InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint);
106103

107104
private:
108-
CHIP_ERROR AppendPendingPreset(Delegate * delegate, const Structs::PresetStruct::Type & preset);
105+
CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset);
106+
107+
void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override;
108+
109+
struct AtomicWriteSession
110+
{
111+
AtomicWriteState state = kAtomicWriteState_Closed;
112+
ScopedNodeId nodeId;
113+
EndpointId endpointId = kInvalidEndpointId;
114+
};
109115

110-
ScopedNodeId mAtomicWriteNodeIds[kThermostatEndpointCount];
111-
bool mAtomicWriteState[kThermostatEndpointCount];
116+
AtomicWriteSession mAtomicWriteSessions[kThermostatEndpointCount];
112117
};
113118

114119
/**

src/app/common/templates/config-data.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ ClustersWithShutdownFunctions:
8181
- Color Control
8282
- Sample MEI
8383
- Scenes Management
84+
- Thermostat
8485

8586
ClustersWithPreAttributeChangeFunctions:
8687
- Door Lock

0 commit comments

Comments
 (0)