blob: 18ffe5a3dde27b1a5646e34e382389bfa976e677 [file]
/*
* 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/ConcreteAttributePath.h>
#include <app/InteractionModelEngine.h>
#include <app/clusters/energy-evse-server/EnergyEvseCluster.h>
#include <app/server-cluster/AttributeListBuilder.h>
#include <clusters/EnergyEvse/Metadata.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::DataModel;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::EnergyEvse;
using namespace chip::app::Clusters::EnergyEvse::Attributes;
using chip::Protocols::InteractionModel::Status;
namespace chip {
namespace app {
namespace Clusters {
namespace EnergyEvse {
CHIP_ERROR EnergyEvseCluster::Startup(ServerClusterContext & context)
{
if (mDelegate.GetEndpointId() != mPath.mEndpointId)
{
ChipLogError(Zcl, "EVSE: EndpointId mismatch - delegate has %d, cluster has %d", mDelegate.GetEndpointId(),
mPath.mEndpointId);
return CHIP_ERROR_INVALID_ARGUMENT;
}
return DefaultServerCluster::Startup(context);
}
CHIP_ERROR EnergyEvseCluster::SetState(StateEnum newValue)
{
VerifyOrReturnError(mState != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue < StateEnum::kUnknownEnumValue, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mState = newValue;
mDelegate.OnStateChanged(newValue);
NotifyAttributeChanged(State::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetSupplyState(SupplyStateEnum newValue)
{
VerifyOrReturnError(mSupplyState != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue < SupplyStateEnum::kUnknownEnumValue, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mSupplyState = newValue;
mDelegate.OnSupplyStateChanged(newValue);
NotifyAttributeChanged(SupplyState::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetFaultState(FaultStateEnum newValue)
{
VerifyOrReturnError(mFaultState != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue < FaultStateEnum::kUnknownEnumValue, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mFaultState = newValue;
mDelegate.OnFaultStateChanged(newValue);
NotifyAttributeChanged(FaultState::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetChargingEnabledUntil(DataModel::Nullable<uint32_t> newValue)
{
VerifyOrReturnError(mChargingEnabledUntil != newValue, CHIP_NO_ERROR);
mChargingEnabledUntil = newValue;
mDelegate.OnChargingEnabledUntilChanged(newValue);
NotifyAttributeChanged(ChargingEnabledUntil::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetDischargingEnabledUntil(DataModel::Nullable<uint32_t> newValue)
{
VerifyOrReturnError(mDischargingEnabledUntil != newValue, CHIP_NO_ERROR);
mDischargingEnabledUntil = newValue;
mDelegate.OnDischargingEnabledUntilChanged(newValue);
NotifyAttributeChanged(DischargingEnabledUntil::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetCircuitCapacity(int64_t newValue)
{
VerifyOrReturnError(mCircuitCapacity != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue >= 0, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mCircuitCapacity = newValue;
mDelegate.OnCircuitCapacityChanged(newValue);
NotifyAttributeChanged(CircuitCapacity::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetMinimumChargeCurrent(int64_t newValue)
{
VerifyOrReturnError(mMinimumChargeCurrent != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue >= 0, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mMinimumChargeCurrent = newValue;
mDelegate.OnMinimumChargeCurrentChanged(newValue);
NotifyAttributeChanged(MinimumChargeCurrent::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetMaximumChargeCurrent(int64_t newValue)
{
VerifyOrReturnError(mMaximumChargeCurrent != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue >= 0, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mMaximumChargeCurrent = newValue;
mDelegate.OnMaximumChargeCurrentChanged(newValue);
NotifyAttributeChanged(MaximumChargeCurrent::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetMaximumDischargeCurrent(int64_t newValue)
{
VerifyOrReturnError(mMaximumDischargeCurrent != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue >= 0, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mMaximumDischargeCurrent = newValue;
mDelegate.OnMaximumDischargeCurrentChanged(newValue);
NotifyAttributeChanged(MaximumDischargeCurrent::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetUserMaximumChargeCurrent(int64_t newValue)
{
VerifyOrReturnError(mUserMaximumChargeCurrent != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue >= 0, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mUserMaximumChargeCurrent = newValue;
mDelegate.OnUserMaximumChargeCurrentChanged(newValue);
NotifyAttributeChanged(UserMaximumChargeCurrent::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetRandomizationDelayWindow(uint32_t newValue)
{
VerifyOrReturnError(mRandomizationDelayWindow != newValue, CHIP_NO_ERROR);
VerifyOrReturnError(newValue <= kMaxRandomizationDelayWindowSec, CHIP_IM_GLOBAL_STATUS(ConstraintError));
mRandomizationDelayWindow = newValue;
mDelegate.OnRandomizationDelayWindowChanged(newValue);
NotifyAttributeChanged(RandomizationDelayWindow::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetNextChargeStartTime(DataModel::Nullable<uint32_t> newValue)
{
VerifyOrReturnError(mNextChargeStartTime != newValue, CHIP_NO_ERROR);
mNextChargeStartTime = newValue;
mDelegate.OnNextChargeStartTimeChanged(newValue);
NotifyAttributeChanged(NextChargeStartTime::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetNextChargeTargetTime(DataModel::Nullable<uint32_t> newValue)
{
VerifyOrReturnError(mNextChargeTargetTime != newValue, CHIP_NO_ERROR);
mNextChargeTargetTime = newValue;
mDelegate.OnNextChargeTargetTimeChanged(newValue);
NotifyAttributeChanged(NextChargeTargetTime::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetNextChargeRequiredEnergy(DataModel::Nullable<int64_t> newValue)
{
VerifyOrReturnError(mNextChargeRequiredEnergy != newValue, CHIP_NO_ERROR);
mNextChargeRequiredEnergy = newValue;
mDelegate.OnNextChargeRequiredEnergyChanged(newValue);
NotifyAttributeChanged(NextChargeRequiredEnergy::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetNextChargeTargetSoC(DataModel::Nullable<Percent> newValue)
{
VerifyOrReturnError(mNextChargeTargetSoC != newValue, CHIP_NO_ERROR);
mNextChargeTargetSoC = newValue;
mDelegate.OnNextChargeTargetSoCChanged(newValue);
NotifyAttributeChanged(NextChargeTargetSoC::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetApproximateEVEfficiency(DataModel::Nullable<uint16_t> newValue)
{
VerifyOrReturnError(mApproximateEVEfficiency != newValue, CHIP_NO_ERROR);
mApproximateEVEfficiency = newValue;
mDelegate.OnApproximateEVEfficiencyChanged(newValue);
NotifyAttributeChanged(ApproximateEVEfficiency::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetStateOfCharge(DataModel::Nullable<Percent> newValue)
{
VerifyOrReturnError(mStateOfCharge != newValue, CHIP_NO_ERROR);
mStateOfCharge = newValue;
mDelegate.OnStateOfChargeChanged(newValue);
NotifyAttributeChanged(StateOfCharge::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetBatteryCapacity(DataModel::Nullable<int64_t> newValue)
{
VerifyOrReturnError(mBatteryCapacity != newValue, CHIP_NO_ERROR);
mBatteryCapacity = newValue;
mDelegate.OnBatteryCapacityChanged(newValue);
NotifyAttributeChanged(BatteryCapacity::Id);
return CHIP_NO_ERROR;
}
DataModel::Nullable<CharSpan> EnergyEvseCluster::GetVehicleID() const
{
if (mVehicleID.IsNull())
{
return DataModel::NullNullable;
}
return DataModel::MakeNullable(CharSpan(mVehicleIDBuffer, mVehicleID.Value().size()));
}
CHIP_ERROR EnergyEvseCluster::SetVehicleID(DataModel::Nullable<CharSpan> newValue)
{
if (newValue.IsNull())
{
if (mVehicleID.IsNull())
{
return CHIP_NO_ERROR;
}
mVehicleID = DataModel::NullNullable;
}
else
{
VerifyOrReturnError(newValue.Value().size() <= kMaxVehicleIDBufSize, CHIP_ERROR_BUFFER_TOO_SMALL);
if (!mVehicleID.IsNull() && newValue.Value().data_equal(mVehicleID.Value()))
{
return CHIP_NO_ERROR;
}
memcpy(mVehicleIDBuffer, newValue.Value().data(), newValue.Value().size());
mVehicleID = DataModel::MakeNullable(CharSpan(mVehicleIDBuffer, newValue.Value().size()));
}
mDelegate.OnVehicleIDChanged(mVehicleID);
NotifyAttributeChanged(VehicleID::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetSessionID(DataModel::Nullable<uint32_t> newValue)
{
VerifyOrReturnError(mSessionID != newValue, CHIP_NO_ERROR);
mSessionID = newValue;
mDelegate.OnSessionIDChanged(newValue);
NotifyAttributeChanged(SessionID::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetSessionDuration(DataModel::Nullable<uint32_t> newValue)
{
// We do not check for equality here because the tests expect reports at session boundaries (start/stop) even when the value has
// not changed (e.g. energy stayed at 0 because no charging occurred).
mSessionDuration = newValue;
mDelegate.OnSessionDurationChanged(newValue);
NotifyAttributeChanged(SessionDuration::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetSessionEnergyCharged(DataModel::Nullable<int64_t> newValue)
{
// We do not check for equality here because the tests expect reports at session boundaries (start/stop) even when the value has
// not changed (e.g. energy stayed at 0 because no charging occurred).
mSessionEnergyCharged = newValue;
mDelegate.OnSessionEnergyChargedChanged(newValue);
NotifyAttributeChanged(SessionEnergyCharged::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::SetSessionEnergyDischarged(DataModel::Nullable<int64_t> newValue)
{
// We do not check for equality here because the tests expect reports at session boundaries (start/stop) even when the value has
// not changed (e.g. energy stayed at 0 because no charging occurred).
mSessionEnergyDischarged = newValue;
mDelegate.OnSessionEnergyDischargedChanged(newValue);
NotifyAttributeChanged(SessionEnergyDischarged::Id);
return CHIP_NO_ERROR;
}
DataModel::ActionReturnStatus EnergyEvseCluster::ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder)
{
switch (request.path.mAttributeId)
{
case FeatureMap::Id:
return encoder.Encode(mFeatureFlags);
case ClusterRevision::Id:
return encoder.Encode(kRevision);
case State::Id:
return encoder.Encode(mState);
case SupplyState::Id:
return encoder.Encode(mSupplyState);
case FaultState::Id:
return encoder.Encode(mFaultState);
case ChargingEnabledUntil::Id:
return encoder.Encode(mChargingEnabledUntil);
case DischargingEnabledUntil::Id:
return encoder.Encode(mDischargingEnabledUntil);
case CircuitCapacity::Id:
return encoder.Encode(mCircuitCapacity);
case MinimumChargeCurrent::Id:
return encoder.Encode(mMinimumChargeCurrent);
case MaximumChargeCurrent::Id:
return encoder.Encode(mMaximumChargeCurrent);
case MaximumDischargeCurrent::Id:
return encoder.Encode(mMaximumDischargeCurrent);
case UserMaximumChargeCurrent::Id:
return encoder.Encode(mUserMaximumChargeCurrent);
case RandomizationDelayWindow::Id:
return encoder.Encode(mRandomizationDelayWindow);
case NextChargeStartTime::Id:
return encoder.Encode(mNextChargeStartTime);
case NextChargeTargetTime::Id:
return encoder.Encode(mNextChargeTargetTime);
case NextChargeRequiredEnergy::Id:
return encoder.Encode(mNextChargeRequiredEnergy);
case NextChargeTargetSoC::Id:
return encoder.Encode(mNextChargeTargetSoC);
case ApproximateEVEfficiency::Id:
return encoder.Encode(mApproximateEVEfficiency);
case StateOfCharge::Id:
return encoder.Encode(mStateOfCharge);
case BatteryCapacity::Id:
return encoder.Encode(mBatteryCapacity);
case VehicleID::Id:
return encoder.Encode(GetVehicleID());
case SessionID::Id:
return encoder.Encode(mSessionID);
case SessionDuration::Id:
return encoder.Encode(mSessionDuration);
case SessionEnergyCharged::Id:
return encoder.Encode(mSessionEnergyCharged);
case SessionEnergyDischarged::Id:
return encoder.Encode(mSessionEnergyDischarged);
default:
return Status::UnsupportedAttribute;
}
}
DataModel::ActionReturnStatus EnergyEvseCluster::WriteAttribute(const DataModel::WriteAttributeRequest & request,
AttributeValueDecoder & decoder)
{
switch (request.path.mAttributeId)
{
case UserMaximumChargeCurrent::Id: {
UserMaximumChargeCurrent::TypeInfo::DecodableType value;
ReturnErrorOnFailure(decoder.Decode(value));
VerifyOrReturnValue(mUserMaximumChargeCurrent != value, ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);
return SetUserMaximumChargeCurrent(value);
}
case RandomizationDelayWindow::Id: {
RandomizationDelayWindow::TypeInfo::DecodableType value;
ReturnErrorOnFailure(decoder.Decode(value));
VerifyOrReturnValue(mRandomizationDelayWindow != value, ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);
return SetRandomizationDelayWindow(value);
}
case ApproximateEVEfficiency::Id: {
ApproximateEVEfficiency::TypeInfo::DecodableType value;
ReturnErrorOnFailure(decoder.Decode(value));
VerifyOrReturnValue(mApproximateEVEfficiency != value, ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);
return SetApproximateEVEfficiency(value);
}
default:
return Status::UnsupportedAttribute;
}
}
std::optional<DataModel::ActionReturnStatus> EnergyEvseCluster::InvokeCommand(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments,
CommandHandler * handler)
{
using namespace Commands;
switch (request.path.mCommandId)
{
case Disable::Id:
return HandleDisable(request, input_arguments, handler);
case EnableCharging::Id:
return HandleEnableCharging(request, input_arguments, handler);
case EnableDischarging::Id:
return HandleEnableDischarging(request, input_arguments, handler);
case StartDiagnostics::Id:
return HandleStartDiagnostics(request, input_arguments, handler);
case SetTargets::Id:
return HandleSetTargets(request, input_arguments, handler);
case GetTargets::Id:
return HandleGetTargets(request, input_arguments, handler);
case ClearTargets::Id:
return HandleClearTargets(request, input_arguments, handler);
default:
return Status::UnsupportedCommand;
}
}
CHIP_ERROR EnergyEvseCluster::Attributes(const ConcreteClusterPath & path,
ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
{
AttributeListBuilder::OptionalAttributeEntry optionalAttributes[] = {
// V2x feature attributes
{ mFeatureFlags.Has(Feature::kV2x), DischargingEnabledUntil::kMetadataEntry },
{ mFeatureFlags.Has(Feature::kV2x), MaximumDischargeCurrent::kMetadataEntry },
{ mFeatureFlags.Has(Feature::kV2x), SessionEnergyDischarged::kMetadataEntry },
// ChargingPreferences feature attributes
{ mFeatureFlags.Has(Feature::kChargingPreferences), NextChargeStartTime::kMetadataEntry },
{ mFeatureFlags.Has(Feature::kChargingPreferences), NextChargeTargetTime::kMetadataEntry },
{ mFeatureFlags.Has(Feature::kChargingPreferences), NextChargeRequiredEnergy::kMetadataEntry },
{ mFeatureFlags.Has(Feature::kChargingPreferences), NextChargeTargetSoC::kMetadataEntry },
{ (mFeatureFlags.Has(Feature::kChargingPreferences) &&
mOptionalAttrs.Has(OptionalAttributes::kSupportsApproximateEvEfficiency)),
ApproximateEVEfficiency::kMetadataEntry },
// SoCReporting feature attributes
{ mFeatureFlags.Has(Feature::kSoCReporting), StateOfCharge::kMetadataEntry },
{ mFeatureFlags.Has(Feature::kSoCReporting), BatteryCapacity::kMetadataEntry },
// PlugAndCharge feature attribute
{ mFeatureFlags.Has(Feature::kPlugAndCharge), VehicleID::kMetadataEntry },
// Optional attributes (not feature-based)
{ mOptionalAttrs.Has(OptionalAttributes::kSupportsUserMaximumChargingCurrent), UserMaximumChargeCurrent::kMetadataEntry },
{ mOptionalAttrs.Has(OptionalAttributes::kSupportsRandomizationWindow), RandomizationDelayWindow::kMetadataEntry },
};
AttributeListBuilder listBuilder(builder);
return listBuilder.Append(Span(Attributes::kMandatoryMetadata), Span(optionalAttributes));
}
CHIP_ERROR EnergyEvseCluster::AcceptedCommands(const ConcreteClusterPath & path,
ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
{
using namespace Commands;
ReturnErrorOnFailure(builder.AppendElements({ Disable::kMetadataEntry, EnableCharging::kMetadataEntry }));
if (mFeatureFlags.Has(Feature::kV2x))
{
ReturnErrorOnFailure(builder.AppendElements({ EnableDischarging::kMetadataEntry }));
}
if (mOptionalCmds.Has(OptionalCommands::kSupportsStartDiagnostics))
{
ReturnErrorOnFailure(builder.AppendElements({ StartDiagnostics::kMetadataEntry }));
}
if (mFeatureFlags.Has(Feature::kChargingPreferences))
{
ReturnErrorOnFailure(
builder.AppendElements({ SetTargets::kMetadataEntry, GetTargets::kMetadataEntry, ClearTargets::kMetadataEntry }));
}
return CHIP_NO_ERROR;
}
CHIP_ERROR EnergyEvseCluster::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder)
{
using namespace Commands;
if (mFeatureFlags.Has(Feature::kChargingPreferences))
{
ReturnErrorOnFailure(builder.AppendElements({ GetTargetsResponse::Id }));
}
return CHIP_NO_ERROR;
}
DataModel::ActionReturnStatus EnergyEvseCluster::HandleDisable(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments, CommandHandler * handler)
{
return mDelegate.Disable();
}
DataModel::ActionReturnStatus EnergyEvseCluster::HandleEnableCharging(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments, CommandHandler * handler)
{
Commands::EnableCharging::DecodableType commandData;
ReturnErrorOnFailure(DataModel::Decode(input_arguments, commandData));
auto & chargingEnabledUntil = commandData.chargingEnabledUntil;
auto & minimumChargeCurrent = commandData.minimumChargeCurrent;
auto & maximumChargeCurrent = commandData.maximumChargeCurrent;
VerifyOrReturnValue(minimumChargeCurrent >= kMinimumChargeCurrentLimit, Status::ConstraintError);
VerifyOrReturnValue(maximumChargeCurrent >= kMinimumChargeCurrentLimit, Status::ConstraintError);
VerifyOrReturnValue(minimumChargeCurrent <= maximumChargeCurrent, Status::ConstraintError);
return mDelegate.EnableCharging(chargingEnabledUntil, minimumChargeCurrent, maximumChargeCurrent);
}
DataModel::ActionReturnStatus EnergyEvseCluster::HandleEnableDischarging(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments, CommandHandler * handler)
{
Commands::EnableDischarging::DecodableType commandData;
ReturnErrorOnFailure(DataModel::Decode(input_arguments, commandData));
auto & dischargingEnabledUntil = commandData.dischargingEnabledUntil;
auto & maximumDischargeCurrent = commandData.maximumDischargeCurrent;
VerifyOrReturnValue(maximumDischargeCurrent >= kMinimumChargeCurrentLimit, Status::ConstraintError);
return mDelegate.EnableDischarging(dischargingEnabledUntil, maximumDischargeCurrent);
}
DataModel::ActionReturnStatus EnergyEvseCluster::HandleStartDiagnostics(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments, CommandHandler * handler)
{
return mDelegate.StartDiagnostics();
}
Status EnergyEvseCluster::ValidateTargets(
const DataModel::DecodableList<Structs::ChargingTargetScheduleStruct::DecodableType> & chargingTargetSchedules)
{
uint8_t dayOfWeekBitmap = 0;
auto iter = chargingTargetSchedules.begin();
while (iter.Next())
{
auto & entry = iter.GetValue();
uint8_t bitmask = entry.dayOfWeekForSequence.GetField(static_cast<TargetDayOfWeekBitmap>(kDayOfWeekBitmapMask));
ChipLogProgress(AppServer, "DayOfWeekForSequence = 0x%02x", bitmask);
VerifyOrReturnValue((dayOfWeekBitmap & bitmask) == 0, Status::ConstraintError,
ChipLogError(AppServer, "DayOfWeekForSequence bit already set"));
dayOfWeekBitmap |= bitmask;
auto iterInner = entry.chargingTargets.begin();
uint8_t innerIdx = 0;
while (iterInner.Next())
{
auto & targetStruct = iterInner.GetValue();
uint16_t minutesPastMidnight = targetStruct.targetTimeMinutesPastMidnight;
ChipLogProgress(AppServer, "[%d] MinutesPastMidnight : %d", innerIdx,
static_cast<short unsigned int>(minutesPastMidnight));
VerifyOrReturnValue(minutesPastMidnight <= kMaxMinutesPastMidnight, Status::ConstraintError,
ChipLogError(AppServer, "MinutesPastMidnight invalid: %d", static_cast<int>(minutesPastMidnight)));
if (mFeatureFlags.Has(Feature::kSoCReporting))
{
VerifyOrReturnValue(targetStruct.targetSoC.HasValue(), Status::InvalidCommand,
ChipLogError(AppServer, "SoCReporting enabled but TargetSoC missing"));
VerifyOrReturnValue(
targetStruct.targetSoC.Value() <= kMaxTargetSoCPercent, Status::ConstraintError,
ChipLogError(AppServer, "TargetSoC invalid: %d", static_cast<int>(targetStruct.targetSoC.Value())));
}
else
{
VerifyOrReturnValue(!targetStruct.targetSoC.HasValue() || targetStruct.targetSoC.Value() == kMaxTargetSoCPercent,
Status::ConstraintError,
ChipLogError(AppServer, "TargetSoC must be 100 if SOC feature not supported"));
}
VerifyOrReturnValue(targetStruct.targetSoC.HasValue() || targetStruct.addedEnergy.HasValue(), Status::Failure,
ChipLogError(AppServer, "Must have one of AddedEnergy or TargetSoC"));
VerifyOrReturnValue(
!targetStruct.addedEnergy.HasValue() || targetStruct.addedEnergy.Value() >= 0, Status::ConstraintError,
ChipLogError(AppServer, "AddedEnergy invalid: %ld", static_cast<long>(targetStruct.addedEnergy.Value())));
innerIdx++;
}
VerifyOrReturnValue(innerIdx <= kEvseTargetsMaxTargetsPerDay, Status::ResourceExhausted,
ChipLogError(AppServer, "Too many targets: %d", innerIdx));
VerifyOrReturnValue(iterInner.GetStatus() == CHIP_NO_ERROR, Status::InvalidCommand);
}
VerifyOrReturnValue(iter.GetStatus() == CHIP_NO_ERROR, Status::InvalidCommand);
return Status::Success;
}
DataModel::ActionReturnStatus EnergyEvseCluster::HandleSetTargets(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments, CommandHandler * handler)
{
Commands::SetTargets::DecodableType commandData;
ReturnErrorOnFailure(DataModel::Decode(input_arguments, commandData));
auto & chargingTargetSchedules = commandData.chargingTargetSchedules;
Status status = ValidateTargets(chargingTargetSchedules);
VerifyOrReturnValue(status == Status::Success, status, ChipLogError(AppServer, "SetTargets validation failed"));
return mDelegate.SetTargets(chargingTargetSchedules);
}
std::optional<DataModel::ActionReturnStatus> EnergyEvseCluster::HandleGetTargets(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments,
CommandHandler * handler)
{
Commands::GetTargetsResponse::Type response;
Status status = mDelegate.GetTargets(response.chargingTargetSchedules);
VerifyOrReturnValue(status == Status::Success, status);
handler->AddResponse(request.path, response);
return std::nullopt;
}
DataModel::ActionReturnStatus EnergyEvseCluster::HandleClearTargets(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments, CommandHandler * handler)
{
return mDelegate.ClearTargets();
}
} // namespace EnergyEvse
} // namespace Clusters
} // namespace app
} // namespace chip