Enabled Electrical Energy Measurement in example-energy-management-ap… (#31622)
* Enabled Electrical Energy Measurement in example-energy-management-app ZAP and regen all.
* Added ElectricalEnergyMeasurement to chip-repl __init__.py
* Restyled by isort
* Changed feature map to indicate CUME and IMP support only
* Adding into __all__ some missing clusters which are causing flake8 issues.
* Removed out of date comment
* Added a test event trigger to fake energy reporting readings into the EEM cluster (hooks ready for EPM cluster)
* Added new Energy Reporting TE Trigger delegate
* Updated random calculation and scaled into mWh (divide by 3600)
* Restyled by whitespace
* Restyled by gn
* Removed accidental launch.json inclusion
* Fix for ARM linux etc builds due to gn dependency check error
* Added code review suggestions.
* More code review suggestions fixed.
* Added electrical-energy-measurement-server to CMakelist to fix linker issue.
---------
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/examples/energy-management-app/energy-management-common/energy-management-app.matter b/examples/energy-management-app/energy-management-common/energy-management-app.matter
index 2c580b5..dae1893 100644
--- a/examples/energy-management-app/energy-management-common/energy-management-app.matter
+++ b/examples/energy-management-app/energy-management-common/energy-management-app.matter
@@ -900,6 +900,85 @@
fabric command access(invoke: administer) KeySetReadAllIndices(): KeySetReadAllIndicesResponse = 4;
}
+/** This cluster provides a mechanism for querying data about the electrical energy imported or provided by the server. */
+provisional cluster ElectricalEnergyMeasurement = 145 {
+ revision 1;
+
+ enum MeasurementTypeEnum : enum16 {
+ kUnspecified = 0;
+ kVoltage = 1;
+ kActiveCurrent = 2;
+ kReactiveCurrent = 3;
+ kApparentCurrent = 4;
+ kActivePower = 5;
+ kReactivePower = 6;
+ kApparentPower = 7;
+ kRMSVoltage = 8;
+ kRMSCurrent = 9;
+ kRMSPower = 10;
+ kFrequency = 11;
+ kPowerFactor = 12;
+ kNeutralCurrent = 13;
+ kElectricalEnergy = 14;
+ }
+
+ bitmap Feature : bitmap32 {
+ kImportedEnergy = 0x1;
+ kExportedEnergy = 0x2;
+ kCumulativeEnergy = 0x4;
+ kPeriodicEnergy = 0x8;
+ }
+
+ struct MeasurementAccuracyRangeStruct {
+ int64s rangeMin = 0;
+ int64s rangeMax = 1;
+ optional percent100ths percentMax = 2;
+ optional percent100ths percentMin = 3;
+ optional percent100ths percentTypical = 4;
+ optional int64u fixedMax = 5;
+ optional int64u fixedMin = 6;
+ optional int64u fixedTypical = 7;
+ }
+
+ struct MeasurementAccuracyStruct {
+ MeasurementTypeEnum measurementType = 0;
+ boolean measured = 1;
+ int64s minMeasuredValue = 2;
+ int64s maxMeasuredValue = 3;
+ MeasurementAccuracyRangeStruct accuracyRanges[] = 4;
+ }
+
+ struct EnergyMeasurementStruct {
+ int64s energy = 0;
+ optional epoch_s startTimestamp = 1;
+ optional epoch_s endTimestamp = 2;
+ optional systime_ms startSystime = 3;
+ optional systime_ms endSystime = 4;
+ }
+
+ info event CumulativeEnergyMeasured = 0 {
+ optional EnergyMeasurementStruct energyImported = 0;
+ optional EnergyMeasurementStruct energyExported = 1;
+ }
+
+ info event PeriodicEnergyMeasured = 1 {
+ optional EnergyMeasurementStruct energyImported = 0;
+ optional EnergyMeasurementStruct energyExported = 1;
+ }
+
+ readonly attribute MeasurementAccuracyStruct accuracy = 0;
+ readonly attribute optional nullable EnergyMeasurementStruct cumulativeEnergyImported = 1;
+ readonly attribute optional nullable EnergyMeasurementStruct cumulativeEnergyExported = 2;
+ readonly attribute optional nullable EnergyMeasurementStruct periodicEnergyImported = 3;
+ readonly attribute optional nullable EnergyMeasurementStruct periodicEnergyExported = 4;
+ readonly attribute command_id generatedCommandList[] = 65528;
+ readonly attribute command_id acceptedCommandList[] = 65529;
+ readonly attribute event_id eventList[] = 65530;
+ readonly attribute attrib_id attributeList[] = 65531;
+ readonly attribute bitmap32 featureMap = 65532;
+ readonly attribute int16u clusterRevision = 65533;
+}
+
/** This cluster allows a client to manage the power draw of a device. An example of such a client could be an Energy Management System (EMS) which controls an Energy Smart Appliance (ESA). */
provisional cluster DeviceEnergyManagement = 152 {
revision 3;
@@ -1598,6 +1677,18 @@
callback attribute clusterRevision;
}
+ server cluster ElectricalEnergyMeasurement {
+ emits event CumulativeEnergyMeasured;
+ callback attribute accuracy;
+ callback attribute cumulativeEnergyImported;
+ callback attribute generatedCommandList;
+ callback attribute acceptedCommandList;
+ callback attribute eventList;
+ callback attribute attributeList;
+ ram attribute featureMap default = 5;
+ ram attribute clusterRevision default = 1;
+ }
+
server cluster DeviceEnergyManagement {
emits event PowerAdjustStart;
emits event PowerAdjustEnd;
diff --git a/examples/energy-management-app/energy-management-common/energy-management-app.zap b/examples/energy-management-app/energy-management-common/energy-management-app.zap
index 78485df..0e1f15b 100644
--- a/examples/energy-management-app/energy-management-common/energy-management-app.zap
+++ b/examples/energy-management-app/energy-management-common/energy-management-app.zap
@@ -2518,6 +2518,154 @@
]
},
{
+ "name": "Electrical Energy Measurement",
+ "code": 145,
+ "mfgCode": null,
+ "define": "ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER",
+ "side": "server",
+ "enabled": 1,
+ "apiMaturity": "provisional",
+ "attributes": [
+ {
+ "name": "Accuracy",
+ "code": 0,
+ "mfgCode": null,
+ "side": "server",
+ "type": "MeasurementAccuracyStruct",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "CumulativeEnergyImported",
+ "code": 1,
+ "mfgCode": null,
+ "side": "server",
+ "type": "EnergyMeasurementStruct",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "GeneratedCommandList",
+ "code": 65528,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "AcceptedCommandList",
+ "code": 65529,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "EventList",
+ "code": 65530,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "AttributeList",
+ "code": 65531,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "FeatureMap",
+ "code": 65532,
+ "mfgCode": null,
+ "side": "server",
+ "type": "bitmap32",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "5",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "ClusterRevision",
+ "code": 65533,
+ "mfgCode": null,
+ "side": "server",
+ "type": "int16u",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "1",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ }
+ ],
+ "events": [
+ {
+ "name": "CumulativeEnergyMeasured",
+ "code": 0,
+ "mfgCode": null,
+ "side": "server",
+ "included": 1
+ }
+ ]
+ },
+ {
"name": "Device Energy Management",
"code": 152,
"mfgCode": null,
diff --git a/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h b/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h
index df757f7..f1c3eac 100644
--- a/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h
+++ b/examples/energy-management-app/energy-management-common/include/EVSEManufacturerImpl.h
@@ -60,6 +60,56 @@
*/
static void ApplicationCallbackHandler(const EVSECbInfo * cb, intptr_t arg);
+ /**
+ * @brief Allows a client application to send in power readings into the system
+ *
+ * @param[in] aEndpointId - Endpoint to send to EPM Cluster
+ * @param[in] aActivePower_mW - Power measured in milli-watts
+ * @param[in] aVoltage_mV - Voltage measured in milli-volts
+ * @param[in] aCurrent_mA - Current measured in milli-amps
+ */
+ CHIP_ERROR SendPowerReading(EndpointId aEndpointId, int64_t aActivePower_mW, int64_t aVoltage_mV, int64_t aCurrent_mA);
+
+ /**
+ * @brief Allows a client application to send in energy readings into the system
+ *
+ * This is a helper function to add timestamps to the readings
+ *
+ * @param[in] aCumulativeEnergyImported -total energy imported in milli-watthours
+ * @param[in] aCumulativeEnergyExported -total energy exported in milli-watthours
+ */
+ CHIP_ERROR SendEnergyReading(EndpointId aEndpointId, int64_t aCumulativeEnergyImported, int64_t aCumulativeEnergyExported);
+
+ /** Fake Meter data generation - used for testing EPM/EEM clusters */
+ /**
+ * @brief Starts a fake load/generator to periodically callback the power and energy
+ * clusters.
+ * @param[in] aEndpointId - which endpoint is the meter to be updated on
+ * @param[in] aPower_mW - the mean power of the load
+ * Positive power indicates Imported energy (e.g. a load)
+ * Negative power indicated Exported energy (e.g. a generator)
+ * @param[in] aPowerRandomness_mW This is used to scale random power fluctuations around the mean power of the load
+ *
+ * @param[in] aInterval_s - the callback interval in seconds
+ * @param[in] bReset - boolean: true will reset the energy values to 0
+ */
+ void StartFakeReadings(EndpointId aEndpointId, int64_t aPower_mW, uint32_t aPowerRandomness_mW, uint8_t aInterval_s,
+ bool bReset);
+
+ /**
+ * @brief Stops any active updates to the fake load data callbacks
+ */
+ void StopFakeReadings();
+
+ /**
+ * @brief Sends fake meter data into the cluster and restarts the timer
+ */
+ void FakeReadingsUpdate();
+ /**
+ * @brief Timer expiry callback to handle fake load
+ */
+ static void FakeReadingsTimerExpiry(System::Layer * systemLayer, void * manufacturer);
+
private:
EnergyEvseManager * mInstance;
diff --git a/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h b/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h
index 558f7c1..e6f5c8c 100644
--- a/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h
+++ b/examples/energy-management-app/energy-management-common/include/EnergyEvseDelegateImpl.h
@@ -26,6 +26,14 @@
#include <cstring>
using chip::Protocols::InteractionModel::Status;
+
+/**
+ * @brief Helper function to get current timestamp in Epoch format
+ *
+ * @param chipEpoch reference to hold return timestamp
+ */
+CHIP_ERROR GetEpochTS(uint32_t & chipEpoch);
+
namespace chip {
namespace app {
namespace Clusters {
diff --git a/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp
index fe51c9d..70cd63e 100644
--- a/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp
+++ b/examples/energy-management-app/energy-management-common/src/EVSEManufacturerImpl.cpp
@@ -18,12 +18,16 @@
#include <EVSEManufacturerImpl.h>
#include <EnergyEvseManager.h>
+#include <app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.h>
+#include <app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.h>
#include <app/clusters/energy-evse-server/EnergyEvseTestEventTriggerDelegate.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::EnergyEvse;
+using namespace chip::app::Clusters::ElectricalEnergyMeasurement;
+using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs;
CHIP_ERROR EVSEManufacturer::Init()
{
@@ -90,6 +94,179 @@
}
/**
+ * @brief Allows a client application to send in power readings into the system
+ *
+ * @param[in] aEndpointId - Endpoint to send to EPM Cluster
+ * @param[in] aActivePower_mW - Power measured in milli-watts
+ * @param[in] aVoltage_mV - Voltage measured in milli-volts
+ * @param[in] aCurrent_mA - Current measured in milli-amps
+ */
+CHIP_ERROR EVSEManufacturer::SendPowerReading(EndpointId aEndpointId, int64_t aActivePower_mW, int64_t aVoltage_mV,
+ int64_t aCurrent_mA)
+{
+ // TODO add Power Readings when EPM cluster is merged
+
+ return CHIP_NO_ERROR;
+}
+
+/**
+ * @brief Allows a client application to send in energy readings into the system
+ *
+ * This is a helper function to add timestamps to the readings
+ *
+ * @param[in] aCumulativeEnergyImported -total energy imported in milli-watthours
+ * @param[in] aCumulativeEnergyExported -total energy exported in milli-watthours
+ */
+CHIP_ERROR EVSEManufacturer::SendEnergyReading(EndpointId aEndpointId, int64_t aCumulativeEnergyImported,
+ int64_t aCumulativeEnergyExported)
+{
+ MeasurementData * data = MeasurementDataForEndpoint(aEndpointId);
+
+ EnergyMeasurementStruct::Type energyImported;
+ EnergyMeasurementStruct::Type energyExported;
+
+ // Get current timestamp
+ uint32_t currentTimestamp;
+ CHIP_ERROR err = GetEpochTS(currentTimestamp);
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(AppServer, "GetEpochTS returned error getting timestamp");
+ return err;
+ }
+
+ /** IMPORT */
+ // Copy last endTimestamp into new startTimestamp if it exists
+ energyImported.startTimestamp.ClearValue();
+ if (data->cumulativeImported.HasValue())
+ {
+ energyImported.startTimestamp = data->cumulativeImported.Value().endTimestamp;
+ }
+
+ energyImported.endTimestamp.SetValue(currentTimestamp);
+ energyImported.energy = aCumulativeEnergyImported;
+
+ /** EXPORT */
+ // Copy last endTimestamp into new startTimestamp if it exists
+ energyExported.startTimestamp.ClearValue();
+ if (data->cumulativeExported.HasValue())
+ {
+ energyExported.startTimestamp = data->cumulativeExported.Value().endTimestamp;
+ }
+ energyExported.endTimestamp.SetValue(currentTimestamp);
+ energyExported.energy = aCumulativeEnergyExported;
+
+ // call the SDK to update attributes and generate an event
+ if (!NotifyCumulativeEnergyMeasured(aEndpointId, MakeOptional(energyImported), MakeOptional(energyExported)))
+ {
+ ChipLogError(AppServer, "Failed to notify Cumulative Energy reading.");
+ return CHIP_ERROR_INTERNAL;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+struct FakeReadingsData
+{
+ bool bEnabled; /* If enabled then the timer callback will re-trigger */
+ EndpointId mEndpointId; /* Which endpoint the meter is on */
+ uint8_t mInterval_s; /* Interval in seconds to callback */
+ int64_t mPower_mW; /* Power on the load in mW (signed value) +ve = imported */
+ uint32_t mPowerRandomness_mW; /* The amount to randomize the Power on the load in mW */
+ /* These energy values can only be positive values.
+ * however the underlying energy type (power_mWh) is signed, so keeping with that convention */
+ int64_t mTotalEnergyImported = 0; /* Energy Imported which is updated if mPower > 0 */
+ int64_t mTotalEnergyExported = 0; /* Energy Imported which is updated if mPower < 0 */
+};
+
+static FakeReadingsData gFakeReadingsData;
+
+/* This helper routine starts and handles a callback */
+/**
+ * @brief Starts a fake load/generator to periodically callback the power and energy
+ * clusters.
+ * @param[in] aEndpointId - which endpoint is the meter to be updated on
+ * @param[in] aPower_mW - the mean power of the load
+ * Positive power indicates Imported energy (e.g. a load)
+ * Negative power indicated Exported energy (e.g. a generator)
+ * @param[in] aPowerRandomness_mW This is used to define the std.dev of the
+ * random power values around the mean power of the load
+ *
+ * @param[in] aInterval_s - the callback interval in seconds
+ * @param[in] bReset - boolean: true will reset the energy values to 0
+ */
+void EVSEManufacturer::StartFakeReadings(EndpointId aEndpointId, int64_t aPower_mW, uint32_t aPowerRandomness_mW,
+ uint8_t aInterval_s, bool bReset)
+{
+ gFakeReadingsData.bEnabled = true;
+ gFakeReadingsData.mEndpointId = aEndpointId;
+ gFakeReadingsData.mPower_mW = aPower_mW;
+ gFakeReadingsData.mPowerRandomness_mW = aPowerRandomness_mW;
+ gFakeReadingsData.mInterval_s = aInterval_s;
+
+ if (bReset)
+ {
+ gFakeReadingsData.mTotalEnergyImported = 0;
+ gFakeReadingsData.mTotalEnergyExported = 0;
+ }
+
+ // Call update function to kick off regular readings
+ FakeReadingsUpdate();
+}
+/**
+ * @brief Stops any active updates to the fake load data callbacks
+ */
+void EVSEManufacturer::StopFakeReadings()
+{
+ gFakeReadingsData.bEnabled = false;
+}
+/**
+ * @brief Sends fake meter data into the cluster and restarts the timer
+ */
+void EVSEManufacturer::FakeReadingsUpdate()
+{
+ /* Check to see if the fake Load is still running - don't send updates if the timer was already cancelled */
+ if (!gFakeReadingsData.bEnabled)
+ {
+ return;
+ }
+
+ // Update meter values
+ // Avoid using floats - so we will do a basic rand() call which will generate a integer value between 0 and RAND_MAX
+ // first compute power as a mean + some random value in range 0 to mPowerRandomness_mW
+ int64_t power = (rand() % gFakeReadingsData.mPowerRandomness_mW);
+ power += gFakeReadingsData.mPower_mW; // add in the base power
+
+ // TODO call the EPM cluster to send a power reading
+
+ // update the energy meter - we'll assume that the power has been constant during the previous interval
+ if (gFakeReadingsData.mPower_mW > 0)
+ {
+ // Positive power - means power is imported
+ gFakeReadingsData.mTotalEnergyImported += ((power * gFakeReadingsData.mInterval_s) / 3600);
+ }
+ else
+ {
+ // Negative power - means power is exported, but the cumulative energy is positive
+ gFakeReadingsData.mTotalEnergyExported += ((-power * gFakeReadingsData.mInterval_s) / 3600);
+ }
+
+ SendEnergyReading(gFakeReadingsData.mEndpointId, gFakeReadingsData.mTotalEnergyImported,
+ gFakeReadingsData.mTotalEnergyExported);
+
+ // start/restart the timer
+ DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(gFakeReadingsData.mInterval_s), FakeReadingsTimerExpiry, this);
+}
+/**
+ * @brief Timer expiry callback to handle fake load
+ */
+void EVSEManufacturer::FakeReadingsTimerExpiry(System::Layer * systemLayer, void * manufacturer)
+{
+ EVSEManufacturer * mn = reinterpret_cast<EVSEManufacturer *>(manufacturer);
+
+ mn->FakeReadingsUpdate();
+}
+
+/**
* @brief Main Callback handler - to be implemented by Manufacturer
*
* @param EVSECbInfo describes the type of call back, and a union of structs
@@ -231,6 +408,37 @@
dg->HwDiagnosticsComplete();
}
+void SetTestEventTrigger_FakeReadingsLoadStart()
+{
+ EVSEManufacturer * mn = GetEvseManufacturer();
+ VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null");
+
+ int64_t aPower_mW = 1'000'000; // Fake load 1000 W
+ uint32_t aPowerRandomness_mW = 20'000; // randomness 20W
+ uint8_t aInterval_s = 2; // 2s updates
+ bool bReset = true;
+ mn->StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aInterval_s, bReset);
+}
+
+void SetTestEventTrigger_FakeReadingsGeneratorStart()
+{
+ EVSEManufacturer * mn = GetEvseManufacturer();
+ VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null");
+
+ int64_t aPower_mW = -3'000'000; // Fake Generator -3000 W
+ uint32_t aPowerRandomness_mW = 20'000; // randomness 20W
+ uint8_t aInterval_s = 5; // 5s updates
+ bool bReset = true;
+ mn->StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aInterval_s, bReset);
+}
+
+void SetTestEventTrigger_FakeReadingsStop()
+{
+ EVSEManufacturer * mn = GetEvseManufacturer();
+ VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null");
+ mn->StopFakeReadings();
+}
+
bool HandleEnergyEvseTestEventTrigger(uint64_t eventTrigger)
{
EnergyEvseTrigger trigger = static_cast<EnergyEvseTrigger>(eventTrigger);
@@ -284,3 +492,29 @@
return true;
}
+
+bool HandleEnergyReportingTestEventTrigger(uint64_t eventTrigger)
+{
+ EnergyReportingTrigger trigger = static_cast<EnergyReportingTrigger>(eventTrigger);
+
+ switch (trigger)
+ {
+ case EnergyReportingTrigger::kFakeReadingsStop:
+ ChipLogProgress(Support, "[EnergyReporting-Test-Event] => Stop Fake load");
+ SetTestEventTrigger_FakeReadingsStop();
+ break;
+ case EnergyReportingTrigger::kFakeReadingsLoadStart_1kW_2s:
+ ChipLogProgress(Support, "[EnergyReporting-Test-Event] => Start Fake load 1kW @2s Import");
+ SetTestEventTrigger_FakeReadingsLoadStart();
+ break;
+ case EnergyReportingTrigger::kFakeReadingsGenStart_3kW_5s:
+ ChipLogProgress(Support, "[EnergyReporting-Test-Event] => Start Fake generator 3kW @5s Export");
+ SetTestEventTrigger_FakeReadingsGeneratorStart();
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
diff --git a/examples/energy-management-app/esp32/main/CMakeLists.txt b/examples/energy-management-app/esp32/main/CMakeLists.txt
index 46f97d7..edba8f9 100644
--- a/examples/energy-management-app/esp32/main/CMakeLists.txt
+++ b/examples/energy-management-app/esp32/main/CMakeLists.txt
@@ -65,6 +65,7 @@
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/groups-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/mode-base-server"
+ "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/electrical-energy-measurement-server"
)
set(PRIV_REQUIRES_LIST chip QRCode bt led_strip app_update openthread driver nvs_flash spi_flash)
diff --git a/examples/energy-management-app/linux/args.gni b/examples/energy-management-app/linux/args.gni
index b7c71c3..e1000e6 100644
--- a/examples/energy-management-app/linux/args.gni
+++ b/examples/energy-management-app/linux/args.gni
@@ -30,3 +30,4 @@
chip_enable_read_client = false
chip_enable_energy_evse_trigger = true
+chip_enable_energy_reporting_trigger = true
diff --git a/examples/energy-management-app/linux/main.cpp b/examples/energy-management-app/linux/main.cpp
index 824fa17..a150b82 100644
--- a/examples/energy-management-app/linux/main.cpp
+++ b/examples/energy-management-app/linux/main.cpp
@@ -208,7 +208,6 @@
}
/* Now create EVSEManufacturer */
- // TODO this takes just the EVSE Instance for now, but will need the DEM adding
gEvseManufacturer = std::make_unique<EVSEManufacturer>(gEvseInstance.get());
if (!gEvseManufacturer)
{
diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp
index 933502d..04274ab 100644
--- a/examples/platform/linux/AppMain.cpp
+++ b/examples/platform/linux/AppMain.cpp
@@ -86,6 +86,9 @@
#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER
#include <app/clusters/energy-evse-server/EnergyEvseTestEventTriggerDelegate.h>
#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER
+#include <app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.h>
+#endif
#include <app/TestEventTriggerDelegate.h>
#include <signal.h>
@@ -571,6 +574,12 @@
};
otherDelegate = &energyEvseTestEventTriggerDelegate;
#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER
+ static EnergyReportingTestEventTriggerDelegate energyReportingTestEventTriggerDelegate{
+ ByteSpan(LinuxDeviceOptions::GetInstance().testEventTriggerEnableKey), otherDelegate
+ };
+ otherDelegate = &energyReportingTestEventTriggerDelegate;
+#endif
// For general testing of TestEventTrigger, we have a common "core" event trigger delegate.
static SampleTestEventTriggerDelegate testEventTriggerDelegate;
diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn
index fbbf7e1..f82702b 100644
--- a/examples/platform/linux/BUILD.gn
+++ b/examples/platform/linux/BUILD.gn
@@ -23,6 +23,7 @@
chip_enable_smoke_co_trigger = false
chip_enable_boolean_state_configuration_trigger = false
chip_enable_energy_evse_trigger = false
+ chip_enable_energy_reporting_trigger = false
}
config("app-main-config") {
@@ -47,6 +48,10 @@
sources = [ "${chip_root}/src/app/clusters/energy-evse-server/EnergyEvseTestEventTriggerDelegate.h" ]
}
+source_set("energy-reporting-test-event-trigger") {
+ sources = [ "${chip_root}/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.h" ]
+}
+
source_set("app-main") {
defines = [ "ENABLE_TRACING=${matter_enable_tracing_support}" ]
sources = [
@@ -71,6 +76,7 @@
public_deps = [
":boolean-state-configuration-test-event-trigger",
":energy-evse-test-event-trigger",
+ ":energy-reporting-test-event-trigger",
":smco-test-event-trigger",
"${chip_root}/src/lib",
"${chip_root}/src/platform/logging:force_stdio",
@@ -110,6 +116,7 @@
"CHIP_DEVICE_CONFIG_ENABLE_SMOKE_CO_TRIGGER=${chip_enable_smoke_co_trigger}",
"CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER=${chip_enable_boolean_state_configuration_trigger}",
"CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER=${chip_enable_energy_evse_trigger}",
+ "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER=${chip_enable_energy_reporting_trigger}",
]
public_configs = [ ":app-main-config" ]
diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni
index 7f8f477..6404eb3 100644
--- a/src/app/chip_data_model.gni
+++ b/src/app/chip_data_model.gni
@@ -352,6 +352,13 @@
"${_app_root}/clusters/${cluster}/BDXDiagnosticLogsProvider.h",
"${_app_root}/clusters/${cluster}/DiagnosticLogsProviderDelegate.h",
]
+ } else if (cluster == "electrical-energy-measurement-server") {
+ sources += [
+ "${_app_root}/clusters/${cluster}/${cluster}.cpp",
+ "${_app_root}/clusters/${cluster}/${cluster}.h",
+ "${_app_root}/clusters/${cluster}/EnergyReportingTestEventTriggerDelegate.cpp",
+ "${_app_root}/clusters/${cluster}/EnergyReportingTestEventTriggerDelegate.h",
+ ]
} else {
sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ]
}
diff --git a/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.cpp b/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.cpp
new file mode 100644
index 0000000..210ab40
--- /dev/null
+++ b/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.cpp
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright (c) 2024 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 "EnergyReportingTestEventTriggerDelegate.h"
+
+namespace chip {
+
+bool EnergyReportingTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enableKey) const
+{
+ return !mEnableKey.empty() && mEnableKey.data_equal(enableKey);
+}
+
+CHIP_ERROR EnergyReportingTestEventTriggerDelegate::HandleEventTrigger(uint64_t eventTrigger)
+{
+ if (HandleEnergyReportingTestEventTrigger(eventTrigger))
+ {
+ return CHIP_NO_ERROR;
+ }
+ if (mOtherDelegate != nullptr)
+ {
+ return mOtherDelegate->HandleEventTrigger(eventTrigger);
+ }
+ return CHIP_ERROR_INVALID_ARGUMENT;
+}
+
+} // namespace chip
diff --git a/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.h b/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.h
new file mode 100644
index 0000000..a7beef6
--- /dev/null
+++ b/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerDelegate.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright (c) 2024 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.
+ */
+
+#pragma once
+
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/TestEventTriggerDelegate.h>
+
+namespace chip {
+
+/*
+ * These Test EventTrigger values can be used to fake meter reading data
+ *
+ * They are sent along with the enableKey (manufacturer defined secret)
+ * in the General Diagnostic cluster TestEventTrigger command
+ */
+enum class EnergyReportingTrigger : uint64_t
+{
+ // We use the Electrical Energy Measurement cluster ID as our TE trigger space
+ // Stop Fake readings
+ kFakeReadingsStop = 0x0091'0000'0000'0000,
+ // Fake a load (importing) readings at 1kW 230V 4.34A with 2s updates
+ kFakeReadingsLoadStart_1kW_2s = 0x0091'0000'0000'0001,
+ // Fake a generator (exporting) readings at 3kW 230V 3.33A with 5s updates
+ kFakeReadingsGenStart_3kW_5s = 0x0091'0000'0000'0002,
+
+};
+
+class EnergyReportingTestEventTriggerDelegate : public TestEventTriggerDelegate
+{
+public:
+ /**
+ * This class expects the enableKey ByteSpan to be valid forever.
+ * Typically this feature is only enabled in certification testing
+ * and uses a static secret key in the device for testing (e.g. in factory data)
+ */
+ explicit EnergyReportingTestEventTriggerDelegate(const ByteSpan & enableKey, TestEventTriggerDelegate * otherDelegate) :
+ mEnableKey(enableKey), mOtherDelegate(otherDelegate)
+ {}
+
+ /* This function returns True if the enableKey received in the TestEventTrigger command
+ * matches the value passed into the constructor.
+ */
+ bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override;
+
+ /** This function must return True if the eventTrigger is recognised and handled
+ * It must return False to allow a higher level TestEvent handler to check other
+ * clusters that may handle it.
+ */
+ CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override;
+
+private:
+ ByteSpan mEnableKey;
+ TestEventTriggerDelegate * mOtherDelegate;
+};
+
+} // namespace chip
+
+/**
+ * @brief User handler for handling the test event trigger
+ *
+ * @note If TestEventTrigger is enabled, it needs to be implemented in the app
+ *
+ * @param eventTrigger Event trigger to handle
+ *
+ * @retval true on success
+ * @retval false if error happened
+ */
+bool HandleEnergyReportingTestEventTrigger(uint64_t eventTrigger);
diff --git a/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp b/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp
index fcc75ef..728b41d 100644
--- a/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp
+++ b/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.cpp
@@ -33,36 +33,9 @@
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Attributes;
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs;
-struct MeasurementData
-{
- MeasurementAccuracyStruct::Type measurementAccuracy;
- Optional<EnergyMeasurementStruct::Type> cumulativeImported;
- Optional<EnergyMeasurementStruct::Type> cumulativeExported;
- Optional<EnergyMeasurementStruct::Type> periodicImported;
- Optional<EnergyMeasurementStruct::Type> periodicExported;
-};
-
MeasurementData
gMeasurements[EMBER_AF_ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT];
-MeasurementData * MeasurementDataForEndpoint(EndpointId endpointId)
-{
- auto index = emberAfGetClusterServerEndpointIndex(endpointId, app::Clusters::ElectricalEnergyMeasurement::Id,
- EMBER_AF_ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER_SERVER_ENDPOINT_COUNT);
-
- if (index == kEmberInvalidEndpointIndex)
- {
- return nullptr;
- }
-
- if (index >= EMBER_AF_ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER_SERVER_ENDPOINT_COUNT)
- {
- ChipLogError(NotSpecified, "Internal error: invalid/unexpected energy measurement index.");
- return nullptr;
- }
- return &gMeasurements[index];
-}
-
class ElectricalEnergyMeasurementAttrAccess : public app::AttributeAccessInterface
{
public:
@@ -126,6 +99,24 @@
namespace Clusters {
namespace ElectricalEnergyMeasurement {
+MeasurementData * MeasurementDataForEndpoint(EndpointId endpointId)
+{
+ auto index = emberAfGetClusterServerEndpointIndex(endpointId, app::Clusters::ElectricalEnergyMeasurement::Id,
+ EMBER_AF_ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER_SERVER_ENDPOINT_COUNT);
+
+ if (index == kEmberInvalidEndpointIndex)
+ {
+ return nullptr;
+ }
+
+ if (index >= EMBER_AF_ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER_SERVER_ENDPOINT_COUNT)
+ {
+ ChipLogError(NotSpecified, "Internal error: invalid/unexpected energy measurement index.");
+ return nullptr;
+ }
+ return &gMeasurements[index];
+}
+
CHIP_ERROR SetMeasurementAccuracy(EndpointId endpointId, const MeasurementAccuracyStruct::Type & accuracy)
{
diff --git a/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.h b/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.h
index 8647b9b..9c3d876 100644
--- a/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.h
+++ b/src/app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.h
@@ -26,6 +26,15 @@
namespace Clusters {
namespace ElectricalEnergyMeasurement {
+struct MeasurementData
+{
+ Structs::MeasurementAccuracyStruct::Type measurementAccuracy;
+ Optional<Structs::EnergyMeasurementStruct::Type> cumulativeImported;
+ Optional<Structs::EnergyMeasurementStruct::Type> cumulativeExported;
+ Optional<Structs::EnergyMeasurementStruct::Type> periodicImported;
+ Optional<Structs::EnergyMeasurementStruct::Type> periodicExported;
+};
+
bool NotifyCumulativeEnergyMeasured(EndpointId endpointId, const Optional<Structs::EnergyMeasurementStruct::Type> & energyImported,
const Optional<Structs::EnergyMeasurementStruct::Type> & energyExported);
@@ -34,6 +43,8 @@
CHIP_ERROR SetMeasurementAccuracy(EndpointId endpointId, const Structs::MeasurementAccuracyStruct::Type & accuracy);
+MeasurementData * MeasurementDataForEndpoint(EndpointId endpointId);
+
} // namespace ElectricalEnergyMeasurement
} // namespace Clusters
} // namespace app
diff --git a/src/controller/python/chip/clusters/__init__.py b/src/controller/python/chip/clusters/__init__.py
index 1e4fcee..47a53ef 100644
--- a/src/controller/python/chip/clusters/__init__.py
+++ b/src/controller/python/chip/clusters/__init__.py
@@ -28,8 +28,8 @@
BinaryInputBasic, Binding, BooleanState, BooleanStateConfiguration, BridgedDeviceBasicInformation,
CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, Channel, ColorControl,
ContentLauncher, Descriptor, DeviceEnergyManagement, DeviceEnergyManagementMode, DiagnosticLogs,
- DishwasherAlarm, DishwasherMode, DoorLock, ElectricalMeasurement, EnergyEvse, EnergyEvseMode,
- EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement,
+ DishwasherAlarm, DishwasherMode, DoorLock, ElectricalEnergyMeasurement, ElectricalMeasurement, EnergyEvse,
+ EnergyEvseMode, EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement,
FormaldehydeConcentrationMeasurement, GeneralCommissioning, GeneralDiagnostics, GroupKeyManagement, Groups,
HepaFilterMonitoring, IcdManagement, Identify, IlluminanceMeasurement, KeypadInput, LaundryDryerControls,
LaundryWasherControls, LaundryWasherMode, LevelControl, LocalizationConfiguration, LowPower, MediaInput,
@@ -52,7 +52,7 @@
BinaryInputBasic, Binding, BooleanState, BooleanStateConfiguration, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement,
CarbonMonoxideConcentrationMeasurement, Channel,
ColorControl, ContentLauncher, Descriptor, DeviceEnergyManagementMode, DeviceEnergyManagement, DiagnosticLogs, DishwasherAlarm, DishwasherMode,
- DoorLock, ElectricalMeasurement, EnergyEvse, EnergyEvseMode, EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement,
+ DoorLock, ElectricalMeasurement, ElectricalEnergyMeasurement, EnergyEvse, EnergyEvseMode, EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement,
FormaldehydeConcentrationMeasurement, GeneralCommissioning, GeneralDiagnostics, GroupKeyManagement, Groups,
HepaFilterMonitoring, IcdManagement, Identify, IlluminanceMeasurement, KeypadInput, LaundryDryerControls,
LaundryWasherControls, LaundryWasherMode, LevelControl, LocalizationConfiguration, LowPower, MediaInput, MediaPlayback, MicrowaveOvenControl,