blob: f5c8c281615435d510605cbd7a6f67177684c0ad [file] [log] [blame]
/*
*
* Copyright (c) 2023 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.
*/
#include "electrical-energy-measurement-server.h"
#include <protocols/interaction_model/StatusCode.h>
#include <app/AttributeAccessInterfaceRegistry.h>
#include <app/AttributeValueEncoder.h>
#include <app/EventLogging.h>
#include <app/reporting/reporting.h>
#include <app/util/attribute-storage.h>
#include <app/util/config.h>
using chip::Protocols::InteractionModel::Status;
namespace chip {
namespace app {
namespace Clusters {
namespace ElectricalEnergyMeasurement {
using namespace chip;
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Attributes;
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs;
MeasurementData gMeasurements[MATTER_DM_ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER_SERVER_ENDPOINT_COUNT +
CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT];
CHIP_ERROR ElectricalEnergyMeasurementAttrAccess::Init()
{
VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE);
return CHIP_NO_ERROR;
}
void ElectricalEnergyMeasurementAttrAccess::Shutdown()
{
unregisterAttributeAccessOverride(this);
}
CHIP_ERROR ElectricalEnergyMeasurementAttrAccess::Read(const app::ConcreteReadAttributePath & aPath,
app::AttributeValueEncoder & aEncoder)
{
VerifyOrDie(aPath.mClusterId == app::Clusters::ElectricalEnergyMeasurement::Id);
MeasurementData * data = MeasurementDataForEndpoint(aPath.mEndpointId);
switch (aPath.mAttributeId)
{
case FeatureMap::Id:
ReturnErrorOnFailure(aEncoder.Encode(mFeature));
break;
case Accuracy::Id:
if (data == nullptr)
{
return CHIP_ERROR_NOT_FOUND;
}
return aEncoder.Encode(data->measurementAccuracy);
case CumulativeEnergyImported::Id:
VerifyOrReturnError(
HasFeature(ElectricalEnergyMeasurement::Feature::kCumulativeEnergy) &&
HasFeature(ElectricalEnergyMeasurement::Feature::kImportedEnergy),
CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Electrical Energy Measurement: can not get CumulativeEnergyImported, feature is not supported"));
if ((data == nullptr) || !data->cumulativeImported.HasValue())
{
return aEncoder.EncodeNull();
}
return aEncoder.Encode(data->cumulativeImported.Value());
case CumulativeEnergyExported::Id:
VerifyOrReturnError(
HasFeature(ElectricalEnergyMeasurement::Feature::kCumulativeEnergy) &&
HasFeature(ElectricalEnergyMeasurement::Feature::kExportedEnergy),
CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Electrical Energy Measurement: can not get CumulativeEnergyExported, feature is not supported"));
if ((data == nullptr) || !data->cumulativeExported.HasValue())
{
return aEncoder.EncodeNull();
}
return aEncoder.Encode(data->cumulativeExported.Value());
case PeriodicEnergyImported::Id:
VerifyOrReturnError(
HasFeature(ElectricalEnergyMeasurement::Feature::kPeriodicEnergy) &&
HasFeature(ElectricalEnergyMeasurement::Feature::kImportedEnergy),
CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Electrical Energy Measurement: can not get PeriodicEnergyImported, feature is not supported"));
if ((data == nullptr) || !data->periodicImported.HasValue())
{
return aEncoder.EncodeNull();
}
return aEncoder.Encode(data->periodicImported.Value());
case PeriodicEnergyExported::Id:
VerifyOrReturnError(
HasFeature(ElectricalEnergyMeasurement::Feature::kPeriodicEnergy) &&
HasFeature(ElectricalEnergyMeasurement::Feature::kExportedEnergy),
CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Electrical Energy Measurement: can not get PeriodicEnergyExported, feature is not supported"));
if ((data == nullptr) || !data->periodicExported.HasValue())
{
return aEncoder.EncodeNull();
}
return aEncoder.Encode(data->periodicExported.Value());
case CumulativeEnergyReset::Id:
VerifyOrReturnError(
HasFeature(ElectricalEnergyMeasurement::Feature::kCumulativeEnergy), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE,
ChipLogError(Zcl, "Electrical Energy Measurement: can not get CumulativeEnergyReset, feature is not supported"));
if (!SupportsOptAttr(OptionalAttributes::kOptionalAttributeCumulativeEnergyReset))
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
}
if ((data == nullptr) || !data->cumulativeReset.HasValue())
{
return aEncoder.EncodeNull();
}
return aEncoder.Encode(data->cumulativeReset.Value());
}
return CHIP_NO_ERROR;
}
bool ElectricalEnergyMeasurementAttrAccess::HasFeature(Feature aFeature) const
{
return mFeature.Has(aFeature);
}
bool ElectricalEnergyMeasurementAttrAccess::SupportsOptAttr(OptionalAttributes aOptionalAttrs) const
{
return mOptionalAttrs.Has(aOptionalAttrs);
}
MeasurementData * MeasurementDataForEndpoint(EndpointId endpointId)
{
auto index = emberAfGetClusterServerEndpointIndex(endpointId, app::Clusters::ElectricalEnergyMeasurement::Id,
MATTER_DM_ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER_SERVER_ENDPOINT_COUNT);
if (index == kEmberInvalidEndpointIndex)
{
return nullptr;
}
if (index >= ArraySize(gMeasurements))
{
ChipLogError(NotSpecified, "Internal error: invalid/unexpected energy measurement index.");
return nullptr;
}
return &gMeasurements[index];
}
CHIP_ERROR SetMeasurementAccuracy(EndpointId endpointId, const MeasurementAccuracyStruct::Type & accuracy)
{
MeasurementData * data = MeasurementDataForEndpoint(endpointId);
VerifyOrReturnError(data != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
data->measurementAccuracy = accuracy;
MatterReportingAttributeChangeCallback(endpointId, ElectricalEnergyMeasurement::Id, Accuracy::Id);
return CHIP_NO_ERROR;
}
CHIP_ERROR SetCumulativeReset(EndpointId endpointId, const Optional<CumulativeEnergyResetStruct::Type> & cumulativeReset)
{
MeasurementData * data = MeasurementDataForEndpoint(endpointId);
VerifyOrReturnError(data != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
data->cumulativeReset = cumulativeReset;
MatterReportingAttributeChangeCallback(endpointId, ElectricalEnergyMeasurement::Id, CumulativeEnergyReset::Id);
return CHIP_NO_ERROR;
}
bool NotifyCumulativeEnergyMeasured(EndpointId endpointId, const Optional<EnergyMeasurementStruct::Type> & energyImported,
const Optional<EnergyMeasurementStruct::Type> & energyExported)
{
MeasurementData * data = MeasurementDataForEndpoint(endpointId);
if (data != nullptr)
{
data->cumulativeImported = energyImported;
data->cumulativeExported = energyExported;
}
Events::CumulativeEnergyMeasured::Type event;
event.energyImported = energyImported;
event.energyExported = energyExported;
EventNumber eventNumber;
CHIP_ERROR error = app::LogEvent(event, endpointId, eventNumber);
if (CHIP_NO_ERROR != error)
{
ChipLogError(Zcl, "[NotifyCumulativeEnergyMeasured] Unable to send event: %" CHIP_ERROR_FORMAT " [endpointId=%d]",
error.Format(), endpointId);
return false;
}
ChipLogProgress(Zcl, "[NotifyCumulativeEnergyMeasured] Sent event [endpointId=%d,eventNumber=%lu]", endpointId,
static_cast<unsigned long>(eventNumber));
return true;
}
bool NotifyPeriodicEnergyMeasured(EndpointId endpointId, const Optional<EnergyMeasurementStruct::Type> & energyImported,
const Optional<EnergyMeasurementStruct::Type> & energyExported)
{
MeasurementData * data = MeasurementDataForEndpoint(endpointId);
if (data != nullptr)
{
data->periodicImported = energyImported;
data->periodicExported = energyExported;
}
Events::PeriodicEnergyMeasured::Type event;
event.energyImported = energyImported;
event.energyExported = energyExported;
EventNumber eventNumber;
CHIP_ERROR error = app::LogEvent(event, endpointId, eventNumber);
if (CHIP_NO_ERROR != error)
{
ChipLogError(Zcl, "[NotifyPeriodicEnergyMeasured] Unable to send event: %" CHIP_ERROR_FORMAT " [endpointId=%d]",
error.Format(), endpointId);
return false;
}
ChipLogProgress(Zcl, "[NotifyPeriodicEnergyMeasured] Sent event [endpointId=%d,eventNumber=%lu]", endpointId,
static_cast<unsigned long>(eventNumber));
return true;
}
} // namespace ElectricalEnergyMeasurement
} // namespace Clusters
} // namespace app
} // namespace chip