diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 4ac888ca3d3838..8b8c4f01d689da 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -456,6 +456,12 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/ArlEncoder.cpp", "${_app_root}/clusters/${cluster}/ArlEncoder.h", ] + } else if (cluster == "closure-control-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/${cluster}.h", + "${_app_root}/clusters/${cluster}/closure-control-cluster-objects.h", + ] } else { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ] } diff --git a/src/app/clusters/closure-control-server/closure-control-cluster-objects.h b/src/app/clusters/closure-control-server/closure-control-cluster-objects.h new file mode 100644 index 00000000000000..4405cd505b8858 --- /dev/null +++ b/src/app/clusters/closure-control-server/closure-control-cluster-objects.h @@ -0,0 +1,102 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ClosureControl { + +/** + * Structure represents the overall state of a closure control cluster derivation instance. + */ +struct GenericOverallState : public Structs::OverallStateStruct::Type +{ + GenericOverallState(Optional positioningValue = NullOptional, + Optional latchingValue = NullOptional, + Optional speedValue = NullOptional, + Optional extraInfoValue = NullOptional) + { + Set(positioningValue, latchingValue, speedValue, extraInfoValue); + } + + GenericOverallState(const GenericOverallState & overallState) { *this = overallState; } + + GenericOverallState & operator=(const GenericOverallState & overallState) + { + Set(overallState.positioning, overallState.latching, overallState.speed, overallState.extraInfo); + return *this; + } + + void Set(Optional positioningValue = NullOptional, Optional latchingValue = NullOptional, + Optional speedValue = NullOptional, Optional extraInfoValue = NullOptional) + { + positioning = positioningValue; + latching = latchingValue; + speed = speedValue; + extraInfo = extraInfoValue; + } + + bool operator==(const Structs::OverallStateStruct::Type & rhs) const + { + return positioning == rhs.positioning && latching == rhs.latching && speed == rhs.speed && extraInfo == rhs.extraInfo; + } +}; + +/** + * Structure represents the overall target state of a closure control cluster derivation instance. + */ +struct GenericOverallTarget : public Structs::OverallTargetStruct::Type +{ + GenericOverallTarget(Optional tagPositionValue = NullOptional, + Optional tagLatchValue = NullOptional, + Optional speedValue = NullOptional) + { + Set(tagPositionValue, tagLatchValue, speedValue); + } + + GenericOverallTarget(const GenericOverallTarget & overallTarget) { *this = overallTarget; } + + GenericOverallTarget & operator=(const GenericOverallTarget & overallTarget) + { + Set(overallTarget.tagPosition, overallTarget.tagLatch, overallTarget.speed); + return *this; + } + + void Set(Optional tagPositionValue = NullOptional, Optional tagLatchValue = NullOptional, + Optional speedValue = NullOptional) + { + tagPosition = tagPositionValue; + tagLatch = tagLatchValue; + speed = speedValue; + } + + bool operator==(const Structs::OverallTargetStruct::Type & rhs) const + { + return tagPosition == rhs.tagPosition && tagLatch == rhs.tagLatch && speed == rhs.speed; + } +}; + +} // namespace ClosureControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/closure-control-server/closure-control-server.cpp b/src/app/clusters/closure-control-server/closure-control-server.cpp new file mode 100644 index 00000000000000..9e4290378a7634 --- /dev/null +++ b/src/app/clusters/closure-control-server/closure-control-server.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2025 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "closure-control-server.h" + +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::ClosureControl; +using namespace chip::app::Clusters::ClosureControl::Attributes; +using namespace chip::app::Clusters::ClosureControl::Commands; +using chip::Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace ClosureControl { + +CHIP_ERROR Instance::Init() +{ + ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(this)); + VerifyOrReturnError(AttributeAccessInterfaceRegistry::Instance().Register(this), CHIP_ERROR_INCORRECT_STATE); + + return CHIP_NO_ERROR; +} + +void Instance::Shutdown() +{ + CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this); + AttributeAccessInterfaceRegistry::Instance().Unregister(this); +} + +bool Instance::HasFeature(Feature aFeatures) const +{ + return mFeatures.Has(aFeatures); +} + +bool Instance::SupportsOptAttr(OptionalAttribute aOptionalAttrs) const +{ + return mOptionalAttrs.Has(aOptionalAttrs); +} + +bool Instance::IsSupportedState(MainStateEnum aMainState) +{ + switch (aMainState) + { + case MainStateEnum::kCalibrating: + return HasFeature(Feature::kCalibration); + case MainStateEnum::kProtected: + return HasFeature(Feature::kProtection); + case MainStateEnum::kDisengaged: + return HasFeature(Feature::kManuallyOperable); + case MainStateEnum::kPendingFallback: + return HasFeature(Feature::kFallback); + default: + // Remaining MainState have Mandatory conformance,so will be supported. + return true; + } + return true; +} + +CHIP_ERROR Instance::SetMainState(MainStateEnum aMainState) +{ + if (!IsSupportedState(aMainState)) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + // If the Main State has changed, trigger the attribute change callback + if (mMainState != aMainState) + { + mMainState = aMainState; + MatterReportingAttributeChangeCallback(mDelegate.GetEndpointId(), ClosureControl::Id, Attributes::MainState::Id); + UpdateCountdownTimeFromClusterLogic(); + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR Instance::SetOverallState(const GenericOverallState & aOverallState) +{ + // If the overall state has changed, trigger the attribute change callback + if (!(mOverallState == aOverallState)) + { + mOverallState = aOverallState; + MatterReportingAttributeChangeCallback(mDelegate.GetEndpointId(), ClosureControl::Id, Attributes::OverallState::Id); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Instance::SetOverallTarget(const GenericOverallTarget & aOverallTarget) +{ + // If the overall target has changed, trigger the attribute change callback + if (!(mOverallTarget == aOverallTarget)) + { + mOverallTarget = aOverallTarget; + MatterReportingAttributeChangeCallback(mDelegate.GetEndpointId(), ClosureControl::Id, Attributes::OverallTarget::Id); + } + + return CHIP_NO_ERROR; +} + +MainStateEnum Instance::GetMainState() const +{ + return mMainState; +} + +const GenericOverallState & Instance::GetOverallState() const +{ + return mOverallState; +} + +const GenericOverallTarget & Instance::GetOverallTarget() const +{ + return mOverallTarget; +} + +void Instance::UpdateCountdownTime(bool fromDelegate) +{ + app::DataModel::Nullable newCountdownTime = mDelegate.GetCountdownTime(); + auto now = System::SystemClock().GetMonotonicTimestamp(); + + bool markDirty = false; + + if (fromDelegate) + { + // Updates from delegate are reduce-reported to every 1s max (choice of this implementation), in addition + // to default change-from-null, change-from-zero and increment policy. + System::Clock::Milliseconds64 reportInterval = System::Clock::Milliseconds64(1000); + auto predicate = mCountdownTime.GetPredicateForSufficientTimeSinceLastDirty(reportInterval); + markDirty = (mCountdownTime.SetValue(newCountdownTime, now, predicate) == AttributeDirtyState::kMustReport); + } + else + { + auto predicate = [](const decltype(mCountdownTime)::SufficientChangePredicateCandidate &) -> bool { return true; }; + markDirty = (mCountdownTime.SetValue(newCountdownTime, now, predicate) == AttributeDirtyState::kMustReport); + } + + if (markDirty) + { + MatterReportingAttributeChangeCallback(mDelegate.GetEndpointId(), ClosureControl::Id, Attributes::CountdownTime::Id); + } +} + +// AttributeAccessInterface +CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == ClosureControl::Id); + + switch (aPath.mAttributeId) + { + case CountdownTime::Id: + // Optional Attribute + if (SupportsOptAttr(OptionalAttribute::kCountdownTime)) + { + return aEncoder.Encode(mDelegate.GetCountdownTime()); + } + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + + case MainState::Id: + return aEncoder.Encode(GetMainState()); + + case CurrentErrorList::Id: + return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR { return this->EncodeCurrentErrorList(encoder); }); + + case OverallState::Id: + return aEncoder.Encode(GetOverallState()); + + case OverallTarget::Id: + return aEncoder.Encode(GetOverallTarget()); + + /* FeatureMap - is held locally */ + case FeatureMap::Id: + return aEncoder.Encode(mFeatures); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Instance::EncodeCurrentErrorList(const AttributeValueEncoder::ListEncodeHelper & encoder) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + ReturnErrorOnFailure(mDelegate.StartCurrentErrorListRead()); + for (size_t i = 0; true; i++) + { + ClosureErrorEnum error; + + err = mDelegate.GetCurrentErrorListAtIndex(i, error); + // Convert end of list to CHIP_NO_ERROR + VerifyOrExit(err != CHIP_ERROR_PROVIDER_LIST_EXHAUSTED, err = CHIP_NO_ERROR); + + // Check if another error occurred before trying to encode + SuccessOrExit(err); + + err = encoder.Encode(error); + SuccessOrExit(err); + } + +exit: + // Tell the delegate the read is complete + ReturnErrorOnFailure(mDelegate.EndCurrentErrorListRead()); + return err; +} + +CHIP_ERROR Instance::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) +{ + VerifyOrDie(aPath.mClusterId == ClosureControl::Id); + + switch (aPath.mAttributeId) + { + case CountdownTime::Id: + if (SupportsOptAttr(OptionalAttribute::kCountdownTime)) + { + return CHIP_IM_GLOBAL_STATUS(UnsupportedWrite); + } + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + case MainState::Id: + case CurrentErrorList::Id: + case OverallState::Id: + case OverallTarget::Id: + return CHIP_IM_GLOBAL_STATUS(UnsupportedWrite); + default: + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } +} + +// CommandHandlerInterface +void Instance::InvokeCommand(HandlerContext & handlerContext) +{ + using namespace Commands; + + switch (handlerContext.mRequestPath.mCommandId) + { + case Stop::Id: + if (!HasFeature(Feature::kInstantaneous)) + { + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleStop(ctx, commandData); }); + } + else + { + handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, Status::UnsupportedCommand); + } + break; + case MoveTo::Id: + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleMoveTo(ctx, commandData); }); + break; + case Calibrate::Id: + if (HasFeature(Feature::kCalibration)) + { + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleCalibrate(ctx, commandData); }); + } + else + { + handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, Status::UnsupportedCommand); + } + break; + } +} + +void Instance::HandleStop(HandlerContext & ctx, const Commands::Stop::DecodableType & commandData) +{ + Status status = mDelegate.Stop(); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); +} + +void Instance::HandleMoveTo(HandlerContext & ctx, const Commands::MoveTo::DecodableType & commandData) +{ + Status status = mDelegate.MoveTo(commandData.tag, commandData.latch, commandData.speed); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); +} + +void Instance::HandleCalibrate(HandlerContext & ctx, const Commands::Calibrate::DecodableType & commandData) +{ + Status status = mDelegate.Calibrate(); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); +} + +} // namespace ClosureControl +} // namespace Clusters +} // namespace app +} // namespace chip + +// ----------------------------------------------------------------------------- +// Plugin initialization + +void MatterClosureControlPluginServerInitCallback() {} diff --git a/src/app/clusters/closure-control-server/closure-control-server.h b/src/app/clusters/closure-control-server/closure-control-server.h new file mode 100644 index 00000000000000..f5324251f802fc --- /dev/null +++ b/src/app/clusters/closure-control-server/closure-control-server.h @@ -0,0 +1,209 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "closure-control-cluster-objects.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ClosureControl { + +/** + * @brief Defines methods for implementing application-specific logic for the Closure Control Cluster. + */ +class Delegate +{ +public: + virtual ~Delegate() = default; + + // Only Cluster Instance should be calling SetEdpointId. + void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } + EndpointId GetEndpointId() { return mEndpointId; } + + // ------------------------------------------------------------------ + // Commands + virtual Protocols::InteractionModel::Status Stop() = 0; + virtual Protocols::InteractionModel::Status MoveTo(const Optional & tag, const Optional & latch, + const Optional & speed) = 0; + virtual Protocols::InteractionModel::Status Calibrate() = 0; + + // ------------------------------------------------------------------ + // Get attribute methods + virtual DataModel::Nullable GetCountdownTime() = 0; + + /* These functions are called by the ReadAttribute handler to iterate through lists + * The cluster server will call StartRead to allow the delegate to create a temporary + * lock on the data. + * The delegate is expected to not change these values once StartRead has been called + * until the EndRead() has been called (i.e. releasing a lock on the data) + */ + virtual CHIP_ERROR StartCurrentErrorListRead() = 0; + // The delegate is expected to return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED to indicate end of list. + virtual CHIP_ERROR GetCurrentErrorListAtIndex(size_t, ClosureErrorEnum &) = 0; + virtual CHIP_ERROR EndCurrentErrorListRead() = 0; + +protected: + EndpointId mEndpointId = chip::kInvalidEndpointId; +}; + +enum class OptionalAttribute : uint32_t +{ + kCountdownTime = 0x1 +}; + +class Instance : public AttributeAccessInterface, public CommandHandlerInterface +{ +public: + /** + * @brief Creates a closure control cluster instance. The Init() function needs to be called for + * this instance to be registered and called by the interaction model at the appropriate times. + * @param[in] aEndpointId The endpoint on which this cluster exists. + * @param[in] aDelegate The Delegate used by this Instance. + * @param[in] aFeatures The bitmask value that identifies which features are supported by this instance. + * @param[in] aOptionalAttrs The bitmask Value that identifies which optional attributes are supported by this instance. + */ + Instance(EndpointId aEndpointId, Delegate & aDelegate, BitMask aFeatures, BitMask aOptionalAttrs) : + AttributeAccessInterface(MakeOptional(aEndpointId), ClosureControl::Id), + CommandHandlerInterface(MakeOptional(aEndpointId), ClosureControl::Id), mDelegate(aDelegate), mFeatures(aFeatures), + mOptionalAttrs(aOptionalAttrs) + { + /* set the base class delegates endpointId */ + mDelegate.SetEndpointId(aEndpointId); + /* set Countdown Time quiet reporting policy as per reporting requirements in specification */ + mCountdownTime.policy() + .Set(QuieterReportingPolicyEnum::kMarkDirtyOnIncrement) + .Set(QuieterReportingPolicyEnum::kMarkDirtyOnChangeToFromZero); + } + ~Instance() { Shutdown(); } + + CHIP_ERROR Init(); + void Shutdown(); + + bool HasFeature(Feature aFeatures) const; + bool SupportsOptAttr(OptionalAttribute aOptionalAttrs) const; + + // Attribute setters + /** + * @brief Set Main State. + * @param[in] aMainState The Main state that should now be the current one. + * @return CHIP_NO_ERROR if set was successful. + */ + CHIP_ERROR SetMainState(MainStateEnum aMainState); + + /** + * @brief Set OverallState. + * @param[in] aOverallState The OverallState that should now be the current State. + * @return CHIP_NO_ERROR if set was successful. + */ + CHIP_ERROR SetOverallState(const GenericOverallState & aOverallState); + + /** + * @brief Set OverallTarget. + * @param[in] aOverallTarget The OverallTarget that should now be the Overall Target. + * @return CHIP_NO_ERROR if set was successful. + */ + CHIP_ERROR SetOverallTarget(const GenericOverallTarget & aOverallTarget); + + // Attribute getters + /** + * @brief Get MainState. + * @return Current MainState. + */ + MainStateEnum GetMainState() const; + + /** + * @brief Get OverallState. + * @return Current OverallState. + */ + const GenericOverallState & GetOverallState() const; + + /** + * @brief Get OverallTarget. + * @return Current OverallTarget. + */ + const GenericOverallTarget & GetOverallTarget() const; + + /** + * @brief Whenever application wants to trigger an update to report a new updated time, + * call this method. The `GetCountdownTime()` method will be called on the application. + */ + inline void UpdateCountdownTimeFromDelegate() { UpdateCountdownTime(/* fromDelegate = */ true); } + + /** + * @brief This function checks if Main State is supported or not based on features supported. + * @param[in] aMainState MainState + * @return true if State is supported, false if State is not supported + */ + bool IsSupportedState(MainStateEnum aMainState); + +protected: + /** + * @brief Causes reporting/udpating of CountdownTime attribute from driver if sufficient changes have + * occurred (based on Q quality definition for operational state). Calls the Delegate::GetCountdownTime() method. + * + * @param[in] fromDelegate true if the change notice was triggered by the delegate, false if internal to cluster logic. + */ + void UpdateCountdownTime(bool fromDelegate); + + /** + * @brief Whenever cluster logic wants to trigger an update to report a new updated time, + * call this method. + */ + inline void UpdateCountdownTimeFromClusterLogic() { UpdateCountdownTime(/* fromDelegate=*/false); } + +private: + Delegate & mDelegate; + BitMask mFeatures; + BitMask mOptionalAttrs; + + app::QuieterReportingAttribute mCountdownTime{ DataModel::NullNullable }; + MainStateEnum mMainState; + GenericOverallState mOverallState; + GenericOverallTarget mOverallTarget; + + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; + + CHIP_ERROR EncodeCurrentErrorList(const AttributeValueEncoder::ListEncodeHelper & aEncoder); + + // CommandHandlerInterface + void InvokeCommand(HandlerContext & handlerContext) override; + + void HandleStop(HandlerContext & ctx, const Commands::Stop::DecodableType & commandData); + void HandleMoveTo(HandlerContext & ctx, const Commands::MoveTo::DecodableType & commandData); + void HandleCalibrate(HandlerContext & ctx, const Commands::Calibrate::DecodableType & commandData); +}; + +} // namespace ClosureControl +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index 53e4362e9a6192..c7d41ef505859a 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -55,6 +55,7 @@ CommandHandlerInterfaceOnlyClusters: - Wi-Fi Network Diagnostics - Administrator Commissioning - Actions + - Closure Control # We need a more configurable way of deciding which clusters have which init functions.... # See https://github.com/project-chip/connectedhomeip/issues/4369 diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index bf9ad0e418d3fe..23ecde30a04c19 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -177,7 +177,7 @@ ], "CHANNEL_CLUSTER": ["channel-server"], "CHIME_CLUSTER": ["chime-server"], - "CLOSURE_CONTROL_CLUSTER": [], + "CLOSURE_CONTROL_CLUSTER": ["closure-control-server"], "COLOR_CONTROL_CLUSTER": ["color-control-server"], "COMMISSIONER_CONTROL_CLUSTER": ["commissioner-control-server"], "COMMISSIONING_CLUSTER": [], diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 819cda45d5ac75..9fa94b0c282a0e 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -6367,36 +6367,6 @@ bool emberAfWindowCoveringClusterGoToTiltValueCallback( bool emberAfWindowCoveringClusterGoToTiltPercentageCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::WindowCovering::Commands::GoToTiltPercentage::DecodableType & commandData); -/** - * @brief Closure Control Cluster Stop Command callback (from client) - */ -bool emberAfClosureControlClusterStopCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ClosureControl::Commands::Stop::DecodableType & commandData); -/** - * @brief Closure Control Cluster MoveTo Command callback (from client) - */ -bool emberAfClosureControlClusterMoveToCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ClosureControl::Commands::MoveTo::DecodableType & commandData); -/** - * @brief Closure Control Cluster Calibrate Command callback (from client) - */ -bool emberAfClosureControlClusterCalibrateCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ClosureControl::Commands::Calibrate::DecodableType & commandData); -/** - * @brief Closure Control Cluster ConfigureFallback Command callback (from client) - */ -bool emberAfClosureControlClusterConfigureFallbackCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ClosureControl::Commands::ConfigureFallback::DecodableType & commandData); -/** - * @brief Closure Control Cluster CancelFallback Command callback (from client) - */ -bool emberAfClosureControlClusterCancelFallbackCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ClosureControl::Commands::CancelFallback::DecodableType & commandData); /** * @brief Thermostat Cluster SetpointRaiseLower Command callback (from client) */