Skip to content

Commit bf6da6f

Browse files
shripad621gitsoares-sergiolpbeliveau-silabs
authored
Backport the fan-control circular callback changes to v1.3-branch. (project-chip#40305)
* fan_control_server: Fix circular callback issue (project-chip#36489) * fan_control_server: Fix circular callback issue This PR fixes a circular callback bug in fan control server using flags when updating SpeedSetting and PercentSetting. Before this change, a PercentSetting write to 25% would end up circling back to 30% as shown: ``` [MatterTest] 11-12 19:16:40.792 INFO @@@ WRITE PercentSetting to 25 [MatterTest] 11-12 19:16:40.801 INFO @@@ ATTRIB: EP1/FanControl/SpeedSetting: 3 [MatterTest] 11-12 19:16:40.802 INFO @@@ ATTRIB: EP1/FanControl/SpeedCurrent: 3 [MatterTest] 11-12 19:16:40.802 INFO @@@ ATTRIB: EP1/FanControl/PercentSetting: 30 [MatterTest] 11-12 19:16:40.802 INFO @@@ ATTRIB: EP1/FanControl/PercentCurrent: 30 ``` Now it behaves as expected: ``` [MatterTest] 11-13 18:54:27.961 INFO @@@ WRITE PercentSetting to 25 [MatterTest] 11-13 18:54:27.970 INFO @@@ ATTRIB: EP1/FanControl/SpeedSetting: 3 [MatterTest] 11-13 18:54:27.970 INFO @@@ ATTRIB: EP1/FanControl/SpeedCurrent: 3 [MatterTest] 11-13 18:54:27.970 INFO @@@ ATTRIB: EP1/FanControl/PercentSetting: 25 [MatterTest] 11-13 18:54:27.971 INFO @@@ ATTRIB: EP1/FanControl/PercentCurrent: 25 ``` Co-authored-by: lpbeliveau-silabs <[email protected]> * Addressed review suggestions --------- Co-authored-by: lpbeliveau-silabs <[email protected]> * fan-control-server: Fix FanMode circular callback issue (project-chip#36515) Similar to what was done for Speed and Percent, this PR fixes a bug where a FanMode could result in a circular callback. For example, setting the FanMode to kAuto, could trigger this issue. --------- Co-authored-by: Sergio Soares <[email protected]> Co-authored-by: lpbeliveau-silabs <[email protected]>
1 parent d895bb7 commit bf6da6f

File tree

1 file changed

+42
-35
lines changed

1 file changed

+42
-35
lines changed

src/app/clusters/fan-control-server/fan-control-server.cpp

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <app/clusters/fan-control-server/fan-control-server.h>
3434
#include <app/util/attribute-storage.h>
3535
#include <lib/support/CodeUtils.h>
36+
#include <lib/support/Scoped.h>
3637
#include <lib/support/logging/CHIPLogging.h>
3738
#include <protocols/interaction_model/StatusCode.h>
3839

@@ -88,6 +89,11 @@ namespace {
8889
// Indicates if the write operation is from the cluster server itself
8990
bool gWriteFromClusterLogic = false;
9091

92+
// Avoid circular callback calls when adjusting SpeedSetting and PercentSetting together.
93+
ScopedChangeOnly gSpeedWriteInProgress(false);
94+
ScopedChangeOnly gPercentWriteInProgress(false);
95+
ScopedChangeOnly gFanModeWriteInProgress(false);
96+
9197
Status SetFanModeToOff(EndpointId endpointId)
9298
{
9399
FanModeEnum currentFanMode;
@@ -158,12 +164,16 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute
158164
switch (attributePath.mAttributeId)
159165
{
160166
case FanMode::Id: {
167+
if (gFanModeWriteInProgress)
168+
{
169+
return Status::WriteIgnored;
170+
}
161171
if (*value == to_underlying(FanModeEnum::kOn))
162172
{
163173
FanMode::Set(attributePath.mEndpointId, FanModeEnum::kHigh);
164-
res = Status::WriteIgnored;
174+
return Status::WriteIgnored;
165175
}
166-
else if (*value == to_underlying(FanModeEnum::kSmart))
176+
if (*value == to_underlying(FanModeEnum::kSmart))
167177
{
168178
FanModeSequenceEnum fanModeSequence;
169179
Status status = FanModeSequence::Get(attributePath.mEndpointId, &fanModeSequence);
@@ -188,6 +198,10 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute
188198
break;
189199
}
190200
case SpeedSetting::Id: {
201+
if (gSpeedWriteInProgress)
202+
{
203+
return Status::WriteIgnored;
204+
}
191205
if (SupportsMultiSpeed(attributePath.mEndpointId))
192206
{
193207
// Check if the SpeedSetting is null.
@@ -227,6 +241,10 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute
227241
break;
228242
}
229243
case PercentSetting::Id: {
244+
if (gPercentWriteInProgress)
245+
{
246+
return Status::WriteIgnored;
247+
}
230248
// Check if the PercentSetting is null.
231249
if (NumericAttributeTraits<Percent>::IsNullValue(*value))
232250
{
@@ -318,6 +336,9 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
318336
Status status = FanMode::Get(attributePath.mEndpointId, &mode);
319337
VerifyOrReturn(Status::Success == status);
320338

339+
// Avoid circular callback calls
340+
ScopedChange FanModeWriteInProgress(gFanModeWriteInProgress, true);
341+
321342
// Setting the FanMode value to Off SHALL set the values of PercentSetting, PercentCurrent,
322343
// SpeedSetting, SpeedCurrent attributes to 0 (zero).
323344
if (mode == FanModeEnum::kOff)
@@ -365,39 +386,32 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
365386
DataModel::Nullable<Percent> percentSetting;
366387
Status status = PercentSetting::Get(attributePath.mEndpointId, percentSetting);
367388
VerifyOrReturn(Status::Success == status && !percentSetting.IsNull());
389+
uint8_t speedMax;
390+
status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
391+
VerifyOrReturn(Status::Success == status,
392+
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));
368393

394+
// Avoid circular callback calls
395+
ScopedChange PercentWriteInProgress(gPercentWriteInProgress, true);
369396
// If PercentSetting is set to 0, the server SHALL set the FanMode attribute value to Off.
370397
if (percentSetting.Value() == 0)
371398
{
372399
status = SetFanModeToOff(attributePath.mEndpointId);
373-
VerifyOrReturn(Status::Success == status,
400+
VerifyOrReturn(status == Status::Success,
374401
ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", to_underlying(status)));
375402
}
376403

377404
if (SupportsMultiSpeed(attributePath.mEndpointId))
378405
{
379406
// Adjust SpeedSetting from a percent value change for PercentSetting
380407
// speed = ceil( SpeedMax * (percent * 0.01) )
381-
uint8_t speedMax;
382-
status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
383-
VerifyOrReturn(Status::Success == status,
384-
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));
385-
386-
DataModel::Nullable<uint8_t> currentSpeedSetting;
387-
status = SpeedSetting::Get(attributePath.mEndpointId, currentSpeedSetting);
388-
VerifyOrReturn(Status::Success == status,
389-
ChipLogError(Zcl, "Failed to get SpeedSetting with error: 0x%02x", to_underlying(status)));
390-
391408
uint16_t percent = percentSetting.Value();
392409
// Plus 99 then integer divide by 100 instead of multiplying 0.01 to avoid floating point precision error
393410
uint8_t speedSetting = static_cast<uint8_t>((speedMax * percent + 99) / 100);
394411

395-
if (currentSpeedSetting.IsNull() || speedSetting != currentSpeedSetting.Value())
396-
{
397-
status = SpeedSetting::Set(attributePath.mEndpointId, speedSetting);
398-
VerifyOrReturn(Status::Success == status,
399-
ChipLogError(Zcl, "Failed to set SpeedSetting with error: 0x%02x", to_underlying(status)));
400-
}
412+
status = SpeedSetting::Set(attributePath.mEndpointId, speedSetting);
413+
VerifyOrDo(Status::Success == status,
414+
ChipLogError(Zcl, "Failed to set SpeedSetting with error: 0x%02x", to_underlying(status)));
401415
}
402416
break;
403417
}
@@ -407,7 +421,13 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
407421
DataModel::Nullable<uint8_t> speedSetting;
408422
Status status = SpeedSetting::Get(attributePath.mEndpointId, speedSetting);
409423
VerifyOrReturn(Status::Success == status && !speedSetting.IsNull());
424+
uint8_t speedMax;
425+
status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
426+
VerifyOrReturn(Status::Success == status,
427+
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));
410428

429+
// Avoid circular callback calls
430+
ScopedChange SpeedWriteInProgress(gSpeedWriteInProgress, true);
411431
// If SpeedSetting is set to 0, the server SHALL set the FanMode attribute value to Off.
412432
if (speedSetting.Value() == 0)
413433
{
@@ -418,25 +438,12 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
418438

419439
// Adjust PercentSetting from a speed value change for SpeedSetting
420440
// percent = floor( speed/SpeedMax * 100 )
421-
uint8_t speedMax;
422-
status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
423-
VerifyOrReturn(Status::Success == status,
424-
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));
425-
426-
DataModel::Nullable<Percent> currentPercentSetting;
427-
status = PercentSetting::Get(attributePath.mEndpointId, currentPercentSetting);
428-
VerifyOrReturn(Status::Success == status,
429-
ChipLogError(Zcl, "Failed to get PercentSetting with error: 0x%02x", to_underlying(status)));
430-
431441
float speed = speedSetting.Value();
432442
Percent percentSetting = static_cast<Percent>(speed / speedMax * 100);
433443

434-
if (currentPercentSetting.IsNull() || percentSetting != currentPercentSetting.Value())
435-
{
436-
status = PercentSetting::Set(attributePath.mEndpointId, percentSetting);
437-
VerifyOrReturn(Status::Success == status,
438-
ChipLogError(Zcl, "Failed to set PercentSetting with error: 0x%02x", to_underlying(status)));
439-
}
444+
status = PercentSetting::Set(attributePath.mEndpointId, percentSetting);
445+
VerifyOrDo(Status::Success == status,
446+
ChipLogError(Zcl, "Failed to set PercentSetting with error: 0x%02x", to_underlying(status)));
440447
}
441448
break;
442449
}

0 commit comments

Comments
 (0)