Skip to content

Commit a2bb5d6

Browse files
committedJul 27, 2024
Address review comments.
1 parent f027069 commit a2bb5d6

File tree

24 files changed

+87
-81
lines changed

24 files changed

+87
-81
lines changed
 

‎examples/air-purifier-app/air-purifier-common/air-purifier-app.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ cluster Thermostat = 513 {
12781278
kWake = 4;
12791279
kVacation = 5;
12801280
kGoingToSleep = 6;
1281-
kUserDefined = 7;
1281+
kUserDefined = 254;
12821282
}
12831283

12841284
enum SetpointChangeSourceEnum : enum8 {

‎examples/all-clusters-app/all-clusters-common/all-clusters-app.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -5007,7 +5007,7 @@ cluster Thermostat = 513 {
50075007
kWake = 4;
50085008
kVacation = 5;
50095009
kGoingToSleep = 6;
5010-
kUserDefined = 7;
5010+
kUserDefined = 254;
50115011
}
50125012

50135013
enum SetpointChangeSourceEnum : enum8 {

‎examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -3552,7 +3552,7 @@ cluster Thermostat = 513 {
35523552
kWake = 4;
35533553
kVacation = 5;
35543554
kGoingToSleep = 6;
3555-
kUserDefined = 7;
3555+
kUserDefined = 254;
35563556
}
35573557

35583558
enum SetpointChangeSourceEnum : enum8 {

‎examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,7 @@ cluster Thermostat = 513 {
12011201
kWake = 4;
12021202
kVacation = 5;
12031203
kGoingToSleep = 6;
1204-
kUserDefined = 7;
1204+
kUserDefined = 254;
12051205
}
12061206

12071207
enum SetpointChangeSourceEnum : enum8 {

‎examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,7 @@ cluster Thermostat = 513 {
15581558
kWake = 4;
15591559
kVacation = 5;
15601560
kGoingToSleep = 6;
1561-
kUserDefined = 7;
1561+
kUserDefined = 254;
15621562
}
15631563

15641564
enum SetpointChangeSourceEnum : enum8 {

‎examples/chef/devices/rootnode_roomairconditioner_9cf3607804.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,7 @@ cluster Thermostat = 513 {
11411141
kWake = 4;
11421142
kVacation = 5;
11431143
kGoingToSleep = 6;
1144-
kUserDefined = 7;
1144+
kUserDefined = 254;
11451145
}
11461146

11471147
enum SetpointChangeSourceEnum : enum8 {

‎examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1361,7 +1361,7 @@ cluster Thermostat = 513 {
13611361
kWake = 4;
13621362
kVacation = 5;
13631363
kGoingToSleep = 6;
1364-
kUserDefined = 7;
1364+
kUserDefined = 254;
13651365
}
13661366

13671367
enum SetpointChangeSourceEnum : enum8 {

‎examples/placeholder/linux/apps/app1/config.matter

+2-2
Original file line numberDiff line numberDiff line change
@@ -4798,7 +4798,7 @@ cluster Thermostat = 513 {
47984798
kWake = 4;
47994799
kVacation = 5;
48004800
kGoingToSleep = 6;
4801-
kUserDefined = 7;
4801+
kUserDefined = 254;
48024802
}
48034803

48044804
enum SetpointChangeSourceEnum : enum8 {
@@ -5148,7 +5148,7 @@ cluster Thermostat = 513 {
51485148
kWake = 4;
51495149
kVacation = 5;
51505150
kGoingToSleep = 6;
5151-
kUserDefined = 7;
5151+
kUserDefined = 254;
51525152
}
51535153

51545154
enum SetpointChangeSourceEnum : enum8 {

‎examples/placeholder/linux/apps/app2/config.matter

+2-2
Original file line numberDiff line numberDiff line change
@@ -4755,7 +4755,7 @@ cluster Thermostat = 513 {
47554755
kWake = 4;
47564756
kVacation = 5;
47574757
kGoingToSleep = 6;
4758-
kUserDefined = 7;
4758+
kUserDefined = 254;
47594759
}
47604760

47614761
enum SetpointChangeSourceEnum : enum8 {
@@ -5105,7 +5105,7 @@ cluster Thermostat = 513 {
51055105
kWake = 4;
51065106
kVacation = 5;
51075107
kGoingToSleep = 6;
5108-
kUserDefined = 7;
5108+
kUserDefined = 254;
51095109
}
51105110

51115111
enum SetpointChangeSourceEnum : enum8 {

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ namespace Thermostat {
3535

3636
static constexpr uint8_t kMaxNumberOfPresetTypes = 6;
3737

38+
// TODO: #34556 Support multiple presets of each type.
3839
// We will support only one preset of each preset type.
3940
static constexpr uint8_t kMaxNumberOfPresetsOfEachType = 1;
4041

@@ -83,8 +84,8 @@ class ThermostatDelegate : public Delegate
8384
uint8_t mNumberOfPresets;
8485

8586
Structs::PresetTypeStruct::Type mPresetTypes[kMaxNumberOfPresetTypes];
86-
PresetStructWithOwnedMembers mPresets[kMaxNumberOfPresetTypes * kMaxNumberOfPresetTypesOfEachType];
87-
PresetStructWithOwnedMembers mPendingPresets[kMaxNumberOfPresetTypes * kMaxNumberOfPresetTypesOfEachType];
87+
PresetStructWithOwnedMembers mPresets[kMaxNumberOfPresetTypes * kMaxNumberOfPresetsOfEachType];
88+
PresetStructWithOwnedMembers mPendingPresets[kMaxNumberOfPresetTypes * kMaxNumberOfPresetsOfEachType];
8889

8990
uint8_t mNextFreeIndexInPendingPresetsList;
9091
uint8_t mNextFreeIndexInPresetsList;

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ bool PresetHandlesExistAndMatch(const PresetStructWithOwnedMembers & preset, con
4949

5050
ThermostatDelegate::ThermostatDelegate()
5151
{
52-
mNumberOfPresets = kMaxNumberOfPresetTypes * kMaxNumberOfPresetTypesOfEachType;
52+
mNumberOfPresets = kMaxNumberOfPresetTypes * kMaxNumberOfPresetsOfEachType;
5353
mNextFreeIndexInPresetsList = 0;
5454
mNextFreeIndexInPendingPresetsList = 0;
5555

@@ -72,7 +72,7 @@ void ThermostatDelegate::InitializePresetTypes()
7272
for (PresetScenarioEnum presetScenario : presetScenarioEnumArray)
7373
{
7474
mPresetTypes[index].presetScenario = presetScenario;
75-
mPresetTypes[index].numberOfPresets = kMaxNumberOfPresetTypesOfEachType;
75+
mPresetTypes[index].numberOfPresets = kMaxNumberOfPresetsOfEachType;
7676
mPresetTypes[index].presetTypeFeatures =
7777
(presetScenario == PresetScenarioEnum::kOccupied || presetScenario == PresetScenarioEnum::kUnoccupied)
7878
? PresetTypeFeaturesBitmap::kAutomatic
@@ -193,6 +193,8 @@ CHIP_ERROR ThermostatDelegate::GetPendingPresetAtIndex(size_t index, PresetStruc
193193

194194
CHIP_ERROR ThermostatDelegate::ApplyPendingPresets()
195195
{
196+
197+
// TODO: #34546 - Need to support deletion of presets that are removed from Presets.
196198
for (uint8_t indexInPendingPresets = 0; indexInPendingPresets < mNextFreeIndexInPendingPresetsList; indexInPendingPresets++)
197199
{
198200
const PresetStructWithOwnedMembers & pendingPreset = mPendingPresets[indexInPendingPresets];
@@ -215,6 +217,9 @@ CHIP_ERROR ThermostatDelegate::ApplyPendingPresets()
215217

216218
mPresets[mNextFreeIndexInPresetsList] = pendingPreset;
217219

220+
// TODO: #34556 Since we support only one preset of each type, using the octet string containing the preset scenario
221+
// suffices as the unique preset handle. Need to fix this to actually provide unique handles once multiple presets of
222+
// each type are supported.
218223
const uint8_t handle[] = { static_cast<uint8_t>(pendingPreset.GetPresetScenario()) };
219224
mPresets[mNextFreeIndexInPresetsList].SetPresetHandle(DataModel::MakeNullable(ByteSpan(handle)));
220225
mNextFreeIndexInPresetsList++;

‎examples/thermostat/linux/thermostat-manager.cpp

+29-31
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ using namespace chip::app::Clusters::Thermostat::Structs;
4242
using namespace chip::app::Clusters::Thermostat::Attributes;
4343
using namespace chip::app::Clusters::TemperatureMeasurement;
4444
using namespace chip::app::Clusters::TemperatureMeasurement::Attributes;
45+
using namespace Protocols::InteractionModel;
4546

4647
using namespace chip::DeviceLayer;
4748

@@ -60,6 +61,12 @@ ThermostatManager ThermostatManager::sThermostatMgr;
6061

6162
namespace {
6263

64+
CHIP_ERROR ChipErrorFromStatusCode(Status status)
65+
{
66+
StatusIB statusIB(status);
67+
return statusIB.ToChipError();
68+
}
69+
6370
template <typename DecodableAttributeType>
6471
static void OnAttributeChangeReported(const ConcreteDataAttributePath & path, const DecodableAttributeType & value);
6572

@@ -70,30 +77,30 @@ void OnAttributeChangeReported<MeasuredValue::TypeInfo::DecodableType>(const Con
7077
ClusterId clusterId = path.mClusterId;
7178
if (clusterId != TemperatureMeasurement::Id)
7279
{
73-
ChipLogError(AppServer, "Attribute change reported for TemperatureMeasurement cluster on incorrect cluster id %u",
74-
clusterId);
80+
ChipLogError(AppServer, "Attribute change reported for TemperatureMeasurement cluster on incorrect cluster id " ChipLogFormatMEI,
81+
ChipLogValueMEI(clusterId));
7582
return;
7683
}
7784

7885
AttributeId attributeId = path.mAttributeId;
7986
if (attributeId != MeasuredValue::Id)
8087
{
81-
ChipLogError(AppServer, "Attribute change reported for TemperatureMeasurement cluster for incorrect attribute %u",
82-
attributeId);
88+
ChipLogError(AppServer, "Attribute change reported for TemperatureMeasurement cluster for incorrect attribute" ChipLogFormatMEI,
89+
ChipLogValueMEI(attributeId));
8390
return;
8491
}
8592

8693
if (!value.IsNull())
8794
{
8895
ChipLogDetail(AppServer, "Attribute change reported for TemperatureMeasurement cluster - MeasuredValue is %d",
89-
static_cast<short>(value.Value()));
96+
value.Value());
9097
}
9198
}
9299

93100
static void OnError(const ConcreteDataAttributePath * path, ChipError err)
94101
{
95-
ChipLogError(AppServer, "Subscribing to cluster Id %u and attribute Id %u failed with error %" CHIP_ERROR_FORMAT,
96-
path->mClusterId, path->mAttributeId, err.Format());
102+
ChipLogError(AppServer, "Subscribing to cluster Id " ChipLogFormatMEI " and attribute Id " ChipLogFormatMEI " failed with error %" CHIP_ERROR_FORMAT,
103+
ChipLogValueMEI(path->mClusterId), ChipLogValueMEI(path->mAttributeId), err.Format());
97104
}
98105

99106
static void OnSubscriptionEstablished(const ReadClient & client, unsigned int value)
@@ -111,7 +118,7 @@ void SubscribeToAttribute(ClusterId clusterId, AttributeId attributeId, const Em
111118
SubscribeAttribute<DecodableAttributeType>(
112119
peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), binding.remote, clusterId, attributeId,
113120
&OnAttributeChangeReported<DecodableAttributeType>, &OnError, 0, kMaxIntervalCeilingSeconds, &OnSubscriptionEstablished,
114-
nullptr, true /* fabricFiltered */, true /* keepExistingSubscription */);
121+
nullptr, true /* fabricFiltered */, false /* keepExistingSubscription */);
115122
}
116123

117124
static void ThermostatBoundDeviceChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device,
@@ -183,7 +190,7 @@ CHIP_ERROR ThermostatManager::Init()
183190
"mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n "
184191
"mOccupiedCoolingSetpoint: %d"
185192
"NumberOfPresets: %d",
186-
static_cast<uint8_t>(mSystemMode), SystemModeString(mSystemMode), static_cast<uint8_t>(mRunningMode),
193+
to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode),
187194
RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint,
188195
GetNumberOfPresets());
189196

@@ -229,21 +236,21 @@ void ThermostatManager::ThermostatClusterAttributeChangeHandler(AttributeId attr
229236
switch (attributeId)
230237
{
231238
case LocalTemperature::Id: {
232-
mLocalTemperature = static_cast<int16_t>(Encoding::LittleEndian::Get16(value));
239+
memcpy(&mLocalTemperature, value, size);
233240
ChipLogError(AppServer, "Local temperature changed to %d", mLocalTemperature);
234241
EvalThermostatState();
235242
}
236243
break;
237244

238245
case OccupiedCoolingSetpoint::Id: {
239-
mOccupiedCoolingSetpoint = static_cast<int16_t>(Encoding::LittleEndian::Get16(value));
246+
memcpy(&mOccupiedCoolingSetpoint, value, size);
240247
ChipLogError(AppServer, "Cooling temperature changed to %d", mOccupiedCoolingSetpoint);
241248
EvalThermostatState();
242249
}
243250
break;
244251

245252
case OccupiedHeatingSetpoint::Id: {
246-
mOccupiedHeatingSetpoint = static_cast<int16_t>(Encoding::LittleEndian::Get16(value));
253+
memcpy(&mOccupiedHeatingSetpoint, value, size);
247254
ChipLogError(AppServer, "Heating temperature changed to %d", mOccupiedHeatingSetpoint);
248255
EvalThermostatState();
249256
}
@@ -313,52 +320,43 @@ uint8_t ThermostatManager::GetNumberOfPresets()
313320

314321
CHIP_ERROR ThermostatManager::SetSystemMode(SystemModeEnum systemMode)
315322
{
316-
uint8_t systemModeValue = static_cast<uint8_t>(systemMode);
323+
uint8_t systemModeValue = to_underlying(systemMode);
317324
if (mSystemMode == systemMode)
318325
{
319326
ChipLogDetail(AppServer, "Already in system mode: %u (%s)", systemModeValue, SystemModeString(systemMode));
320327
return CHIP_NO_ERROR;
321328
}
322329

323330
ChipLogError(AppServer, "Setting system mode: %u (%s)", systemModeValue, SystemModeString(systemMode));
324-
Protocols::InteractionModel::Status status = SystemMode::Set(kThermostatEndpoint, systemMode);
325-
326-
// TODO: CHIP_ERROR_WRITE_FAILED might not be the best error code to send
327-
return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR : CHIP_ERROR_WRITE_FAILED;
331+
return ChipErrorFromStatusCode(SystemMode::Set(kThermostatEndpoint, systemMode));
328332
}
329333

330334
CHIP_ERROR ThermostatManager::SetRunningMode(ThermostatRunningModeEnum runningMode)
331335
{
332-
uint8_t runningModeValue = static_cast<uint8_t>(runningMode);
336+
uint8_t runningModeValue = to_underlying(runningMode);
333337
if (mRunningMode == runningMode)
334338
{
335339
ChipLogDetail(AppServer, "Already in running mode: %u (%s)", runningModeValue, RunningModeString(runningMode));
336340
return CHIP_NO_ERROR;
337341
}
338342

339343
ChipLogError(AppServer, "Setting running mode: %u (%s)", runningModeValue, RunningModeString(runningMode));
340-
Protocols::InteractionModel::Status status = ThermostatRunningMode::Set(kThermostatEndpoint, runningMode);
341-
342-
// TODO: CHIP_ERROR_WRITE_FAILED might not be the best error code to send
343-
return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR : CHIP_ERROR_WRITE_FAILED;
344+
return ChipErrorFromStatusCode(ThermostatRunningMode::Set(kThermostatEndpoint, runningMode));
344345
}
345346

346347
CHIP_ERROR ThermostatManager::SetCurrentTemperature(int16_t temperature)
347348
{
348-
Protocols::InteractionModel::Status status = LocalTemperature::Set(kThermostatEndpoint, temperature);
349-
return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR : CHIP_ERROR_WRITE_FAILED;
349+
return ChipErrorFromStatusCode(LocalTemperature::Set(kThermostatEndpoint, temperature));
350350
}
351351

352352
CHIP_ERROR ThermostatManager::SetCurrentHeatingSetPoint(int16_t heatingSetpoint)
353353
{
354-
Protocols::InteractionModel::Status status = OccupiedHeatingSetpoint::Set(kThermostatEndpoint, heatingSetpoint);
355-
return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR : CHIP_ERROR_WRITE_FAILED;
354+
return ChipErrorFromStatusCode(OccupiedHeatingSetpoint::Set(kThermostatEndpoint, heatingSetpoint));
356355
}
357356

358357
CHIP_ERROR ThermostatManager::SetCurrentCoolingSetPoint(int16_t coolingSetpoint)
359358
{
360-
Protocols::InteractionModel::Status status = OccupiedCoolingSetpoint::Set(kThermostatEndpoint, coolingSetpoint);
361-
return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR : CHIP_ERROR_WRITE_FAILED;
359+
return ChipErrorFromStatusCode(OccupiedCoolingSetpoint::Set(kThermostatEndpoint, coolingSetpoint));
362360
}
363361

364362
void ThermostatManager::EvalThermostatState()
@@ -367,7 +365,7 @@ void ThermostatManager::EvalThermostatState()
367365
"Eval Thermostat Running Mode \n "
368366
"mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n "
369367
"mOccupiedCoolingSetpoint: %d",
370-
static_cast<uint8_t>(mSystemMode), SystemModeString(mSystemMode), static_cast<uint8_t>(mRunningMode),
368+
to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode),
371369
RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint);
372370

373371
switch (mSystemMode)
@@ -495,8 +493,8 @@ void MatterPostAttributeChangeCallback(const ConcreteAttributePath & attributePa
495493
ChipLogProgress(AppServer, "Cluster callback: " ChipLogFormatMEI, ChipLogValueMEI(clusterId));
496494

497495
ChipLogProgress(AppServer,
498-
"Attribute ID changed: " ChipLogFormatMEI " Endpoint: %d ClusterId: %d Type: %u Value: %u, length %u",
499-
ChipLogValueMEI(attributeId), attributePath.mEndpointId, clusterId, type, *value, size);
496+
"Attribute ID changed: " ChipLogFormatMEI " Endpoint: %d ClusterId: " ChipLogFormatMEI " Type: %u Value: %u, length %u",
497+
ChipLogValueMEI(attributeId), attributePath.mEndpointId, ChipLogValueMEI(clusterId), type, *value, size);
500498

501499
ThermostatMgr().AttributeChangeHandler(attributePath.mEndpointId, clusterId, attributeId, value, size);
502500
}

‎examples/thermostat/nxp/zap/thermostat_matter_thread.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1952,7 +1952,7 @@ cluster Thermostat = 513 {
19521952
kWake = 4;
19531953
kVacation = 5;
19541954
kGoingToSleep = 6;
1955-
kUserDefined = 7;
1955+
kUserDefined = 254;
19561956
}
19571957

19581958
enum SetpointChangeSourceEnum : enum8 {

‎examples/thermostat/nxp/zap/thermostat_matter_wifi.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,7 @@ cluster Thermostat = 513 {
18631863
kWake = 4;
18641864
kVacation = 5;
18651865
kGoingToSleep = 6;
1866-
kUserDefined = 7;
1866+
kUserDefined = 254;
18671867
}
18681868

18691869
enum SetpointChangeSourceEnum : enum8 {

‎examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,7 @@ cluster Thermostat = 513 {
15601560
kWake = 4;
15611561
kVacation = 5;
15621562
kGoingToSleep = 6;
1563-
kUserDefined = 7;
1563+
kUserDefined = 254;
15641564
}
15651565

15661566
enum SetpointChangeSourceEnum : enum8 {

‎examples/thermostat/thermostat-common/thermostat.matter

+1-1
Original file line numberDiff line numberDiff line change
@@ -1740,7 +1740,7 @@ cluster Thermostat = 513 {
17401740
kWake = 4;
17411741
kVacation = 5;
17421742
kGoingToSleep = 6;
1743-
kUserDefined = 7;
1743+
kUserDefined = 254;
17441744
}
17451745

17461746
enum SetpointChangeSourceEnum : enum8 {

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ namespace Clusters {
2727
namespace Thermostat {
2828

2929
/** @brief
30-
* Defines methods for implementing application-specific logic for the extensions to the thermostat cluster.
31-
* It defines the interfaces that a thermostat should implement to support Presets and other extension features.
30+
* Defines methods for implementing application-specific logic for handling Presets in the thermostat cluster.
31+
* It defines the interfaces that a thermostat should implement to enable support for reading and writing the
32+
* Presets attribute and reading and writing the ActivePresetHandle attribute.
3233
*/
3334
class Delegate
3435
{
@@ -105,8 +106,9 @@ class Delegate
105106
/**
106107
* @brief Updates the presets attribute with the content of the pending presets list. If the preset in the pending presets list
107108
* matches i.e. has the same presetHandle as an existing entry in the Presets attribute, the thermostat will update the entry
108-
* with the new preset values, otherwise it will add a new preset to the Presets attribute. This will be called when the
109-
* Thermostat receives a CommitPresetsSchedulesRequest command to commit the pending preset changes.
109+
* with the new preset values, otherwise it will add a new preset to the Presets attribute. For new presets that get added,
110+
* it is the responsibility of this API to allocate unique preset handles to the presets before saving the preset. This will be
111+
* called when the Thermostat receives a CommitPresetsSchedulesRequest command to commit the pending preset changes.
110112
*
111113
* @return CHIP_NO_ERROR if the updates to the presets attribute has been committed successfully.
112114
* @return CHIP_ERROR if the updates to the presets attribute failed to commit for some reason.

0 commit comments

Comments
 (0)