blob: 39367412bf8e8eb8dca2a0aa8b08c57f396a2c91 [file]
/**
*
* Copyright (c) 2020-2026 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 "WindowCoveringCluster.h"
#include <app/data-model-provider/MetadataTypes.h>
#include <app/persistence/AttributePersistence.h>
#include <app/server-cluster/AttributeListBuilder.h>
#include <clusters/WindowCovering/Metadata.h>
#include <lib/support/TypeTraits.h>
using namespace chip;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::WindowCovering;
using chip::Protocols::InteractionModel::Status;
namespace {
bool IsPercent100thsValid(Percent100ths percent100ths)
{
return (percent100ths >= kWcPercent100thsMinOpen) && (percent100ths <= kWcPercent100thsMaxClosed);
}
} // namespace
namespace chip {
namespace app {
namespace Clusters {
namespace WindowCovering {
WindowCoveringCluster::WindowCoveringCluster(EndpointId endpointId, const Config & config) :
DefaultServerCluster(ConcreteClusterPath(endpointId, WindowCovering::Id)), mDelegate(config.mDelegate),
mFeatureMap(config.mFeatures), mOptionalAttributes(config.mOptionalAttributes), mType(config.mType),
mEndProductType(config.mEndProductType)
{
// Lift or Tilt must be enabled.
VerifyOrDieWithMsg(mFeatureMap.Has(Feature::kLift) || mFeatureMap.Has(Feature::kTilt), AppServer,
"Validation failed: Neither Lift nor Tilt is enabled.");
if (mFeatureMap.Has(Feature::kPositionAwareLift))
{
VerifyOrDieWithMsg(mFeatureMap.Has(Feature::kLift), AppServer,
"Validation failed: PositionAwareLift requires Lift feature.");
}
if (mFeatureMap.Has(Feature::kPositionAwareTilt))
{
VerifyOrDieWithMsg(mFeatureMap.Has(Feature::kTilt), AppServer,
"Validation failed: PositionAwareTilt requires Tilt feature.");
}
}
CHIP_ERROR WindowCoveringCluster::Startup(ServerClusterContext & context)
{
ReturnErrorOnFailure(DefaultServerCluster::Startup(context));
AttributePersistence attributePersistence(context.attributeStorage);
attributePersistence.LoadNativeEndianValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::NumberOfActuationsLift::Id),
mNumberOfActuationsLift, mNumberOfActuationsLift);
attributePersistence.LoadNativeEndianValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::NumberOfActuationsTilt::Id),
mNumberOfActuationsTilt, mNumberOfActuationsTilt);
attributePersistence.LoadNativeEndianValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::CurrentPositionLiftPercent100ths::Id),
mCurrentPositionLiftPercent100ths, mCurrentPositionLiftPercent100ths);
attributePersistence.LoadNativeEndianValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::CurrentPositionTiltPercent100ths::Id),
mCurrentPositionTiltPercent100ths, mCurrentPositionTiltPercent100ths);
uint8_t rawMode = mMode.Raw();
attributePersistence.LoadNativeEndianValue(ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::Mode::Id),
rawMode, rawMode);
mMode = chip::BitMask<Mode>(rawMode);
uint8_t rawConfigStatus = mConfigStatus.Raw();
attributePersistence.LoadNativeEndianValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::ConfigStatus::Id), rawConfigStatus,
rawConfigStatus);
mConfigStatus = chip::BitMask<ConfigStatus>(rawConfigStatus);
return CHIP_NO_ERROR;
}
void WindowCoveringCluster::SetNumberOfActuationsLift(uint16_t numOfLifts)
{
VerifyOrReturn(SetAttributeValue(mNumberOfActuationsLift, numOfLifts, Attributes::NumberOfActuationsLift::Id));
VerifyOrReturn(mContext != nullptr);
LogErrorOnFailure(mContext->attributeStorage.WriteValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::NumberOfActuationsLift::Id),
ByteSpan(reinterpret_cast<const uint8_t *>(&mNumberOfActuationsLift), sizeof(mNumberOfActuationsLift))));
}
void WindowCoveringCluster::SetNumberOfActuationsTilt(uint16_t numOfTilts)
{
VerifyOrReturn(SetAttributeValue(mNumberOfActuationsTilt, numOfTilts, Attributes::NumberOfActuationsTilt::Id));
VerifyOrReturn(mContext != nullptr);
LogErrorOnFailure(mContext->attributeStorage.WriteValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::NumberOfActuationsTilt::Id),
ByteSpan(reinterpret_cast<const uint8_t *>(&mNumberOfActuationsTilt), sizeof(mNumberOfActuationsTilt))));
}
void WindowCoveringCluster::SetConfigStatus(chip::BitMask<ConfigStatus> status)
{
VerifyOrReturn(SetAttributeValue(mConfigStatus, status, Attributes::ConfigStatus::Id));
VerifyOrReturn(mContext != nullptr);
uint8_t rawConfigStatus = mConfigStatus.Raw();
LogErrorOnFailure(mContext->attributeStorage.WriteValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::ConfigStatus::Id),
ByteSpan(reinterpret_cast<const uint8_t *>(&rawConfigStatus), sizeof(rawConfigStatus))));
}
NPercent WindowCoveringCluster::PercentFromPercent100ths(NPercent100ths percent100ths)
{
if (percent100ths.IsNull())
{
return NPercent();
}
return NPercent(static_cast<Percent>(percent100ths.Value() / 100));
}
void WindowCoveringCluster::SetOperationalStatus(chip::BitMask<OperationalStatus> newStatus)
{
SetAttributeValue(mOperationalStatus, newStatus, Attributes::OperationalStatus::Id);
}
void WindowCoveringCluster::SetTargetPositionLiftPercent100ths(NPercent100ths newTargetLift)
{
VerifyOrReturn(
SetAttributeValue(mTargetPositionLiftPercent100ths, newTargetLift, Attributes::TargetPositionLiftPercent100ths::Id));
OperationalState opLift = OperationalState::Stall;
if (!mTargetPositionLiftPercent100ths.IsNull() && !mCurrentPositionLiftPercent100ths.IsNull() &&
mCurrentPositionLiftPercent100ths.Value() != mTargetPositionLiftPercent100ths.Value())
{
opLift = (mCurrentPositionLiftPercent100ths.Value() < mTargetPositionLiftPercent100ths.Value())
? OperationalState::MovingDownOrClose
: OperationalState::MovingUpOrOpen;
}
UpdateOperationalStateForField(OperationalStatus::kLift, opLift);
}
void WindowCoveringCluster::SetTargetPositionTiltPercent100ths(NPercent100ths newTargetTilt)
{
VerifyOrReturn(
SetAttributeValue(mTargetPositionTiltPercent100ths, newTargetTilt, Attributes::TargetPositionTiltPercent100ths::Id));
OperationalState opTilt = OperationalState::Stall;
if (!mTargetPositionTiltPercent100ths.IsNull() && !mCurrentPositionTiltPercent100ths.IsNull() &&
mCurrentPositionTiltPercent100ths.Value() != mTargetPositionTiltPercent100ths.Value())
{
opTilt = (mCurrentPositionTiltPercent100ths.Value() < mTargetPositionTiltPercent100ths.Value())
? OperationalState::MovingDownOrClose
: OperationalState::MovingUpOrOpen;
}
UpdateOperationalStateForField(OperationalStatus::kTilt, opTilt);
}
void WindowCoveringCluster::SetEndProductType(EndProductType type)
{
SetAttributeValue(mEndProductType, type, Attributes::EndProductType::Id);
}
void WindowCoveringCluster::SetCurrentPositionLiftPercent100ths(NPercent100ths curLiftPercent100ths)
{
NPercent previousPercentage = PercentFromPercent100ths(mCurrentPositionLiftPercent100ths);
VerifyOrReturn(SetAttributeValue(mCurrentPositionLiftPercent100ths, curLiftPercent100ths,
Attributes::CurrentPositionLiftPercent100ths::Id));
if (mOptionalAttributes.IsSet(Attributes::CurrentPositionLiftPercentage::Id) &&
(PercentFromPercent100ths(mCurrentPositionLiftPercent100ths) != previousPercentage))
{
NotifyAttributeChanged(Attributes::CurrentPositionLiftPercentage::Id);
}
VerifyOrReturn(mContext != nullptr);
NumericAttributeTraits<Percent100ths>::StorageType storageValue;
DataModel::NullableToStorage(curLiftPercent100ths, storageValue);
LogErrorOnFailure(mContext->attributeStorage.WriteValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::CurrentPositionLiftPercent100ths::Id),
ByteSpan(reinterpret_cast<const uint8_t *>(&storageValue), sizeof(storageValue))));
OperationalState opLift = static_cast<OperationalState>(mOperationalStatus.GetField(OperationalStatus::kLift));
if ((OperationalState::Stall != opLift) && (mCurrentPositionLiftPercent100ths == mTargetPositionLiftPercent100ths))
{
UpdateOperationalStateForField(OperationalStatus::kLift, OperationalState::Stall);
}
}
void WindowCoveringCluster::SetCurrentPositionTiltPercent100ths(NPercent100ths curTiltPercent100ths)
{
NPercent previousPercentage = PercentFromPercent100ths(mCurrentPositionTiltPercent100ths);
VerifyOrReturn(SetAttributeValue(mCurrentPositionTiltPercent100ths, curTiltPercent100ths,
Attributes::CurrentPositionTiltPercent100ths::Id));
if (mOptionalAttributes.IsSet(Attributes::CurrentPositionTiltPercentage::Id) &&
(PercentFromPercent100ths(mCurrentPositionTiltPercent100ths) != previousPercentage))
{
NotifyAttributeChanged(Attributes::CurrentPositionTiltPercentage::Id);
}
VerifyOrReturn(mContext != nullptr);
NumericAttributeTraits<Percent100ths>::StorageType storageValue;
DataModel::NullableToStorage(curTiltPercent100ths, storageValue);
LogErrorOnFailure(mContext->attributeStorage.WriteValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::CurrentPositionTiltPercent100ths::Id),
ByteSpan(reinterpret_cast<const uint8_t *>(&storageValue), sizeof(storageValue))));
OperationalState opTilt = static_cast<OperationalState>(mOperationalStatus.GetField(OperationalStatus::kTilt));
if ((OperationalState::Stall != opTilt) && (mCurrentPositionTiltPercent100ths == mTargetPositionTiltPercent100ths))
{
UpdateOperationalStateForField(OperationalStatus::kTilt, OperationalState::Stall);
}
}
void WindowCoveringCluster::SetMode(chip::BitMask<Mode> mode)
{
if (mode.HasAll(Mode::kMaintenanceMode, Mode::kCalibrationMode))
{
mode.Clear(Mode::kCalibrationMode);
}
VerifyOrReturn(SetAttributeValue(mMode, mode, Attributes::Mode::Id));
if (mContext != nullptr)
{
uint8_t rawMode = mMode.Raw();
LogErrorOnFailure(mContext->attributeStorage.WriteValue(
ConcreteAttributePath(mPath.mEndpointId, WindowCovering::Id, Attributes::Mode::Id),
ByteSpan(reinterpret_cast<const uint8_t *>(&rawMode), sizeof(rawMode))));
}
chip::BitMask<ConfigStatus> newStatus = mConfigStatus;
newStatus.Set(ConfigStatus::kOperational, !mMode.HasAny(Mode::kMaintenanceMode, Mode::kCalibrationMode));
newStatus.Set(ConfigStatus::kLiftMovementReversed, mMode.Has(Mode::kMotorDirectionReversed));
SetConfigStatus(newStatus);
}
void WindowCoveringCluster::SetSafetyStatus(chip::BitMask<SafetyStatus> status)
{
SetAttributeValue(mSafetyStatus, status, Attributes::SafetyStatus::Id);
}
DataModel::ActionReturnStatus WindowCoveringCluster::ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder)
{
switch (request.path.mAttributeId)
{
case Attributes::ClusterRevision::Id:
return encoder.Encode(WindowCovering::kRevision);
case Attributes::FeatureMap::Id:
return encoder.Encode(GetFeatureMap().Raw());
case Attributes::Type::Id:
return encoder.Encode(GetType());
case Attributes::NumberOfActuationsLift::Id:
return encoder.Encode(GetNumberOfActuationsLift());
case Attributes::NumberOfActuationsTilt::Id:
return encoder.Encode(GetNumberOfActuationsTilt());
case Attributes::ConfigStatus::Id:
return encoder.Encode(GetConfigStatus().Raw());
case Attributes::CurrentPositionLiftPercentage::Id:
return encoder.Encode(PercentFromPercent100ths(mCurrentPositionLiftPercent100ths));
case Attributes::CurrentPositionTiltPercentage::Id:
return encoder.Encode(PercentFromPercent100ths(mCurrentPositionTiltPercent100ths));
case Attributes::OperationalStatus::Id:
return encoder.Encode(GetOperationalStatus().Raw());
case Attributes::TargetPositionLiftPercent100ths::Id:
return encoder.Encode(GetTargetPositionLiftPercent100ths());
case Attributes::TargetPositionTiltPercent100ths::Id:
return encoder.Encode(GetTargetPositionTiltPercent100ths());
case Attributes::EndProductType::Id:
return encoder.Encode(GetEndProductType());
case Attributes::CurrentPositionLiftPercent100ths::Id:
return encoder.Encode(GetCurrentPositionLiftPercent100ths());
case Attributes::CurrentPositionTiltPercent100ths::Id:
return encoder.Encode(GetCurrentPositionTiltPercent100ths());
case Attributes::Mode::Id:
return encoder.Encode(GetMode().Raw());
case Attributes::SafetyStatus::Id:
return encoder.Encode(GetSafetyStatus().Raw());
default:
return Status::UnsupportedAttribute;
}
}
DataModel::ActionReturnStatus WindowCoveringCluster::WriteAttribute(const DataModel::WriteAttributeRequest & request,
AttributeValueDecoder & decoder)
{
switch (request.path.mAttributeId)
{
case Attributes::Mode::Id: {
chip::BitMask<Mode> mode;
ReturnErrorOnFailure(decoder.Decode(mode));
VerifyOrReturnValue(mode.Raw() <= 0x0F, Status::ConstraintError);
// TODO: Spec 5.3.6.14.2: "In a write interaction, setting this bit to 0, while the device
// Disabled because Test_TC_WNCV_2_3 Step 2d writes Mode=0x00 to exit calibration,
// which contradicts the spec. Needs test update
// VerifyOrReturnValue(!mMode.Has(Mode::kCalibrationMode) || mode.Has(Mode::kCalibrationMode), Status::Failure);
SetMode(mode);
return Status::Success;
}
default:
return Status::UnsupportedWrite;
}
}
CHIP_ERROR WindowCoveringCluster::Attributes(const ConcreteClusterPath & path,
ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
{
AttributeListBuilder listBuilder(builder);
const AttributeListBuilder::OptionalAttributeEntry optionalAttributes[] = {
{ mOptionalAttributes.IsSet(Attributes::NumberOfActuationsLift::Id), Attributes::NumberOfActuationsLift::kMetadataEntry },
{ mOptionalAttributes.IsSet(Attributes::NumberOfActuationsTilt::Id), Attributes::NumberOfActuationsTilt::kMetadataEntry },
{ mOptionalAttributes.IsSet(Attributes::CurrentPositionLiftPercentage::Id),
Attributes::CurrentPositionLiftPercentage::kMetadataEntry },
{ GetFeatureMap().Has(Feature::kPositionAwareLift), Attributes::TargetPositionLiftPercent100ths::kMetadataEntry },
{ GetFeatureMap().Has(Feature::kPositionAwareLift), Attributes::CurrentPositionLiftPercent100ths::kMetadataEntry },
{ mOptionalAttributes.IsSet(Attributes::CurrentPositionTiltPercentage::Id),
Attributes::CurrentPositionTiltPercentage::kMetadataEntry },
{ GetFeatureMap().Has(Feature::kPositionAwareTilt), Attributes::TargetPositionTiltPercent100ths::kMetadataEntry },
{ GetFeatureMap().Has(Feature::kPositionAwareTilt), Attributes::CurrentPositionTiltPercent100ths::kMetadataEntry },
{ mOptionalAttributes.IsSet(Attributes::SafetyStatus::Id), Attributes::SafetyStatus::kMetadataEntry },
};
return listBuilder.Append(Span(Attributes::kMandatoryMetadata), Span(optionalAttributes));
}
CHIP_ERROR WindowCoveringCluster::AcceptedCommands(const ConcreteClusterPath & path,
ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
{
static constexpr DataModel::AcceptedCommandEntry kMandatoryCommands[] = {
Commands::UpOrOpen::kMetadataEntry,
Commands::DownOrClose::kMetadataEntry,
Commands::StopMotion::kMetadataEntry,
};
static constexpr DataModel::AcceptedCommandEntry kGoToLiftPercentageCommand[] = {
Commands::GoToLiftPercentage::kMetadataEntry,
};
static constexpr DataModel::AcceptedCommandEntry kGoToTiltPercentageCommand[] = {
Commands::GoToTiltPercentage::kMetadataEntry,
};
if (GetFeatureMap().Has(Feature::kLift))
{
ReturnErrorOnFailure(builder.ReferenceExisting(kGoToLiftPercentageCommand));
}
if (GetFeatureMap().Has(Feature::kTilt))
{
ReturnErrorOnFailure(builder.ReferenceExisting(kGoToTiltPercentageCommand));
}
ReturnErrorOnFailure(builder.ReferenceExisting(kMandatoryCommands));
return CHIP_NO_ERROR;
}
std::optional<DataModel::ActionReturnStatus> WindowCoveringCluster::InvokeCommand(const DataModel::InvokeRequest & request,
chip::TLV::TLVReader & input_arguments,
CommandHandler * handler)
{
VerifyOrReturnValue(handler != nullptr, Status::Failure);
switch (request.path.mCommandId)
{
case Commands::UpOrOpen::Id:
return HandleUpOrOpen();
case Commands::DownOrClose::Id:
return HandleDownOrClose();
case Commands::StopMotion::Id: {
Commands::StopMotion::DecodableType commandData;
ReturnErrorOnFailure(commandData.Decode(input_arguments));
return HandleStopMotion(commandData);
}
case Commands::GoToLiftPercentage::Id: {
Commands::GoToLiftPercentage::DecodableType commandData;
ReturnErrorOnFailure(commandData.Decode(input_arguments));
return HandleGoToLiftPercentage(commandData);
}
case Commands::GoToTiltPercentage::Id: {
Commands::GoToTiltPercentage::DecodableType commandData;
ReturnErrorOnFailure(commandData.Decode(input_arguments));
return HandleGoToTiltPercentage(commandData);
}
default:
return Status::UnsupportedCommand;
}
}
void WindowCoveringCluster::UpdateOperationalStateForField(chip::BitMask<OperationalStatus> field, OperationalState state)
{
if ((OperationalStatus::kLift == field) || (OperationalStatus::kTilt == field))
{
chip::BitMask<OperationalStatus> status = mOperationalStatus;
status.SetField(field, static_cast<uint8_t>(state));
chip::BitMask<OperationalStatus> opGlobal =
status.HasAny(OperationalStatus::kLift) ? OperationalStatus::kLift : OperationalStatus::kTilt;
status.SetField(OperationalStatus::kGlobal, status.GetField(opGlobal));
SetOperationalStatus(status);
}
}
std::optional<DataModel::ActionReturnStatus> WindowCoveringCluster::GetMotionLockStatus() const
{
// Is the device locked?
if (!mConfigStatus.Has(ConfigStatus::kOperational))
{
if (mMode.Has(Mode::kMaintenanceMode))
{
// Maintenance Mode
return Status::Busy;
}
if (mMode.Has(Mode::kCalibrationMode))
{
// Calibration Mode
return Status::Failure;
}
}
return Status::Success;
}
std::optional<DataModel::ActionReturnStatus> WindowCoveringCluster::HandleUpOrOpen()
{
ChipLogProgress(Zcl, "UpOrOpen command received");
std::optional<DataModel::ActionReturnStatus> lockStatus = GetMotionLockStatus();
VerifyOrReturnValue(lockStatus == Status::Success, lockStatus, ChipLogProgress(Zcl, "Err device locked"));
if (GetFeatureMap().Has(Feature::kPositionAwareLift))
{
SetTargetPositionLiftPercent100ths(NPercent100ths(kWcPercent100thsMinOpen));
}
if (GetFeatureMap().Has(Feature::kPositionAwareTilt))
{
SetTargetPositionTiltPercent100ths(NPercent100ths(kWcPercent100thsMinOpen));
}
if (GetFeatureMap().Has(Feature::kLift))
{
LogErrorOnFailure(mDelegate.HandleMovement(WindowCoveringType::Lift));
}
if (GetFeatureMap().Has(Feature::kTilt))
{
LogErrorOnFailure(mDelegate.HandleMovement(WindowCoveringType::Tilt));
}
return Status::Success;
}
std::optional<DataModel::ActionReturnStatus> WindowCoveringCluster::HandleDownOrClose()
{
ChipLogProgress(Zcl, "DownOrClose command received");
std::optional<DataModel::ActionReturnStatus> lockStatus = GetMotionLockStatus();
VerifyOrReturnValue(lockStatus == Status::Success, lockStatus, ChipLogProgress(Zcl, "Err device locked"));
if (GetFeatureMap().Has(Feature::kPositionAwareLift))
{
SetTargetPositionLiftPercent100ths(NPercent100ths(kWcPercent100thsMaxClosed));
}
if (GetFeatureMap().Has(Feature::kPositionAwareTilt))
{
SetTargetPositionTiltPercent100ths(NPercent100ths(kWcPercent100thsMaxClosed));
}
if (GetFeatureMap().Has(Feature::kLift))
{
LogErrorOnFailure(mDelegate.HandleMovement(WindowCoveringType::Lift));
}
if (GetFeatureMap().Has(Feature::kTilt))
{
LogErrorOnFailure(mDelegate.HandleMovement(WindowCoveringType::Tilt));
}
return Status::Success;
}
std::optional<DataModel::ActionReturnStatus>
WindowCoveringCluster::HandleStopMotion(const Commands::StopMotion::DecodableType & fields)
{
ChipLogProgress(Zcl, "StopMotion command received");
std::optional<DataModel::ActionReturnStatus> lockStatus = GetMotionLockStatus();
VerifyOrReturnValue(lockStatus == Status::Success, lockStatus, ChipLogProgress(Zcl, "Err device locked"));
bool changeTarget = true;
CHIP_ERROR err = mDelegate.HandleStopMotion();
if (err == CHIP_ERROR_IN_PROGRESS)
{
changeTarget = false;
}
else
{
LogErrorOnFailure(err);
}
if (changeTarget)
{
if (GetFeatureMap().Has(Feature::kPositionAwareLift))
{
SetTargetPositionLiftPercent100ths(GetCurrentPositionLiftPercent100ths());
}
if (GetFeatureMap().Has(Feature::kPositionAwareTilt))
{
SetTargetPositionTiltPercent100ths(GetCurrentPositionTiltPercent100ths());
}
}
return Status::Success;
}
std::optional<DataModel::ActionReturnStatus>
WindowCoveringCluster::HandleGoToLiftPercentage(const Commands::GoToLiftPercentage::DecodableType & fields)
{
const Percent100ths percent100ths = fields.liftPercent100thsValue;
ChipLogProgress(Zcl, "GoToLiftPercentage %u command received", percent100ths);
std::optional<DataModel::ActionReturnStatus> lockStatus = GetMotionLockStatus();
VerifyOrReturnValue(lockStatus == Status::Success, lockStatus, ChipLogProgress(Zcl, "Err device locked"));
VerifyOrReturnValue(GetFeatureMap().Has(Feature::kLift), Status::UnsupportedCommand,
ChipLogProgress(Zcl, "Err Device is tilt-only"));
if (!GetFeatureMap().Has(Feature::kPositionAwareLift))
{
return (percent100ths == 0) ? HandleUpOrOpen() : HandleDownOrClose();
}
VerifyOrReturnValue(IsPercent100thsValid(percent100ths), Status::ConstraintError);
SetTargetPositionLiftPercent100ths(NPercent100ths(percent100ths));
LogErrorOnFailure(mDelegate.HandleMovement(WindowCoveringType::Lift));
return Status::Success;
}
std::optional<DataModel::ActionReturnStatus>
WindowCoveringCluster::HandleGoToTiltPercentage(const Commands::GoToTiltPercentage::DecodableType & fields)
{
const Percent100ths percent100ths = fields.tiltPercent100thsValue;
ChipLogProgress(Zcl, "GoToTiltPercentage %u command received", percent100ths);
std::optional<DataModel::ActionReturnStatus> lockStatus = GetMotionLockStatus();
VerifyOrReturnValue(lockStatus == Status::Success, lockStatus, ChipLogProgress(Zcl, "Err device locked"));
VerifyOrReturnValue(GetFeatureMap().Has(Feature::kTilt), Status::UnsupportedCommand,
ChipLogProgress(Zcl, "Err Device is lift-only"));
if (!GetFeatureMap().Has(Feature::kPositionAwareTilt))
{
return (percent100ths == 0) ? HandleUpOrOpen() : HandleDownOrClose();
}
VerifyOrReturnValue(IsPercent100thsValid(percent100ths), Status::ConstraintError);
SetTargetPositionTiltPercent100ths(NPercent100ths(percent100ths));
LogErrorOnFailure(mDelegate.HandleMovement(WindowCoveringType::Tilt));
return Status::Success;
}
} // namespace WindowCovering
} // namespace Clusters
} // namespace app
} // namespace chip