Skip to content

Commit a579c8d

Browse files
authored
Support On/Off controls to fan device (project-chip#37814)
* Add on-off command handlers for fan control * Disallow step when fan is Off * Add On/Off cluster to fan device * Fix compilation bugs * Fix compilation bugs * Fix compilation bugs * Handle onOff event in MatterPostAttributeChangeCallback since emberAFCommandHandler callbacks are already defined * Fix compilation * Fix compilation * Fix compilation * Fix unreachable code. * Remove hard checks on on-off to prevent tests from breaking * On command should set Fan to HIGH * Set default OnOff state to Off to match default fanMode
1 parent 52213a8 commit a579c8d

5 files changed

+356
-0
lines changed

examples/chef/common/chef-fan-control-manager.cpp

+129
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class ChefFanControlManager : public Delegate
4848
Status HandleStep(StepDirectionEnum aDirection, bool aWrap, bool aLowestOff) override;
4949
DataModel::Nullable<uint8_t> GetSpeedSetting();
5050
DataModel::Nullable<Percent> GetPercentSetting();
51+
Protocols::InteractionModel::Status OnCommand(EndpointId endpointId);
52+
Protocols::InteractionModel::Status OffCommand(EndpointId endpointId);
5153

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

334+
Protocols::InteractionModel::Status ChefFanControlManager::OnCommand(EndpointId endpointId)
335+
{
336+
ChipLogProgress(DeviceLayer, "ChefFanControlManager::OnCommand");
337+
338+
FanControl::FanModeEnum fanMode;
339+
FanControl::Attributes::FanMode::Get(endpointId, &fanMode);
340+
341+
if (fanMode == FanControl::FanModeEnum::kOff) // Off mode implies Speed/Percent setting values are 0. Set fan to HIGH.
342+
{
343+
uint8_t speedMax;
344+
Status status = SpeedMax::Get(mEndpoint, &speedMax);
345+
if (status == Status::Success)
346+
{
347+
status = FanControl::Attributes::SpeedSetting::Set(mEndpoint, speedMax);
348+
if (status == Status::Success)
349+
{
350+
// Atribute change handler sets SpeedCurrent equal to SpeedSetting and updates FanMode.
351+
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::SpeedSetting::Id);
352+
}
353+
else
354+
{
355+
ChipLogError(DeviceLayer, "Error setting SpeedSetting: %d", to_underlying(status));
356+
return status; // Speed is enabled since SpeedMax read was successful. So return failed status.
357+
}
358+
}
359+
else
360+
{
361+
// Not returning error as speed is optional.
362+
ChipLogError(DeviceLayer, "Error getting SpeedMax: %d", to_underlying(status));
363+
}
364+
status = FanControl::Attributes::PercentSetting::Set(mEndpoint, 100);
365+
if (status == Status::Success)
366+
{
367+
// Atribute change handler sets PercentCurrent equal to PercentSetting.
368+
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::PercentSetting::Id);
369+
}
370+
else
371+
{
372+
return status; // Percent is mandatory. So return failed status.
373+
}
374+
}
375+
376+
Status status;
377+
378+
DataModel::Nullable<uint8_t> speedSetting(GetSpeedSetting());
379+
380+
if (!speedSetting.IsNull() && speedSetting.Value())
381+
{
382+
status = FanControl::Attributes::SpeedCurrent::Set(endpointId, speedSetting.Value());
383+
if (status != Status::Success)
384+
{
385+
ChipLogError(DeviceLayer, "Error setting SpeedCurrent: %d", to_underlying(status));
386+
return status;
387+
}
388+
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::SpeedCurrent::Id);
389+
}
390+
391+
DataModel::Nullable<uint8_t> percentSetting(GetPercentSetting());
392+
393+
if (!percentSetting.IsNull() && percentSetting.Value())
394+
{
395+
status = FanControl::Attributes::PercentCurrent::Set(endpointId, percentSetting.Value());
396+
if (status != Status::Success)
397+
{
398+
ChipLogError(DeviceLayer, "Error setting PercentCurrent: %d", to_underlying(status));
399+
return status;
400+
}
401+
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::PercentCurrent::Id);
402+
}
403+
404+
return Status::Success;
405+
}
406+
407+
Protocols::InteractionModel::Status ChefFanControlManager::OffCommand(EndpointId endpointId)
408+
{
409+
ChipLogProgress(DeviceLayer, "ChefFanControlManager::OffCommand");
410+
411+
FanControl::FanModeEnum fanMode;
412+
FanControl::Attributes::FanMode::Get(endpointId, &fanMode);
413+
414+
if (fanMode == FanControl::FanModeEnum::kOff) // Off mode implies Speed/Percent current values are 0.
415+
{
416+
return Status::Success;
417+
}
418+
419+
Status status;
420+
421+
uint8_t speedCurrent;
422+
status = SpeedCurrent::Get(endpointId, &speedCurrent);
423+
424+
if (status == Protocols::InteractionModel::Status::Success && speedCurrent)
425+
{
426+
status = FanControl::Attributes::SpeedCurrent::Set(endpointId, 0);
427+
if (status != Status::Success)
428+
{
429+
ChipLogError(DeviceLayer, "Error setting SpeedCurrent: %d", to_underlying(status));
430+
return status;
431+
}
432+
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::SpeedCurrent::Id);
433+
}
434+
435+
uint8_t percentCurrent;
436+
status = PercentCurrent::Get(endpointId, &percentCurrent);
437+
VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status);
438+
439+
if (percentCurrent)
440+
{
441+
status = FanControl::Attributes::PercentCurrent::Set(endpointId, 0);
442+
if (status != Status::Success)
443+
{
444+
ChipLogError(DeviceLayer, "Error setting PercentCurrent: %d", to_underlying(status));
445+
return status;
446+
}
447+
MatterReportingAttributeChangeCallback(endpointId, FanControl::Id, FanControl::Attributes::PercentCurrent::Id);
448+
}
449+
450+
return Status::Success;
451+
}
452+
453+
void HandleOnOffAttributeChangeForFan(EndpointId endpointId, bool value)
454+
{
455+
if (value)
456+
mFanControlManager->OnCommand(endpointId);
457+
else
458+
mFanControlManager->OffCommand(endpointId);
459+
}
460+
332461
void HandleFanControlAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value)
333462
{
334463
mFanControlManager->HandleFanControlAttributeChange(attributeId, type, size, value);

examples/chef/common/chef-fan-control-manager.h

+5
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@
1919

2020
#ifdef MATTER_DM_PLUGIN_FAN_CONTROL_SERVER
2121
void HandleFanControlAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value);
22+
23+
#ifdef MATTER_DM_PLUGIN_ON_OFF_SERVER
24+
void HandleOnOffAttributeChangeForFan(EndpointId endpointId, bool value);
25+
#endif // MATTER_DM_PLUGIN_ON_OFF_SERVER
26+
2227
#endif

examples/chef/common/stubs.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,11 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath &
252252
{
253253
ChipLogProgress(Zcl, "OnOff attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u", ChipLogValueMEI(attributeId),
254254
type, *value, size);
255+
#ifdef MATTER_DM_PLUGIN_FAN_CONTROL_SERVER // Handle OnOff for fan
256+
#ifdef MATTER_DM_PLUGIN_ON_OFF_SERVER
257+
HandleOnOffAttributeChangeForFan(attributePath.mEndpointId, bool(*value));
258+
#endif // MATTER_DM_PLUGIN_ON_OFF_SERVER
259+
#endif // MATTER_DM_PLUGIN_FAN_CONTROL_SERVER
255260
}
256261
else if (clusterId == LevelControl::Id)
257262
{

examples/chef/devices/rootnode_fan_7N2TobIlOX.matter

+85
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,78 @@ cluster Groups = 4 {
368368
fabric command access(invoke: manage) AddGroupIfIdentifying(AddGroupIfIdentifyingRequest): DefaultSuccess = 5;
369369
}
370370

371+
/** Attributes and commands for switching devices between 'On' and 'Off' states. */
372+
cluster OnOff = 6 {
373+
revision 6;
374+
375+
enum DelayedAllOffEffectVariantEnum : enum8 {
376+
kDelayedOffFastFade = 0;
377+
kNoFade = 1;
378+
kDelayedOffSlowFade = 2;
379+
}
380+
381+
enum DyingLightEffectVariantEnum : enum8 {
382+
kDyingLightFadeOff = 0;
383+
}
384+
385+
enum EffectIdentifierEnum : enum8 {
386+
kDelayedAllOff = 0;
387+
kDyingLight = 1;
388+
}
389+
390+
enum StartUpOnOffEnum : enum8 {
391+
kOff = 0;
392+
kOn = 1;
393+
kToggle = 2;
394+
}
395+
396+
bitmap Feature : bitmap32 {
397+
kLighting = 0x1;
398+
kDeadFrontBehavior = 0x2;
399+
kOffOnly = 0x4;
400+
}
401+
402+
bitmap OnOffControlBitmap : bitmap8 {
403+
kAcceptOnlyWhenOn = 0x1;
404+
}
405+
406+
readonly attribute boolean onOff = 0;
407+
readonly attribute optional boolean globalSceneControl = 16384;
408+
attribute optional int16u onTime = 16385;
409+
attribute optional int16u offWaitTime = 16386;
410+
attribute access(write: manage) optional nullable StartUpOnOffEnum startUpOnOff = 16387;
411+
readonly attribute command_id generatedCommandList[] = 65528;
412+
readonly attribute command_id acceptedCommandList[] = 65529;
413+
readonly attribute event_id eventList[] = 65530;
414+
readonly attribute attrib_id attributeList[] = 65531;
415+
readonly attribute bitmap32 featureMap = 65532;
416+
readonly attribute int16u clusterRevision = 65533;
417+
418+
request struct OffWithEffectRequest {
419+
EffectIdentifierEnum effectIdentifier = 0;
420+
enum8 effectVariant = 1;
421+
}
422+
423+
request struct OnWithTimedOffRequest {
424+
OnOffControlBitmap onOffControl = 0;
425+
int16u onTime = 1;
426+
int16u offWaitTime = 2;
427+
}
428+
429+
/** 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. */
430+
command Off(): DefaultSuccess = 0;
431+
/** 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. */
432+
command On(): DefaultSuccess = 1;
433+
/** 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. */
434+
command Toggle(): DefaultSuccess = 2;
435+
/** The OffWithEffect command allows devices to be turned off using enhanced ways of fading. */
436+
command OffWithEffect(OffWithEffectRequest): DefaultSuccess = 64;
437+
/** The OnWithRecallGlobalScene command allows the recall of the settings when the device was turned off. */
438+
command OnWithRecallGlobalScene(): DefaultSuccess = 65;
439+
/** 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. */
440+
command OnWithTimedOff(OnWithTimedOffRequest): DefaultSuccess = 66;
441+
}
442+
371443
/** The Descriptor Cluster is meant to replace the support from the Zigbee Device Object (ZDO) for describing a node, its endpoints and clusters. */
372444
cluster Descriptor = 29 {
373445
revision 2;
@@ -1884,6 +1956,19 @@ endpoint 1 {
18841956
handle command AddGroupIfIdentifying;
18851957
}
18861958

1959+
server cluster OnOff {
1960+
ram attribute onOff default = 0;
1961+
callback attribute generatedCommandList;
1962+
callback attribute acceptedCommandList;
1963+
callback attribute attributeList;
1964+
ram attribute featureMap default = 0;
1965+
ram attribute clusterRevision default = 6;
1966+
1967+
handle command Off;
1968+
handle command On;
1969+
handle command Toggle;
1970+
}
1971+
18871972
server cluster Descriptor {
18881973
callback attribute deviceTypeList;
18891974
callback attribute serverList;

0 commit comments

Comments
 (0)