Skip to content

Commit

Permalink
[HVAC] Allow Thermostat delegate to supply atomic write timeout (#34823)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* Address Review Comments

* Address review comments

* Fix default timeout after other timeouts changed

* Restyled by autopep8

* Fix linter error

---------

Co-authored-by: C Freeman <[email protected]>
Co-authored-by: Restyled.io <[email protected]>

* 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 <[email protected]>

* comments addressed

* more comments addressed

* lint pass

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: C Freeman <[email protected]>

* 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 <[email protected]>

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: Terence Hampson <[email protected]>

* Update src/python_testing/TC_BRBINFO_4_1.py

Co-authored-by: Terence Hampson <[email protected]>

* comments addressed

* Restyled by autopep8

---------

Co-authored-by: Terence Hampson <[email protected]>
Co-authored-by: C Freeman <[email protected]>
Co-authored-by: Restyled.io <[email protected]>

* 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 <[email protected]>

* Update src/python_testing/TC_SEAR_1_2.py

Co-authored-by: William <[email protected]>

* 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 <[email protected]>

* address code review feedback

* remove PICS checked by the TC_SEAR_1.6

* more code review updates

* Restyled by autopep8

---------

Co-authored-by: William <[email protected]>
Co-authored-by: Kiel Oleson <[email protected]>
Co-authored-by: Restyled.io <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Boris Zbarsky <[email protected]>

* [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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Andrei Litvin <[email protected]>

* 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 <[email protected]>
Date:   Fri May 10 14:26:07 2024 -0400

    remove some unwanted diffs in provision files

commit be160931dc600de7e7ead378b70d6a43c3945e46
Author: Michael Rupp <[email protected]>
Date:   Fri May 10 14:24:25 2024 -0400

    revert changes to generator.project.mak

commit 14b6605887166e6d5284a61feb2bf407d850bdcf
Author: Michael Rupp <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* removing unneccessary git fetch (#34698)

* Restyle patch

* Regen to fix ordering of global structs

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Restyled.io <[email protected]>
Co-authored-by: Nivi Sarkar <[email protected]>
Co-authored-by: Boris Zbarsky <[email protected]>
Co-authored-by: Terence Hampson <[email protected]>
Co-authored-by: Tennessee Carmel-Veilleux <[email protected]>
Co-authored-by: Chris Letnick <[email protected]>
Co-authored-by: C Freeman <[email protected]>
Co-authored-by: Douglas Rocha Ferraz <[email protected]>
Co-authored-by: Petru Lauric <[email protected]>
Co-authored-by: William <[email protected]>
Co-authored-by: Kiel Oleson <[email protected]>
Co-authored-by: Karsten Sperling <[email protected]>
Co-authored-by: Anu Biradar <[email protected]>
Co-authored-by: mkardous-silabs <[email protected]>
Co-authored-by: Rob Bultman <[email protected]>
Co-authored-by: Andrei Litvin <[email protected]>
Co-authored-by: Shao Ling Tan <[email protected]>
Co-authored-by: Amine Alami <[email protected]>
Co-authored-by: Michael Rupp <[email protected]>
  • Loading branch information
1 parent 2b5194b commit c0b140b
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 158 deletions.
4 changes: 4 additions & 0 deletions examples/thermostat/linux/include/thermostat-delegate-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ class ThermostatDelegate : public Delegate
public:
static inline ThermostatDelegate & GetInstance() { return sInstance; }

std::optional<System::Clock::Milliseconds16>
GetAtomicWriteTimeout(DataModel::DecodableList<chip::AttributeId> attributeRequests,
System::Clock::Milliseconds16 timeoutRequest) override;

CHIP_ERROR GetPresetTypeAtIndex(size_t index, Structs::PresetTypeStruct::Type & presetType) override;

uint8_t GetNumberOfPresets() override;
Expand Down
41 changes: 41 additions & 0 deletions examples/thermostat/linux/thermostat-delegate-impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,47 @@ CHIP_ERROR ThermostatDelegate::SetActivePresetHandle(const DataModel::Nullable<B
return CHIP_NO_ERROR;
}

std::optional<System::Clock::Milliseconds16>
ThermostatDelegate::GetAtomicWriteTimeout(DataModel::DecodableList<AttributeId> attributeRequests,
System::Clock::Milliseconds16 timeoutRequest)
{
auto attributeIdsIter = attributeRequests.begin();
bool requestedPresets = false, requestedSchedules = false;
while (attributeIdsIter.Next())
{
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)
{
// 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)
{
// If the client expects to edit the schedules, then we'll give it 9 seconds to do so
timeout += std::chrono::milliseconds(9000);
}
// If the client requested an even smaller timeout, then use that one
return std::min(timeoutRequest, timeout);
}

void ThermostatDelegate::InitializePendingPresets()
{
mNextFreeIndexInPendingPresetsList = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2217,6 +2217,7 @@
}; \
const EmberAfGenericClusterFunction chipFuncArrayThermostatServer[] = { \
(EmberAfGenericClusterFunction) emberAfThermostatClusterServerInitCallback, \
(EmberAfGenericClusterFunction) MatterThermostatClusterServerShutdownCallback, \
(EmberAfGenericClusterFunction) MatterThermostatClusterServerPreAttributeChangedCallback, \
}; \
const EmberAfGenericClusterFunction chipFuncArrayFanControlServer[] = { \
Expand Down Expand Up @@ -3755,7 +3756,7 @@
.attributes = ZAP_ATTRIBUTE_INDEX(616), \
.attributeCount = 26, \
.clusterSize = 72, \
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \
.functions = chipFuncArrayThermostatServer, \
.acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 241 ), \
.generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 246 ), \
Expand Down
11 changes: 11 additions & 0 deletions src/app/clusters/thermostat-server/thermostat-delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ class Delegate

virtual ~Delegate() = default;

/**
* @brief Get the maximum timeout for atomically writing to a set of attributes
*
* @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.
*/
virtual std::optional<System::Clock::Milliseconds16>
GetAtomicWriteTimeout(DataModel::DecodableList<AttributeId> attributeRequests,
System::Clock::Milliseconds16 timeoutRequest) = 0;

/**
* @brief Get the preset type at a given index in the PresetTypes attribute
*
Expand Down
95 changes: 59 additions & 36 deletions src/app/clusters/thermostat-server/thermostat-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include <app/CommandHandler.h>
#include <app/ConcreteAttributePath.h>
#include <app/ConcreteCommandPath.h>
#include <app/server/Server.h>
#include <app/util/endpoint-config-api.h>
#include <lib/core/CHIPEncoding.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>

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

delegate->ClearPendingPresetList();
gThermostatAttrAccess.SetAtomicWrite(endpoint, false);
gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId());
gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed);
}

/**
Expand Down Expand Up @@ -205,8 +206,7 @@ void resetAtomicWrite(Delegate * delegate, EndpointId endpoint)
delegate->ClearPendingPresetList();
}
ClearTimer(endpoint);
gThermostatAttrAccess.SetAtomicWrite(endpoint, false);
gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId());
gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed);
}

/**
Expand Down Expand Up @@ -605,14 +605,16 @@ void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate)
}
}

void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, bool inProgress)
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(mAtomicWriteState))
if (ep < ArraySize(mAtomicWriteSessions))
{
mAtomicWriteState[ep] = inProgress;
mAtomicWriteSessions[ep].state = state;
mAtomicWriteSessions[ep].endpointId = endpoint;
mAtomicWriteSessions[ep].nodeId = originatorNodeId;
}
}

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

if (ep < ArraySize(mAtomicWriteState))
if (ep < ArraySize(mAtomicWriteSessions))
{
inAtomicWrite = mAtomicWriteState[ep];
inAtomicWrite = (mAtomicWriteSessions[ep].state == kAtomicWriteState_Open);
}
return inAtomicWrite;
}
Expand All @@ -649,26 +651,15 @@ bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId
return GetAtomicWriteScopedNodeId(endpoint) == sourceNodeId;
}

void ThermostatAttrAccess::SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId)
{
uint16_t ep =
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);

if (ep < ArraySize(mAtomicWriteNodeIds))
{
mAtomicWriteNodeIds[ep] = originatorNodeId;
}
}

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(mAtomicWriteNodeIds))
if (ep < ArraySize(mAtomicWriteSessions))
{
originatorNodeId = mAtomicWriteNodeIds[ep];
originatorNodeId = mAtomicWriteSessions[ep].nodeId;
}
return originatorNodeId;
}
Expand Down Expand Up @@ -704,7 +695,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A
}
break;
case PresetTypes::Id: {
Delegate * delegate = GetDelegate(aPath.mEndpointId);
auto delegate = GetDelegate(aPath.mEndpointId);
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));

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

ReturnErrorOnFailure(aEncoder.Encode(delegate->GetNumberOfPresets()));
}
break;
case Presets::Id: {
Delegate * delegate = GetDelegate(aPath.mEndpointId);
auto delegate = GetDelegate(aPath.mEndpointId);
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));

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

uint8_t buffer[kPresetHandleSize];
Expand Down Expand Up @@ -812,7 +803,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath,
{
case Presets::Id: {

Delegate * delegate = GetDelegate(endpoint);
auto delegate = GetDelegate(endpoint);
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null"));

// Presets are not editable, return INVALID_IN_STATE.
Expand Down Expand Up @@ -897,7 +888,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath,
return CHIP_NO_ERROR;
}

CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const PresetStruct::Type & preset)
CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset)
{
if (!IsValidPresetEntry(preset))
{
Expand Down Expand Up @@ -951,6 +942,23 @@ CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const
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 delegate = GetDelegate(atomicWriteState.endpointId);
if (delegate == nullptr)
{
continue;
}
resetAtomicWrite(delegate, atomicWriteState.endpointId);
}
}
}

} // namespace Thermostat
} // namespace Clusters
} // namespace app
Expand Down Expand Up @@ -1394,8 +1402,6 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath &
return;
}

auto timeout = commandData.timeout.Value();

if (!validAtomicAttributes(commandData, false))
{
commandObj->AddStatus(commandPath, imcode::InvalidCommand);
Expand All @@ -1412,13 +1418,18 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath &
// needs to keep track of a pending preset list now.
delegate->InitializePendingPresets();

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

ScheduleTimer(endpoint, System::Clock::Milliseconds16(timeout));
gThermostatAttrAccess.SetAtomicWrite(endpoint, true);
gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, GetSourceScopedNodeId(commandObj));
sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, MakeOptional(timeout));
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)
Expand Down Expand Up @@ -1868,5 +1879,17 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co

void MatterThermostatPluginServerInitCallback()
{
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);
}
}
37 changes: 21 additions & 16 deletions src/app/clusters/thermostat-server/thermostat-server.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,22 @@ namespace Thermostat {
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
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;

/**
* @brief Sets the scoped node id of the originator that sent the last successful
* AtomicRequest of type BeginWrite for the given endpoint.
*
* @param[in] endpoint The endpoint.
* @param[in] originatorNodeId The originator scoped node id.
*/
void SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId);

/**
* @brief Gets the scoped node id of the originator that sent the last successful
* AtomicRequest of type BeginWrite for the given endpoint.
Expand All @@ -68,12 +64,13 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface
ScopedNodeId GetAtomicWriteScopedNodeId(EndpointId endpoint);

/**
* @brief Sets 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.
* @param[in] inProgress Whether or not an atomic write is in progress.
* @param[in] originatorNodeId The originator scoped node id.
* @param[in] state Whether or not an atomic write is open or closed.
*/
void SetAtomicWrite(EndpointId endpoint, bool inProgress);
void SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state);

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

private:
CHIP_ERROR AppendPendingPreset(Delegate * delegate, const Structs::PresetStruct::Type & preset);
CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset);

void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override;

struct AtomicWriteSession
{
AtomicWriteState state = kAtomicWriteState_Closed;
ScopedNodeId nodeId;
EndpointId endpointId = kInvalidEndpointId;
};

ScopedNodeId mAtomicWriteNodeIds[kThermostatEndpointCount];
bool mAtomicWriteState[kThermostatEndpointCount];
AtomicWriteSession mAtomicWriteSessions[kThermostatEndpointCount];
};

/**
Expand Down
1 change: 1 addition & 0 deletions src/app/common/templates/config-data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ ClustersWithShutdownFunctions:
- Color Control
- Sample MEI
- Scenes Management
- Thermostat

ClustersWithPreAttributeChangeFunctions:
- Door Lock
Expand Down
Loading

0 comments on commit c0b140b

Please sign in to comment.