blob: d47742570ed2e0c624d0f56646faa7df0c4227d7 [file] [log] [blame]
/**
*
* Copyright (c) 2023 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 <app-common/zap-generated/attributes/Accessors.h>
#include <app/AttributeAccessInterfaceRegistry.h>
#include <app/InteractionModelEngine.h>
#include <app/clusters/microwave-oven-control-server/microwave-oven-control-server.h>
#include <app/clusters/mode-base-server/mode-base-server.h>
#include <app/reporting/reporting.h>
#include <app/util/attribute-storage.h>
#include <app/util/ember-compatibility-functions.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::OperationalState;
using namespace chip::app::Clusters::MicrowaveOvenControl;
using namespace chip::app::Clusters::ModeBase;
using namespace chip::app::Clusters::MicrowaveOvenMode;
using namespace chip::app::Clusters::MicrowaveOvenControl::Attributes;
using Status = Protocols::InteractionModel::Status;
namespace chip {
namespace app {
namespace Clusters {
namespace MicrowaveOvenControl {
Instance::Instance(Delegate * aDelegate, EndpointId aEndpointId, ClusterId aClusterId,
BitMask<MicrowaveOvenControl::Feature> aFeature, Clusters::OperationalState::Instance & aOpStateInstance,
Clusters::ModeBase::Instance & aMicrowaveOvenModeInstance) :
CommandHandlerInterface(MakeOptional(aEndpointId), aClusterId),
AttributeAccessInterface(MakeOptional(aEndpointId), aClusterId), mDelegate(aDelegate), mEndpointId(aEndpointId),
mClusterId(aClusterId), mFeature(aFeature), mOpStateInstance(aOpStateInstance),
mMicrowaveOvenModeInstance(aMicrowaveOvenModeInstance)
{
mDelegate->SetInstance(this);
}
Instance::~Instance()
{
InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this);
unregisterAttributeAccessOverride(this);
}
CHIP_ERROR Instance::Init()
{
// Check if the cluster has been selected in zap
VerifyOrReturnError(
emberAfContainsServer(mEndpointId, mClusterId), CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(Zcl, "Microwave Oven Control: The cluster with ID %lu was not enabled in zap.", long(mClusterId)));
// Exactly one of the PowerAsNumber and PowerInWatts features must be supported, per spec.
VerifyOrReturnError(
mFeature.Has(MicrowaveOvenControl::Feature::kPowerAsNumber) || mFeature.Has(MicrowaveOvenControl::Feature::kPowerInWatts),
CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(Zcl,
"Microwave Oven Control: feature bits error, feature must support one of PowerInWatts and PowerAsNumber"));
// Check that the feature bits do not include both PowerAsNumber and PowerInWatts
VerifyOrReturnError(
!(mFeature.Has(MicrowaveOvenControl::Feature::kPowerAsNumber) &&
mFeature.Has(MicrowaveOvenControl::Feature::kPowerInWatts)),
CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(Zcl,
"Microwave Oven Control: feature bits error, feature could not support both PowerAsNumber and PowerInWatts"));
// Per spec, the PowerNumberLimits feature is only allowed if the PowerAsNumber feature is supported.
VerifyOrReturnError(
!mFeature.Has(MicrowaveOvenControl::Feature::kPowerNumberLimits) ||
mFeature.Has(MicrowaveOvenControl::Feature::kPowerAsNumber),
CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(
Zcl,
"Microwave Oven Control: feature bits error, if feature supports PowerNumberLimits it must support PowerAsNumber"));
ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->RegisterCommandHandler(this));
VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE);
// If the PowerInWatts feature is supported, get the count of supported watt levels so we can later
// ensure incoming watt level values are valid.
if (HasFeature(MicrowaveOvenControl::Feature::kPowerInWatts))
{
mSupportedWattLevels = GetCountOfSupportedWattLevels();
VerifyOrReturnError(mSupportedWattLevels > 0, CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(Zcl, "Microwave Oven Control: supported watt levels is empty"));
}
return CHIP_NO_ERROR;
}
bool Instance::HasFeature(MicrowaveOvenControl::Feature feature) const
{
return mFeature.Has(feature);
}
uint8_t Instance::GetCountOfSupportedWattLevels() const
{
uint8_t wattIndex = 0;
uint16_t watt = 0;
while (mDelegate->GetWattSettingByIndex(wattIndex, watt) == CHIP_NO_ERROR)
{
wattIndex++;
}
return wattIndex;
}
uint32_t Instance::GetCookTimeSec() const
{
return mCookTimeSec;
}
void Instance::SetCookTimeSec(uint32_t cookTimeSec)
{
uint32_t oldCookTimeSec = mCookTimeSec;
mCookTimeSec = cookTimeSec;
if (mCookTimeSec != oldCookTimeSec)
{
MatterReportingAttributeChangeCallback(mEndpointId, mClusterId, Attributes::CookTime::Id);
}
}
CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
{
ChipLogError(Zcl, "Microwave Oven Control: Reading");
switch (aPath.mAttributeId)
{
case MicrowaveOvenControl::Attributes::CookTime::Id:
return aEncoder.Encode(GetCookTimeSec());
case MicrowaveOvenControl::Attributes::MaxCookTime::Id:
return aEncoder.Encode(mDelegate->GetMaxCookTimeSec());
case MicrowaveOvenControl::Attributes::PowerSetting::Id:
VerifyOrReturnError(HasFeature(MicrowaveOvenControl::Feature::kPowerAsNumber), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Microwave Oven Control: can not get PowerSetting number, feature is not supported"));
return aEncoder.Encode(mDelegate->GetPowerSettingNum());
case MicrowaveOvenControl::Attributes::MinPower::Id:
VerifyOrReturnError(HasFeature(MicrowaveOvenControl::Feature::kPowerAsNumber), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Microwave Oven Control: can not get MinPower number, feature is not supported"));
return aEncoder.Encode(HasFeature(MicrowaveOvenControl::Feature::kPowerNumberLimits) ? mDelegate->GetMinPowerNum()
: kDefaultMinPowerNum);
case MicrowaveOvenControl::Attributes::MaxPower::Id:
VerifyOrReturnError(HasFeature(MicrowaveOvenControl::Feature::kPowerAsNumber), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Microwave Oven Control: can not get MaxPower number, feature is not supported"));
return aEncoder.Encode(HasFeature(MicrowaveOvenControl::Feature::kPowerNumberLimits) ? mDelegate->GetMaxPowerNum()
: kDefaultMaxPowerNum);
case MicrowaveOvenControl::Attributes::PowerStep::Id:
VerifyOrReturnError(HasFeature(MicrowaveOvenControl::Feature::kPowerAsNumber), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Microwave Oven Control: can not get PowerStep number, feature is not supported"));
return aEncoder.Encode(HasFeature(MicrowaveOvenControl::Feature::kPowerNumberLimits) ? mDelegate->GetPowerStepNum()
: kDefaultPowerStepNum);
case MicrowaveOvenControl::Attributes::SupportedWatts::Id:
VerifyOrReturnError(HasFeature(MicrowaveOvenControl::Feature::kPowerInWatts), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Microwave Oven Control: can not get SuppoertWatts list, feature is not supported"));
return aEncoder.EncodeList([delegate = mDelegate](const auto & encoder) -> CHIP_ERROR {
uint16_t wattRating;
uint8_t index = 0;
CHIP_ERROR err = CHIP_NO_ERROR;
while ((err = delegate->GetWattSettingByIndex(index, wattRating)) == CHIP_NO_ERROR)
{
ReturnErrorOnFailure(encoder.Encode(wattRating));
index++;
}
if (err == CHIP_ERROR_NOT_FOUND)
{
return CHIP_NO_ERROR;
}
return err;
});
case MicrowaveOvenControl::Attributes::SelectedWattIndex::Id:
VerifyOrReturnError(
HasFeature(MicrowaveOvenControl::Feature::kPowerInWatts), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Microwave Oven Control: can not get SelectedWattIndex number, feature is not supported"));
return aEncoder.Encode(mDelegate->GetCurrentWattIndex());
case MicrowaveOvenControl::Attributes::WattRating::Id:
return aEncoder.Encode(mDelegate->GetWattRating());
case MicrowaveOvenControl::Attributes::FeatureMap::Id:
return aEncoder.Encode(mFeature.Raw());
}
return CHIP_NO_ERROR;
}
void Instance::InvokeCommand(HandlerContext & handlerContext)
{
ChipLogDetail(Zcl, "Microwave Oven Control: InvokeCommand");
switch (handlerContext.mRequestPath.mCommandId)
{
case Commands::SetCookingParameters::Id:
ChipLogDetail(Zcl, "Microwave Oven Control: Entering SetCookingParameters");
CommandHandlerInterface::HandleCommand<Commands::SetCookingParameters::DecodableType>(
handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleSetCookingParameters(ctx, req); });
break;
case Commands::AddMoreTime::Id:
ChipLogDetail(Zcl, "Microwave Oven Control: Entering AddMoreTime");
CommandHandlerInterface::HandleCommand<Commands::AddMoreTime::DecodableType>(
handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleAddMoreTime(ctx, req); });
break;
}
}
void Instance::HandleSetCookingParameters(HandlerContext & ctx, const Commands::SetCookingParameters::DecodableType & req)
{
ChipLogDetail(Zcl, "Microwave Oven Control: HandleSetCookingParameters");
Status status = Status::Success;
uint8_t opState;
uint8_t modeValue;
uint8_t reqCookMode;
uint32_t reqCookTimeSec;
bool reqStartAfterSetting;
auto & cookMode = req.cookMode;
auto & cookTimeSec = req.cookTime;
auto & powerSetting = req.powerSetting;
auto & wattSettingIndex = req.wattSettingIndex;
auto & startAfterSetting = req.startAfterSetting;
opState = mOpStateInstance.GetCurrentOperationalState();
VerifyOrExit(opState == to_underlying(OperationalStateEnum::kStopped), status = Status::InvalidInState);
if (startAfterSetting.HasValue())
{
VerifyOrExit(
ServerClusterCommandExists(
ConcreteCommandPath(mEndpointId, OperationalState::Id, OperationalState::Commands::Start::Id)) == Status::Success,
status = Status::InvalidCommand;
ChipLogError(
Zcl,
"Microwave Oven Control: Failed to set cooking parameters, Start command of operational state is not supported"));
}
reqStartAfterSetting = startAfterSetting.ValueOr(false);
modeValue = 0;
VerifyOrExit(mMicrowaveOvenModeInstance.GetModeValueByModeTag(to_underlying(MicrowaveOvenMode::ModeTag::kNormal), modeValue) ==
CHIP_NO_ERROR,
status = Status::InvalidCommand;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set cookMode, Normal mode is not found"));
reqCookMode = cookMode.ValueOr(modeValue);
VerifyOrExit(mMicrowaveOvenModeInstance.IsSupportedMode(reqCookMode), status = Status::ConstraintError;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set cookMode, cookMode is not supported"));
reqCookTimeSec = cookTimeSec.ValueOr(MicrowaveOvenControl::kDefaultCookTimeSec);
VerifyOrExit(IsCookTimeSecondsInRange(reqCookTimeSec, mDelegate->GetMaxCookTimeSec()), status = Status::ConstraintError;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set cookTime, cookTime value is out of range"));
if (HasFeature(MicrowaveOvenControl::Feature::kPowerAsNumber))
{
// if using power as number, check if the param is invalid and set PowerSetting number.
uint8_t reqPowerSettingNum;
uint8_t maxPowerNum = kDefaultMaxPowerNum;
uint8_t minPowerNum = kDefaultMinPowerNum;
uint8_t powerStepNum = kDefaultPowerStepNum;
VerifyOrExit(!wattSettingIndex.HasValue(), status = Status::InvalidCommand; ChipLogError(
Zcl, "Microwave Oven Control: Failed to set cooking parameters, should have no value for wattSettingIndex"));
VerifyOrExit(
cookMode.HasValue() || cookTimeSec.HasValue() || powerSetting.HasValue(), status = Status::InvalidCommand;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set cooking parameters, all command fields are missing "));
if (HasFeature(MicrowaveOvenControl::Feature::kPowerNumberLimits))
{
maxPowerNum = mDelegate->GetMaxPowerNum();
minPowerNum = mDelegate->GetMinPowerNum();
powerStepNum = mDelegate->GetPowerStepNum();
}
reqPowerSettingNum = powerSetting.ValueOr(maxPowerNum);
VerifyOrExit(IsPowerSettingNumberInRange(reqPowerSettingNum, minPowerNum, maxPowerNum), status = Status::ConstraintError;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set PowerSetting, PowerSetting value is out of range"));
VerifyOrExit(
(reqPowerSettingNum - minPowerNum) % powerStepNum == 0, status = Status::ConstraintError; ChipLogError(
Zcl,
"Microwave Oven Control: Failed to set PowerSetting, PowerSetting value must be multiple of PowerStep number"));
status = mDelegate->HandleSetCookingParametersCallback(reqCookMode, reqCookTimeSec, reqStartAfterSetting,
MakeOptional(reqPowerSettingNum), NullOptional);
}
else
{
// if using power in watt, check if the param is invalid and set wattSettingIndex number.
uint8_t reqWattSettingIndex;
VerifyOrExit(!powerSetting.HasValue(), status = Status::InvalidCommand; ChipLogError(
Zcl, "Microwave Oven Control: Failed to set cooking parameters, should have no value for powerSetting "));
VerifyOrExit(
cookMode.HasValue() || cookTimeSec.HasValue() || wattSettingIndex.HasValue(), status = Status::InvalidCommand;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set cooking parameters, all command fields are missing "));
// count of supported watt levels must greater than 0
VerifyOrExit(
mSupportedWattLevels > 0,
ChipLogError(Zcl, "Microwave Oven Control: Failed to set wattSettingIndex, count of supported watt levels is 0"));
uint8_t maxWattSettingIndex = static_cast<uint8_t>(mSupportedWattLevels - 1);
reqWattSettingIndex = wattSettingIndex.ValueOr(maxWattSettingIndex);
VerifyOrExit(reqWattSettingIndex <= maxWattSettingIndex, status = Status::ConstraintError;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set wattSettingIndex, wattSettingIndex is out of range"));
status = mDelegate->HandleSetCookingParametersCallback(reqCookMode, reqCookTimeSec, reqStartAfterSetting, NullOptional,
MakeOptional(reqWattSettingIndex));
}
exit:
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status);
}
void Instance::HandleAddMoreTime(HandlerContext & ctx, const Commands::AddMoreTime::DecodableType & req)
{
ChipLogDetail(Zcl, "Microwave Oven Control: HandleAddMoreTime");
Status status;
uint8_t opState;
uint32_t finalCookTimeSec;
opState = mOpStateInstance.GetCurrentOperationalState();
VerifyOrExit(opState != to_underlying(OperationalStateEnum::kError), status = Status::InvalidInState);
// if the added cooking time is greater than the max cooking time, the cooking time stay unchanged.
VerifyOrExit(req.timeToAdd + GetCookTimeSec() <= mDelegate->GetMaxCookTimeSec(), status = Status::ConstraintError;
ChipLogError(Zcl, "Microwave Oven Control: Failed to set cookTime, cookTime value is out of range"));
finalCookTimeSec = GetCookTimeSec() + req.timeToAdd;
status = mDelegate->HandleModifyCookTimeSecondsCallback(finalCookTimeSec);
exit:
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status);
}
bool IsCookTimeSecondsInRange(uint32_t cookTimeSec, uint32_t maxCookTimeSec)
{
return MicrowaveOvenControl::kMinCookTimeSec <= cookTimeSec && cookTimeSec <= maxCookTimeSec;
}
bool IsPowerSettingNumberInRange(uint8_t powerSettingNum, uint8_t minCookPowerNum, uint8_t maxCookPowerNum)
{
return minCookPowerNum <= powerSettingNum && powerSettingNum <= maxCookPowerNum;
}
} // namespace MicrowaveOvenControl
} // namespace Clusters
} // namespace app
} // namespace chip
/** @brief Microwave Oven Control Cluster Server Init
*
* Server Init
*
*/
void MatterMicrowaveOvenControlPluginServerInitCallback() {}