Skip to content

Commit bcc7109

Browse files
Added write attribute access interface to prevent circular callbacks when changing speed
1 parent 657da9e commit bcc7109

File tree

3 files changed

+76
-17
lines changed

3 files changed

+76
-17
lines changed

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

+66-16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <app-common/zap-generated/cluster-objects.h>
2525
#include <app-common/zap-generated/ids/Attributes.h>
2626
#include <app-common/zap-generated/ids/Clusters.h>
27+
#include <app/AttributeAccessInterfaceRegistry.h>
2728
#include <app/CommandHandler.h>
2829
#include <app/ConcreteCommandPath.h>
2930
#include <app/clusters/fan-control-server/fan-control-server.h>
@@ -50,6 +51,8 @@ static_assert(kFanControlDelegateTableSize <= kEmberInvalidEndpointIndex, "FanCo
5051

5152
Delegate * gDelegateTable[kFanControlDelegateTableSize] = { nullptr };
5253

54+
FanControlAttributeAccessInterface gFanControlAttributeAccess;
55+
5356
} // anonymous namespace
5457

5558
namespace chip {
@@ -413,33 +416,75 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
413416
ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", to_underlying(status)));
414417
}
415418

416-
// Adjust PercentSetting from a speed value change for SpeedSetting
417-
// percent = floor( speed/SpeedMax * 100 )
418-
uint8_t speedMax;
419-
status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
420-
VerifyOrReturn(Status::Success == status,
421-
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));
419+
// Adjust PercentSetting from a speed value change for SpeedSetting only when the SpeedSetting change was received
420+
// on a write command, not when it was changed by the server or the app logic. This avoids circular logic such as
421+
//(with a SpeedMax of 10):
422+
// 1. Client sets the PercetSetting to 25%
423+
// 2. Server sets the SpeedSetting to 3 through the server callbackm, which sets the PercentSetting to 30%
424+
// 3. Server sets the PercentSetting to 30% through the server callback
425+
}
426+
break;
427+
}
428+
default:
429+
break;
430+
}
431+
}
422432

423-
DataModel::Nullable<Percent> currentPercentSetting;
424-
status = PercentSetting::Get(attributePath.mEndpointId, currentPercentSetting);
425-
VerifyOrReturn(Status::Success == status,
426-
ChipLogError(Zcl, "Failed to get PercentSetting with error: 0x%02x", to_underlying(status)));
433+
CHIP_ERROR FanControlAttributeAccessInterface::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder)
434+
{
435+
Status status = Status::Success;
436+
switch (aPath.mAttributeId)
437+
{
438+
case SpeedSetting::Id: {
439+
DataModel::Nullable<uint8_t> speedSetting;
440+
ReturnErrorOnFailure(aDecoder.Decode(speedSetting));
427441

428-
float speed = speedSetting.Value();
429-
Percent percentSetting = static_cast<Percent>(speed / speedMax * 100);
442+
DataModel::Nullable<uint8_t> currentSpeedSetting;
443+
status = SpeedSetting::Get(aPath.mEndpointId, currentSpeedSetting);
444+
ReturnLogErrorOnFailure(StatusIB(status).ToChipError());
430445

431-
if (currentPercentSetting.IsNull() || percentSetting != currentPercentSetting.Value())
446+
if (speedSetting != currentSpeedSetting)
447+
{
448+
status = SpeedSetting::Set(aPath.mEndpointId, speedSetting);
449+
ReturnLogErrorOnFailure(StatusIB(status).ToChipError());
450+
// Skip the last step if we are writing NULL
451+
VerifyOrReturnValue(!speedSetting.IsNull(), CHIP_NO_ERROR);
452+
453+
if (SupportsMultiSpeed(aPath.mEndpointId))
432454
{
433-
status = PercentSetting::Set(attributePath.mEndpointId, percentSetting);
434-
VerifyOrReturn(Status::Success == status,
435-
ChipLogError(Zcl, "Failed to set PercentSetting with error: 0x%02x", to_underlying(status)));
455+
// If SpeedSetting is set to 0, the server SHALL set the FanMode attribute value to Off.
456+
if (speedSetting.Value() == 0)
457+
{
458+
status = SetFanModeToOff(aPath.mEndpointId);
459+
ReturnLogErrorOnFailure(StatusIB(status).ToChipError());
460+
}
461+
462+
// Adjust PercentSetting from a speed value change for SpeedSetting
463+
// percent = floor( speed/SpeedMax * 100 )
464+
uint8_t speedMax;
465+
status = SpeedMax::Get(aPath.mEndpointId, &speedMax);
466+
ReturnLogErrorOnFailure(StatusIB(status).ToChipError());
467+
468+
DataModel::Nullable<Percent> currentPercentSetting;
469+
status = PercentSetting::Get(aPath.mEndpointId, currentPercentSetting);
470+
ReturnLogErrorOnFailure(StatusIB(status).ToChipError());
471+
472+
float speed = speedSetting.Value();
473+
Percent percentSetting = static_cast<Percent>(speed / speedMax * 100);
474+
475+
if (currentPercentSetting.IsNull() || percentSetting != currentPercentSetting.Value())
476+
{
477+
status = PercentSetting::Set(aPath.mEndpointId, percentSetting);
478+
ReturnLogErrorOnFailure(StatusIB(status).ToChipError());
479+
}
436480
}
437481
}
438482
break;
439483
}
440484
default:
441485
break;
442486
}
487+
return CHIP_NO_ERROR;
443488
}
444489

445490
bool emberAfFanControlClusterStepCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath,
@@ -477,3 +522,8 @@ bool emberAfFanControlClusterStepCallback(app::CommandHandler * commandObj, cons
477522
commandObj->AddStatus(commandPath, status);
478523
return true;
479524
}
525+
526+
void MatterFanControlPluginServerInitCallback()
527+
{
528+
AttributeAccessInterfaceRegistry::Instance().Register(&gFanControlAttributeAccess);
529+
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,23 @@
1919

2020
#include "fan-control-delegate.h"
2121
#include <app-common/zap-generated/cluster-objects.h>
22+
#include <app/AttributeAccessInterface.h>
2223
#include <app/util/af-types.h>
2324

2425
namespace chip {
2526
namespace app {
2627
namespace Clusters {
2728
namespace FanControl {
2829

30+
class FanControlAttributeAccessInterface : public AttributeAccessInterface
31+
{
32+
public:
33+
FanControlAttributeAccessInterface() : AttributeAccessInterface(Optional<EndpointId>(), Id) {}
34+
35+
CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override;
36+
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override { return CHIP_NO_ERROR; }
37+
};
38+
2939
void SetDefaultDelegate(EndpointId aEndpoint, Delegate * aDelegate);
3040
Delegate * GetDelegate(EndpointId aEndpoint);
3141

src/app/util/util.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ void MatterUnitLocalizationPluginServerInitCallback() {}
107107
void MatterProxyValidPluginServerInitCallback() {}
108108
void MatterProxyDiscoveryPluginServerInitCallback() {}
109109
void MatterProxyConfigurationPluginServerInitCallback() {}
110-
void MatterFanControlPluginServerInitCallback() {}
111110
void MatterActivatedCarbonFilterMonitoringPluginServerInitCallback() {}
112111
void MatterHepaFilterMonitoringPluginServerInitCallback() {}
113112
void MatterAirQualityPluginServerInitCallback() {}

0 commit comments

Comments
 (0)