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>
@@ -389,7 +390,7 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
389390 // Plus 99 then integer divide by 100 instead of multiplying 0.01 to avoid floating point precision error
390391 uint8_t speedSetting = static_cast <uint8_t >((speedMax * percent + 99 ) / 100 );
391392
392- if (currentSpeedSetting. IsNull () || speedSetting != currentSpeedSetting. Value () )
393+ if (currentSpeedSetting != speedSetting )
393394 {
394395 status = SpeedSetting::Set (attributePath.mEndpointId , speedSetting);
395396 VerifyOrReturn (Status::Success == status,
@@ -413,33 +414,75 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt
413414 ChipLogError (Zcl, " Failed to set FanMode to off with error: 0x%02x" , to_underlying (status)));
414415 }
415416
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)));
417+ // Adjust PercentSetting from a speed value change for SpeedSetting only when the SpeedSetting change was received
418+ // on a write command, not when it was changed by the server or the app logic. This avoids circular logic such as
419+ // (with a SpeedMax of 10):
420+ // 1. Client sets the PercetSetting to 25%
421+ // 2. Server sets the SpeedSetting to 3 through the server callbackm, which sets the PercentSetting to 30%
422+ // 3. Server sets the PercentSetting to 30% through the server callback
423+ }
424+ break ;
425+ }
426+ default :
427+ break ;
428+ }
429+ }
422430
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)));
431+ CHIP_ERROR FanControlAttributeAccessInterface::Write (const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder)
432+ {
433+ Status status = Status::Success;
434+ switch (aPath.mAttributeId )
435+ {
436+ case SpeedSetting::Id: {
437+ DataModel::Nullable<uint8_t > speedSetting;
438+ ReturnErrorOnFailure (aDecoder.Decode (speedSetting));
439+
440+ DataModel::Nullable<uint8_t > currentSpeedSetting;
441+ status = SpeedSetting::Get (aPath.mEndpointId , currentSpeedSetting);
442+ ReturnLogErrorOnFailure (StatusIB (status).ToChipError ());
427443
428- float speed = speedSetting.Value ();
429- Percent percentSetting = static_cast <Percent>(speed / speedMax * 100 );
444+ if (speedSetting != currentSpeedSetting)
445+ {
446+ status = SpeedSetting::Set (aPath.mEndpointId , speedSetting);
447+ ReturnLogErrorOnFailure (StatusIB (status).ToChipError ());
448+ // Skip the last step if we are writing NULL
449+ VerifyOrReturnValue (!speedSetting.IsNull (), CHIP_NO_ERROR);
430450
431- if (currentPercentSetting. IsNull () || percentSetting != currentPercentSetting. Value ( ))
451+ if (SupportsMultiSpeed (aPath. mEndpointId ))
432452 {
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)));
453+ // If SpeedSetting is set to 0, the server SHALL set the FanMode attribute value to Off.
454+ if (speedSetting.Value () == 0 )
455+ {
456+ status = SetFanModeToOff (aPath.mEndpointId );
457+ ReturnLogErrorOnFailure (StatusIB (status).ToChipError ());
458+ }
459+
460+ // Adjust PercentSetting from a speed value change for SpeedSetting
461+ // percent = floor( speed/SpeedMax * 100 )
462+ uint8_t speedMax;
463+ status = SpeedMax::Get (aPath.mEndpointId , &speedMax);
464+ ReturnLogErrorOnFailure (StatusIB (status).ToChipError ());
465+
466+ DataModel::Nullable<Percent> currentPercentSetting;
467+ status = PercentSetting::Get (aPath.mEndpointId , currentPercentSetting);
468+ ReturnLogErrorOnFailure (StatusIB (status).ToChipError ());
469+
470+ float speed = speedSetting.Value ();
471+ Percent percentSetting = static_cast <Percent>(speed / speedMax * 100 );
472+
473+ if (currentPercentSetting != percentSetting)
474+ {
475+ status = PercentSetting::Set (aPath.mEndpointId , percentSetting);
476+ ReturnLogErrorOnFailure (StatusIB (status).ToChipError ());
477+ }
436478 }
437479 }
438480 break ;
439481 }
440482 default :
441483 break ;
442484 }
485+ return CHIP_NO_ERROR;
443486}
444487
445488bool emberAfFanControlClusterStepCallback (app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath,
0 commit comments