Skip to content

Commit b79e144

Browse files
authored
Merge branch 'master' into feature/easy-review-pr
2 parents 29ac106 + c0b140b commit b79e144

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)