Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fan_control_server: Fix circular callback issue #36489

Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 38 additions & 36 deletions src/app/clusters/fan-control-server/fan-control-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ namespace {
// Indicates if the write operation is from the cluster server itself
bool gWriteFromClusterLogic = false;

// Avoid circular callback calls when adjusting SpeedSetting and PercentSetting together.
bool gSpeedWriteInProgress = false;
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
bool gPercentWriteInProgress = false;
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved

Status SetFanModeToOff(EndpointId endpointId)
{
FanModeEnum currentFanMode;
Expand Down Expand Up @@ -185,6 +189,10 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute
break;
}
case SpeedSetting::Id: {
if (gSpeedWriteInProgress)
{
return Status::WriteIgnored;
}
if (SupportsMultiSpeed(attributePath.mEndpointId))
{
// Check if the SpeedSetting is null.
Expand Down Expand Up @@ -224,6 +232,10 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute
break;
}
case PercentSetting::Id: {
if (gPercentWriteInProgress)
{
return Status::WriteIgnored;
}
// Check if the PercentSetting is null.
if (NumericAttributeTraits<Percent>::IsNullValue(*value))
{
Expand Down Expand Up @@ -360,80 +372,70 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
}
case PercentSetting::Id: {
DataModel::Nullable<Percent> percentSetting;
uint8_t speedMax;
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
Status status = PercentSetting::Get(attributePath.mEndpointId, percentSetting);
VerifyOrReturn(Status::Success == status && !percentSetting.IsNull());

status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));

// Avoid circular callback calls
gPercentWriteInProgress = true;
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
// If PercentSetting is set to 0, the server SHALL set the FanMode attribute value to Off.
if (percentSetting.Value() == 0)
{
status = SetFanModeToOff(attributePath.mEndpointId);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", to_underlying(status)));
VerifyOrDo(status == Status::Success,
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", to_underlying(status)));
}

if (SupportsMultiSpeed(attributePath.mEndpointId))
{
// Adjust SpeedSetting from a percent value change for PercentSetting
// speed = ceil( SpeedMax * (percent * 0.01) )
uint8_t speedMax;
status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));

DataModel::Nullable<uint8_t> currentSpeedSetting;
status = SpeedSetting::Get(attributePath.mEndpointId, currentSpeedSetting);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to get SpeedSetting with error: 0x%02x", to_underlying(status)));

uint16_t percent = percentSetting.Value();
// Plus 99 then integer divide by 100 instead of multiplying 0.01 to avoid floating point precision error
uint8_t speedSetting = static_cast<uint8_t>((speedMax * percent + 99) / 100);

if (currentSpeedSetting.IsNull() || speedSetting != currentSpeedSetting.Value())
{
status = SpeedSetting::Set(attributePath.mEndpointId, speedSetting);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to set SpeedSetting with error: 0x%02x", to_underlying(status)));
}
status = SpeedSetting::Set(attributePath.mEndpointId, speedSetting);
VerifyOrDo(Status::Success == status,
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
ChipLogError(Zcl, "Failed to set SpeedSetting with error: 0x%02x", to_underlying(status)));
}
gPercentWriteInProgress = false;
break;
}
case SpeedSetting::Id: {
if (SupportsMultiSpeed(attributePath.mEndpointId))
{
DataModel::Nullable<uint8_t> speedSetting;
uint8_t speedMax;
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
Status status = SpeedSetting::Get(attributePath.mEndpointId, speedSetting);
VerifyOrReturn(Status::Success == status && !speedSetting.IsNull());

status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));

// Avoid circular callback calls
gSpeedWriteInProgress = true;
// If SpeedSetting is set to 0, the server SHALL set the FanMode attribute value to Off.
if (speedSetting.Value() == 0)
{
status = SetFanModeToOff(attributePath.mEndpointId);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", to_underlying(status)));
VerifyOrDo(Status::Success == status,
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", to_underlying(status)));
}

// Adjust PercentSetting from a speed value change for SpeedSetting
// percent = floor( speed/SpeedMax * 100 )
uint8_t speedMax;
status = SpeedMax::Get(attributePath.mEndpointId, &speedMax);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", to_underlying(status)));

DataModel::Nullable<Percent> currentPercentSetting;
status = PercentSetting::Get(attributePath.mEndpointId, currentPercentSetting);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to get PercentSetting with error: 0x%02x", to_underlying(status)));

float speed = speedSetting.Value();
Percent percentSetting = static_cast<Percent>(speed / speedMax * 100);

if (currentPercentSetting.IsNull() || percentSetting != currentPercentSetting.Value())
{
status = PercentSetting::Set(attributePath.mEndpointId, percentSetting);
VerifyOrReturn(Status::Success == status,
ChipLogError(Zcl, "Failed to set PercentSetting with error: 0x%02x", to_underlying(status)));
}
status = PercentSetting::Set(attributePath.mEndpointId, percentSetting);
VerifyOrDo(Status::Success == status,
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
ChipLogError(Zcl, "Failed to set PercentSetting with error: 0x%02x", to_underlying(status)));
gSpeedWriteInProgress = false;
}
break;
}
Expand Down
Loading