diff --git a/examples/chef/common/chef-fan-control-manager.cpp b/examples/chef/common/chef-fan-control-manager.cpp index 42555e8052bea0..b1c074102b62d5 100644 --- a/examples/chef/common/chef-fan-control-manager.cpp +++ b/examples/chef/common/chef-fan-control-manager.cpp @@ -48,6 +48,8 @@ class ChefFanControlManager : public Delegate Status HandleStep(StepDirectionEnum aDirection, bool aWrap, bool aLowestOff) override; DataModel::Nullable GetSpeedSetting(); DataModel::Nullable 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 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 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); diff --git a/examples/chef/common/chef-fan-control-manager.h b/examples/chef/common/chef-fan-control-manager.h index 36a90e6a886e92..139660c9029a1f 100644 --- a/examples/chef/common/chef-fan-control-manager.h +++ b/examples/chef/common/chef-fan-control-manager.h @@ -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 diff --git a/examples/chef/common/stubs.cpp b/examples/chef/common/stubs.cpp index 16e9b1b8694c64..6b0b6a4e6dbc28 100644 --- a/examples/chef/common/stubs.cpp +++ b/examples/chef/common/stubs.cpp @@ -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) { diff --git a/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter b/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter index 6ff2e4471f31a8..7897235c8ae891 100644 --- a/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter +++ b/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter @@ -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; diff --git a/examples/chef/devices/rootnode_fan_7N2TobIlOX.zap b/examples/chef/devices/rootnode_fan_7N2TobIlOX.zap index 790dd1d52a5773..2b071aff48fe58 100644 --- a/examples/chef/devices/rootnode_fan_7N2TobIlOX.zap +++ b/examples/chef/devices/rootnode_fan_7N2TobIlOX.zap @@ -2643,6 +2643,138 @@ } ] }, + { + "name": "On/Off", + "code": 6, + "mfgCode": null, + "define": "ON_OFF_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Off", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "On", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Toggle", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "OnOff", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "6", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Descriptor", "code": 29,