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

Support On/Off controls to fan device #37814

Merged
merged 18 commits into from
Mar 5, 2025
Merged
Changes from all commits
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
129 changes: 129 additions & 0 deletions examples/chef/common/chef-fan-control-manager.cpp
Original file line number Diff line number Diff line change
@@ -48,6 +48,8 @@ class ChefFanControlManager : public Delegate
Status HandleStep(StepDirectionEnum aDirection, bool aWrap, bool aLowestOff) override;
DataModel::Nullable<uint8_t> GetSpeedSetting();
DataModel::Nullable<Percent> GetPercentSetting();
Protocols::InteractionModel::Status OnCommand(EndpointId endpointId);
Protocols::InteractionModel::Status OffCommand(EndpointId endpointId);

private:
uint8_t mPercentCurrent = 0;
@@ -329,6 +331,133 @@ void emberAfFanControlClusterInitCallback(EndpointId endpoint)
mFanControlManager->Init();
}

Protocols::InteractionModel::Status ChefFanControlManager::OnCommand(EndpointId endpointId)
{
ChipLogProgress(DeviceLayer, "ChefFanControlManager::OnCommand");

FanControl::FanModeEnum fanMode;
FanControl::Attributes::FanMode::Get(endpointId, &fanMode);

if (fanMode == FanControl::FanModeEnum::kOff) // Off mode implies Speed/Percent setting values are 0. Set fan to HIGH.
{
uint8_t speedMax;
Status status = SpeedMax::Get(mEndpoint, &speedMax);
if (status == Status::Success)
{
status = FanControl::Attributes::SpeedSetting::Set(mEndpoint, speedMax);
if (status == Status::Success)
{
// Atribute change handler sets SpeedCurrent equal to SpeedSetting and updates FanMode.
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::SpeedSetting::Id);
}
else
{
ChipLogError(DeviceLayer, "Error setting SpeedSetting: %d", to_underlying(status));
return status; // Speed is enabled since SpeedMax read was successful. So return failed status.
}
}
else
{
// Not returning error as speed is optional.
ChipLogError(DeviceLayer, "Error getting SpeedMax: %d", to_underlying(status));
}
status = FanControl::Attributes::PercentSetting::Set(mEndpoint, 100);
if (status == Status::Success)
{
// Atribute change handler sets PercentCurrent equal to PercentSetting.
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::PercentSetting::Id);
}
else
{
return status; // Percent is mandatory. So return failed status.
}
}

Status status;

DataModel::Nullable<uint8_t> speedSetting(GetSpeedSetting());

if (!speedSetting.IsNull() && speedSetting.Value())
{
status = FanControl::Attributes::SpeedCurrent::Set(endpointId, speedSetting.Value());
if (status != Status::Success)
{
ChipLogError(DeviceLayer, "Error setting SpeedCurrent: %d", to_underlying(status));
return status;
}
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::SpeedCurrent::Id);
}

DataModel::Nullable<uint8_t> percentSetting(GetPercentSetting());

if (!percentSetting.IsNull() && percentSetting.Value())
{
status = FanControl::Attributes::PercentCurrent::Set(endpointId, percentSetting.Value());
if (status != Status::Success)
{
ChipLogError(DeviceLayer, "Error setting PercentCurrent: %d", to_underlying(status));
return status;
}
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::PercentCurrent::Id);
}

return Status::Success;
}

Protocols::InteractionModel::Status ChefFanControlManager::OffCommand(EndpointId endpointId)
{
ChipLogProgress(DeviceLayer, "ChefFanControlManager::OffCommand");

FanControl::FanModeEnum fanMode;
FanControl::Attributes::FanMode::Get(endpointId, &fanMode);

if (fanMode == FanControl::FanModeEnum::kOff) // Off mode implies Speed/Percent current values are 0.
{
return Status::Success;
}

Status status;

uint8_t speedCurrent;
status = SpeedCurrent::Get(endpointId, &speedCurrent);

if (status == Protocols::InteractionModel::Status::Success && speedCurrent)
{
status = FanControl::Attributes::SpeedCurrent::Set(endpointId, 0);
if (status != Status::Success)
{
ChipLogError(DeviceLayer, "Error setting SpeedCurrent: %d", to_underlying(status));
return status;
}
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::SpeedCurrent::Id);
}

uint8_t percentCurrent;
status = PercentCurrent::Get(endpointId, &percentCurrent);
VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status);

if (percentCurrent)
{
status = FanControl::Attributes::PercentCurrent::Set(endpointId, 0);
if (status != Status::Success)
{
ChipLogError(DeviceLayer, "Error setting PercentCurrent: %d", to_underlying(status));
return status;
}
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::PercentCurrent::Id);
}

return Status::Success;
}

void HandleOnOffAttributeChangeForFan(EndpointId endpointId, bool value)
{
if (value)
mFanControlManager->OnCommand(endpointId);
else
mFanControlManager->OffCommand(endpointId);
}

void HandleFanControlAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value)
{
mFanControlManager->HandleFanControlAttributeChange(attributeId, type, size, value);
5 changes: 5 additions & 0 deletions examples/chef/common/chef-fan-control-manager.h
Original file line number Diff line number Diff line change
@@ -19,4 +19,9 @@

#ifdef MATTER_DM_PLUGIN_FAN_CONTROL_SERVER
void HandleFanControlAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value);

#ifdef MATTER_DM_PLUGIN_ON_OFF_SERVER
void HandleOnOffAttributeChangeForFan(EndpointId endpointId, bool value);
#endif // MATTER_DM_PLUGIN_ON_OFF_SERVER

#endif
5 changes: 5 additions & 0 deletions examples/chef/common/stubs.cpp
Original file line number Diff line number Diff line change
@@ -248,6 +248,11 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath &
{
ChipLogProgress(Zcl, "OnOff attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u", ChipLogValueMEI(attributeId),
type, *value, size);
#ifdef MATTER_DM_PLUGIN_FAN_CONTROL_SERVER // Handle OnOff for fan
#ifdef MATTER_DM_PLUGIN_ON_OFF_SERVER
HandleOnOffAttributeChangeForFan(attributePath.mEndpointId, bool(*value));
#endif // MATTER_DM_PLUGIN_ON_OFF_SERVER
#endif // MATTER_DM_PLUGIN_FAN_CONTROL_SERVER
}
else if (clusterId == LevelControl::Id)
{
85 changes: 85 additions & 0 deletions examples/chef/devices/rootnode_fan_7N2TobIlOX.matter
Original file line number Diff line number Diff line change
@@ -368,6 +368,78 @@ cluster Groups = 4 {
fabric command access(invoke: manage) AddGroupIfIdentifying(AddGroupIfIdentifyingRequest): DefaultSuccess = 5;
}

/** Attributes and commands for switching devices between 'On' and 'Off' states. */
cluster OnOff = 6 {
revision 6;

enum DelayedAllOffEffectVariantEnum : enum8 {
kDelayedOffFastFade = 0;
kNoFade = 1;
kDelayedOffSlowFade = 2;
}

enum DyingLightEffectVariantEnum : enum8 {
kDyingLightFadeOff = 0;
}

enum EffectIdentifierEnum : enum8 {
kDelayedAllOff = 0;
kDyingLight = 1;
}

enum StartUpOnOffEnum : enum8 {
kOff = 0;
kOn = 1;
kToggle = 2;
}

bitmap Feature : bitmap32 {
kLighting = 0x1;
kDeadFrontBehavior = 0x2;
kOffOnly = 0x4;
}

bitmap OnOffControlBitmap : bitmap8 {
kAcceptOnlyWhenOn = 0x1;
}

readonly attribute boolean onOff = 0;
readonly attribute optional boolean globalSceneControl = 16384;
attribute optional int16u onTime = 16385;
attribute optional int16u offWaitTime = 16386;
attribute access(write: manage) optional nullable StartUpOnOffEnum startUpOnOff = 16387;
readonly attribute command_id generatedCommandList[] = 65528;
readonly attribute command_id acceptedCommandList[] = 65529;
readonly attribute event_id eventList[] = 65530;
readonly attribute attrib_id attributeList[] = 65531;
readonly attribute bitmap32 featureMap = 65532;
readonly attribute int16u clusterRevision = 65533;

request struct OffWithEffectRequest {
EffectIdentifierEnum effectIdentifier = 0;
enum8 effectVariant = 1;
}

request struct OnWithTimedOffRequest {
OnOffControlBitmap onOffControl = 0;
int16u onTime = 1;
int16u offWaitTime = 2;
}

/** On receipt of this command, a device SHALL enter its ‘Off’ state. This state is device dependent, but it is recommended that it is used for power off or similar functions. On receipt of the Off command, the OnTime attribute SHALL be set to 0. */
command Off(): DefaultSuccess = 0;
/** On receipt of this command, a device SHALL enter its ‘On’ state. This state is device dependent, but it is recommended that it is used for power on or similar functions. On receipt of the On command, if the value of the OnTime attribute is equal to 0, the device SHALL set the OffWaitTime attribute to 0. */
command On(): DefaultSuccess = 1;
/** On receipt of this command, if a device is in its ‘Off’ state it SHALL enter its ‘On’ state. Otherwise, if it is in its ‘On’ state it SHALL enter its ‘Off’ state. On receipt of the Toggle command, if the value of the OnOff attribute is equal to FALSE and if the value of the OnTime attribute is equal to 0, the device SHALL set the OffWaitTime attribute to 0. If the value of the OnOff attribute is equal to TRUE, the OnTime attribute SHALL be set to 0. */
command Toggle(): DefaultSuccess = 2;
/** The OffWithEffect command allows devices to be turned off using enhanced ways of fading. */
command OffWithEffect(OffWithEffectRequest): DefaultSuccess = 64;
/** The OnWithRecallGlobalScene command allows the recall of the settings when the device was turned off. */
command OnWithRecallGlobalScene(): DefaultSuccess = 65;
/** The OnWithTimedOff command allows devices to be turned on for a specific duration with a guarded off duration so that SHOULD the device be subsequently switched off, further OnWithTimedOff commands, received during this time, are prevented from turning the devices back on. */
command OnWithTimedOff(OnWithTimedOffRequest): DefaultSuccess = 66;
}

/** The Descriptor Cluster is meant to replace the support from the Zigbee Device Object (ZDO) for describing a node, its endpoints and clusters. */
cluster Descriptor = 29 {
revision 2;
@@ -1884,6 +1956,19 @@ endpoint 1 {
handle command AddGroupIfIdentifying;
}

server cluster OnOff {
ram attribute onOff default = 0;
callback attribute generatedCommandList;
callback attribute acceptedCommandList;
callback attribute attributeList;
ram attribute featureMap default = 0;
ram attribute clusterRevision default = 6;

handle command Off;
handle command On;
handle command Toggle;
}

server cluster Descriptor {
callback attribute deviceTypeList;
callback attribute serverList;
Loading
Loading